go 41 lines · 6 steps

How a request-timeout middleware works in Gin

A Gin middleware runs the handler chain in a goroutine and races it against a deadline, aborting with 504 if time runs out.

Explained by highlit
1package middleware
2 
3import (
4 "context"
5 "net/http"
6 "time"
7 
8 "github.com/gin-gonic/gin"
9)
10 
11func Timeout(d time.Duration) gin.HandlerFunc {
12 return func(c *gin.Context) {
13 ctx, cancel := context.WithTimeout(c.Request.Context(), d)
14 defer cancel()
15 
16 c.Request = c.Request.WithContext(ctx)
17 
18 done := make(chan struct{})
19 panicChan := make(chan any, 1)
20 
21 go func() {
22 defer func() {
23 if p := recover(); p != nil {
24 panicChan <- p
25 }
26 }()
27 c.Next()
28 close(done)
29 }()
30 
31 select {
32 case p := <-panicChan:
33 panic(p)
34 case <-done:
35 case <-ctx.Done():
36 c.AbortWithStatusJSON(http.StatusGatewayTimeout, gin.H{
37 "error": "request timed out",
38 })
39 }
40 }
41}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Attaching a context deadline to the request lets downstream handlers observe cancellation via the standard context API.
  2. 2Running work in a goroutine and selecting over channels turns a blocking handler into a cancellable race against a timeout.
  3. 3Panics in a spawned goroutine must be captured and re-raised on the main path, or they crash the process instead of being handled.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How a request-timeout middleware works in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code