Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions backend.go
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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,
Expand Down
8 changes: 5 additions & 3 deletions xqd_request.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down