Skip to content

Instantly share code, notes, and snippets.

@brandonhimpfen
Created February 1, 2026 21:08
Show Gist options
  • Select an option

  • Save brandonhimpfen/c1f9398053063818a5737be743645bbf to your computer and use it in GitHub Desktop.

Select an option

Save brandonhimpfen/c1f9398053063818a5737be743645bbf to your computer and use it in GitHub Desktop.
Go HTTP client example with context cancellation + sensible timeouts, returning response body safely (idiomatic net/http).
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"time"
)
// newHTTPClient returns an HTTP client with sensible defaults.
//
// Notes:
// - http.Client.Timeout covers the full request (connect + redirects + reading body).
// - Transport timeouts provide more granular control.
func newHTTPClient() *http.Client {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 5 * time.Second, // TCP connect timeout
KeepAlive: 30 * time.Second, // keepalive
}).DialContext,
TLSHandshakeTimeout: 5 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
IdleConnTimeout: 90 * time.Second,
MaxIdleConns: 100,
}
return &http.Client{
Transport: transport,
Timeout: 15 * time.Second, // hard upper bound for whole request
}
}
// get performs a GET request with a context.
// It returns the response body as bytes and fails if status is not 2xx.
func get(ctx context.Context, client *http.Client, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("new request: %w", err)
}
req.Header.Set("User-Agent", "go-http-client-timeout-context/1.0")
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, fmt.Errorf("do request: %w", err)
}
defer resp.Body.Close()
// Optional but recommended: limit response body size
const maxBodySize = 2 << 20 // 2MB
body, err := io.ReadAll(io.LimitReader(resp.Body, maxBodySize))
if err != nil {
return nil, fmt.Errorf("read body: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("http status %d: %s", resp.StatusCode, string(body))
}
return body, nil
}
func main() {
client := newHTTPClient()
// Context timeout (cancels the request if it exceeds this time)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
url := "https://httpbin.org/json"
body, err := get(ctx, client, url)
if err != nil {
log.Fatalf("request failed: %v", err)
}
fmt.Println(string(body))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment