fix(inputs.http_response): Fix for IPv4 and IPv6 addresses when interface is set (#15496)

This commit is contained in:
Paweł Żak 2024-06-13 15:35:18 +02:00 committed by GitHub
parent caa0cf3a80
commit fc3cbb8256
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 264 additions and 120 deletions

View File

@ -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
View File

@ -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
View File

@ -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=

View File

@ -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"
@ -39,37 +41,42 @@ 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
Body string `toml:"body"`
BodyForm map[string][]string `toml:"body_form"`
Method string
ResponseTimeout config.Duration
Method string `toml:"method"`
ResponseTimeout config.Duration `toml:"response_timeout"`
HTTPHeaderTags map[string]string `toml:"http_header_tags"`
Headers map[string]string
FollowRedirects bool
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

View File

@ -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
}