Skip to content

Commit

Permalink
fix EOF errors on http response with content and encoding headers
Browse files Browse the repository at this point in the history
Before this on a response which has a Content-Encoding value, but no
body will error out with EOF as we still try to decode it.

As the code and the semantics of network requests mean that we might
not know if we are going to be able to read something and whether we got
and EOF because of network error or because there was no body - we need
to figure out if we should try to read at all.

Unfortunately `Content-Encoding: chunked` exists which makes a more
general solution much harder IMO.

As such the current solution is to short circuit on known status codes
that do not have content. Namely all 1xx and 204 (aptly named no
content) and 304 (not modified).

https://www.rfc-editor.org/rfc/rfc9110.html#section-6.4.1-8

Additionally a bare minimum tests was added reproducing what the user
reported.

https://community.grafana.com/t/error-decompressing-response-body-eof-despite-http-request-returning-204/106927
  • Loading branch information
mstoykov committed Nov 6, 2023
1 parent e0827b4 commit 6dc5783
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 0 deletions.
23 changes: 23 additions & 0 deletions js/modules/k6/http/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2386,3 +2386,26 @@ func GetTestServerWithCertificate(t *testing.T, certPem, key []byte, suitesIds .
s.Listener = tls.NewListener(s.Listener, s.TLS)
return s, client
}

func TestGzipped204Response(t *testing.T) {
t.Parallel()
ts := newTestCase(t)
tb := ts.tb
rt := ts.runtime.VU.Runtime()
state := ts.runtime.VU.State()
state.Options.Throw = null.BoolFrom(true)
sr := tb.Replacer.Replace
// We should not try to decode it
tb.Mux.HandleFunc("/gzipempty", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Encoding", "gzip")
w.WriteHeader(http.StatusNoContent)
}))

_, err := rt.RunString(sr(`
var res = http.get("HTTPBIN_URL/gzipempty");
if (res.status != 204) {
throw new Error("unexpected status code: " + res.status)
}
`))
assert.NoError(t, err)
}
7 changes: 7 additions & 0 deletions lib/netext/httpext/compression.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,13 @@ func readResponseBody(
_ = respBody.Close()
}(resp.Body)

if (resp.StatusCode >= 100 && resp.StatusCode <= 199) || // 1xx
resp.StatusCode == http.StatusNoContent || resp.StatusCode == http.StatusNotModified {
// for all three of this status code there is always no content
// https://www.rfc-editor.org/rfc/rfc9110.html#section-6.4.1-8
// this also prevents trying to read
return nil, nil //nolint:nilnil
}
contentEncodings := strings.Split(resp.Header.Get("Content-Encoding"), ",")
// Transparently decompress the body if it's has a content-encoding we
// support. If not, simply return it as it is.
Expand Down

0 comments on commit 6dc5783

Please sign in to comment.