github.com/hellofresh/janus@v0.0.0-20230925145208-ce8de8183c67/pkg/plugin/retry/middleware.go (about) 1 package retry 2 3 import ( 4 "errors" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/Knetic/govaluate" 10 "github.com/felixge/httpsnoop" 11 "github.com/rafaeljesus/retry-go" 12 log "github.com/sirupsen/logrus" 13 14 janusErr "github.com/hellofresh/janus/pkg/errors" 15 "github.com/hellofresh/janus/pkg/metrics" 16 ) 17 18 const ( 19 defaultPredicate = "statusCode == 0 || statusCode >= 500" 20 proxySection = "proxy" 21 ) 22 23 // NewRetryMiddleware creates a new retry middleware 24 func NewRetryMiddleware(cfg Config) func(http.Handler) http.Handler { 25 return func(handler http.Handler) http.Handler { 26 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 27 log.WithFields(log.Fields{ 28 "attempts": cfg.Attempts, 29 "backoff": cfg.Backoff, 30 }).Debug("Starting retry middleware") 31 32 if cfg.Predicate == "" { 33 cfg.Predicate = defaultPredicate 34 } 35 36 expression, err := govaluate.NewEvaluableExpression(cfg.Predicate) 37 if err != nil { 38 log.WithError(err).Error("could not create an expression with this predicate") 39 handler.ServeHTTP(w, r) 40 return 41 } 42 43 if err := retry.Do(func() error { 44 m := httpsnoop.CaptureMetrics(handler, w, r) 45 46 params := make(map[string]interface{}, 8) 47 params["statusCode"] = m.Code 48 params["request"] = r 49 50 result, err := expression.Evaluate(params) 51 if err != nil { 52 return errors.New("cannot evaluate the expression") 53 } 54 55 if result.(bool) { 56 return fmt.Errorf("%s %s request failed", r.Method, r.URL) 57 } 58 59 return nil 60 }, cfg.Attempts, time.Duration(cfg.Backoff)); err != nil { 61 statsClient := metrics.WithContext(r.Context()) 62 statsClient.SetHTTPRequestSection(proxySection).TrackRequest(r, nil, false).ResetHTTPRequestSection() 63 janusErr.Handler(w, r, fmt.Errorf("request failed too many times: %w", err)) 64 } 65 }) 66 } 67 }