×

go · level 3 · unit 6 · lesson 4

  1. meet it
  2. guess first
  3. your turn
  4. fix it
  5. 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.