1
votes

I wrote a timeout middleware. In this timeout middleware i created a goroutine for processing next handlers but after two seconds when timeout happens the response returns with 408 timeout status code but the goroutine runs in background and executes next handlers that are in handlers chain. So how to write a correct timeout middleware?

Timeout Middleware:

type TimeoutHandler struct {
    Next http.Handler
}

func (handler *TimeoutHandler) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) {
     /*if nil 'Next' handler.*/
     if handler.Next == nil {
         /*delegate to default serve mux.*/
         handler.Next = http.DefaultServeMux
     }

     /*context with timeout.*/
     requestContext := request.Context()
     timeoutContext, cancelFunction := context.WithTimeout(requestContext, 2*time.Second)
     defer cancelFunction()

     /*request with new context.*/
     request = request.WithContext(timeoutContext)
     chanDone := make(chan bool)

     go func() {
          /*delegate request to `Next` handler*/
          handler.Next.ServeHTTP(responseWriter, request)
          chanDone <- true
     }()

     select {
     case <-timeoutContext.Done():
          /*status 408 request timeout.*/
          responseWriter.WriteHeader(http.StatusRequestTimeout)
     case <-chanDone:
          return
    }
}

These are the problems that i have:

  1. After timeout, next handler runs no matter what.
  2. If i use more handlers after timeout middleware, there is warning that says "http: multiple response.WriteHeader calls."
1
How about posting the middleware so we can take a look?sberry
If handler.Next doesn't stop running, it's because handler.Next doesn't check for a timeout. Every handler in the chain needs to be cancellable. Don't start additional http handlers in goroutines, because there's no logical way to handle an http response concurrently or asynchronously.JimB
How to write custom timeout handler?samadadi
I'm not sure what you're asking. You're adding the context to the request already, and you see how to check the Done() channel, you just need to have each handler honor the context.JimB
I want a custom timeout handler based on request url.samadadi

1 Answers

3
votes

I found better solution in a forum and i hope this solution helps other go newcomers.

/**
*
*/
type TimeoutHandler struct {
    Next http.Handler
}

/**
 *
 */
func (h *TimeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    /*nil 'Next' handler.*/
    if h.Next == nil {
        /*delegate to default serve mux.*/
        h.Next = http.DefaultServeMux
    }

    /*delegate request to new timeout handler.*/
    timeoutHandler := http.TimeoutHandler(h.Next, 4 * time.Second, `Request Timeout.`)
    timeoutHandler.ServeHTTP(w, r)
}