diff --git a/httpbin/handlers.go b/httpbin/handlers.go index 42faa1c8d5ff67cceddfba73204b7f3fb446391c..4e8ffa7142587d472aee9bde731393b98fb7a170 100644 --- a/httpbin/handlers.go +++ b/httpbin/handlers.go @@ -457,7 +457,7 @@ func (h *HTTPBin) Delay(w http.ResponseWriter, r *http.Request) { return } - delay, err := parseBoundedDuration(parts[2], 0, h.options.MaxResponseTime) + delay, err := parseBoundedDuration(parts[2], 0, h.options.MaxDuration) if err != nil { http.Error(w, "Invalid duration", http.StatusBadRequest) return @@ -481,7 +481,7 @@ func (h *HTTPBin) Drip(w http.ResponseWriter, r *http.Request) { userDuration := q.Get("duration") if userDuration != "" { - duration, err = parseBoundedDuration(userDuration, 0, h.options.MaxResponseTime) + duration, err = parseBoundedDuration(userDuration, 0, h.options.MaxDuration) if err != nil { http.Error(w, "Invalid duration", http.StatusBadRequest) return @@ -490,7 +490,7 @@ func (h *HTTPBin) Drip(w http.ResponseWriter, r *http.Request) { userDelay := q.Get("delay") if userDelay != "" { - delay, err = parseBoundedDuration(userDelay, 0, h.options.MaxResponseTime) + delay, err = parseBoundedDuration(userDelay, 0, h.options.MaxDuration) if err != nil { http.Error(w, "Invalid delay", http.StatusBadRequest) return @@ -500,7 +500,7 @@ func (h *HTTPBin) Drip(w http.ResponseWriter, r *http.Request) { userNumBytes := q.Get("numbytes") if userNumBytes != "" { numbytes, err = strconv.ParseInt(userNumBytes, 10, 64) - if err != nil || numbytes <= 0 || numbytes > h.options.MaxResponseSize { + if err != nil || numbytes <= 0 || numbytes > h.options.MaxMemory { http.Error(w, "Invalid numbytes", http.StatusBadRequest) return } @@ -515,7 +515,7 @@ func (h *HTTPBin) Drip(w http.ResponseWriter, r *http.Request) { } } - if duration+delay > h.options.MaxResponseTime { + if duration+delay > h.options.MaxDuration { http.Error(w, "Too much time", http.StatusBadRequest) return } diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go index 73a450f10cf2f91b1036fc330aeddd59b3c9a95d..006ee194df85042ee50e70832442a6d80ac486a9 100644 --- a/httpbin/handlers_test.go +++ b/httpbin/handlers_test.go @@ -20,13 +20,11 @@ import ( ) const maxMemory int64 = 1024 * 1024 -const maxResponseSize = 1024 -const maxResponseTime time.Duration = 1 * time.Second +const maxDuration time.Duration = 1 * time.Second -var app = NewHTTPBin(&Options{ - MaxMemory: maxMemory, - MaxResponseSize: maxResponseSize, - MaxResponseTime: maxResponseTime, +var app = NewHTTPBinWithOptions(&Options{ + MaxMemory: maxMemory, + MaxDuration: maxDuration, }) var handler = app.Handler() @@ -60,13 +58,6 @@ func assertBodyEquals(t *testing.T, w *httptest.ResponseRecorder, want string) { } } -func TestNewHTTPBin__NilOptions(t *testing.T) { - h := NewHTTPBin(nil) - if h.options.MaxMemory != 0 { - t.Fatalf("expected default MaxMemory == 0, got %#v", h.options.MaxMemory) - } -} - func TestIndex(t *testing.T) { r, _ := http.NewRequest("GET", "/", nil) w := httptest.NewRecorder() @@ -1341,7 +1332,7 @@ func TestDelay(t *testing.T) { // as are floating point seconds {"/delay/0", 0}, {"/delay/0.5", 500 * time.Millisecond}, - {"/delay/1", maxResponseTime}, + {"/delay/1", maxDuration}, } for _, test := range okTests { t.Run("ok"+test.url, func(t *testing.T) { @@ -1418,7 +1409,7 @@ func TestDrip(t *testing.T) { {&url.Values{"numbytes": {"1"}}, 0, 1, http.StatusOK}, {&url.Values{"numbytes": {"101"}}, 0, 101, http.StatusOK}, - {&url.Values{"numbytes": {fmt.Sprintf("%d", maxResponseSize)}}, 0, maxResponseSize, http.StatusOK}, + {&url.Values{"numbytes": {fmt.Sprintf("%d", maxMemory)}}, 0, int(maxMemory), http.StatusOK}, {&url.Values{"code": {"100"}}, 0, 10, 100}, {&url.Values{"code": {"404"}}, 0, 10, 404}, @@ -1472,7 +1463,7 @@ func TestDrip(t *testing.T) { {&url.Values{"numbytes": {"0"}}, http.StatusBadRequest}, {&url.Values{"numbytes": {"-1"}}, http.StatusBadRequest}, {&url.Values{"numbytes": {"0xff"}}, http.StatusBadRequest}, - {&url.Values{"numbytes": {fmt.Sprintf("%d", maxResponseSize+1)}}, http.StatusBadRequest}, + {&url.Values{"numbytes": {fmt.Sprintf("%d", maxMemory+1)}}, http.StatusBadRequest}, {&url.Values{"code": {"foo"}}, http.StatusBadRequest}, {&url.Values{"code": {"-1"}}, http.StatusBadRequest}, diff --git a/httpbin/httpbin.go b/httpbin/httpbin.go index 93d26900942a66c4eb8dda26fe353f13d3c8aadd..ebce5607c80acc29c7281eb627672fa3cf40bb18 100644 --- a/httpbin/httpbin.go +++ b/httpbin/httpbin.go @@ -6,6 +6,12 @@ import ( "time" ) +// Default configuration values +const ( + DefaultMaxMemory int64 = 1024 * 1024 + DefaultMaxDuration = 10 * time.Second +) + const jsonContentType = "application/json; encoding=utf-8" const htmlContentType = "text/html; charset=utf-8" @@ -72,9 +78,13 @@ type streamResponse struct { // Options are used to configure HTTPBin type Options struct { - MaxMemory int64 - MaxResponseSize int64 - MaxResponseTime time.Duration + // How much memory a request is allowed to consume in bytes, as a limit on + // the size of incoming request bodies and on responses generated + MaxMemory int64 + + // Maximum duration of a request, for those requests that allow user + // control over timing (e.g. /delay) + MaxDuration time.Duration } // HTTPBin contains the business logic @@ -166,11 +176,18 @@ func (h *HTTPBin) Handler() http.Handler { return handler } -// NewHTTPBin creates a new HTTPBin -func NewHTTPBin(options *Options) *HTTPBin { - if options == nil { - options = &Options{} +// NewHTTPBin creates a new HTTPBin instance with default options +func NewHTTPBin() *HTTPBin { + return &HTTPBin{ + options: &Options{ + MaxMemory: DefaultMaxMemory, + MaxDuration: DefaultMaxDuration, + }, } +} + +// NewHTTPBinWithOptions creates a new HTTPBin instance with the given options +func NewHTTPBinWithOptions(options *Options) *HTTPBin { return &HTTPBin{ options: options, } diff --git a/httpbin/httpbin_test.go b/httpbin/httpbin_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8e2e991d4b4a5ee9ab615250535e149a8f77117c --- /dev/null +++ b/httpbin/httpbin_test.go @@ -0,0 +1,30 @@ +package httpbin + +import ( + "testing" + "time" +) + +func TestNewHTTPBin__Defaults(t *testing.T) { + h := NewHTTPBin() + if h.options.MaxMemory != DefaultMaxMemory { + t.Fatalf("expected default MaxMemory == %d, got %#v", DefaultMaxMemory, h.options.MaxMemory) + } + if h.options.MaxDuration != DefaultMaxDuration { + t.Fatalf("expected default MaxDuration == %s, got %#v", DefaultMaxDuration, h.options.MaxDuration) + } +} + +func TestNewHTTPBinWithOptions__Defaults(t *testing.T) { + o := &Options{ + MaxDuration: 1 * time.Second, + MaxMemory: 1024, + } + h := NewHTTPBinWithOptions(o) + if h.options.MaxMemory != o.MaxMemory { + t.Fatalf("expected MaxMemory == %d, got %#v", o.MaxMemory, h.options.MaxMemory) + } + if h.options.MaxDuration != o.MaxDuration { + t.Fatalf("expected MaxDuration == %s, got %#v", o.MaxDuration, h.options.MaxDuration) + } +}