8.11 net/http Internals — Specification¶
A one-page reference for the contracts that govern
net/http. The sections below are the rules; the longer files in this leaf are the explanations.
1. Handler interface¶
Rules:
ServeHTTPruns in a goroutine owned by the server (one per conn).- The server recovers panics from
ServeHTTP. A panic ofhttp.ErrAbortHandleris silent; any other value is logged with a stack trace. ServeHTTPshould not retainrorwpast return.- The server may have already started writing the response when
ServeHTTPreturns; do not call methods onwafter return.
2. ResponseWriter interface¶
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
Rules:
Header()returns a map valid for setting headers until the firstWriteorWriteHeader. After commit, mutations are silently ignored except for trailers.WriteHeadermay be called at most once. Subsequent calls are logged ("superfluous WriteHeader") and ignored.- The first
WritecallsWriteHeader(http.StatusOK)if not yet called. Writemay auto-setContent-Typefrom the first 512 bytes viaDetectContentTypeif not already set.Writereturns an error if the conn is broken; the handler should stop writing but continue normally otherwise.- Status codes 1xx (except 101) are interim and may be written multiple times via
Write1xx/WriteHeader(Go 1.21+). - After hijack,
Write/WriteHeader/Headerpanic or no-op; do not call them.
3. Request semantics¶
| Field | Lifecycle |
|---|---|
Method, URL, Proto* | Set by server, immutable for handler |
Header | Read-only (changing has no effect) |
Body | io.ReadCloser, never nil; closed by server on return |
ContentLength | -1 = chunked; 0 = empty; N = framed |
Form, PostForm | Populated by ParseForm/ParseMultipartForm |
MultipartForm | Populated by ParseMultipartForm |
RemoteAddr | "ip:port", set by server |
RequestURI | Raw URI from request line; do not mutate |
TLS | Non-nil if conn is TLS |
ctx | Canceled when client disconnects, handler returns, or server shuts down |
Rules:
- The handler should not modify
r. To pass a modified copy, user.Clone(ctx)orr.WithContext(ctx). - Reading
r.Bodypast handler return returns an error; reads inside the handler may block on the network. r.ParseFormmay be called multiple times; subsequent calls are no-ops.r.ParseMultipartForm(maxMem)reads at mostmaxMeminto memory; the rest spills to a temp file inos.TempDir.
4. Server configuration¶
type Server struct {
Addr string
Handler Handler
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
ErrorLog *log.Logger
DisableGeneralOptionsHandler bool
}
Defaults:
| Field | Default |
|---|---|
ReadTimeout | 0 (no limit) |
ReadHeaderTimeout | 0 (no limit) |
WriteTimeout | 0 (no limit) |
IdleTimeout | falls back to ReadTimeout |
MaxHeaderBytes | DefaultMaxHeaderBytes = 1 << 20 (1 MiB) |
TLSConfig | nil |
BaseContext | context.Background |
Lifecycle methods:
| Method | Behavior |
|---|---|
ListenAndServe | Listen on Addr, call Serve |
ListenAndServeTLS | Same with TLS |
Serve(ln) | Accept loop on existing listener |
Shutdown(ctx) | Stop new accepts, close idle conns, wait for active handlers up to ctx deadline |
Close | Hard close: kills in-flight conns immediately |
RegisterOnShutdown(fn) | Adds a callback run at start of Shutdown |
ListenAndServe returns http.ErrServerClosed after Shutdown — treat as success.
5. ConnState transitions¶
| State | Triggered by |
|---|---|
StateNew | conn accepted, before any bytes |
StateActive | first byte of request received |
StateIdle | handler returned, conn in keep-alive |
StateHijacked | Hijack() called |
StateClosed | conn closed for any reason |
ConnState callback runs in the conn goroutine; do not block.
6. Client and Transport¶
Client.Timeout: total wall-clock budget covering dial, TLS, request write, response headers, response body. Zero = no timeout.
Transport pool keys: (scheme, host:port, proxyURL, isH2). Idle conns recycled per LRU.
| Field | Default | Effect |
|---|---|---|
MaxIdleConns | 100 | Total idle conns across all keys |
MaxIdleConnsPerHost | 2 | Idle conns per key |
MaxConnsPerHost | 0 (∞) | Total conns per key |
IdleConnTimeout | 90s | Max time idle in pool |
TLSHandshakeTimeout | 10s | Per-handshake cap |
ResponseHeaderTimeout | 0 | Time from request-write to response-headers |
ExpectContinueTimeout | 1s | Wait for 100 Continue |
DisableKeepAlives | false | Force one-shot conns |
DisableCompression | false | Disable transparent gzip |
ForceAttemptHTTP2 | true (default) | Try HTTP/2 over TLS |
RoundTripper contract:
- Do not modify the input
Request. - On nil error, return non-nil
ResponsewhoseBodyreads the response. Caller closesBody. - On non-nil error, return nil
Response, or a non-nilResponsewith closed-or-nilBody. - Honor
req.Context()for cancellation.
7. Body framing¶
Server sends one of:
Content-Length: N— exactly N body bytes follow.Transfer-Encoding: chunked— chunks until terminating zero-length chunk.Connection: close— body until conn closes.
Server picks based on:
- Handler set
Content-Lengthheader → use that (subject to truncation rules). - Handler
Writereturned for the entire response before flush → compute and setContent-Length. - Handler streams (multiple flushes) → chunked.
- HTTP/1.0 →
Connection: close.
8. Optional ResponseWriter interfaces¶
| Interface | Method | Purpose |
|---|---|---|
Flusher | Flush() | Push buffered output to client |
Hijacker | Hijack() | Take ownership of conn (HTTP/1 only) |
Pusher | Push(target, opts) | HTTP/2 server push (deprecated) |
CloseNotifier | CloseNotify() | Deprecated. Use r.Context().Done() |
http.NewResponseController(w) is the modern, wrapper-safe way to call these. Wrappers should implement Unwrap() ResponseWriter so the controller can find the underlying writer.
9. MaxBytesReader¶
- Subsequent reads return
*MaxBytesErroroncemaxis exceeded. - The underlying conn is marked for close on response complete.
10. ServeMux (Go 1.22+) pattern syntax¶
[METHOD] [HOST]/[PATH]
| Element | Form | Effect |
|---|---|---|
| METHOD | GET, POST, * | Restrict by method (omitted = any) |
| HOST | api.example.com/ | Restrict by Host: header |
| PATH segment | literal | Exact match |
| PATH segment | {name} | Captures one segment |
| PATH suffix | {name...} | Captures rest of path |
| PATH suffix | / | Subtree (matches itself + descendants) |
| PATH suffix | {$} | Exact match (no subtree) |
Conflicting patterns at registration → panic.
11. Hop-by-hop headers (RFC 7230)¶
Stripped by ReverseProxy automatically:
ConnectionKeep-AliveProxy-AuthenticateProxy-AuthorizationTeTrailersTransfer-EncodingUpgrade
Plus any header name listed in Connection:.
12. Errors and sentinels¶
| Sentinel | Meaning |
|---|---|
http.ErrServerClosed | Server.Serve exited because Shutdown/Close was called |
http.ErrAbortHandler | Panic value to terminate handler silently |
http.ErrBodyNotAllowed | Write on a 1xx/204/304 response |
http.ErrHijacked | Method called on a hijacked conn |
http.ErrContentLength | More bytes written than declared Content-Length |
http.ErrUseLastResponse | CheckRedirect returns this to stop following redirects |
*http.MaxBytesError | Body exceeded MaxBytesReader cap |
*url.Error | Wraps client-side errors with Op + URL |
13. Request.Context cancellation triggers¶
The context is canceled when any of:
- The client closes the conn (TCP RST/FIN).
- The handler returns.
- The server is
ShutdownorClosed. - A
BaseContextancestor is canceled.
Handlers that propagate the context to downstream calls (DB, HTTP) get cancellation for free.
14. Cross-references¶
- junior.md, middle.md, senior.md, professional.md — narrative coverage of the rules above.
../10-net/specification.md— the underlyingConnandListenerrules.