From cb0cde7337489afd6915277c5871c96f8f81e1bb Mon Sep 17 00:00:00 2001 From: Chris Prather Date: Thu, 29 Oct 2020 03:44:19 -0400 Subject: [PATCH] Skip decoding if Content-Length is zero (#63) * Some servers send a 0 length response, without a 204 StatusNoContent. Skip decoding these responses, as if they're 204s Co-authored-by: Dalton Hubble --- sling.go | 15 ++++++++------- sling_test.go | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/sling.go b/sling.go index 889407d..797b7fe 100644 --- a/sling.go +++ b/sling.go @@ -360,9 +360,9 @@ func (s *Sling) ReceiveSuccess(successV interface{}) (*http.Response, error) { // Receive creates a new HTTP request and returns the response. Success // responses (2XX) are JSON decoded into the value pointed to by successV and // other responses are JSON decoded into the value pointed to by failureV. -// If the status code of response is 204(no content), decoding is skipped. -// Any error creating the request, sending it, or decoding the response is -// returned. +// If the status code of response is 204(no content) or the Content-Lenght is 0, +// decoding is skipped. Any error creating the request, sending it, or decoding +// the response is returned. // Receive is shorthand for calling Request and Do. func (s *Sling) Receive(successV, failureV interface{}) (*http.Response, error) { req, err := s.Request() @@ -375,8 +375,9 @@ func (s *Sling) Receive(successV, failureV interface{}) (*http.Response, error) // Do sends an HTTP request and returns the response. Success responses (2XX) // are JSON decoded into the value pointed to by successV and other responses // are JSON decoded into the value pointed to by failureV. -// If the status code of response is 204(no content), decoding is skipped. -// Any error sending the request or decoding the response is returned. +// If the status code of response is 204(no content) or the Content-Length is 0, +// decoding is skipped. Any error sending the request or decoding the response +// is returned. func (s *Sling) Do(req *http.Request, successV, failureV interface{}) (*http.Response, error) { resp, err := s.httpClient.Do(req) if err != nil { @@ -391,8 +392,8 @@ func (s *Sling) Do(req *http.Request, successV, failureV interface{}) (*http.Res // See: https://golang.org/pkg/net/http/#Response defer io.Copy(ioutil.Discard, resp.Body) - // Don't try to decode on 204s - if resp.StatusCode == http.StatusNoContent { + // Don't try to decode on 204s or Content-Length is 0 + if resp.StatusCode == http.StatusNoContent || resp.ContentLength == 0 { return resp, nil } diff --git a/sling_test.go b/sling_test.go index 132edec..aca29aa 100644 --- a/sling_test.go +++ b/sling_test.go @@ -828,6 +828,38 @@ func TestReceive_success(t *testing.T) { } } +func TestReceive_StatusOKNoContent(t *testing.T) { + client, mux, server := testServer() + defer server.Close() + mux.HandleFunc("/foo/submit", func(w http.ResponseWriter, r *http.Request) { + assertMethod(t, "POST", r) + w.WriteHeader(201) + w.Header().Set("Location", "/foo/latest") + }) + + endpoint := New().Client(client).Base("http://example.com/").Path("foo/").Post("submit") + // fake a post response for testing purposes, checking that it's valid happens in other tests + params := FakeParams{} + model := new(FakeModel) + apiError := new(APIError) + resp, err := endpoint.New().BodyForm(params).Receive(model, apiError) + + if err != nil { + t.Errorf("expected nil, got %v", err) + } + if resp.StatusCode != 201 { + t.Errorf("expected %d, got %d", 201, resp.StatusCode) + } + expectedModel := &FakeModel{} + if !reflect.DeepEqual(expectedModel, model) { + t.Errorf("expected %v, got %v", expectedModel, model) + } + expectedAPIError := &APIError{} + if !reflect.DeepEqual(expectedAPIError, apiError) { + t.Errorf("failureV should be zero valued, exepcted %v, got %v", expectedAPIError, apiError) + } +} + func TestReceive_failure(t *testing.T) { client, mux, server := testServer() defer server.Close()