-
-
Save m00zi/9440a49a8591c5c1eb450c58a760ed8b to your computer and use it in GitHub Desktop.
Gin request timeout middleware and handler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"context" | |
"log" | |
"net/http" | |
"time" | |
"github.com/gin-gonic/gin" | |
) | |
// timeout middleware wraps the request context with a timeout | |
func timeoutMiddleware(timeout time.Duration) func(c *gin.Context) { | |
return func(c *gin.Context) { | |
// wrap the request context with a timeout | |
ctx, cancel := context.WithTimeout(c.Request.Context(), timeout) | |
defer func() { | |
// check if context timeout was reached | |
if ctx.Err() == context.DeadlineExceeded { | |
// write response and abort the request | |
c.Writer.WriteHeader(http.StatusGatewayTimeout) | |
c.Abort() | |
} | |
//cancel to clear resources after finished | |
cancel() | |
}() | |
// replace request with context wrapped request | |
c.Request = c.Request.WithContext(ctx) | |
c.Next() | |
} | |
} | |
func timedHandler(duration time.Duration) func(c *gin.Context) { | |
return func(c *gin.Context) { | |
// get the underlying request context | |
ctx := c.Request.Context() | |
// create the response data type to use as a channel type | |
type responseData struct { | |
status int | |
body map[string]interface{} | |
} | |
// create a done channel to tell the request it's done | |
doneChan := make(chan responseData) | |
// here you put the actual work needed for the request | |
// and then send the doneChan with the status and body | |
// to finish the request by writing the response | |
go func() { | |
time.Sleep(duration) | |
doneChan <- responseData{ | |
status: 200, | |
body: gin.H{"hello": "world"}, | |
} | |
}() | |
// non-blocking select on two channels see if the request | |
// times out or finishes | |
select { | |
// if the context is done it timed out or was cancelled | |
// so don't return anything | |
case <-ctx.Done(): | |
return | |
// if the request finished then finish the request by | |
// writing the response | |
case res := <-doneChan: | |
c.JSON(res.status, res.body) | |
} | |
} | |
} | |
func main() { | |
// create new gin without any middleware | |
engine := gin.New() | |
// add timeout middleware with 2 second duration | |
engine.Use(timeoutMiddleware(time.Second * 2)) | |
// create a handler that will last 1 seconds | |
engine.GET("/short", timedHandler(time.Second)) | |
// create a route that will last 5 seconds | |
engine.GET("/long", timedHandler(time.Second*5)) | |
// run the server | |
log.Fatal(engine.Run(":8080")) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment