Skip to content

Commit

Permalink
Readd dial timeout error code and a test
Browse files Browse the repository at this point in the history
This was dropped as part of #2008 but it will be better if we
differentiate between different timeouts

Also add test for the `request timeout` error code in `httpext`
  • Loading branch information
mstoykov committed May 20, 2021
1 parent 630109c commit 202f0e7
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 2 deletions.
2 changes: 2 additions & 0 deletions lib/netext/httpext/error_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (
tcpBrokenPipeErrorCode errCode = 1201
netUnknownErrnoErrorCode errCode = 1202
tcpDialErrorCode errCode = 1210
tcpDialTimeoutErrorCode errCode = 1211
tcpDialRefusedErrorCode errCode = 1212
tcpDialUnknownErrnoCode errCode = 1213
tcpResetByPeerErrorCode errCode = 1220
Expand Down Expand Up @@ -86,6 +87,7 @@ const (

const (
tcpResetByPeerErrorCodeMsg = "%s: connection reset by peer"
tcpDialTimeoutErrorCodeMsg = "dial: i/o timeout"
tcpDialRefusedErrorCodeMsg = "dial: connection refused"
tcpBrokenPipeErrorCodeMsg = "%s: broken pipe"
netUnknownErrnoErrorCodeMsg = "%s: unknown errno `%d` on %s with message `%s`"
Expand Down
117 changes: 116 additions & 1 deletion lib/netext/httpext/request_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httptest"
"net/url"
Expand Down Expand Up @@ -228,7 +229,8 @@ func TestURL(t *testing.T) {
})
}

func TestMakeRequestTimeout(t *testing.T) {
func TestMakeRequestTimeoutInTheMiddle(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Length", "100000")
w.WriteHeader(200)
Expand Down Expand Up @@ -357,3 +359,116 @@ func TestTrailFailed(t *testing.T) {
})
}
}

func TestMakeRequestDialTimeout(t *testing.T) {
t.Parallel()
ln, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatal(err)
}
addr := ln.Addr()
defer func() {
require.NoError(t, ln.Close())
}()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
samples := make(chan stats.SampleContainer, 10)
logger := logrus.New()
logger.Level = logrus.DebugLevel
state := &lib.State{
Options: lib.Options{
RunTags: &stats.SampleTags{},
SystemTags: &stats.DefaultSystemTagSet,
},
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 1 * time.Microsecond,
}).DialContext,
},
Samples: samples,
Logger: logger,
BPool: bpool.NewBufferPool(100),
}

ctx = lib.WithState(ctx, state)
req, _ := http.NewRequest("GET", "http://"+addr.String(), nil)
preq := &ParsedHTTPRequest{
Req: req,
URL: &URL{u: req.URL, URL: req.URL.String()},
Body: new(bytes.Buffer),
Timeout: 500 * time.Millisecond,
ResponseCallback: func(i int) bool { return i == 0 },
}

res, err := MakeRequest(ctx, preq)
require.NoError(t, err)
assert.NotNil(t, res)
assert.Len(t, samples, 1)
sampleCont := <-samples
allSamples := sampleCont.GetSamples()
require.Len(t, allSamples, 9)
expTags := map[string]string{
"error": "dial: i/o timeout",
"error_code": "1211",
"status": "0",
"expected_response": "true", // we wait for status code 0
"method": "GET",
"url": req.URL.String(),
"name": req.URL.String(),
}
for _, s := range allSamples {
assert.Equal(t, expTags, s.Tags.CloneTags())
}
}

func TestMakeRequestTimeoutInTheBegining(t *testing.T) {
t.Parallel()
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
time.Sleep(100 * time.Millisecond)
}))
defer srv.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
samples := make(chan stats.SampleContainer, 10)
logger := logrus.New()
logger.Level = logrus.DebugLevel
state := &lib.State{
Options: lib.Options{
RunTags: &stats.SampleTags{},
SystemTags: &stats.DefaultSystemTagSet,
},
Transport: srv.Client().Transport,
Samples: samples,
Logger: logger,
BPool: bpool.NewBufferPool(100),
}
ctx = lib.WithState(ctx, state)
req, _ := http.NewRequest("GET", srv.URL, nil)
preq := &ParsedHTTPRequest{
Req: req,
URL: &URL{u: req.URL, URL: srv.URL},
Body: new(bytes.Buffer),
Timeout: 50 * time.Millisecond,
ResponseCallback: func(i int) bool { return i == 0 },
}

res, err := MakeRequest(ctx, preq)
require.NoError(t, err)
assert.NotNil(t, res)
assert.Len(t, samples, 1)
sampleCont := <-samples
allSamples := sampleCont.GetSamples()
require.Len(t, allSamples, 9)
expTags := map[string]string{
"error": "Request timeout",
"error_code": "1050",
"status": "0",
"expected_response": "true", // we wait for status code 0
"method": "GET",
"url": srv.URL,
"name": srv.URL,
}
for _, s := range allSamples {
assert.Equal(t, expTags, s.Tags.CloneTags())
}
}
8 changes: 7 additions & 1 deletion lib/netext/httpext/transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,14 @@ func (t *transport) RoundTrip(req *http.Request) (*http.Response, error) {

var netError net.Error
if errors.As(err, &netError) && netError.Timeout() {
err = NewK6Error(requestTimeoutErrorCode, requestTimeoutErrorCodeMsg, netError)
var netOpError *net.OpError
if errors.As(err, &netOpError) && netOpError.Op == "dial" {
err = NewK6Error(tcpDialTimeoutErrorCode, tcpDialTimeoutErrorCodeMsg, netError)
} else {
err = NewK6Error(requestTimeoutErrorCode, requestTimeoutErrorCodeMsg, netError)
}
}

t.saveCurrentRequest(&unfinishedRequest{
ctx: ctx,
tracer: tracer,
Expand Down

0 comments on commit 202f0e7

Please sign in to comment.