From b1abd83f5b6de17e7317942c671802a83a459f8b Mon Sep 17 00:00:00 2001 From: Will McCutchen <will@mccutch.org> Date: Sun, 18 Dec 2016 19:53:15 -0800 Subject: [PATCH] Add /delay/:n --- httpbin/handlers.go | 24 ++++++++++++++++ httpbin/handlers_test.go | 61 ++++++++++++++++++++++++++++++++++++++-- httpbin/httpbin.go | 6 +++- main.go | 4 ++- 4 files changed, 91 insertions(+), 4 deletions(-) diff --git a/httpbin/handlers.go b/httpbin/handlers.go index c9255e4..9b5fa37 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -425,3 +425,27 @@ func (h *HTTPBin) Stream(w http.ResponseWriter, r *http.Request) { f.Flush() } } + +// Delay waits for n seconds before responding +func (h *HTTPBin) Delay(w http.ResponseWriter, r *http.Request) { + parts := strings.Split(r.URL.Path, "/") + if len(parts) != 3 { + http.Error(w, "Not found", http.StatusNotFound) + return + } + n, err := strconv.ParseFloat(parts[2], 64) + if err != nil { + http.Error(w, "Invalid float", http.StatusBadRequest) + } + + // n is seconds as a float, and we allow down to millisecond resolution + delay := time.Duration(n*1000) * time.Millisecond + if delay > h.options.MaxResponseTime { + delay = h.options.MaxResponseTime + } else if delay < 0 { + delay = 0 + } + + <-time.After(delay) + h.RequestWithBody(w, r) +} diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go index a9e7aea..88eeb33 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -19,10 +19,12 @@ import ( "time" ) -const maxMemory = 1024 * 1024 +const maxMemory int64 = 1024 * 1024 +const maxResponseTime time.Duration = 1 * time.Second var app = NewHTTPBin(&Options{ - MaxMemory: maxMemory, + MaxMemory: maxMemory, + MaxResponseTime: maxResponseTime, }) var handler = app.Handler() @@ -1225,3 +1227,58 @@ func TestStream(t *testing.T) { }) } } + +func TestDelay(t *testing.T) { + var okTests = []struct { + url string + expectedDelay time.Duration + }{ + {"/delay/0", 0}, + {"/delay/0.5", 500 * time.Millisecond}, + {"/delay/1", maxResponseTime}, + {"/delay/1.5", maxResponseTime}, + {"/delay/-1", 0}, + {"/delay/-3.14", 0}, + } + for _, test := range okTests { + t.Run("ok"+test.url, func(t *testing.T) { + start := time.Now() + + r, _ := http.NewRequest("GET", test.url, nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + + elapsed := time.Now().Sub(start) + + assertStatusCode(t, w, http.StatusOK) + assertHeader(t, w, "Content-Type", jsonContentType) + + var resp *bodyResponse + err := json.Unmarshal(w.Body.Bytes(), &resp) + if err != nil { + t.Fatalf("error unmarshalling response: %s", err) + } + + if elapsed < test.expectedDelay { + t.Fatalf("expected delay of %s, got %s", test.expectedDelay, elapsed) + } + }) + } + + var badTests = []struct { + url string + code int + }{ + {"/delay", http.StatusNotFound}, + {"/delay/foo", http.StatusBadRequest}, + } + + for _, test := range badTests { + t.Run("bad"+test.url, func(t *testing.T) { + r, _ := http.NewRequest("GET", test.url, nil) + w := httptest.NewRecorder() + handler.ServeHTTP(w, r) + assertStatusCode(t, w, test.code) + }) + } +} diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index 5de8208..04cad81 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -3,6 +3,7 @@ package httpbin import ( "net/http" "net/url" + "time" ) const jsonContentType = "application/json; encoding=utf-8" @@ -71,7 +72,8 @@ type streamResponse struct { // Options are used to configure HTTPBin type Options struct { - MaxMemory int64 + MaxMemory int64 + MaxResponseTime time.Duration } // HTTPBin contains the business logic @@ -115,12 +117,14 @@ func (h *HTTPBin) Handler() http.Handler { mux.HandleFunc("/gzip", h.Gzip) mux.HandleFunc("/stream/", h.Stream) + mux.HandleFunc("/delay/", h.Delay) // Make sure our ServeMux doesn't "helpfully" redirect these invalid // endpoints by adding a trailing slash. See the ServeMux docs for more // info: https://golang.org/pkg/net/http/#ServeMux mux.HandleFunc("/absolute-redirect", http.NotFound) mux.HandleFunc("/basic-auth", http.NotFound) + mux.HandleFunc("/delay", http.NotFound) mux.HandleFunc("/digest-auth", http.NotFound) mux.HandleFunc("/hidden-basic-auth", http.NotFound) mux.HandleFunc("/redirect", http.NotFound) diff --git a/main.go b/main.go index e4f8310..47c9d90 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,15 @@ package main import ( "log" "net/http" + "time" "github.com/mccutchen/go-httpbin/httpbin" ) func main() { h := httpbin.NewHTTPBin(&httpbin.Options{ - MaxMemory: 1024 * 1024 * 5, + MaxMemory: 1024 * 1024 * 5, + MaxResponseTime: 10 * time.Second, }) log.Printf("listening on 9999") err := http.ListenAndServe(":9999", h.Handler()) -- GitLab