2016-06-15 19:07:33 +02:00
package middlewares
import (
"bufio"
"bytes"
2017-02-02 17:09:47 +01:00
"io/ioutil"
2016-06-15 19:07:33 +02:00
"net"
"net/http"
2016-12-30 09:21:13 +01:00
"github.com/containous/traefik/log"
"github.com/vulcand/oxy/utils"
2016-06-15 19:07:33 +02:00
)
2016-08-03 14:50:52 +02:00
var (
2017-05-13 19:02:06 +02:00
_ Stateful = & ResponseRecorder { }
2016-08-03 14:50:52 +02:00
)
2016-06-15 19:07:33 +02:00
// Retry is a middleware that retries requests
type Retry struct {
attempts int
next http . Handler
}
// NewRetry returns a new Retry instance
func NewRetry ( attempts int , next http . Handler ) * Retry {
return & Retry {
attempts : attempts ,
next : next ,
}
}
func ( retry * Retry ) ServeHTTP ( rw http . ResponseWriter , r * http . Request ) {
2017-02-02 17:09:47 +01:00
// if we might make multiple attempts, swap the body for an ioutil.NopCloser
// cf https://github.com/containous/traefik/issues/1008
if retry . attempts > 1 {
body := r . Body
defer body . Close ( )
r . Body = ioutil . NopCloser ( body )
}
2016-06-15 19:07:33 +02:00
attempts := 1
for {
recorder := NewRecorder ( )
recorder . responseWriter = rw
retry . next . ServeHTTP ( recorder , r )
if ! isNetworkError ( recorder . Code ) || attempts >= retry . attempts {
2016-07-04 19:32:19 +02:00
utils . CopyHeaders ( rw . Header ( ) , recorder . Header ( ) )
2016-06-15 19:07:33 +02:00
rw . WriteHeader ( recorder . Code )
rw . Write ( recorder . Body . Bytes ( ) )
break
}
attempts ++
log . Debugf ( "New attempt %d for request: %v" , attempts , r . URL )
}
}
func isNetworkError ( status int ) bool {
return status == http . StatusBadGateway || status == http . StatusGatewayTimeout
}
// ResponseRecorder is an implementation of http.ResponseWriter that
// records its mutations for later inspection in tests.
type ResponseRecorder struct {
Code int // the HTTP response code from WriteHeader
HeaderMap http . Header // the HTTP response headers
Body * bytes . Buffer // if non-nil, the bytes.Buffer to append written data to
responseWriter http . ResponseWriter
2016-08-03 14:50:52 +02:00
err error
2016-06-15 19:07:33 +02:00
}
// NewRecorder returns an initialized ResponseRecorder.
func NewRecorder ( ) * ResponseRecorder {
return & ResponseRecorder {
HeaderMap : make ( http . Header ) ,
Body : new ( bytes . Buffer ) ,
Code : 200 ,
}
}
// Header returns the response headers.
func ( rw * ResponseRecorder ) Header ( ) http . Header {
m := rw . HeaderMap
if m == nil {
m = make ( http . Header )
rw . HeaderMap = m
}
return m
}
// Write always succeeds and writes to rw.Body, if not nil.
func ( rw * ResponseRecorder ) Write ( buf [ ] byte ) ( int , error ) {
2016-08-03 14:50:52 +02:00
if rw . err != nil {
return 0 , rw . err
2016-06-15 19:07:33 +02:00
}
2016-08-03 14:50:52 +02:00
return rw . Body . Write ( buf )
2016-06-15 19:07:33 +02:00
}
// WriteHeader sets rw.Code.
func ( rw * ResponseRecorder ) WriteHeader ( code int ) {
2016-07-04 19:32:19 +02:00
rw . Code = code
2016-06-15 19:07:33 +02:00
}
// Hijack hijacks the connection
func ( rw * ResponseRecorder ) Hijack ( ) ( net . Conn , * bufio . ReadWriter , error ) {
return rw . responseWriter . ( http . Hijacker ) . Hijack ( )
}
2016-08-03 14:50:52 +02:00
// CloseNotify returns a channel that receives at most a
// single value (true) when the client connection has gone
// away.
func ( rw * ResponseRecorder ) CloseNotify ( ) <- chan bool {
return rw . responseWriter . ( http . CloseNotifier ) . CloseNotify ( )
}
// Flush sends any buffered data to the client.
func ( rw * ResponseRecorder ) Flush ( ) {
_ , err := rw . responseWriter . Write ( rw . Body . Bytes ( ) )
if err != nil {
log . Errorf ( "Error writing response in ResponseRecorder: %s" , err )
rw . err = err
}
rw . Body . Reset ( )
flusher , ok := rw . responseWriter . ( http . Flusher )
if ok {
flusher . Flush ( )
}
}