Skip to content

Commit 34670f4

Browse files
Ivan Mirićcuonglm
Ivan Mirić
authored andcommitted
fix(httpext): return error on unsupported 101 response
The hanging behavior described in #971 happens after this Golang change: golang/go@5416204 , released in 1.12. If a `101 Switching Protocols` response is received, `response.Body` will be replaced by an underlying `net.Conn`, and reading it blocks indefinitely without a protocol implementation. This is not a big problem for k6 in this case, since we don't support protocol upgrades with the current HTTP API as shown in the example, so the agreed solution was to detect it and return an error. This Golang change is currently incompatible with client timeouts[1], though this will hopefully be resolved by the time we add support for h2c (#970) or other protocols. [1]: golang/go#31391 Closes #971
1 parent 6ad8bbc commit 34670f4

File tree

2 files changed

+37
-0
lines changed

2 files changed

+37
-0
lines changed

lib/netext/httpext/request.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ package httpext
2323
import (
2424
"bytes"
2525
"context"
26+
"fmt"
2627
"io"
2728
"io/ioutil"
2829
"net"
@@ -314,6 +315,15 @@ func MakeRequest(ctx context.Context, preq *ParsedHTTPRequest) (*Response, error
314315
mreq := preq.Req.WithContext(ctx)
315316
res, resErr := client.Do(mreq)
316317

318+
// TODO(imiric): It would be safer to check for a writeable
319+
// response body here instead of status code, but those are
320+
// wrapped in a read-only body when using client timeouts and are
321+
// unusable until https://github.com/golang/go/issues/31391 is fixed.
322+
if res != nil && res.StatusCode == http.StatusSwitchingProtocols {
323+
_ = res.Body.Close()
324+
return nil, fmt.Errorf("unsupported response status: %s", res.Status)
325+
}
326+
317327
resp.Body, resErr = readResponseBody(state, preq.ResponseType, res, resErr)
318328
finishedReq := tracerTransport.processLastSavedRequest(wrapDecompressionError(resErr))
319329
if finishedReq != nil {

lib/netext/httpext/request_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,14 @@ import (
66
"io"
77
"io/ioutil"
88
"net/http"
9+
"net/http/httptest"
910
"net/url"
1011
"testing"
1112

13+
"github.com/loadimpact/k6/lib"
14+
"github.com/loadimpact/k6/stats"
1215
"github.com/pkg/errors"
16+
"github.com/stretchr/testify/assert"
1317
"github.com/stretchr/testify/require"
1418
)
1519

@@ -82,6 +86,29 @@ func TestMakeRequestError(t *testing.T) {
8286
require.Error(t, err)
8387
require.Equal(t, err.Error(), "unknown compressionType CompressionType(13)")
8488
})
89+
90+
t.Run("invalid upgrade response", func(t *testing.T) {
91+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
92+
w.Header().Add("Connection", "Upgrade")
93+
w.Header().Add("Upgrade", "h2c")
94+
w.WriteHeader(http.StatusSwitchingProtocols)
95+
}))
96+
defer srv.Close()
97+
ctx, cancel := context.WithCancel(context.Background())
98+
defer cancel()
99+
state := &lib.State{
100+
Options: lib.Options{RunTags: &stats.SampleTags{}},
101+
Transport: srv.Client().Transport,
102+
}
103+
ctx = lib.WithState(ctx, state)
104+
req, _ := http.NewRequest("GET", srv.URL, nil)
105+
var preq = &ParsedHTTPRequest{Req: req, URL: &URL{u: req.URL}, Body: new(bytes.Buffer)}
106+
107+
res, err := MakeRequest(ctx, preq)
108+
109+
assert.Nil(t, res)
110+
assert.EqualError(t, err, "unsupported response status: 101 Switching Protocols")
111+
})
85112
}
86113

87114
func TestURL(t *testing.T) {

0 commit comments

Comments
 (0)