Fix error reading closed response body on redirect (#6372)
This commit is contained in:
parent
cd99ceea62
commit
15dd43344d
|
@ -141,7 +141,7 @@ func (h *HTTPResponse) createHttpClient() (*http.Client, error) {
|
||||||
|
|
||||||
if h.FollowRedirects == false {
|
if h.FollowRedirects == false {
|
||||||
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
||||||
return ErrRedirectAttempted
|
return http.ErrUseLastResponse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return client, nil
|
return client, nil
|
||||||
|
@ -254,17 +254,8 @@ func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]
|
||||||
|
|
||||||
// Any error not recognized by `set_error` is considered a "connection_failed"
|
// Any error not recognized by `set_error` is considered a "connection_failed"
|
||||||
setResult("connection_failed", fields, tags)
|
setResult("connection_failed", fields, tags)
|
||||||
|
|
||||||
// If the error is a redirect we continue processing and log the HTTP code
|
|
||||||
urlError, isUrlError := err.(*url.Error)
|
|
||||||
if !h.FollowRedirects && isUrlError && urlError.Err == ErrRedirectAttempted {
|
|
||||||
err = nil
|
|
||||||
} else {
|
|
||||||
// If the error isn't a timeout or a redirect stop
|
|
||||||
// processing the request
|
|
||||||
return fields, tags, nil
|
return fields, tags, nil
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := fields["response_time"]; !ok {
|
if _, ok := fields["response_time"]; !ok {
|
||||||
fields["response_time"] = response_time
|
fields["response_time"] = response_time
|
||||||
|
|
|
@ -10,9 +10,9 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -36,6 +36,7 @@ func checkAbsentTags(t *testing.T, tags []string, acc *testutil.Accumulator) {
|
||||||
// Receives a dictionary and with expected fields and their values. If a value is nil, it will only check
|
// Receives a dictionary and with expected fields and their values. If a value is nil, it will only check
|
||||||
// that the field exists, but not its contents
|
// that the field exists, but not its contents
|
||||||
func checkFields(t *testing.T, fields map[string]interface{}, acc *testutil.Accumulator) {
|
func checkFields(t *testing.T, fields map[string]interface{}, acc *testutil.Accumulator) {
|
||||||
|
t.Helper()
|
||||||
for key, field := range fields {
|
for key, field := range fields {
|
||||||
switch v := field.(type) {
|
switch v := field.(type) {
|
||||||
case int:
|
case int:
|
||||||
|
@ -121,6 +122,7 @@ func setUpTestMux() http.Handler {
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkOutput(t *testing.T, acc *testutil.Accumulator, presentFields map[string]interface{}, presentTags map[string]interface{}, absentFields []string, absentTags []string) {
|
func checkOutput(t *testing.T, acc *testutil.Accumulator, presentFields map[string]interface{}, presentTags map[string]interface{}, absentFields []string, absentTags []string) {
|
||||||
|
t.Helper()
|
||||||
if presentFields != nil {
|
if presentFields != nil {
|
||||||
checkFields(t, presentFields, acc)
|
checkFields(t, presentFields, acc)
|
||||||
}
|
}
|
||||||
|
@ -651,12 +653,11 @@ func TestTimeout(t *testing.T) {
|
||||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, absentTags)
|
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, absentTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPluginErrors(t *testing.T) {
|
func TestBadRegex(t *testing.T) {
|
||||||
mux := setUpTestMux()
|
mux := setUpTestMux()
|
||||||
ts := httptest.NewServer(mux)
|
ts := httptest.NewServer(mux)
|
||||||
defer ts.Close()
|
defer ts.Close()
|
||||||
|
|
||||||
// Bad regex test. Should return an error and return nothing
|
|
||||||
h := &HTTPResponse{
|
h := &HTTPResponse{
|
||||||
Address: ts.URL + "/good",
|
Address: ts.URL + "/good",
|
||||||
Body: "{ 'test': 'data'}",
|
Body: "{ 'test': 'data'}",
|
||||||
|
@ -676,36 +677,6 @@ func TestPluginErrors(t *testing.T) {
|
||||||
absentFields := []string{"http_response_code", "response_time", "content_length", "response_string_match", "result_type", "result_code"}
|
absentFields := []string{"http_response_code", "response_time", "content_length", "response_string_match", "result_type", "result_code"}
|
||||||
absentTags := []string{"status_code", "result", "server", "method"}
|
absentTags := []string{"status_code", "result", "server", "method"}
|
||||||
checkOutput(t, &acc, nil, nil, absentFields, absentTags)
|
checkOutput(t, &acc, nil, nil, absentFields, absentTags)
|
||||||
|
|
||||||
// Attempt to read empty body test
|
|
||||||
h = &HTTPResponse{
|
|
||||||
Address: ts.URL + "/redirect",
|
|
||||||
Body: "",
|
|
||||||
Method: "GET",
|
|
||||||
ResponseStringMatch: ".*",
|
|
||||||
ResponseTimeout: internal.Duration{Duration: time.Second * 20},
|
|
||||||
FollowRedirects: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
acc = testutil.Accumulator{}
|
|
||||||
err = h.Gather(&acc)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expectedFields := map[string]interface{}{
|
|
||||||
"http_response_code": http.StatusMovedPermanently,
|
|
||||||
"response_string_match": 0,
|
|
||||||
"result_type": "body_read_error",
|
|
||||||
"result_code": 2,
|
|
||||||
"response_time": nil,
|
|
||||||
"content_length": nil,
|
|
||||||
}
|
|
||||||
expectedTags := map[string]interface{}{
|
|
||||||
"server": nil,
|
|
||||||
"method": "GET",
|
|
||||||
"status_code": "301",
|
|
||||||
"result": "body_read_error",
|
|
||||||
}
|
|
||||||
checkOutput(t, &acc, expectedFields, expectedTags, nil, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNetworkErrors(t *testing.T) {
|
func TestNetworkErrors(t *testing.T) {
|
||||||
|
@ -827,3 +798,50 @@ func TestContentLength(t *testing.T) {
|
||||||
absentFields = []string{"response_string_match"}
|
absentFields = []string{"response_string_match"}
|
||||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRedirect(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(http.NotFoundHandler())
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
ts.Config.Handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Location", "http://example.org")
|
||||||
|
w.WriteHeader(http.StatusMovedPermanently)
|
||||||
|
w.Write([]byte("test"))
|
||||||
|
})
|
||||||
|
|
||||||
|
plugin := &HTTPResponse{
|
||||||
|
URLs: []string{ts.URL},
|
||||||
|
ResponseStringMatch: "test",
|
||||||
|
}
|
||||||
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
err := plugin.Gather(&acc)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
expected := []telegraf.Metric{
|
||||||
|
testutil.MustMetric(
|
||||||
|
"http_response",
|
||||||
|
map[string]string{
|
||||||
|
"server": ts.URL,
|
||||||
|
"method": "GET",
|
||||||
|
"result": "success",
|
||||||
|
"status_code": "301",
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"result_code": 0,
|
||||||
|
"result_type": "success",
|
||||||
|
"http_response_code": 301,
|
||||||
|
"response_string_match": 1,
|
||||||
|
"content_length": 4,
|
||||||
|
},
|
||||||
|
time.Unix(0, 0),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
for _, m := range actual {
|
||||||
|
m.RemoveField("response_time")
|
||||||
|
}
|
||||||
|
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue