Skip to content

Commit

Permalink
http_proxy: add proxy errors metric
Browse files Browse the repository at this point in the history
Fixes #458
  • Loading branch information
Choraden authored and mmatczuk committed Oct 17, 2023
1 parent 8088def commit af5d444
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 10 deletions.
27 changes: 17 additions & 10 deletions http_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,33 @@ func (hp *HTTPProxy) errorResponse(req *http.Request, err error) *http.Response
}

var (
code int
msg string
code int
msg, label string
)
for _, h := range handlers {
code, msg = h(req, err)
code, msg, label = h(req, err)
if code != 0 {
break
}
}
if code == 0 {
code = http.StatusInternalServerError
msg = "An unexpected error occurred"
label = "unexpected_error"
}

hp.metrics.error(label)

resp := proxyutil.NewResponse(code, bytes.NewBufferString(msg+"\n"), req)
resp.Header.Set(ErrorHeader, err.Error())
resp.Header.Set("Content-Type", "text/plain; charset=utf-8")
resp.ContentLength = int64(len(msg) + 1)
return resp
}

type errorHandler func(*http.Request, error) (int, string)
type errorHandler func(*http.Request, error) (int, string, string)

func handleNetError(_ *http.Request, err error) (code int, msg string) {
func handleNetError(_ *http.Request, err error) (code int, msg, label string) {
var netErr *net.OpError
if errors.As(err, &netErr) {
if netErr.Timeout() {
Expand All @@ -72,36 +75,40 @@ func handleNetError(_ *http.Request, err error) (code int, msg string) {
code = http.StatusBadGateway
msg = "Failed to connect to remote host"
}
label = "net_" + netErr.Op
}

return
}

func handleTLSRecordHeader(_ *http.Request, err error) (code int, msg string) {
func handleTLSRecordHeader(_ *http.Request, err error) (code int, msg, label string) {
var headerErr *tls.RecordHeaderError
if errors.As(err, &headerErr) {
code = http.StatusBadGateway
msg = "TLS handshake failed"
label = "tls_record_header"
}

return
}

func handleTLSCertificateError(_ *http.Request, err error) (code int, msg string) {
func handleTLSCertificateError(_ *http.Request, err error) (code int, msg, label string) {
var certErr *tls.CertificateVerificationError
if errors.As(err, &certErr) {
code = http.StatusBadGateway
msg = "TLS handshake failed"
label = "tls_certificate"
}

return
}

func handleDenyError(req *http.Request, err error) (code int, msg string) {
func handleDenyError(req *http.Request, err error) (code int, msg, label string) {
var denyErr denyError
if errors.As(err, &denyErr) {
code = http.StatusForbidden
msg = fmt.Sprintf("proxying is denied to host %q", req.Host)
label = "denied"
}

return
Expand All @@ -110,11 +117,11 @@ func handleDenyError(req *http.Request, err error) (code int, msg string) {
// There is a difference between sending HTTP and HTTPS requests in the presence of an upstream proxy.
// For HTTPS client issues a CONNECT request to the proxy and then sends the original request.
// In case the proxy responds with status code 4XX or 5XX to the CONNECT request, the client interprets it as URL error.
func handleStatusText(req *http.Request, err error) (code int, msg string) {
func handleStatusText(req *http.Request, err error) (code int, msg, label string) {
if req.URL.Scheme == "https" && err != nil {
for i := 400; i < 600; i++ {
if err.Error() == http.StatusText(i) {
return i, err.Error()
return i, err.Error(), "https_status_text"
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions http_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ type HTTPProxy struct {
creds *CredentialsMatcher
transport http.RoundTripper
log log.Logger
metrics *httpProxyMetrics
proxy *martian.Proxy
mitmCACert *x509.Certificate
proxyFunc ProxyFunc
Expand Down Expand Up @@ -200,6 +201,7 @@ func newHTTPProxy(cfg *HTTPProxyConfig, pr PACResolver, cm *CredentialsMatcher,
creds: cm,
transport: rt,
log: log,
metrics: newMetrics(cfg.PromRegistry, cfg.PromNamespace),
}

if err := hp.configureProxy(); err != nil {
Expand Down
35 changes: 35 additions & 0 deletions http_proxy_metrics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Copyright 2023 Sauce Labs Inc. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

package forwarder

import (
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
)

type httpProxyMetrics struct {
errors *prometheus.CounterVec
}

func newMetrics(r prometheus.Registerer, namespace string) *httpProxyMetrics {
if r == nil {
r = prometheus.NewRegistry() // This registry will be discarded.
}
f := promauto.With(r)

return &httpProxyMetrics{
errors: f.NewCounterVec(prometheus.CounterOpts{
Name: "proxy_errors_total",
Namespace: namespace,
Help: "Number of proxy errors",
}, []string{"reason"}),
}
}

func (m *httpProxyMetrics) error(reason string) {
m.errors.WithLabelValues(reason).Inc()
}

0 comments on commit af5d444

Please sign in to comment.