fix(inputs.http_response): Fix for IPv4 and IPv6 addresses when interface is set (#15496)
This commit is contained in:
parent
caa0cf3a80
commit
fc3cbb8256
|
|
@ -323,6 +323,8 @@ following works:
|
|||
- github.com/russross/blackfriday [BSD 2-Clause "Simplified" License](https://github.com/russross/blackfriday/blob/master/LICENSE.txt)
|
||||
- github.com/safchain/ethtool [Apache License 2.0](https://github.com/safchain/ethtool/blob/master/LICENSE)
|
||||
- github.com/samber/lo [MIT License](https://github.com/samber/lo/blob/master/LICENSE)
|
||||
- github.com/seancfoley/bintree [Apache License 2.0](https://github.com/seancfoley/bintree/blob/master/LICENSE)
|
||||
- github.com/seancfoley/ipaddress-go [Apache License 2.0](https://github.com/seancfoley/ipaddress-go/blob/master/LICENSE)
|
||||
- github.com/shirou/gopsutil [BSD 3-Clause Clear License](https://github.com/shirou/gopsutil/blob/master/LICENSE)
|
||||
- github.com/shoenig/go-m1cpu [Mozilla Public License 2.0](https://github.com/shoenig/go-m1cpu/blob/main/LICENSE)
|
||||
- github.com/shopspring/decimal [MIT License](https://github.com/shopspring/decimal/blob/master/LICENSE)
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -169,6 +169,7 @@ require (
|
|||
github.com/robinson/gos7 v0.0.0-20240315073918-1f14519e4846
|
||||
github.com/safchain/ethtool v0.3.0
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
|
||||
github.com/seancfoley/ipaddress-go v1.6.0
|
||||
github.com/sensu/sensu-go/api/core/v2 v2.16.0
|
||||
github.com/shirou/gopsutil/v3 v3.24.4
|
||||
github.com/showwin/speedtest-go v1.7.7
|
||||
|
|
@ -437,6 +438,7 @@ require (
|
|||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/samber/lo v1.38.1 // indirect
|
||||
github.com/seancfoley/bintree v1.3.1 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/signalfx/com_signalfx_metrics_protobuf v0.0.3 // indirect
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -2113,6 +2113,10 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21 h1:yWfiTPwYxB0l5fGMhl/G+liULu
|
|||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.21/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=
|
||||
github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
|
||||
github.com/seancfoley/ipaddress-go v1.6.0 h1:9z7yGmOnV4P2ML/dlR/kCJiv5tp8iHOOetJvxJh/R5w=
|
||||
github.com/seancfoley/ipaddress-go v1.6.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
|
||||
github.com/sensu/sensu-go/api/core/v2 v2.16.0 h1:HOq4rFkQ1S5ZjxmMTLc5J5mAbECrnKWvtXXbMqr3j9s=
|
||||
github.com/sensu/sensu-go/api/core/v2 v2.16.0/go.mod h1:MjM7+MCGEyTAgaZ589SiGHwYiaYF7N/58dU0J070u/0=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ import (
|
|||
"unicode/utf8"
|
||||
|
||||
"github.com/benbjohnson/clock"
|
||||
"github.com/seancfoley/ipaddress-go/ipaddr"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/config"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
|
|
@ -36,40 +38,45 @@ const (
|
|||
|
||||
// HTTPResponse struct
|
||||
type HTTPResponse struct {
|
||||
Address string `toml:"address" deprecated:"1.12.0;1.35.0;use 'urls' instead"`
|
||||
URLs []string `toml:"urls"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
Body string
|
||||
Address string `toml:"address" deprecated:"1.12.0;1.35.0;use 'urls' instead"`
|
||||
URLs []string `toml:"urls"`
|
||||
HTTPProxy string `toml:"http_proxy"`
|
||||
Body string `toml:"body"`
|
||||
BodyForm map[string][]string `toml:"body_form"`
|
||||
Method string
|
||||
ResponseTimeout config.Duration
|
||||
HTTPHeaderTags map[string]string `toml:"http_header_tags"`
|
||||
Headers map[string]string
|
||||
FollowRedirects bool
|
||||
Method string `toml:"method"`
|
||||
ResponseTimeout config.Duration `toml:"response_timeout"`
|
||||
HTTPHeaderTags map[string]string `toml:"http_header_tags"`
|
||||
Headers map[string]string `toml:"headers"`
|
||||
FollowRedirects bool `toml:"follow_redirects"`
|
||||
// Absolute path to file with Bearer token
|
||||
BearerToken string `toml:"bearer_token"`
|
||||
ResponseBodyField string `toml:"response_body_field"`
|
||||
ResponseBodyMaxSize config.Size `toml:"response_body_max_size"`
|
||||
ResponseStringMatch string
|
||||
ResponseStatusCode int
|
||||
Interface string
|
||||
ResponseStringMatch string `toml:"response_string_match"`
|
||||
ResponseStatusCode int `toml:"response_status_code"`
|
||||
Interface string `toml:"interface"`
|
||||
// HTTP Basic Auth Credentials
|
||||
Username config.Secret `toml:"username"`
|
||||
Password config.Secret `toml:"password"`
|
||||
tls.ClientConfig
|
||||
cookie.CookieAuthConfig
|
||||
|
||||
Log telegraf.Logger
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
compiledStringMatch *regexp.Regexp
|
||||
client httpClient
|
||||
clients []client
|
||||
}
|
||||
|
||||
type client struct {
|
||||
httpClient httpClient
|
||||
address string
|
||||
}
|
||||
|
||||
type httpClient interface {
|
||||
Do(req *http.Request) (*http.Response, error)
|
||||
}
|
||||
|
||||
// Set the proxy. A configured proxy overwrites the system wide proxy.
|
||||
// Set the proxy. A configured proxy overwrites the system-wide proxy.
|
||||
func getProxyFunc(httpProxy string) func(*http.Request) (*url.URL, error) {
|
||||
if httpProxy == "" {
|
||||
return http.ProxyFromEnvironment
|
||||
|
|
@ -85,9 +92,9 @@ func getProxyFunc(httpProxy string) func(*http.Request) (*url.URL, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// createHTTPClient creates an http client which will timeout at the specified
|
||||
// createHTTPClient creates an http client which will time out at the specified
|
||||
// timeout period and can follow redirects if specified
|
||||
func (h *HTTPResponse) createHTTPClient() (*http.Client, error) {
|
||||
func (h *HTTPResponse) createHTTPClient(address url.URL) (*http.Client, error) {
|
||||
tlsCfg, err := h.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -96,7 +103,7 @@ func (h *HTTPResponse) createHTTPClient() (*http.Client, error) {
|
|||
dialer := &net.Dialer{}
|
||||
|
||||
if h.Interface != "" {
|
||||
dialer.LocalAddr, err = localAddress(h.Interface)
|
||||
dialer.LocalAddr, err = localAddress(h.Interface, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -127,7 +134,7 @@ func (h *HTTPResponse) createHTTPClient() (*http.Client, error) {
|
|||
return client, nil
|
||||
}
|
||||
|
||||
func localAddress(interfaceName string) (net.Addr, error) {
|
||||
func localAddress(interfaceName string, address url.URL) (net.Addr, error) {
|
||||
i, err := net.InterfaceByName(interfaceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -138,14 +145,43 @@ func localAddress(interfaceName string) (net.Addr, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
urlInIPv6, zone := isURLInIPv6(address)
|
||||
for _, addr := range addrs {
|
||||
if naddr, ok := addr.(*net.IPNet); ok {
|
||||
// leaving port set to zero to let kernel pick
|
||||
return &net.TCPAddr{IP: naddr.IP}, nil
|
||||
ipNetInIPv6 := isIPNetInIPv6(naddr)
|
||||
|
||||
// choose interface address in the same format as server address
|
||||
if ipNetInIPv6 == urlInIPv6 {
|
||||
// leaving port set to zero to let kernel pick, but set zone
|
||||
return &net.TCPAddr{IP: naddr.IP, Zone: zone}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("cannot create local address for interface %q", interfaceName)
|
||||
return nil, fmt.Errorf("cannot create local address for interface %q and server address %q", interfaceName, address.String())
|
||||
}
|
||||
|
||||
// isURLInIPv6 returns (true, zoneName) only when URL is in IPv6 format.
|
||||
// For other cases (host part of url cannot be successfully validated, doesn't contain address at all or is in IPv4 format), it returns (false, "").
|
||||
func isURLInIPv6(address url.URL) (bool, string) {
|
||||
host := ipaddr.NewHostName(address.Host)
|
||||
if err := host.Validate(); err != nil {
|
||||
return false, ""
|
||||
}
|
||||
if hostAddr := host.AsAddress(); hostAddr != nil {
|
||||
if ipv6 := hostAddr.ToIPv6(); ipv6 != nil {
|
||||
return true, ipv6.GetZone().String()
|
||||
}
|
||||
}
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
// isIPNetInIPv6 returns true only when IPNet can be represented in IPv6 format.
|
||||
// For other cases (address cannot be successfully parsed or is in IPv4 format), it returns false.
|
||||
func isIPNetInIPv6(address *net.IPNet) bool {
|
||||
ipAddr, err := ipaddr.NewIPAddressFromNetIPNet(address)
|
||||
return err == nil && ipAddr.ToIPv6() != nil
|
||||
}
|
||||
|
||||
func setResult(resultString string, fields map[string]interface{}, tags map[string]string) {
|
||||
|
|
@ -196,10 +232,10 @@ func setError(err error, fields map[string]interface{}, tags map[string]string)
|
|||
}
|
||||
|
||||
// HTTPGather gathers all fields and returns any errors it encounters
|
||||
func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]string, error) {
|
||||
func (h *HTTPResponse) httpGather(cl client) (map[string]interface{}, map[string]string, error) {
|
||||
// Prepare fields and tags
|
||||
fields := make(map[string]interface{})
|
||||
tags := map[string]string{"server": u, "method": h.Method}
|
||||
tags := map[string]string{"server": cl.address, "method": h.Method}
|
||||
|
||||
var body io.Reader
|
||||
if h.Body != "" {
|
||||
|
|
@ -214,7 +250,7 @@ func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]
|
|||
body = strings.NewReader(values.Encode())
|
||||
}
|
||||
|
||||
request, err := http.NewRequest(h.Method, u, body)
|
||||
request, err := http.NewRequest(h.Method, cl.address, body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
|
@ -245,14 +281,14 @@ func (h *HTTPResponse) httpGather(u string) (map[string]interface{}, map[string]
|
|||
|
||||
// Start Timer
|
||||
start := time.Now()
|
||||
resp, err := h.client.Do(request)
|
||||
resp, err := cl.httpClient.Do(request)
|
||||
responseTime := time.Since(start).Seconds()
|
||||
|
||||
// If an error in returned, it means we are dealing with a network error, as
|
||||
// HTTP error codes do not generate errors in the net/http library
|
||||
if err != nil {
|
||||
// Log error
|
||||
h.Log.Debugf("Network error while polling %s: %s", u, err.Error())
|
||||
h.Log.Debugf("Network error while polling %s: %s", cl.address, err.Error())
|
||||
|
||||
// Get error details
|
||||
if setError(err, fields, tags) == nil {
|
||||
|
|
@ -352,10 +388,9 @@ func (*HTTPResponse) SampleConfig() string {
|
|||
return sampleConfig
|
||||
}
|
||||
|
||||
// Gather gets all metric fields and tags and returns any errors it encounters
|
||||
func (h *HTTPResponse) Gather(acc telegraf.Accumulator) error {
|
||||
// Compile the body regex if it exist
|
||||
if h.compiledStringMatch == nil {
|
||||
func (h *HTTPResponse) Init() error {
|
||||
// Compile the body regex if it exists
|
||||
if h.ResponseStringMatch != "" {
|
||||
var err error
|
||||
h.compiledStringMatch, err = regexp.Compile(h.ResponseStringMatch)
|
||||
if err != nil {
|
||||
|
|
@ -367,7 +402,6 @@ func (h *HTTPResponse) Gather(acc telegraf.Accumulator) error {
|
|||
if h.ResponseTimeout < config.Duration(time.Second) {
|
||||
h.ResponseTimeout = config.Duration(time.Second * 5)
|
||||
}
|
||||
// Check send and expected string
|
||||
if h.Method == "" {
|
||||
h.Method = "GET"
|
||||
}
|
||||
|
|
@ -380,32 +414,37 @@ func (h *HTTPResponse) Gather(acc telegraf.Accumulator) error {
|
|||
}
|
||||
}
|
||||
|
||||
if h.client == nil {
|
||||
client, err := h.createHTTPClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.client = client
|
||||
}
|
||||
|
||||
h.clients = make([]client, 0, len(h.URLs))
|
||||
for _, u := range h.URLs {
|
||||
addr, err := url.Parse(u)
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
return fmt.Errorf("%q is not a valid address: %w", u, err)
|
||||
}
|
||||
|
||||
if addr.Scheme != "http" && addr.Scheme != "https" {
|
||||
acc.AddError(errors.New("only http and https are supported"))
|
||||
continue
|
||||
return fmt.Errorf("%q is not a valid address: only http and https types are supported", u)
|
||||
}
|
||||
|
||||
cl, err := h.createHTTPClient(*addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.clients = append(h.clients, client{httpClient: cl, address: u})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Gather gets all metric fields and tags and returns any errors it encounters
|
||||
func (h *HTTPResponse) Gather(acc telegraf.Accumulator) error {
|
||||
for _, c := range h.clients {
|
||||
// Prepare data
|
||||
var fields map[string]interface{}
|
||||
var tags map[string]string
|
||||
|
||||
// Gather data
|
||||
fields, tags, err = h.httpGather(u)
|
||||
fields, tags, err := h.httpGather(c)
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
//go:build !windows
|
||||
|
||||
// TODO: Windows - should be enabled for Windows when https://github.com/influxdata/telegraf/issues/8451 is fixed
|
||||
|
||||
package http_response
|
||||
|
||||
import (
|
||||
|
|
@ -200,9 +196,10 @@ func TestHeaders(t *testing.T) {
|
|||
"Host": "Hello",
|
||||
},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -239,8 +236,8 @@ func TestFields(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -278,8 +275,8 @@ func TestResponseBodyField(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -313,8 +310,8 @@ func TestResponseBodyField(t *testing.T) {
|
|||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"result_type": "body_read_error",
|
||||
|
|
@ -349,6 +346,7 @@ func TestResponseBodyFormField(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
|
|
@ -387,8 +385,8 @@ func TestResponseBodyMaxSize(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"result_type": "body_read_error",
|
||||
|
|
@ -421,8 +419,8 @@ func TestHTTPHeaderTags(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -456,8 +454,8 @@ func TestHTTPHeaderTags(t *testing.T) {
|
|||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedTags = map[string]interface{}{
|
||||
"server": nil,
|
||||
|
|
@ -479,8 +477,8 @@ func TestHTTPHeaderTags(t *testing.T) {
|
|||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"result_type": "connection_failed",
|
||||
|
|
@ -538,8 +536,8 @@ func TestInterface(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -574,9 +572,10 @@ func TestRedirects(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -605,9 +604,10 @@ func TestRedirects(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"result_type": "connection_failed",
|
||||
|
|
@ -642,9 +642,10 @@ func TestMethod(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -673,9 +674,10 @@ func TestMethod(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"http_response_code": http.StatusMethodNotAllowed,
|
||||
|
|
@ -705,9 +707,10 @@ func TestMethod(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"http_response_code": http.StatusMethodNotAllowed,
|
||||
|
|
@ -742,9 +745,10 @@ func TestBody(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -772,9 +776,10 @@ func TestBody(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"http_response_code": http.StatusBadRequest,
|
||||
|
|
@ -808,9 +813,10 @@ func TestStringMatch(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -846,9 +852,10 @@ func TestStringMatchJson(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -886,8 +893,8 @@ func TestStringMatchFail(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -926,9 +933,10 @@ func TestTimeout(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"result_type": "timeout",
|
||||
|
|
@ -962,13 +970,7 @@ func TestBadRegex(t *testing.T) {
|
|||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.Error(t, err)
|
||||
|
||||
absentFields := []string{"http_response_code", "response_time", "content_length", "response_string_match", "result_type", "result_code"}
|
||||
absentTags := []string{"status_code", "result", "server", "method"}
|
||||
checkOutput(t, &acc, nil, nil, absentFields, absentTags)
|
||||
require.ErrorContains(t, h.Init(), "failed to compile regular expression")
|
||||
}
|
||||
|
||||
type fakeClient struct {
|
||||
|
|
@ -981,6 +983,10 @@ func (f *fakeClient) Do(_ *http.Request) (*http.Response, error) {
|
|||
}
|
||||
|
||||
func TestNetworkErrors(t *testing.T) {
|
||||
cl := client{
|
||||
httpClient: &fakeClient{err: &url.Error{Err: &net.OpError{Err: &net.DNSError{Err: "DNS error"}}}},
|
||||
address: "",
|
||||
}
|
||||
// DNS error
|
||||
h := &HTTPResponse{
|
||||
Log: testutil.Logger{},
|
||||
|
|
@ -989,12 +995,12 @@ func TestNetworkErrors(t *testing.T) {
|
|||
Method: "GET",
|
||||
ResponseTimeout: config.Duration(time.Second * 20),
|
||||
FollowRedirects: false,
|
||||
client: &fakeClient{err: &url.Error{Err: &net.OpError{Err: &net.DNSError{Err: "DNS error"}}}},
|
||||
clients: []client{cl},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"result_type": "dns_error",
|
||||
|
|
@ -1020,8 +1026,8 @@ func TestNetworkErrors(t *testing.T) {
|
|||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"result_type": "connection_failed",
|
||||
|
|
@ -1053,9 +1059,10 @@ func TestContentLength(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -1084,9 +1091,10 @@ func TestContentLength(t *testing.T) {
|
|||
},
|
||||
FollowRedirects: true,
|
||||
}
|
||||
|
||||
acc = testutil.Accumulator{}
|
||||
err = h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields = map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -1116,14 +1124,14 @@ func TestRedirect(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
plugin := &HTTPResponse{
|
||||
h := &HTTPResponse{
|
||||
URLs: []string{ts.URL},
|
||||
ResponseStringMatch: "test",
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := plugin.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expected := []telegraf.Metric{
|
||||
testutil.MustMetric(
|
||||
|
|
@ -1175,8 +1183,8 @@ func TestBasicAuth(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -1208,8 +1216,8 @@ func TestStatusCodeMatchFail(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
|
|
@ -1241,8 +1249,8 @@ func TestStatusCodeMatch(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
|
|
@ -1275,8 +1283,8 @@ func TestStatusCodeAndStringMatch(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
|
|
@ -1310,8 +1318,8 @@ func TestStatusCodeAndStringMatchFail(t *testing.T) {
|
|||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusNoContent,
|
||||
|
|
@ -1348,9 +1356,11 @@ func TestSNI(t *testing.T) {
|
|||
ServerName: "super-special-hostname.example.com",
|
||||
},
|
||||
}
|
||||
|
||||
var acc testutil.Accumulator
|
||||
err := h.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, h.Init())
|
||||
require.NoError(t, h.Gather(&acc))
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"http_response_code": http.StatusOK,
|
||||
"result_type": "success",
|
||||
|
|
@ -1367,3 +1377,90 @@ func TestSNI(t *testing.T) {
|
|||
absentFields := []string{"response_string_match"}
|
||||
checkOutput(t, &acc, expectedFields, expectedTags, absentFields, nil)
|
||||
}
|
||||
|
||||
func Test_isURLInIPv6(t *testing.T) {
|
||||
tests := []struct {
|
||||
address url.URL
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
address: parseURL(t, "http://[2001:db8:a0b:12f0::1]/index.html"),
|
||||
want: true,
|
||||
}, {
|
||||
address: parseURL(t, "http://[2001:db8:a0b:12f0::1]:80/index.html"),
|
||||
want: true,
|
||||
}, {
|
||||
address: parseURL(t, "https://[2001:db8:a0b:12f0::1%25eth0]:15000/"), // `%25` escapes `%`
|
||||
want: true,
|
||||
}, {
|
||||
address: parseURL(t, "https://2001:0db8:0001:0000:0000:0ab9:C0A8:0102"),
|
||||
want: true,
|
||||
}, {
|
||||
address: parseURL(t, "http://[2607:f8b0:4005:802::1007]/"),
|
||||
want: true,
|
||||
}, {
|
||||
address: parseURL(t, "https://127.0.0.1"),
|
||||
want: false,
|
||||
}, {
|
||||
address: parseURL(t, "https://google.com"),
|
||||
want: false,
|
||||
}, {
|
||||
address: parseURL(t, "https://thispagemayexist.ornot/index.html"),
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.address.String(), func(t *testing.T) {
|
||||
if got, _ := isURLInIPv6(tt.address); got != tt.want {
|
||||
t.Errorf("isURLInIPv6() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isIPNetInIPv6(t *testing.T) {
|
||||
tests := []struct {
|
||||
address *net.IPNet
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
address: &net.IPNet{
|
||||
IP: net.IPv4(127, 0, 0, 1),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
address: &net.IPNet{
|
||||
IP: net.IP{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||
Mask: net.CIDRMask(128, 128),
|
||||
},
|
||||
want: true,
|
||||
}, {
|
||||
address: &net.IPNet{
|
||||
IP: net.IPv4(192, 168, 0, 1),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
want: false,
|
||||
}, {
|
||||
address: &net.IPNet{
|
||||
IP: net.ParseIP("fe80::43ac:7835:471a:faba"),
|
||||
Mask: net.CIDRMask(64, 128),
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.address.String(), func(t *testing.T) {
|
||||
if got := isIPNetInIPv6(tt.address); got != tt.want {
|
||||
t.Errorf("isIPNetInIPv6() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func parseURL(t *testing.T, address string) url.URL {
|
||||
u, err := url.Parse(address)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, u)
|
||||
return *u
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue