go · level 3 · unit 6 · lesson 4
- meet it
- guess first
- your turn
- fix it
- nailed it
Tap a green word if you get lost.
meet it
Every web request carries a context. Slow handlers should watch it.
Every incoming request carries a : r.Context(). A real server cancels it when the visitor closes the tab or the connection drops. So a handler doing slow work should watch it, and pass it on to anything slow it calls, such as a database query.
When the context ends first, stop and answer with an error status, such as 503 Service Unavailable, using http.Error. Nobody may be listening any more, but the work stops and its resources are freed.
In a test you choose the request context yourself: build a request with httptest.NewRequest, then attach a context with req.WithContext(ctx).
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"time"
)
func report(w http.ResponseWriter, r *http.Request) {
select {
case <-time.After(time.Second):
fmt.Fprintln(w, "report ready")
case <-r.Context().Done():
http.Error(w, "request cancelled", http.StatusServiceUnavailable)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
defer cancel()
req := httptest.NewRequest("GET", "/report", nil).WithContext(ctx)
rec := httptest.NewRecorder()
report(rec, req)
fmt.Println(rec.Code, rec.Body.String())
} http.Error sets the status code and writes the message in one go.
guess first
Quick work, patient visitor. What prints?
Guess before you run it. Wrong guesses are how you learn, and nothing is marked.
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"time"
)
func report(w http.ResponseWriter, r *http.Request) {
select {
case <-time.After(10 * time.Millisecond):
fmt.Fprintln(w, "report ready")
case <-r.Context().Done():
http.Error(w, "request cancelled", http.StatusServiceUnavailable)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
req := httptest.NewRequest("GET", "/report", nil).WithContext(ctx)
rec := httptest.NewRecorder()
report(rec, req)
fmt.Println(rec.Code, rec.Body.String())
}
your turn
Your turn. Give up when the visitor does.
Fill in the method that gets the request's context, then the status name for the error: ServiceUnavailable (503) or GatewayTimeout (504).
output
press run to see what happens
fix it
The cancelled request still says 200. Can you fix it?
output
press run to see what happens
- The handler noticed the cancellation, so why does the status still say everything went well?
- Writing a body without choosing a status always means 200.
http.Errorsets the status and writes the message. - Replace line 14 with
http.Error(w, "request cancelled", http.StatusServiceUnavailable).
nailed it
your handlers stop when the visitor leaves. :D
r.Context()ends when the request is over.- Watch it in slow work and answer with an error status.
- In tests, attach a context with
req.WithContext(ctx).
new words in your collection
- request context
This lesson isn’t saved. A free account remembers every lesson you finish.