Loading KAIROXO page正在加载 KAIROXO 页面Đang tải trang KAIROXO

KAIROXO
Tutorial

Go HTTP Proxy: Reuse Transports and Bound Requests

Configure an explicit Go proxy, separate credentials from logs, reuse a Transport and verify cancellation before increasing traffic.

KAIROXO

Programming code on a screen in a dark room.

Go HTTP proxy configuration is easy to demonstrate with one request and harder to operate inside a long-running collector. A working response does not prove that credentials stay out of logs, that canceled work releases resources, or that later requests reuse the intended connection pool. Treat those as separate acceptance checks before adding workers.

Imagine a service collecting permitted public catalog pages every hour. Its scheduler creates many jobs, but the network settings change only when the access profile changes. The useful design boundary is therefore a reusable client and Transport per profile, with a context for each job. This guide builds that boundary without changing the application's parsing or storage responsibilities.

Establish the access contract

Write down the proxy scheme, host, port, authentication method and permitted destination before writing network code. The scheme describes the connection to the proxy; an HTTPS destination may still be reached through an HTTP proxy using CONNECT. Confirm whether the endpoint accepts that operation. Do not infer supported protocols from a product name or port number alone.

Choose an approved probe URL that returns a small, recognizable response. A controlled endpoint can report the observed exit address; a target-specific test should also contain a known field. Keep those checks separate. The first establishes routing, while the second establishes that the requested representation can actually support your collection task.

Implementation steps

  1. Build one explicit proxy profile

    Load credentials from the deployment's secret mechanism or environment rather than source control. Parse the endpoint with net/url and attach credentials with url.UserPassword, which handles reserved characters when serializing the URL. Never print the resulting authenticated URL. Configuration errors should identify the missing field, not repeat a password or token.

    Explicit http.ProxyURL configuration makes this client's route independent of process-wide HTTP_PROXY and NO_PROXY settings. ProxyFromEnvironment is useful when the operator intentionally owns those settings, but mixing the two policies makes troubleshooting harder. Pick one policy per access profile and include its non-secret identifier in job records so comparisons remain reproducible.

  2. Reuse the client and bound its resources

    Clone the default Transport before changing its options. Reuse the resulting client across jobs that share the same proxy profile; creating a new Transport for every URL discards the pool and repeats connection setup. MaxConnsPerHost limits connection pressure, while idle-connection settings determine what can remain available between requests. These controls do not replace an application-level work queue.

    The following probe keeps the default TLS verification behavior, sets connection and response boundaries, and reads only a bounded body. In a service, create the client during profile initialization and close its idle connections when the profile is retired. Do not call that cleanup after each ordinary request if you intend to reuse connections.

    Codego
    package main import (    "context"    "errors"    "fmt"    "io"    "net/http"    "net/url"    "os"    "time") func proxyClient(address, username, password string) (*http.Client, *http.Transport, error) {    proxyURL, err := url.Parse(address)    if err != nil || proxyURL.Hostname() == "" || (proxyURL.Scheme != "http" && proxyURL.Scheme != "https") {        return nil, nil, errors.New("invalid proxy endpoint")    }    proxyURL.User = url.UserPassword(username, password)    transport := http.DefaultTransport.(*http.Transport).Clone()    transport.Proxy = http.ProxyURL(proxyURL)    transport.MaxConnsPerHost = 4    transport.MaxIdleConnsPerHost = 4    transport.TLSHandshakeTimeout = 8 * time.Second    transport.ResponseHeaderTimeout = 12 * time.Second    return &http.Client{Transport: transport, Timeout: 20 * time.Second}, transport, nil} func main() {    client, transport, err := proxyClient(os.Getenv("PROXY_URL"), os.Getenv("PROXY_USER"), os.Getenv("PROXY_PASSWORD"))    if err != nil { fmt.Println("check proxy configuration"); return }    defer transport.CloseIdleConnections()    ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)    defer cancel()    request, err := http.NewRequestWithContext(ctx, http.MethodGet, os.Getenv("PROBE_URL"), nil)    if err != nil { fmt.Println("check probe URL"); return }    response, err := client.Do(request)    if err != nil { fmt.Println("request failed; classify the error without logging credentials"); return }    defer response.Body.Close()    const maxBody = 1024 * 1024    count, err := io.Copy(io.Discard, io.LimitReader(response.Body, maxBody+1))    if err != nil || count > maxBody { fmt.Println("body incomplete or exceeds probe limit"); return }    fmt.Printf("status=%d bytes=%d\n", response.StatusCode, count)}
  3. Give every job a cancellation boundary

    Use NewRequestWithContext so a canceled job can stop its network work. A client timeout is a useful upper bound, while Transport options constrain particular phases. Select their values from the workload rather than copying the example as a service-level promise. A large download and a tiny exit probe usually need different budgets and response-size limits.

    Always close a response body. Reading an accepted body through completion makes connection reuse possible; stopping early because the body exceeded your limit can sacrifice reuse for that response, which is an intentional protection. Record that outcome as a size-limit failure. An HTTP status alone cannot establish that the entire body was received or parsed correctly.

  4. Verify before increasing concurrency

    First send one request through the profile and verify the observed exit independently. Then request a permitted target and check status, content type and one expected field. Finally run a small bounded set of jobs, cancel one deliberately and observe completion. These checks expose routing, parsing and resource-management problems without confusing them with high-volume behavior.

    Keep the same target set and request context when comparing profiles. If language, cookies, request headers or destination regions differ, a change in latency may reflect different content rather than a better route. Increase load gradually and stop when the destination's limits or your own resource budget are reached. More goroutines do not manufacture additional useful capacity.

    • Configuration error: check the endpoint fields without logging the authenticated URL.
    • Proxy authentication failure: review the account and access profile before retrying.
    • Timeout or cancellation: record the phase and elapsed time separately.
    • Response received: validate the required content before counting a usable result.

Frequently asked questions

Sources and further reading

Related KAIROXO guide

Loading KAIROXO page正在加载 KAIROXO 页面Đang tải trang KAIROXO