Skip to content
This repository has been archived by the owner on Nov 5, 2021. It is now read-only.

Allow for request bodies larger than 32768 bytes. #448

Merged
merged 1 commit into from
Aug 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions probes/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package http

import (
"bytes"
"context"
"crypto/tls"
"fmt"
Expand Down Expand Up @@ -43,6 +44,7 @@ import (
const (
maxResponseSizeForMetrics = 128
targetsUpdateInterval = 1 * time.Minute
largeBodyThreshold = bytes.MinRead // 512.
)

// Probe holds aggregate information about all probe runs, per-target.
Expand Down Expand Up @@ -76,6 +78,8 @@ type Probe struct {
// statsExportInterval / p.opts.Interval. Metrics are exported when
// (runCnt % statsExportFrequency) == 0
statsExportFrequency int64

requestBody []byte
}

type probeResult struct {
Expand Down Expand Up @@ -108,6 +112,8 @@ func (p *Probe) Init(name string, opts *options.Options) error {
return fmt.Errorf("Invalid Relative URL: %s, must begin with '/'", p.url)
}

p.requestBody = []byte(p.c.GetBody())

// Create a transport for our use. This is mostly based on
// http.DefaultTransport with some timeouts changed.
// TODO(manugarg): Considering cloning DefaultTransport once
Expand Down Expand Up @@ -215,6 +221,11 @@ func isClientTimeout(err error) bool {
// httpRequest executes an HTTP request and updates the provided result struct.
func (p *Probe) doHTTPRequest(req *http.Request, result *probeResult, resultMu *sync.Mutex) {

if len(p.requestBody) >= largeBodyThreshold {
req = req.Clone(req.Context())
req.Body = ioutil.NopCloser(bytes.NewReader(p.requestBody))
}

if p.c.GetKeepAlive() {
trace := &httptrace.ClientTrace{
ConnectDone: func(_, addr string, err error) {
Expand Down
50 changes: 49 additions & 1 deletion probes/http/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ import (
// The Transport is mocked instead of the Client because Client is not an
// interface, but RoundTripper (which Transport implements) is.
type testTransport struct {
noBody io.ReadCloser
noBody io.ReadCloser
lastProcessedRequestBody []byte
}

func newTestTransport() *testTransport {
Expand Down Expand Up @@ -70,6 +71,7 @@ func (tt *testTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return nil, err
}
req.Body.Close()
tt.lastProcessedRequestBody = b

return &http.Response{
Body: &testReadCloser{
Expand Down Expand Up @@ -193,6 +195,52 @@ func TestProbeWithBody(t *testing.T) {
}
}

func TestProbeWithLargeBody(t *testing.T) {
for _, size := range []int{largeBodyThreshold - 1, largeBodyThreshold, largeBodyThreshold + 1, largeBodyThreshold * 2} {
t.Run(fmt.Sprintf("size:%d", size), func(t *testing.T) {
testProbeWithLargeBody(t, size)
})
}
}

func testProbeWithLargeBody(t *testing.T, bodySize int) {
testBody := strings.Repeat("a", bodySize)
testTarget := "test-large-body.com"

p := &Probe{}
err := p.Init("http_test", &options.Options{
Targets: targets.StaticTargets(testTarget),
Interval: 2 * time.Second,
ProbeConf: &configpb.ProbeConf{
Body: &testBody,
// Can't use ExportResponseAsMetrics for large bodies,
// since maxResponseSizeForMetrics is small
ExportResponseAsMetrics: proto.Bool(false),
},
})

if err != nil {
t.Errorf("Error while initializing probe: %v", err)
}
testTransport := newTestTransport()
p.client.Transport = testTransport

// Probe 1st run
p.runProbe(context.Background())

got := string(testTransport.lastProcessedRequestBody)
if got != testBody {
t.Errorf("response body length: got=%d, expected=%d", len(got), len(testBody))
}

// Probe 2nd run (we should get the same request body).
p.runProbe(context.Background())
got = string(testTransport.lastProcessedRequestBody)
if got != testBody {
t.Errorf("response body length: got=%d, expected=%d", len(got), len(testBody))
}
}

func TestMultipleTargetsMultipleRequests(t *testing.T) {
testTargets := []string{"test.com", "fail-test.com"}
reqPerProbe := int64(6)
Expand Down
4 changes: 2 additions & 2 deletions probes/http/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ func (p *Probe) httpRequestForTarget(target endpoint.Endpoint, resolveF resolveF

// Prepare request body
var body io.Reader
if p.c.GetBody() != "" {
body = &requestBody{[]byte(p.c.GetBody())}
if len(p.requestBody) > 0 {
body = &requestBody{p.requestBody}
}
req, err := http.NewRequest(p.method, url, body)
if err != nil {
Expand Down