Timeout is a Gin middleware that wraps a handler and aborts its execution if a specified timeout is reached. This is useful for preventing slow handlers from blocking your server.
- Abort request processing if it exceeds a configurable timeout.
- Customizable timeout response.
- Can be used as route or global middleware.
- Compatible with other Gin middleware.
go get github.com/gin-contrib/timeout
A minimal example that times out a slow handler:
// _example/example01/main.go
package main
import (
"log"
"net/http"
"time"
"github.com/gin-contrib/timeout"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.New()
// Apply timeout middleware to a single route
r.GET("/", timeout.New(
timeout.WithTimeout(100*time.Microsecond),
), func(c *gin.Context) {
time.Sleep(200 * time.Microsecond)
c.String(http.StatusOK, "")
})
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
You can define a custom response when a timeout occurs:
// Custom timeout response for a single route
func testResponse(c *gin.Context) {
c.String(http.StatusRequestTimeout, "custom timeout response")
}
r.GET("/", timeout.New(
timeout.WithTimeout(100*time.Microsecond),
timeout.WithResponse(testResponse),
), func(c *gin.Context) {
time.Sleep(200 * time.Microsecond)
c.String(http.StatusOK, "")
})
Apply the timeout middleware to all routes:
func testResponse(c *gin.Context) {
c.String(http.StatusRequestTimeout, "timeout")
}
func timeoutMiddleware() gin.HandlerFunc {
return timeout.New(
timeout.WithTimeout(500*time.Millisecond),
timeout.WithResponse(testResponse),
)
}
func main() {
r := gin.New()
r.Use(timeoutMiddleware())
r.GET("/slow", func(c *gin.Context) {
time.Sleep(800 * time.Millisecond)
c.Status(http.StatusOK)
})
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
You can combine the timeout middleware with custom logging for timeout events:
import (
"log/slog"
"net/http"
"time"
"github.com/gin-contrib/timeout"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(timeout.New(
timeout.WithTimeout(100*time.Microsecond),
), func(c *gin.Context) {
c.Next()
if c.Writer.Status() == http.StatusRequestTimeout {
slog.Error("request timeout")
}
})
r.GET("/long", func(c *gin.Context) {
time.Sleep(10 * time.Second)
c.String(http.StatusOK, "long time ago")
})
s := &http.Server{
Addr: ":8000",
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
ReadHeaderTimeout: time.Second * 5,
}
if err := s.ListenAndServe(); err != nil {
slog.Error("ListenAndServe failed", "err", err)
}
}
You can stack the timeout middleware with other middleware, such as authentication or logging:
func testResponse(c *gin.Context) {
c.String(http.StatusRequestTimeout, "timeout")
}
// Custom timeout middleware
func timeoutMiddleware() gin.HandlerFunc {
return timeout.New(
timeout.WithTimeout(500*time.Millisecond),
timeout.WithResponse(testResponse),
)
}
// Example auth middleware
func authMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
debug := c.Query("debug")
if debug != "true" {
c.Next()
return
}
c.AbortWithStatus(401)
}
}
func main() {
r := gin.New()
r.Use(gin.Logger())
r.Use(timeoutMiddleware())
r.Use(authMiddleware())
r.Use(gin.Recovery())
r.GET("/", func(c *gin.Context) {
time.Sleep(1 * time.Second)
c.String(http.StatusOK, "Hello world!")
})
if err := r.Run(":8080"); err != nil {
log.Fatal(err)
}
}
Suppose your handler always takes longer than the timeout:
// _example/example04/main.go (handler always times out)
r.GET("/", func(c *gin.Context) {
time.Sleep(1 * time.Second)
c.String(http.StatusOK, "Hello world!")
})
With a 500ms timeout, any request will return HTTP 408:
curl -i http://localhost:8080/
Expected response:
HTTP/1.1 408 Request Timeout
Content-Type: text/plain; charset=utf-8
timeout
The _example
directory contains additional usage scenarios:
- example01: Minimal usage – how to apply a timeout to a single route.
- example02: Using timeout as a global middleware with a custom timeout response.
- example03: Demonstrates logging timeout events and includes a
concurrent_requests.sh
script for load testing. - example04: Shows integration with custom authentication middleware and includes its own README for detailed explanation.
Explore these examples for practical patterns and advanced integration tips.