71 lines
1.9 KiB
Go
71 lines
1.9 KiB
Go
// Package backend — shared HTTP transport pool.
|
|
//
|
|
// SharedTransport returns a per-host *http.Transport that all backend instances
|
|
// targeting the same base URL share. This enables HTTP/2 multiplexing: multiple
|
|
// concurrent ChatStream calls share a single TLS connection to the same host.
|
|
package backend
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"net"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
transportsMu sync.Mutex
|
|
transports = make(map[string]*http.Transport)
|
|
)
|
|
|
|
// SharedTransport returns a shared *http.Transport for the given host key.
|
|
// The key should be "backend:baseURL" (e.g. "anthropic:https://api.anthropic.com").
|
|
// All callers sharing the same key share the same underlying TCP+TLS connections.
|
|
func SharedTransport(key string) *http.Transport {
|
|
transportsMu.Lock()
|
|
defer transportsMu.Unlock()
|
|
|
|
if t, ok := transports[key]; ok {
|
|
return t
|
|
}
|
|
|
|
t := &http.Transport{
|
|
ForceAttemptHTTP2: true,
|
|
MaxIdleConnsPerHost: 4,
|
|
IdleConnTimeout: 5 * time.Minute,
|
|
TLSHandshakeTimeout: 10 * time.Second,
|
|
// No ResponseHeaderTimeout — streaming responses have no header deadline.
|
|
DialContext: (&net.Dialer{
|
|
Timeout: 10 * time.Second,
|
|
KeepAlive: 30 * time.Second,
|
|
}).DialContext,
|
|
TLSClientConfig: &tls.Config{
|
|
MinVersion: tls.VersionTLS12,
|
|
},
|
|
}
|
|
transports[key] = t
|
|
return t
|
|
}
|
|
|
|
// SharedClient returns an *http.Client backed by the shared transport for key.
|
|
func SharedClient(key string) *http.Client {
|
|
return &http.Client{Transport: SharedTransport(key)}
|
|
}
|
|
|
|
// PoolStats returns the number of active transport entries.
|
|
func PoolStats() int {
|
|
transportsMu.Lock()
|
|
defer transportsMu.Unlock()
|
|
return len(transports)
|
|
}
|
|
|
|
// ClosePool closes all idle connections in all transports and clears the pool.
|
|
func ClosePool() {
|
|
transportsMu.Lock()
|
|
defer transportsMu.Unlock()
|
|
for _, t := range transports {
|
|
t.CloseIdleConnections()
|
|
}
|
|
transports = make(map[string]*http.Transport)
|
|
}
|