From c7d9c72f2937c3d8594eaf58694d7dd8bc55b6d9 Mon Sep 17 00:00:00 2001 From: Laurence Date: Fri, 13 Mar 2026 15:28:04 +0000 Subject: [PATCH] Add HTTP client reuse and buffer pooling for performance - Add reusable HTTP client with connection pooling for API requests - Add sync.Pool for 32KB buffers used in connection piping - Clear buffers before returning to pool to prevent data leakage - Reduces GC pressure and improves throughput under load --- proxy/proxy.go | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/proxy/proxy.go b/proxy/proxy.go index f29878e..03579d3 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -69,6 +69,12 @@ type SNIProxy struct { // Trusted upstream proxies that can send PROXY protocol trustedUpstreams map[string]struct{} + + // Reusable HTTP client for API requests + httpClient *http.Client + + // Buffer pool for connection piping + bufferPool *sync.Pool } type activeTunnel struct { @@ -374,6 +380,20 @@ func NewSNIProxy(port int, remoteConfigURL, publicKey, localProxyAddr string, lo localOverrides: overridesMap, activeTunnels: make(map[string]*activeTunnel), trustedUpstreams: trustedMap, + httpClient: &http.Client{ + Timeout: 5 * time.Second, + Transport: &http.Transport{ + MaxIdleConns: 100, + MaxIdleConnsPerHost: 10, + IdleConnTimeout: 90 * time.Second, + }, + }, + bufferPool: &sync.Pool{ + New: func() interface{} { + buf := make([]byte, 32*1024) + return &buf + }, + }, } return proxy, nil @@ -681,9 +701,8 @@ func (p *SNIProxy) getRoute(hostname, clientAddr string) (*RouteRecord, error) { } req.Header.Set("Content-Type", "application/json") - // Make HTTP request - client := &http.Client{Timeout: 5 * time.Second} - resp, err := client.Do(req) + // Make HTTP request using reusable client + resp, err := p.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("API request failed: %w", err) } @@ -773,9 +792,15 @@ func (p *SNIProxy) pipe(clientConn, targetConn net.Conn, clientReader io.Reader) defer wg.Done() defer closeConns() - // Use a large buffer for better performance - buf := make([]byte, 32*1024) - _, err := io.CopyBuffer(targetConn, clientReader, buf) + // Get buffer from pool and return when done + bufPtr := p.bufferPool.Get().(*[]byte) + defer func() { + // Clear buffer before returning to pool to prevent data leakage + clear(*bufPtr) + p.bufferPool.Put(bufPtr) + }() + + _, err := io.CopyBuffer(targetConn, clientReader, *bufPtr) if err != nil && err != io.EOF { logger.Debug("Copy client->target error: %v", err) } @@ -786,9 +811,15 @@ func (p *SNIProxy) pipe(clientConn, targetConn net.Conn, clientReader io.Reader) defer wg.Done() defer closeConns() - // Use a large buffer for better performance - buf := make([]byte, 32*1024) - _, err := io.CopyBuffer(clientConn, targetConn, buf) + // Get buffer from pool and return when done + bufPtr := p.bufferPool.Get().(*[]byte) + defer func() { + // Clear buffer before returning to pool to prevent data leakage + clear(*bufPtr) + p.bufferPool.Put(bufPtr) + }() + + _, err := io.CopyBuffer(clientConn, targetConn, *bufPtr) if err != nil && err != io.EOF { logger.Debug("Copy target->client error: %v", err) }