From 2e76e0a15c5aa50eba3ae8007c8240b7546fecae Mon Sep 17 00:00:00 2001 From: Yvonnick Esnault Date: Wed, 10 Feb 2021 09:29:07 +0100 Subject: [PATCH] feat(executor/http): resolve attribute (#362) * feat(executor/http): resolve attribute Signed-off-by: Yvonnick Esnault --- executors/http/README.md | 3 ++- executors/http/http.go | 28 +++++++++++++++++++++++++++- tests/http_resolve_test.yml | 14 ++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/http_resolve_test.yml diff --git a/executors/http/README.md b/executors/http/README.md index 193af8ba..2503b33a 100644 --- a/executors/http/README.md +++ b/executors/http/README.md @@ -14,6 +14,7 @@ In your yaml file, you can use: - bodyFile optional - headers optional - proxy optional: set to use a proxy server for connection to url + - resolve optional: array of custom resolver of host and port pair. example: foo.com:443:127.0.0.1 - ignore_verify_ssl optional: set to true if you use a self-signed SSL on remote for example - basic_auth_user optional: username to use for HTTP basic authentification - basic_auth_password optional: password to use for HTTP basic authentification @@ -22,7 +23,7 @@ In your yaml file, you can use: - skip_headers: skip the headers result - tls_client_cert optional: a chain of certificates to identify the caller, first certificate in the chain is considered as the leaf, followed by intermediates. Setting it enable mutual TLS authentication - tls_client_key optional: private key corresponding to the certificate. - _ tls_root_ca optional: defines additional root CAs to perform the call. can contains multiple CAs concatenated together + - tls_root_ca optional: defines additional root CAs to perform the call. can contains multiple CAs concatenated together ``` diff --git a/executors/http/http.go b/executors/http/http.go index fe95527f..03bfd148 100644 --- a/executors/http/http.go +++ b/executors/http/http.go @@ -49,6 +49,7 @@ type Executor struct { SkipHeaders bool `json:"skip_headers" yaml:"skip_headers" mapstructure:"skip_headers"` SkipBody bool `json:"skip_body" yaml:"skip_body" mapstructure:"skip_body"` Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"` + Resolve []string `json:"resolve" yaml:"resolve" mapstructure:"resolve"` NoFollowRedirect bool `json:"no_follow_redirect" yaml:"no_follow_redirect" mapstructure:"no_follow_redirect"` UnixSock string `json:"unix_sock" yaml:"unix_sock" mapstructure:"unix_sock"` TLSClientCert string `json:"tls_client_cert" yaml:"tls_client_cert" mapstructure:"tls_client_cert"` @@ -136,7 +137,31 @@ func (Executor) Run(ctx context.Context, step venom.TestStep) (interface{}, erro return nil, err } - if len(e.UnixSock) > 0 { + if len(e.Resolve) > 0 && len(e.UnixSock) > 0 { + return nil, fmt.Errorf("you can't use resolve and unix_sock attributes in the same time") + } + + if len(e.Resolve) > 0 { + tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + // resolve can contains foo.com:443:127.0.0.1 + for _, r := range e.Resolve { + tuple := strings.Split(r, ":") + if len(tuple) != 3 { + return nil, fmt.Errorf("invalid value for resolve attribute: %v", e.Resolve) + } + if addr == tuple[0]+":"+tuple[1] { + addr = tuple[2] + ":" + tuple[1] + } + } + + dialer := &net.Dialer{ + Timeout: 30 * time.Second, + KeepAlive: 30 * time.Second, + DualStack: true, + } + return dialer.DialContext(ctx, network, addr) + } + } else if len(e.UnixSock) > 0 { tr.DialContext = func(_ context.Context, _, _ string) (net.Conn, error) { return net.DialUnix("unix", nil, &net.UnixAddr{ Name: e.UnixSock, @@ -152,6 +177,7 @@ func (Executor) Run(ctx context.Context, step venom.TestStep) (interface{}, erro } tr.Proxy = http.ProxyURL(proxyURL) } + client := &http.Client{Transport: tr} if e.NoFollowRedirect { client.CheckRedirect = func(req *http.Request, via []*http.Request) error { diff --git a/tests/http_resolve_test.yml b/tests/http_resolve_test.yml new file mode 100644 index 00000000..80b1650b --- /dev/null +++ b/tests/http_resolve_test.yml @@ -0,0 +1,14 @@ +name: HTTP testsuite +testcases: +- name: get http testcase + steps: + - type: http + method: GET + url: https://ca.api.ovh.com/1.0/ + resolve: + - ca.api.ovh.com:443:eu.api.ovh.com + ignore_verify_ssl: true + info: + - '{{.result.bodyjson.basepath}}' + assertions: + - result.headers.x-ovh-queryid ShouldContainSubstring "EU.ext"