Skip to content

Commit

Permalink
Update retry proxy to include a recursive constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
nstogner committed Jan 18, 2024
1 parent 363aaa0 commit 17a5396
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 29 deletions.
6 changes: 5 additions & 1 deletion hack/failonceserver/main.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package main

import (
"io"
"net/http"
"os"
"sync"
)

Expand All @@ -10,7 +12,9 @@ func main() {
var mtx sync.RWMutex
paths := map[string]bool{}

http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.ListenAndServe(":8082", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
io.Copy(os.Stdout, r.Body)

mtx.RLock()
shouldSucceed := paths[r.URL.Path]
mtx.RUnlock()
Expand Down
73 changes: 45 additions & 28 deletions hack/retryproxy/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"bytes"
"errors"
"fmt"
"io"
"log"
"net/http"
Expand All @@ -12,52 +13,68 @@ import (

func main() {
http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("serving")

body, err := io.ReadAll(r.Body)
if err != nil {
panic(err)
}
r.Body.Close()

// go run ./hack/failserver
first := newReverseProxy("http://localhost:8081")
fmt.Println("body:", string(body))

backend(body, 0).ServeHTTP(w, newRequest(r, body))
}))

}

var errRetry = errors.New("retry")

func backend(body []byte, attempt int) http.Handler {
// go run ./hack/failserver
proxy := newReverseProxy(getEndpoint(attempt))

first.ModifyResponse = func(r *http.Response) error {
if r.StatusCode == http.StatusServiceUnavailable {
// Returning an error will trigger the ErrorHandler.
return errRetry
}
return nil
proxy.ModifyResponse = func(r *http.Response) error {
if r.StatusCode == http.StatusServiceUnavailable {
// Returning an error will trigger the ErrorHandler.
return errRetry
}
return nil
}

first.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
if err == errRetry {
log.Println("retrying")
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
if err == errRetry {
log.Println("retrying")

// Simulate calling the next backend.
// go run ./hack/successserver
next := newReverseProxy("http://localhost:8082")
next.ServeHTTP(w, newReq(r, body))
return
}
// Simulate calling the next backend.
// go run ./hack/successserver
backend(body, attempt+1).ServeHTTP(w, newRequest(r, body))
return
}
}

log.Println("serving")
first.ServeHTTP(w, newReq(r, body))
}))
return proxy
}

var errRetry = errors.New("retry")

func newReq(r *http.Request, body []byte) *http.Request {
clone := r.Clone(r.Context())
clone.Body = io.NopCloser(bytes.NewReader(body))
return clone
func getEndpoint(attempt int) string {
switch attempt {
case 0:
return "http://localhost:8081"
default:
return "http://localhost:8082"
}
}

func newReverseProxy(addr string) *httputil.ReverseProxy {
u, err := url.Parse(addr)
func newReverseProxy(host string) *httputil.ReverseProxy {
u, err := url.Parse(host)
if err != nil {
panic(err)
}
return httputil.NewSingleHostReverseProxy(u)
}

func newRequest(r *http.Request, body []byte) *http.Request {
clone := r.Clone(r.Context())
clone.Body = io.NopCloser(bytes.NewReader(body))
return clone
}

0 comments on commit 17a5396

Please sign in to comment.