forked from wrenn/wrenn
fix: sandbox network responsiveness under port-binding apps
Running port-binding applications (Jupyter, http.server, NextJS) inside
sandboxes caused severe PTY sluggishness and proxy navigation errors.
Root cause: the CP sandbox proxy and Connect RPC pool shared a single
HTTP transport. Heavy proxy traffic (Jupyter WebSocket, REST polling)
interfered with PTY RPC streams via HTTP/2 flow control contention.
Transport isolation (main fix):
- Add dedicated proxy transport on CP (NewProxyTransport) with HTTP/2
disabled, separate from the RPC pool transport
- Add dedicated proxy transport on host agent, replacing
http.DefaultTransport
- Add dedicated envdclient transport with tuned connection pooling
- Replace http.DefaultClient in file streaming RPCs with per-sandbox
envd client
Proxy path rewriting (navigation fix):
- Add ModifyResponse to rewrite Location headers with /proxy/{id}/{port}
prefix, handling both root-relative and absolute-URL redirects
- Strip prefix back out in CP subdomain proxy for correct browser
behavior
- Replace path.Join with string concat in CP Director to preserve
trailing slashes (prevents redirect loops on directory listings)
Proxy resilience:
- Add dial retry with linear backoff (3 attempts) to handle socat
startup delay when ports are first detected
- Cache ReverseProxy instances per sandbox+port+host in sync.Map
- Add EvictProxy callback wired into sandbox Manager.Destroy
Buffer and server hardening:
- Increase PTY and exec stream channel buffers from 16 to 256
- Add ReadHeaderTimeout (10s) and IdleTimeout (620s) to host agent
HTTP server
Network tuning:
- Set TAP device TxQueueLen to 5000 (up from default 1000)
- Add Firecracker tx_rate_limiter (200 MB/s sustained, 100 MB burst)
to prevent guest traffic from saturating the TAP
This commit is contained in:
@ -3,6 +3,7 @@ package lifecycle
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -115,6 +116,34 @@ func (p *HostClientPool) ResolveAddr(addr string) string {
|
||||
return p.ensureScheme(addr)
|
||||
}
|
||||
|
||||
// NewProxyTransport returns a new http.RoundTripper configured for proxying
|
||||
// user traffic to sandbox services. It is intentionally separate from the RPC
|
||||
// transport returned by Transport() so that heavy proxy traffic (Jupyter
|
||||
// WebSocket, REST API polling) cannot interfere with Connect RPC streams (PTY,
|
||||
// exec) via HTTP/2 flow control or connection pool contention.
|
||||
func (p *HostClientPool) NewProxyTransport() http.RoundTripper {
|
||||
t := &http.Transport{
|
||||
ForceAttemptHTTP2: false, // HTTP/1.1 only — avoids HTTP/2 HOL blocking
|
||||
MaxIdleConnsPerHost: 20,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 120 * time.Second,
|
||||
DisableCompression: true,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 30 * time.Second,
|
||||
KeepAlive: 20 * time.Second,
|
||||
}).DialContext,
|
||||
}
|
||||
|
||||
// If the pool uses TLS, the proxy transport must too.
|
||||
if p.httpClient.Transport != nil {
|
||||
if ht, ok := p.httpClient.Transport.(*http.Transport); ok && ht.TLSClientConfig != nil {
|
||||
t.TLSClientConfig = ht.TLSClientConfig.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
// EnsureScheme adds "http://" if the address has no scheme.
|
||||
// Deprecated: use pool.ResolveAddr which respects the pool's TLS setting.
|
||||
func EnsureScheme(addr string) string {
|
||||
|
||||
Reference in New Issue
Block a user