Skip to content
Snippets Groups Projects
Commit b1abd83f authored by Will McCutchen's avatar Will McCutchen
Browse files

Add /delay/:n

parent ad5dc45b
Branches
Tags
No related merge requests found
...@@ -425,3 +425,27 @@ func (h *HTTPBin) Stream(w http.ResponseWriter, r *http.Request) { ...@@ -425,3 +425,27 @@ func (h *HTTPBin) Stream(w http.ResponseWriter, r *http.Request) {
f.Flush() 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)
}
...@@ -19,10 +19,12 @@ import ( ...@@ -19,10 +19,12 @@ import (
"time" "time"
) )
const maxMemory = 1024 * 1024 const maxMemory int64 = 1024 * 1024
const maxResponseTime time.Duration = 1 * time.Second
var app = NewHTTPBin(&Options{ var app = NewHTTPBin(&Options{
MaxMemory: maxMemory, MaxMemory: maxMemory,
MaxResponseTime: maxResponseTime,
}) })
var handler = app.Handler() var handler = app.Handler()
...@@ -1225,3 +1227,58 @@ func TestStream(t *testing.T) { ...@@ -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)
})
}
}
...@@ -3,6 +3,7 @@ package httpbin ...@@ -3,6 +3,7 @@ package httpbin
import ( import (
"net/http" "net/http"
"net/url" "net/url"
"time"
) )
const jsonContentType = "application/json; encoding=utf-8" const jsonContentType = "application/json; encoding=utf-8"
...@@ -71,7 +72,8 @@ type streamResponse struct { ...@@ -71,7 +72,8 @@ type streamResponse struct {
// Options are used to configure HTTPBin // Options are used to configure HTTPBin
type Options struct { type Options struct {
MaxMemory int64 MaxMemory int64
MaxResponseTime time.Duration
} }
// HTTPBin contains the business logic // HTTPBin contains the business logic
...@@ -115,12 +117,14 @@ func (h *HTTPBin) Handler() http.Handler { ...@@ -115,12 +117,14 @@ func (h *HTTPBin) Handler() http.Handler {
mux.HandleFunc("/gzip", h.Gzip) mux.HandleFunc("/gzip", h.Gzip)
mux.HandleFunc("/stream/", h.Stream) mux.HandleFunc("/stream/", h.Stream)
mux.HandleFunc("/delay/", h.Delay)
// Make sure our ServeMux doesn't "helpfully" redirect these invalid // Make sure our ServeMux doesn't "helpfully" redirect these invalid
// endpoints by adding a trailing slash. See the ServeMux docs for more // endpoints by adding a trailing slash. See the ServeMux docs for more
// info: https://golang.org/pkg/net/http/#ServeMux // info: https://golang.org/pkg/net/http/#ServeMux
mux.HandleFunc("/absolute-redirect", http.NotFound) mux.HandleFunc("/absolute-redirect", http.NotFound)
mux.HandleFunc("/basic-auth", http.NotFound) mux.HandleFunc("/basic-auth", http.NotFound)
mux.HandleFunc("/delay", http.NotFound)
mux.HandleFunc("/digest-auth", http.NotFound) mux.HandleFunc("/digest-auth", http.NotFound)
mux.HandleFunc("/hidden-basic-auth", http.NotFound) mux.HandleFunc("/hidden-basic-auth", http.NotFound)
mux.HandleFunc("/redirect", http.NotFound) mux.HandleFunc("/redirect", http.NotFound)
......
...@@ -3,13 +3,15 @@ package main ...@@ -3,13 +3,15 @@ package main
import ( import (
"log" "log"
"net/http" "net/http"
"time"
"github.com/mccutchen/go-httpbin/httpbin" "github.com/mccutchen/go-httpbin/httpbin"
) )
func main() { func main() {
h := httpbin.NewHTTPBin(&httpbin.Options{ h := httpbin.NewHTTPBin(&httpbin.Options{
MaxMemory: 1024 * 1024 * 5, MaxMemory: 1024 * 1024 * 5,
MaxResponseTime: 10 * time.Second,
}) })
log.Printf("listening on 9999") log.Printf("listening on 9999")
err := http.ListenAndServe(":9999", h.Handler()) err := http.ListenAndServe(":9999", h.Handler())
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment