Network Services and Clients
Building and calling HTTP services with net/http alone: routing, middleware, timeouts, shutdown, client reuse and retries, where most production incidents are born.
part ofGo (Golang)overview, primer and where to startread it →on this pageshowhide
explore
- Handlers and Routing22 questions
- Pattern Matching and Precedence5 questions
- Request Context Lifetime5 questions
- ServeHTTP and Registration4 questions
- Writing Responses and Headers4 questions
- Serving Files and Assets4 questions
- Middleware and Request Flow30 questions
- Chaining and Ordering4 questions
- Rate Limits and Shedding4 questions
- Credential Checks4 questions
- Wrapping ResponseWriter5 questions
- Context Values and IDs4 questions
- Preflight and Origin Headers4 questions
- Panic Recovery in Handlers5 questions
- Server Lifecycle and Limits20 questions
- The Four Timeout Fields5 questions
- Draining and Graceful Shutdown6 questions
- Keep-Alive and Connection Caps5 questions
- Health and Readiness Handlers4 questions
- Outbound Requests27 questions
- Client Failure Modes4 questions
- Deadlines Per Call5 questions
- Bodies and Connection Reuse4 questions
- Following Redirects5 questions
- Retries, Backoff and Idempotency5 questions
- Transport Pooling4 questions
- Streaming and Proxying14 questions
- Flushing and Chunked Responses4 questions
- Forwarding to Upstreams5 questions
- Hijacking and Upgrades5 questions
- Sockets and Dialers19 questions
- Datagrams and Unix Sockets5 questions
- Listeners and Connections5 questions
- DNS Resolution Behavior5 questions
- Deadlines and Half-Close4 questions
- Request Inputs20 questions
- Headers and Client Address4 questions
- Parsing and Escaping URLs4 questions
- Multipart Forms and Uploads4 questions
- Queries, Forms and Bodies4 questions
- Cookie Attributes and Flags4 questions
questions
152 · 7 sectionsWhy is http.FileServer usually wrapped in http.StripPrefix when mounted under /static/?
basics
~20 shttp.FileServer resolves the entire request path against its root directory, so a request for /static/app.css looks for static/app.css inside that root. http.StripPrefix removes the /static/ mount prefix first, so the file server looks up app.css.
In a Go HTTP handler, what does r.Context() return and what cancels it?
basics
~20 sr.Context() returns a context.Context scoped to that one HTTP request. Go's net/http server cancels it when the client's connection goes away and, unconditionally, when your handler returns from ServeHTTP. Pass it to every downstream call.
In a Go http.Handler, why must w.Header().Set calls come before WriteHeader or Write?
basics
~20 sThe header map is only copied onto the wire when the status line is sent. w.WriteHeader sends it, and the first w.Write sends it implicitly with status 200, so any Header().Set after that point is silently ignored.
In Go's http.ServeMux, what does the pattern "GET /items/{id}" match, and how does the handler read id?
basics
~10 sIt matches GET and HEAD requests whose path is exactly two segments: the literal items followed by any single segment. The handler reads that segment with r.PathValue("id"), which returns it as a string.
In Go's net/http, what is the http.Handler interface, and what does http.HandlerFunc do?
basics
~10 shttp.Handler is a one-method interface: ServeHTTP(http.ResponseWriter, *http.Request). http.HandlerFunc is a function type that has its own ServeHTTP method, so converting a plain function to it turns that function into a Handler.
In net/http, how do you write a func(http.Handler) http.Handler wrapper, and where does next.ServeHTTP sit?
basics
~20 sA net/http middleware takes the next http.Handler and returns a new one. Inside its ServeHTTP it runs setup code, calls next.ServeHTTP(w, r) to invoke the wrapped handler, then runs teardown code after that call returns.
In a Go net/http middleware, how do you recognise a CORS preflight request and answer it?
basics
~20 sA CORS preflight is an OPTIONS request that also carries an Access-Control-Request-Method header. A Go middleware tests both, writes the Access-Control-Allow-Origin, -Methods and -Headers response headers, sends 204 via w.WriteHeader(http.StatusNoContent), and returns without calling next.ServeHTTP.
What happens when an http.Handler in a Go net/http server panics?
basics
~20 sGo's net/http server recovers a panic raised by a handler, so the process keeps serving other requests. It prints the panic value and a stack trace to Server.ErrorLog and closes that one connection, so the client gets no HTTP status at all.
What does (*http.Request).WithContext return, and why must middleware pass on that copy?
basics
~20 sWithContext returns a shallow copy of the request carrying the new context and leaves the original untouched. Middleware must reassign r = r.WithContext(ctx) and hand that copy to next.ServeHTTP, or the inner handler still sees the old context.
Why compare an API shared secret with crypto/subtle.ConstantTimeCompare rather than Go's == operator?
basics
~20 sComparing strings with == stops at the first differing byte, so how long it takes depends on how much of the secret the caller guessed. subtle.ConstantTimeCompare examines every byte and returns 1 for equal or 0 otherwise, in time that does not depend on the contents.
Does Go's http.Server reuse a client connection for later requests by default, and how do you stop it?
basics
~20 sGo's http.Server enables HTTP keep-alives by default, so one accepted TCP connection serves many requests. Server.SetKeepAlivesEnabled(false) turns them off for the whole server; setting a Connection: close response header in a handler closes only that one connection.
What does a dependency-free liveness http.HandlerFunc in a Go service do, and what must it not touch?
basics
~20 sA liveness handler reports only that the process is alive and still serving HTTP. In Go it is a tiny http.HandlerFunc that writes 200 OK and touches nothing else: no locks, no database, no outbound calls.
In Go's net/http, how does Server.Shutdown differ from Server.Close?
basics
~10 sServer.Shutdown stops accepting new connections, closes idle ones, and waits for in-flight requests to finish; it takes a context that bounds the wait. Server.Close closes every connection immediately, cutting requests off mid-response.
What does an http.Server bound when ReadTimeout, WriteTimeout and IdleTimeout are all left at zero?
basics
~10 sNothing. Zero means no timeout on every http.Server timeout field, so a connection may spend forever sending a request, receiving a response, or sitting idle. The convenience helper http.ListenAndServe builds exactly such a server.
Why does ListenAndServe return http.ErrServerClosed as soon as Server.Shutdown begins?
basics
~20 sThe first thing Server.Shutdown does is close the listeners, and that is what makes ListenAndServe return. It reports that accepting has stopped, not that the drain is done — the drain ends later, when Shutdown itself returns.
Why must you call resp.Body.Close() on an http.Response, and where does that defer belong?
basics
~20 sAn http.Response body is an open stream on a live connection; closing it releases that connection and its socket. Put defer resp.Body.Close() straight after the error check, because a failed request returns a nil response.
What does Go's http.Client.Timeout field cover, and does it include reading the response body?
basics
~20 shttp.Client.Timeout bounds one whole call: DNS, dial, TLS handshake, sending the request, any redirects, and reading the response body. The clock keeps running after Do returns, so a slow body read fails too. Zero means no timeout.
Why does http.Client.Do return a nil error when the server replies 500?
basics
~10 shttp.Client.Do reports only transport-level failures - DNS, dial, TLS, a deadline, a broken connection. A 500 is a completed HTTP exchange, so err is nil; you must check resp.StatusCode yourself and close the body.
Does Go's http.Get follow a 302 redirect automatically, and how do you find the final URL?
basics
~10 sGo's http.Get follows redirects automatically: http.DefaultClient stops only after ten consecutive hops, so you receive the final response, not the 302. The field resp.Request.URL holds the URL of the last request actually sent.
Why does retrying the same *http.Request send an empty body on the second attempt?
basics
~20 sA request body is a one-shot io.ReadCloser. The first Do drains it and the Transport closes it, so a second Do on the same *http.Request finds nothing left to read. Build a fresh request for every attempt.
Why does a net/http handler that writes a log line every second deliver nothing to the client until it returns?
basics
~20 sGo's HTTP server buffers the response body, so small writes sit in memory until the handler returns. Flush after each line - with http.NewResponseController(w).Flush() or an http.Flusher type assertion - to push the bytes out.
What does httputil.NewSingleHostReverseProxy do, and how do you serve it in a Go HTTP server?
basics
~10 shttputil.NewSingleHostReverseProxy returns a *httputil.ReverseProxy whose Director points every request at one upstream URL's scheme, host and base path. ReverseProxy implements http.Handler, so you mount it on a ServeMux like any other handler.
What does http.Hijacker's Hijack return, and what does a handler take on by calling it?
basics
~10 sHijack returns the underlying net.Conn plus a *bufio.ReadWriter already buffered on it. From then on net/http writes nothing more on that connection, never reuses it and never closes it. The handler owns the socket.
When does Go's net/http server frame a response as chunked instead of sending a Content-Length header?
basics
~20 sWhenever the body length is still unknown when the headers go out. A handler that returns with a short body buffered gets Content-Length; one that flushes first, or overruns the buffer, gets chunked framing on HTTP/1.1.
How do you write the 101 Switching Protocols response yourself after calling Hijack?
basics
~20 sWrite the raw HTTP/1.1 status line and headers through the *bufio.ReadWriter Hijack returned, end the head with a blank CRLF line, then call Flush. The ResponseWriter cannot do it for you: after a hijack it returns http.ErrHijacked.
Why must a Go read loop call net.Conn.SetReadDeadline again before every read?
basics
~20 sA net.Conn deadline is an absolute wall-clock instant, not a per-call timeout. Once that instant passes, every later read fails immediately instead of waiting, so a loop must set a fresh time.Now().Add(...) before each read.
In Go, what does net.ListenPacket give you, and how does PacketConn.ReadFrom differ from reading a stream?
basics
~20 snet.ListenPacket binds a datagram socket and returns a net.PacketConn. There is nothing to accept: one ReadFrom call returns exactly one whole datagram plus the sender's address, while a stream read returns whatever bytes happen to have arrived.
Why can one Read on a net.Conn return fewer bytes than the peer sent in one Write?
basics
~20 sA net.Conn is a byte stream, not a message queue. Read returns whatever has arrived, so writes can split across reads or merge into one. The application supplies its own framing, such as newline-delimited lines or a length prefix.
When does Go's net package use its pure-Go DNS resolver instead of the cgo one?
basics
~20 sGo has two name resolvers: its own DNS client and one that calls the C library. The Go one is the default on Unix; Go falls back to C when the platform forbids direct queries or the host's resolver configuration needs features Go lacks.
In Go, what does net.Listen return, and how do you serve clients from it?
basics
~20 snet.Listen returns a net.Listener bound to the given address. You call its Accept method in a loop: each call blocks until a client connects and returns a net.Conn you read from, write to and close, usually in its own goroutine.
In Go's net/http, how do you set a cookie on a response and read it back on the next request?
basics
~20 sBuild an http.Cookie value and pass it to http.SetCookie(w, c) before writing anything to the response; it adds a Set-Cookie header. On a later request, call r.Cookie("name"), which returns the cookie or the error http.ErrNoCookie.
In a Go HTTP handler, what does r.URL.Query().Get("page") return when the URL carries no page parameter?
basics
~10 sIt returns the empty string and never an error, so a missing parameter and a present-but-empty one look identical. Use url.Values.Has, or index the url.Values map directly, when that difference matters.
Why does r.Header.Get("x-request-id") find a header that r.Header["x-request-id"] misses?
basics
~20 sGo's http.Header is a plain map whose keys are stored in canonical form, such as X-Request-Id. Header.Get canonicalizes the name you pass before the lookup; indexing the map does not, so a lowercase key finds nothing.
In Go, why build a query string with url.Values.Encode instead of concatenating the parameters yourself?
basics
~10 surl.Values.Encode escapes every key and value with url.QueryEscape, so an ampersand or equals sign inside a value cannot invent a new parameter. It also carries repeated keys and emits the pairs sorted by key.
In a Go net/http handler, what does r.FormFile("avatar") return and what must the handler do with it?
basics
~10 sr.FormFile returns a multipart.File to read the upload from, a *multipart.FileHeader carrying the client-supplied filename, size and part headers, and an error. The handler must close the file, normally with defer file.Close().