From fe2ddd9ee7d8d08ba2a8f851528121fcaa0acc8a Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 12:30:11 +0200 Subject: [PATCH 1/7] check_http: improve argument description text make all of them start with uppercase letters --- docs/checks/plugins/check_http.md | 35 ++++++++++++++++--------------- pkg/check_http/check_http.go | 30 +++++++++++++------------- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/docs/checks/plugins/check_http.md b/docs/checks/plugins/check_http.md index 15544fba..4341035f 100644 --- a/docs/checks/plugins/check_http.md +++ b/docs/checks/plugins/check_http.md @@ -67,15 +67,16 @@ Application Options: -s, --string= String to expect in the content --base64-string= Base64 Encoded string to expect the content -A, --useragent= UserAgent to be sent (default: check_http) - -a, --authorization= username:password on sites with basic authentication + -a, --authorization= Pass '[username]:[password]' formatted string to be + used as basic authorization header -k, --header= Any other tags to be sent in http header. Use multiple times for additional headers - -C, --certificate= check certificates instead of content. Specified in + -C, --certificate= Check certificates instead of content. Specified in mandatory days left to warn and optional days to crit with a comma: warn_days[,] - --tls-min=[1.0|1.0+|1.1|1.1+|1.2|1.2+|1.3] minimum supported TLS version. Values with plus set + --tls-min=[1.0|1.0+|1.1|1.1+|1.2|1.2+|1.3] Minimum supported TLS version. Values with plus set the max tls version as well to latest version: 1.3 - --tls-max=[1.0|1.1|1.2|1.3] maximum supported TLS version + --tls-max=[1.0|1.1|1.2|1.3] Maximum supported TLS version --proxy= Proxy that should be used -r, --regex= Search page for case-sensitive regex string -R, --regexi= Search page for case-insensitive regex string @@ -98,26 +99,26 @@ Application Options: unit is given at the end, default of seconds is assumed. Value is truncated to milliseconds. (default: 60) - --wait-for-interval= retry interval (default: 2s) - --wait-for-max= time to wait for success (max.: 180s) - --interim= interval time after successful request for + --wait-for-interval= Retry interval (default: 2s) + --wait-for-max= Time to wait for success (max.: 180s) + --interim= Interval time after successful request for consecutive mode (default: 1s) - --consecutive= number of consecutive successful requests required + --consecutive= Number of consecutive successful requests required (max.: 5) (default: 1) -p, --port= Port number --max-redirs= Maximum redirects before giving up on following - --no-discard raise error when the response body is larger then + --no-discard Raise error when the response body is larger then max-buffer-size - --wait-for retry until successful when enabled - -S, --ssl use https - --sni enable SNI - -4 use tcp4 only - -6 use tcp6 only + --wait-for Retry until successful when enabled + -S, --ssl Use https + --sni Enable SNI + -4 Use tcp4 only + -6 Use tcp6 only -v, --verbose Show verbose output --show-body Print body content below status line - --ignore-certificate-chain by default all certificates are checked in many - aspects. Toggle this option to only check the leaf - (final) certificate. + --ignore-certificate-chain During certificate check, all certificates are + checked in many aspects. Toggle this option to only + check the leaf (final) certificate. --check-cn Subject Common Name of leaf certificate can be checked to match hostname exactly. Common Name field is now largely unused in modern web, with Subject diff --git a/pkg/check_http/check_http.go b/pkg/check_http/check_http.go index 18bd8ace..d54ac5ca 100644 --- a/pkg/check_http/check_http.go +++ b/pkg/check_http/check_http.go @@ -67,11 +67,11 @@ type commandOpts struct { ExpectContent string `short:"s" long:"string" description:"String to expect in the content"` Base64ExpectContent string ` long:"base64-string" description:"Base64 Encoded string to expect the content"` UserAgent string `short:"A" long:"useragent" default:"check_http" description:"UserAgent to be sent"` - Authorization string `short:"a" long:"authorization" description:"username:password on sites with basic authentication"` + Authorization string `short:"a" long:"authorization" description:"Pass '[username]:[password]' formatted string to be used as basic authorization header"` Header []string `short:"k" long:"header" description:"Any other tags to be sent in http header. Use multiple times for additional headers"` - Certificate string `short:"C" long:"certificate" description:"check certificates instead of content. Specified in mandatory days left to warn and optional days to crit with a comma: warn_days[,]" ` - TLSMinVersion string ` long:"tls-min" description:"minimum supported TLS version. Values with plus set the max tls version as well to latest version: 1.3" choice:"1.0" choice:"1.0+" choice:"1.1" choice:"1.1+" choice:"1.2" choice:"1.2+" choice:"1.3"` - TLSMaxVersion string ` long:"tls-max" description:"maximum supported TLS version" choice:"1.0" choice:"1.1" choice:"1.2" choice:"1.3"` + Certificate string `short:"C" long:"certificate" description:"Check certificates instead of content. Specified in mandatory days left to warn and optional days to crit with a comma: warn_days[,]" ` + TLSMinVersion string ` long:"tls-min" description:"Minimum supported TLS version. Values with plus set the max tls version as well to latest version: 1.3" choice:"1.0" choice:"1.0+" choice:"1.1" choice:"1.1+" choice:"1.2" choice:"1.2+" choice:"1.3"` + TLSMaxVersion string ` long:"tls-max" description:"Maximum supported TLS version" choice:"1.0" choice:"1.1" choice:"1.2" choice:"1.3"` Proxy string ` long:"proxy" description:"Proxy that should be used"` RegexStr string `short:"r" long:"regex" description:"Search page for case-sensitive regex string"` RegexiStr string `short:"R" long:"regexi" description:"Search page for case-insensitive regex string"` @@ -80,21 +80,21 @@ type commandOpts struct { TimeoutStr string `short:"t" long:"timeout" default:"10" description:"Timeout to wait for connection. If no time unit is given at the end, default of seconds is assumed"` WarningThresholdStr string `short:"w" long:"warning" default:"30" description:"If the request+response takes longer specified warning threshold, raises a warning. If no time unit is given at the end, default of seconds is assumed. Value is truncated to milliseconds."` CriticalThresholdStr string `short:"c" long:"critical" default:"60" description:"If the request+response takes longer specified critical threshold, raises a critical. If no time unit is given at the end, default of seconds is assumed. Value is truncated to milliseconds."` - WaitForInterval time.Duration ` long:"wait-for-interval" default:"2s" description:"retry interval"` - WaitForMax time.Duration ` long:"wait-for-max" description:"time to wait for success (max.: 180s)"` - Interim time.Duration ` long:"interim" default:"1s" description:"interval time after successful request for consecutive mode"` - Consecutive int ` long:"consecutive" default:"1" description:"number of consecutive successful requests required (max.: 5)"` + WaitForInterval time.Duration ` long:"wait-for-interval" default:"2s" description:"Retry interval"` + WaitForMax time.Duration ` long:"wait-for-max" description:"Time to wait for success (max.: 180s)"` + Interim time.Duration ` long:"interim" default:"1s" description:"Interval time after successful request for consecutive mode"` + Consecutive int ` long:"consecutive" default:"1" description:"Number of consecutive successful requests required (max.: 5)"` Port int `short:"p" long:"port" description:"Port number"` MaxRedirects int ` long:"max-redirs" description:"Maximum redirects before giving up on following"` - NoDiscard bool ` long:"no-discard" description:"raise error when the response body is larger then max-buffer-size"` - WaitFor bool ` long:"wait-for" description:"retry until successful when enabled"` - SSL bool `short:"S" long:"ssl" description:"use https"` - SNI bool ` long:"sni" description:"enable SNI"` - TCP4 bool `short:"4" description:"use tcp4 only"` - TCP6 bool `short:"6" description:"use tcp6 only"` + NoDiscard bool ` long:"no-discard" description:"Raise error when the response body is larger then max-buffer-size"` + WaitFor bool ` long:"wait-for" description:"Retry until successful when enabled"` + SSL bool `short:"S" long:"ssl" description:"Use https"` + SNI bool ` long:"sni" description:"Enable SNI"` + TCP4 bool `short:"4" description:"Use tcp4 only"` + TCP6 bool `short:"6" description:"Use tcp6 only"` Verbose bool `short:"v" long:"verbose" description:"Show verbose output"` ShowBody bool ` long:"show-body" description:"Print body content below status line"` - IgnoreCertificateChain bool ` long:"ignore-certificate-chain" description:"by default all certificates are checked in many aspects. Toggle this option to only check the leaf (final) certificate."` + IgnoreCertificateChain bool ` long:"ignore-certificate-chain" description:"During certificate check, all certificates are checked in many aspects. Toggle this option to only check the leaf (final) certificate."` CheckCN bool ` long:"check-cn" description:"Subject Common Name of leaf certificate can be checked to match hostname exactly. Common Name field is now largely unused in modern web, with Subject Alternative Name fields being more prevalent and used instead of Common Name when present. It is not checked by default, use this flag to enable it."` CheckSAN bool ` long:"check-san" description:"Subject Alternative Names can be checked against the hostname. SANs contain the hostnames and IP addresses this certificate is valid for. They are ignored if the certificate is a Certificate Authority type, meaning they are used to sign other certificates and not for proving security for a hostname. It is not checked by default, use this flag to enable it."` IgnoreNotAfter bool ` long:"ignore-not-after" description:"Certificates are invalid after the timestamp in their NotAfter has passed. This field can be ignored with this flag."` From 01aaea0c4461cd6b53a1d85bc2a9c5a8283391d1 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 12:31:56 +0200 Subject: [PATCH 2/7] check_http: debug log used proxy --- pkg/check_http/check_http.go | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/pkg/check_http/check_http.go b/pkg/check_http/check_http.go index d54ac5ca..f00300b4 100644 --- a/pkg/check_http/check_http.go +++ b/pkg/check_http/check_http.go @@ -115,6 +115,18 @@ func (opts *commandOpts) tracef(format string, args ...any) { } } +func (opts *commandOpts) debugf(format string, args ...any) { + if !opts.flags.Verbose { + return + } + + if opts.log != nil { + opts.log.Debugf(format, args...) + } else { + log.Printf(format, args...) + } +} + func makeTLSConfig(opts *commandOpts) (conf *tls.Config) { //nolint:gosec // TLS check is deliberately skipped, certificate checks are done in its separate function conf = &tls.Config{ @@ -171,12 +183,32 @@ func makeDialer(opts *commandOpts) func(ctx context.Context, _ string, _ string) func makeTransport(opts *commandOpts, dialFunc func(ctx context.Context, _ string, _ string) (net.Conn, error), tlsConfig *tls.Config) (http.RoundTripper, error) { proxy := http.ProxyFromEnvironment + var parsedURL *url.URL + proxyScheme := "" + if opts.flags.Proxy != "" { - parsedURL, err := url.Parse(opts.flags.Proxy) + var err error + parsedURL, err = url.Parse(opts.flags.Proxy) if err != nil { return nil, fmt.Errorf("Error while parsing Proxy URL. Error was: %s", err.Error()) } + opts.debugf("Proxy used: %q", parsedURL) + + proxyScheme = parsedURL.Scheme + if proxyScheme == "" { + proxyScheme = "http" + } + + opts.debugf("Proxy is using scheme: %q", proxyScheme) + + switch proxyScheme { + case "https": + opts.debugf("This means a TLS connection will be established to the proxy") + case "socks4a", "socks5h": + opts.debugf("This means that the proxy will resolve the target hostname") + } + proxy = http.ProxyURL(parsedURL) } From 2ca227ce26930be7bba69101ad3045f121321a81 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 12:33:25 +0200 Subject: [PATCH 3/7] check_http: fix proxy usage dialer function was always dialing the target address/IP, it was not dialing the proxy even if it was present --- pkg/check_http/check_http.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/pkg/check_http/check_http.go b/pkg/check_http/check_http.go index f00300b4..4ec10522 100644 --- a/pkg/check_http/check_http.go +++ b/pkg/check_http/check_http.go @@ -170,10 +170,17 @@ func makeDialer(opts *commandOpts) func(ctx context.Context, _ string, _ string) tcpMode = "tcp6" } - dialFunc := func(ctx context.Context, _, _ string) (net.Conn, error) { - addr := net.JoinHostPort(opts.flags.IPAddress, strconv.Itoa(opts.flags.Port)) + dialFunc := func(ctx context.Context, _ string, addr string) (net.Conn, error) { + // when a proxy is configured, the http transport passes the proxy address as addr, need to dial the proxy instead of the target + if opts.flags.Proxy != "" && addr != "" { + return baseDialFunc(ctx, tcpMode, addr) + } + + // otherwise it according to -I/-p + // also used by the -C certificate check which calls dialFunc with an empty addr + targetAddr := net.JoinHostPort(opts.flags.IPAddress, strconv.Itoa(opts.flags.Port)) - return baseDialFunc(ctx, tcpMode, addr) + return baseDialFunc(ctx, tcpMode, targetAddr) } return dialFunc From 8277868d34c5f45186fde697d94056012a7ab3f9 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 12:38:32 +0200 Subject: [PATCH 4/7] check_http: ai assisted explicitly set transport.DialTLSContext if the proxy schmee is https this is due to prevent possible confusion around TLS checks in the normal usage. the TLS errors regarding the target website are ignored, certificates are only checked when --certificate mode is turned on but if the proxy is using "https" scheme, a valid TLS connection is required and checked, independently of the target website. Add logs around this, and explicitly set transport.DialTlsContext. This is separate from transport.DialContext --- pkg/check_http/check_http.go | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/pkg/check_http/check_http.go b/pkg/check_http/check_http.go index 4ec10522..d6035b43 100644 --- a/pkg/check_http/check_http.go +++ b/pkg/check_http/check_http.go @@ -219,7 +219,7 @@ func makeTransport(opts *commandOpts, dialFunc func(ctx context.Context, _ strin proxy = http.ProxyURL(parsedURL) } - return &http.Transport{ + transport := &http.Transport{ // inherited http.DefaultTransport Proxy: proxy, DialContext: dialFunc, @@ -230,7 +230,32 @@ func makeTransport(opts *commandOpts, dialFunc func(ctx context.Context, _ strin ResponseHeaderTimeout: opts.TimeoutParsed, TLSClientConfig: tlsConfig, ForceAttemptHTTP2: true, - }, nil + } + + if proxyScheme == "https" { + opts.debugf("The proxy certificate will be verified") + + proxyTLSConfig := makeProxyTLSConfig(opts, parsedURL) + transport.DialTLSContext = func(ctx context.Context, network, addr string) (net.Conn, error) { + conn, err := dialFunc(ctx, network, addr) + if err != nil { + return nil, err + } + + return tls.Client(conn, proxyTLSConfig), nil + } + } + + return transport, nil +} + +// makeProxyTLSConfig returns a tls config that verifies the certificate of an https proxy. +func makeProxyTLSConfig(opts *commandOpts, proxyURL *url.URL) *tls.Config { + return &tls.Config{ + ServerName: proxyURL.Hostname(), + MinVersion: opts.tlsMinVersion, + MaxVersion: opts.tlsMaxVersion, + } } func buildRequest(ctx context.Context, opts *commandOpts) (*http.Request, error) { From 1722692dcf500325771e8322c35b6d42d3d1b332 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 13:49:13 +0200 Subject: [PATCH 5/7] check_http: ai assisted add tests regarding proxy usage TestHTTPProxyPlain -> proxy returns immediately and increments a counter, to see if proxy is connected TestHTTPProxySSL -> target is using HTTPS, and proxy is using HTTP. Connection to proxy has nothing to check, and target using a self-signed HTTPS certificate is ignored, as in the default mode. TestHTTPProxySSLSelfSignedProxy -> target is using HTTP, and proxy is using HTTPS. TLS certificate of the proxy is verified, and it fails since it is self-signed, returnin an early CRITICAL before connecting to target. --- pkg/check_http/check_http_test.go | 209 ++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/pkg/check_http/check_http_test.go b/pkg/check_http/check_http_test.go index 020e28be..bdc09887 100644 --- a/pkg/check_http/check_http_test.go +++ b/pkg/check_http/check_http_test.go @@ -2,11 +2,20 @@ package check_http import ( "context" + "crypto/tls" + "fmt" + "io" + "net" + "net/http" + "net/http/httptest" + "net/url" "strings" + "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -232,3 +241,203 @@ func TestHTTPExpectStatusCode(t *testing.T) { expectedStr := `matched option '200'` assert.Containsf(t, output.String(), expectedStr, "expected output to contain '%s'", expectedStr) } + +func TestHTTPProxyPlain(t *testing.T) { + if testing.Short() { + t.Skip("skipping network test in short mode") + } + + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, "direct-target-response") + })) + defer target.Close() + + targetURL, err := url.Parse(target.URL) + require.NoError(t, err) + + var proxied atomic.Bool + proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + proxied.Store(true) + fmt.Fprint(w, "proxied-response") + })) + defer proxy.Close() + + var output strings.Builder + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + code := Check(ctx, &output, []string{ + "check_http", "-H", targetURL.Host, "-p", "80", + "--proxy", proxy.URL, "-s", "proxied-response", + }) + + // it should connect to the proxy first + // the mock proxy just returns a text and does not actually proxy the request + assert.Equalf(t, OK, code, "expected exit code OK (0), got %d, output: %s", code, output.String()) + assert.Truef(t, proxied.Load(), "request did not go through the proxy, output: %s", output.String()) + assert.Containsf(t, output.String(), "proxied-response", "expected output to contain 'proxied-response'") + assert.NotContainsf(t, output.String(), "direct-target-response", "response should come from the proxy, not the target") +} + +func TestHTTPProxySSL(t *testing.T) { + if testing.Short() { + t.Skip("skipping network test in short mode") + } + + // the target is a TLS address on a TLS server + target := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, "proxied-target-response") + })) + defer target.Close() + + targetURL, err := url.Parse(target.URL) + require.NoError(t, err) + + var proxied atomic.Int64 + // this is a more realistic proxy server, which actually proxies the requests + // it is a http server and not HTTPS + proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { + // only accept the HTTP CONNECT method. This should be set by the http client + if req.Method != http.MethodConnect { + http.Error(writer, "expected CONNECT request", http.StatusBadRequest) + + return + } + proxied.Add(1) + + // connect to the target + dest, err := net.Dial("tcp", req.Host) + if err != nil { + http.Error(writer, err.Error(), http.StatusBadGateway) + + return + } + defer dest.Close() + + // hijacker stops the default handling and lifecycle management over the connection + hijacker, ok := writer.(http.Hijacker) + if !ok { + http.Error(writer, "hijacking not supported", http.StatusInternalServerError) + + return + } + + clientConn, _, err := hijacker.Hijack() + if err != nil { + return + } + defer clientConn.Close() + + // this is the header for the proxy connection establishment + fmt.Fprint(clientConn, "HTTP/1.1 200 Connection Established\r\n\r\n") + + // continiously copy the proxy client request to the target + go func() { + _, _ = io.Copy(dest, clientConn) + }() + + // continiously copy the target responses to client + _, _ = io.Copy(clientConn, dest) + })) + defer proxy.Close() + + var output strings.Builder + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + // since the proxy actuall does proxying, this should get the result of the target + // the proxy is using http, there are no certificates to verify about proxy. + // the target is using https, but we are not in the --certificate mode where we perform certificate checks + code := Check(ctx, &output, []string{ + "check_http", "-H", targetURL.Host, "-S", "--sni", + "--proxy", proxy.URL, "-s", "proxied-target-response", + }) + + assert.Equalf(t, OK, code, "expected exit code OK (0), got %d, output: %s", code, output.String()) + assert.GreaterOrEqualf(t, proxied.Load(), int64(1), "expected a CONNECT request to go through the proxy, output: %s", output.String()) + assert.Containsf(t, output.String(), "proxied-target-response", "expected output to contain 'proxied-target-response'") +} + +func TestMakeProxyTLSConfig(t *testing.T) { + opts := &commandOpts{} + opts.tlsMinVersion = tls.VersionTLS12 + opts.tlsMaxVersion = tls.VersionTLS13 + + proxyURL, err := url.Parse("https://proxy.example.com:8080") + require.NoError(t, err) + + // the default config to the proxy, when its using TLS is to + // NOT skip TLS verification, and PERFORM the TLS verification + conf := makeProxyTLSConfig(opts, proxyURL) + assert.Equal(t, "proxy.example.com", conf.ServerName) + assert.False(t, conf.InsecureSkipVerify) + assert.Equal(t, uint16(tls.VersionTLS12), conf.MinVersion) + assert.Equal(t, uint16(tls.VersionTLS13), conf.MaxVersion) +} + +func TestHTTPProxySSLSelfSignedProxy(t *testing.T) { + if testing.Short() { + t.Skip("skipping network test in short mode") + } + + target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + fmt.Fprint(w, "target-response") + })) + defer target.Close() + + targetURL, err := url.Parse(target.URL) + require.NoError(t, err) + + // this is also a working proxy, but it is a TLS server + // newTLSServer is using self-signed certificates by default, the certificates are not changed + proxy := httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { + if req.Method != http.MethodConnect { + http.Error(writer, "expected CONNECT request", http.StatusBadRequest) + + return + } + + dest, err := net.Dial("tcp", req.Host) + if err != nil { + http.Error(writer, err.Error(), http.StatusBadGateway) + + return + } + defer dest.Close() + + hijacker, ok := writer.(http.Hijacker) + if !ok { + http.Error(writer, "hijacking not supported", http.StatusInternalServerError) + + return + } + + clientConn, _, err := hijacker.Hijack() + if err != nil { + return + } + defer clientConn.Close() + + fmt.Fprint(clientConn, "HTTP/1.1 200 Connection Established\r\n\r\n") + + go func() { + _, _ = io.Copy(dest, clientConn) + }() + _, _ = io.Copy(clientConn, dest) + })) + defer proxy.Close() + + var output strings.Builder + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + + // the proxy is using HTTPS, so proxy HTTPS should be verified + // the target is using HTTP, but before reaching the target, the proxy TLS check fails + code := Check(ctx, &output, []string{ + "check_http", "-H", targetURL.Host, "--proxy", proxy.URL, "-u", "/", + }) + + assert.Equalf(t, CRITICAL, code, "expected exit code CRITICAL (2), got %d, output: %s", code, output.String()) + assert.Containsf(t, output.String(), "failed to verify certificate", "expected a proxy certificate verification error, output: %s", output.String()) + assert.Containsf(t, output.String(), "unknown authority", "expected an untrusted certificate error, output: %s", output.String()) +} From 75d812c090ad114d91dba14168ec175a579302ba Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 13:50:26 +0200 Subject: [PATCH 6/7] check_http: improve comments in testing and golangci --- pkg/check_http/check_http_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/check_http/check_http_test.go b/pkg/check_http/check_http_test.go index bdc09887..48c35372 100644 --- a/pkg/check_http/check_http_test.go +++ b/pkg/check_http/check_http_test.go @@ -295,7 +295,7 @@ func TestHTTPProxySSL(t *testing.T) { var proxied atomic.Int64 // this is a more realistic proxy server, which actually proxies the requests - // it is a http server and not HTTPS + // it is an http server and not HTTPS proxy := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { // only accept the HTTP CONNECT method. This should be set by the http client if req.Method != http.MethodConnect { @@ -331,12 +331,12 @@ func TestHTTPProxySSL(t *testing.T) { // this is the header for the proxy connection establishment fmt.Fprint(clientConn, "HTTP/1.1 200 Connection Established\r\n\r\n") - // continiously copy the proxy client request to the target + // continuously copy the proxy client request to the target go func() { _, _ = io.Copy(dest, clientConn) }() - // continiously copy the target responses to client + // continuously copy the target responses to client _, _ = io.Copy(clientConn, dest) })) defer proxy.Close() @@ -345,7 +345,7 @@ func TestHTTPProxySSL(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() - // since the proxy actuall does proxying, this should get the result of the target + // since the proxy actually does proxying, this should get the result of the target // the proxy is using http, there are no certificates to verify about proxy. // the target is using https, but we are not in the --certificate mode where we perform certificate checks code := Check(ctx, &output, []string{ @@ -388,8 +388,9 @@ func TestHTTPProxySSLSelfSignedProxy(t *testing.T) { targetURL, err := url.Parse(target.URL) require.NoError(t, err) - // this is also a working proxy, but it is a TLS server - // newTLSServer is using self-signed certificates by default, the certificates are not changed + // a TLS proxy with a self-signed certificate (httptest.NewTLSServer default). + // The CONNECT-tunnel handler is never reached in this test: the client + // fails to verify the proxy certificate during the TLS handshake. proxy := httptest.NewTLSServer(http.HandlerFunc(func(writer http.ResponseWriter, req *http.Request) { if req.Method != http.MethodConnect { http.Error(writer, "expected CONNECT request", http.StatusBadRequest) @@ -431,7 +432,7 @@ func TestHTTPProxySSLSelfSignedProxy(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() - // the proxy is using HTTPS, so proxy HTTPS should be verified + // the proxy is using HTTPS, so the proxy's HTTPS connection should be verified // the target is using HTTP, but before reaching the target, the proxy TLS check fails code := Check(ctx, &output, []string{ "check_http", "-H", targetURL.Host, "--proxy", proxy.URL, "-u", "/", From ffa4c7a6f97e8b778a2e4c1737bc40f7354d5470 Mon Sep 17 00:00:00 2001 From: Ahmet Oeztuerk Date: Tue, 11 Aug 2026 14:08:02 +0200 Subject: [PATCH 7/7] check_http: ai commit, correctly log about different proxy scheme behavior i let deepseek-v4-flash dig through the golang net code and see its supported proxy schemes --- pkg/check_http/check_http.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/check_http/check_http.go b/pkg/check_http/check_http.go index d6035b43..d1471772 100644 --- a/pkg/check_http/check_http.go +++ b/pkg/check_http/check_http.go @@ -212,8 +212,12 @@ func makeTransport(opts *commandOpts, dialFunc func(ctx context.Context, _ strin switch proxyScheme { case "https": opts.debugf("This means a TLS connection will be established to the proxy") - case "socks4a", "socks5h": - opts.debugf("This means that the proxy will resolve the target hostname") + case "socks5", "socks5h": + opts.debugf("This means the proxy will resolve the target hostname") + case "socks4a": + opts.debugf("socks4a is not supported by the go http client, only socks5/socks5h is supported") + default: + opts.debugf("Using proxy with unsupported scheme: %q", proxyScheme) } proxy = http.ProxyURL(parsedURL)