Skip to content

Instantly share code, notes, and snippets.

@nguyentienlong
Forked from montanaflynn/main.go
Created December 1, 2020 17:45
Show Gist options
  • Select an option

  • Save nguyentienlong/8c73b7ff72894c16866fc587e5ee711c to your computer and use it in GitHub Desktop.

Select an option

Save nguyentienlong/8c73b7ff72894c16866fc587e5ee711c to your computer and use it in GitHub Desktop.

Revisions

  1. @montanaflynn montanaflynn revised this gist Apr 7, 2019. 1 changed file with 18 additions and 11 deletions.
    29 changes: 18 additions & 11 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -35,15 +35,8 @@ func timeoutMiddleware(timeout time.Duration) func(c *gin.Context) {
    }
    }

    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 route that will last 5 seconds
    engine.GET("/long", func(c *gin.Context) {
    func timedHandler(duration time.Duration) func(c *gin.Context) {
    return func(c *gin.Context) {

    // get the underlying request context
    ctx := c.Request.Context()
    @@ -61,7 +54,7 @@ func main() {
    // and then send the doneChan with the status and body
    // to finish the request by writing the response
    go func() {
    time.Sleep(time.Second * 5)
    time.Sleep(duration)
    doneChan <- responseData{
    status: 200,
    body: gin.H{"hello": "world"},
    @@ -82,7 +75,21 @@ func main() {
    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"))
  2. @montanaflynn montanaflynn created this gist Apr 7, 2019.
    89 changes: 89 additions & 0 deletions main.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,89 @@
    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 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 route that will last 5 seconds
    engine.GET("/long", 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(time.Second * 5)
    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)
    }
    })

    // run the server
    log.Fatal(engine.Run(":8080"))
    }