From 0c8f99a1341b335e60a56c8c16ef1a285ead7693 Mon Sep 17 00:00:00 2001 From: dvorakluk Date: Sat, 28 Oct 2023 17:29:27 +0200 Subject: [PATCH] feat: proxy retries with exponential backoff (#255) --- cmd/templ/generatecmd/proxy/proxy.go | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/cmd/templ/generatecmd/proxy/proxy.go b/cmd/templ/generatecmd/proxy/proxy.go index 01571566d..07998e286 100644 --- a/cmd/templ/generatecmd/proxy/proxy.go +++ b/cmd/templ/generatecmd/proxy/proxy.go @@ -4,12 +4,14 @@ import ( "fmt" "io" "log" + "math" "net/http" "net/http/httputil" "net/url" "os" "strconv" "strings" + "time" "github.com/a-h/templ/cmd/templ/generatecmd/sse" @@ -31,6 +33,11 @@ type Handler struct { func New(port int, target *url.URL) *Handler { p := httputil.NewSingleHostReverseProxy(target) p.ErrorLog = log.New(os.Stderr, "Proxy to target error: ", 0) + p.Transport = &roundTripper{ + maxRetries: 10, + initialDelay: 100 * time.Millisecond, + backoffExponent: 1.5, + } p.ModifyResponse = func(r *http.Response) error { if contentType := r.Header.Get("Content-Type"); !strings.HasPrefix(contentType, "text/html") { return nil @@ -74,3 +81,32 @@ func (p *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (p *Handler) SendSSE(eventType string, data string) { p.sse.Send(eventType, data) } + +type roundTripper struct { + maxRetries int + initialDelay time.Duration + backoffExponent float64 +} + +func (rt *roundTripper) RoundTrip(r *http.Request) (*http.Response, error) { + var err error + + for retries := 0; retries < rt.maxRetries; retries++ { + req := r.Clone(r.Context()) + + req.Body, err = r.GetBody() + if err != nil { + return nil, err + } + + resp, err := http.DefaultTransport.RoundTrip(req) + if err != nil { + time.Sleep(rt.initialDelay * time.Duration(math.Pow(rt.backoffExponent, float64(retries)))) + continue + } + + return resp, nil + } + + return nil, fmt.Errorf("max retries reached") +}