From f97e3f65fe1ae298f8bb881e8ddb13b232592d8e Mon Sep 17 00:00:00 2001 From: yusing Date: Fri, 8 Nov 2024 06:14:08 +0800 Subject: [PATCH] go version and deps update, fixed middlewares and metrics - fixed "API JWT secret empty" warning output format - fixed metrics initialized when it should not - fixed middlewares.modifyRequest Host header not working properly --- Dockerfile | 2 +- cmd/main.go | 4 ++ go.mod | 4 +- go.sum | 4 +- internal/common/env.go | 6 --- internal/metrics/labels.go | 36 ++++++++++++++ .../metrics/{http_metrics.go => metrics.go} | 48 ++++--------------- internal/metrics/router_metrics.go | 8 +++- internal/net/http/middleware/middleware.go | 6 ++- .../net/http/middleware/modify_request.go | 9 ++-- .../http/middleware/modify_request_test.go | 6 ++- internal/net/http/middleware/x_forwarded.go | 26 ++++++---- internal/watcher/health/monitor.go | 20 ++++---- 13 files changed, 101 insertions(+), 78 deletions(-) create mode 100644 internal/metrics/labels.go rename internal/metrics/{http_metrics.go => metrics.go} (69%) diff --git a/Dockerfile b/Dockerfile index a9b1798..a16be06 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Stage 1: Builder -FROM golang:1.23.2-alpine AS builder +FROM golang:1.23.3-alpine AS builder RUN apk add --no-cache tzdata make WORKDIR /src diff --git a/cmd/main.go b/cmd/main.go index 2668ebd..ffa8f19 100755 --- a/cmd/main.go +++ b/cmd/main.go @@ -110,6 +110,10 @@ func main() { return } + if common.APIJWTSecret == nil { + logging.Warn().Msg("API JWT secret is empty, authentication is disabled") + } + cfg.StartProxyProviders() config.WatchChanges() diff --git a/go.mod b/go.mod index 0e63e33..60dc8c6 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/yusing/go-proxy -go 1.23.2 +go 1.23.3 require ( github.com/coder/websocket v1.8.12 @@ -25,7 +25,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/cloudflare/cloudflare-go v0.108.0 // indirect + github.com/cloudflare/cloudflare-go v0.109.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/go-connections v0.5.0 // indirect diff --git a/go.sum b/go.sum index edc720b..61772cc 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cloudflare/cloudflare-go v0.108.0 h1:C4Skfjd8I8X3uEOGmQUT4/iGyZcWdkIU7HwvMoLkEE0= -github.com/cloudflare/cloudflare-go v0.108.0/go.mod h1:m492eNahT/9MsN7Ppnoge8AaI7QhVFtEgVm3I9HJFeU= +github.com/cloudflare/cloudflare-go v0.109.0 h1:Wjp+RfJD1lidIFUlrTBqUQnCBrUnmVsLxgzWYiURueg= +github.com/cloudflare/cloudflare-go v0.109.0/go.mod h1:m492eNahT/9MsN7Ppnoge8AaI7QhVFtEgVm3I9HJFeU= github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo= github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= diff --git a/internal/common/env.go b/internal/common/env.go index 93bcd35..9ee7277 100644 --- a/internal/common/env.go +++ b/internal/common/env.go @@ -48,12 +48,6 @@ var ( APIPasswordHash = HashPassword(GetEnvString("API_PASSWORD", "password")) ) -func init() { - if APIJWTSecret == nil && GetArgs().Command == CommandStart { - log.Warn().Msg("API JWT secret is empty, authentication is disabled") - } -} - func GetEnv[T any](key string, defaultValue T, parser func(string) (T, error)) T { var value string var ok bool diff --git a/internal/metrics/labels.go b/internal/metrics/labels.go new file mode 100644 index 0000000..0c42c10 --- /dev/null +++ b/internal/metrics/labels.go @@ -0,0 +1,36 @@ +package metrics + +import "github.com/prometheus/client_golang/prometheus" + +type ( + HTTPRouteMetricLabels struct { + Service, Method, Host, Visitor, Path string + } + StreamRouteMetricLabels struct { + Service, Visitor string + } + HealthMetricLabels string +) + +func (lbl *HTTPRouteMetricLabels) toPromLabels() prometheus.Labels { + return prometheus.Labels{ + "service": lbl.Service, + "method": lbl.Method, + "host": lbl.Host, + "visitor": lbl.Visitor, + "path": lbl.Path, + } +} + +func (lbl *StreamRouteMetricLabels) toPromLabels() prometheus.Labels { + return prometheus.Labels{ + "service": lbl.Service, + "visitor": lbl.Visitor, + } +} + +func (lbl HealthMetricLabels) toPromLabels() prometheus.Labels { + return prometheus.Labels{ + "service": string(lbl), + } +} diff --git a/internal/metrics/http_metrics.go b/internal/metrics/metrics.go similarity index 69% rename from internal/metrics/http_metrics.go rename to internal/metrics/metrics.go index 8d280e0..da48f47 100644 --- a/internal/metrics/http_metrics.go +++ b/internal/metrics/metrics.go @@ -7,23 +7,14 @@ import ( "github.com/yusing/go-proxy/internal/common" ) -type ( - RouteMetrics struct { - HTTPReqTotal, - HTTP2xx3xx, - HTTP4xx, - HTTP5xx *Counter - HTTPReqElapsed *Gauge - HealthStatus *Gauge - } - HTTPRouteMetricLabels struct { - Service, Method, Host, Visitor, Path string - } - StreamRouteMetricLabels struct { - Service, Visitor string - } - HealthMetricLabels string -) +type RouteMetrics struct { + HTTPReqTotal, + HTTP2xx3xx, + HTTP4xx, + HTTP5xx *Counter + HTTPReqElapsed *Gauge + HealthStatus *Gauge +} var rm RouteMetrics @@ -38,29 +29,6 @@ func GetRouteMetrics() *RouteMetrics { return &rm } -func (lbl *HTTPRouteMetricLabels) toPromLabels() prometheus.Labels { - return prometheus.Labels{ - "service": lbl.Service, - "method": lbl.Method, - "host": lbl.Host, - "visitor": lbl.Visitor, - "path": lbl.Path, - } -} - -func (lbl *StreamRouteMetricLabels) toPromLabels() prometheus.Labels { - return prometheus.Labels{ - "service": lbl.Service, - "visitor": lbl.Visitor, - } -} - -func (lbl HealthMetricLabels) toPromLabels() prometheus.Labels { - return prometheus.Labels{ - "service": string(lbl), - } -} - func init() { if !common.PrometheusEnabled { return diff --git a/internal/metrics/router_metrics.go b/internal/metrics/router_metrics.go index 9228e8d..1294bf5 100644 --- a/internal/metrics/router_metrics.go +++ b/internal/metrics/router_metrics.go @@ -1,8 +1,14 @@ package metrics -import "github.com/prometheus/client_golang/prometheus" +import ( + "github.com/prometheus/client_golang/prometheus" + "github.com/yusing/go-proxy/internal/common" +) func InitRouterMetrics(getRPsCount func() int, getStreamsCount func() int) { + if !common.PrometheusEnabled { + return + } prometheus.MustRegister(prometheus.NewGaugeFunc(prometheus.GaugeOpts{ Namespace: "entrypoint", Name: "num_reverse_proxies", diff --git a/internal/net/http/middleware/middleware.go b/internal/net/http/middleware/middleware.go index e6fbb73..537939c 100644 --- a/internal/net/http/middleware/middleware.go +++ b/internal/net/http/middleware/middleware.go @@ -2,7 +2,6 @@ package middleware import ( "encoding/json" - "errors" "net/http" "github.com/rs/zerolog" @@ -167,7 +166,10 @@ func patchReverseProxy(rpName string, rp *ReverseProxy, middlewares []*Middlewar if rp.ModifyResponse != nil { ori := rp.ModifyResponse rp.ModifyResponse = func(res *http.Response) error { - return errors.Join(mid.modifyResponse(res), ori(res)) + if err := mid.modifyResponse(res); err != nil { + return err + } + return ori(res) } } else { rp.ModifyResponse = mid.modifyResponse diff --git a/internal/net/http/middleware/modify_request.go b/internal/net/http/middleware/modify_request.go index 02f531c..758b62e 100644 --- a/internal/net/http/middleware/modify_request.go +++ b/internal/net/http/middleware/modify_request.go @@ -1,6 +1,8 @@ package middleware import ( + "net/http" + "github.com/yusing/go-proxy/internal/common" E "github.com/yusing/go-proxy/internal/error" ) @@ -22,11 +24,9 @@ var ModifyRequest = &Middleware{withOptions: NewModifyRequest} func NewModifyRequest(optsRaw OptionsRaw) (*Middleware, E.Error) { mr := new(modifyRequest) - var mrFunc RewriteFunc + mrFunc := mr.modifyRequest if common.IsDebug { mrFunc = mr.modifyRequestWithTrace - } else { - mrFunc = mr.modifyRequest } mr.m = &Middleware{ impl: mr, @@ -41,6 +41,9 @@ func NewModifyRequest(optsRaw OptionsRaw) (*Middleware, E.Error) { func (mr *modifyRequest) modifyRequest(req *Request) { for k, v := range mr.SetHeaders { + if http.CanonicalHeaderKey(k) == "Host" { + req.Host = v + } req.Header.Set(k, v) } for k, v := range mr.AddHeaders { diff --git a/internal/net/http/middleware/modify_request_test.go b/internal/net/http/middleware/modify_request_test.go index 4a2bc92..3a16c47 100644 --- a/internal/net/http/middleware/modify_request_test.go +++ b/internal/net/http/middleware/modify_request_test.go @@ -9,7 +9,10 @@ import ( func TestSetModifyRequest(t *testing.T) { opts := OptionsRaw{ - "set_headers": map[string]string{"User-Agent": "go-proxy/v0.5.0"}, + "set_headers": map[string]string{ + "User-Agent": "go-proxy/v0.5.0", + "Host": "test.example.com", + }, "add_headers": map[string]string{"Accept-Encoding": "test-value"}, "hide_headers": []string{"Accept"}, } @@ -28,6 +31,7 @@ func TestSetModifyRequest(t *testing.T) { }) ExpectNoError(t, err) ExpectEqual(t, result.RequestHeaders.Get("User-Agent"), "go-proxy/v0.5.0") + ExpectEqual(t, result.RequestHeaders.Get("Host"), "test.example.com") ExpectTrue(t, slices.Contains(result.RequestHeaders.Values("Accept-Encoding"), "test-value")) ExpectEqual(t, result.RequestHeaders.Get("Accept"), "") }) diff --git a/internal/net/http/middleware/x_forwarded.go b/internal/net/http/middleware/x_forwarded.go index 1770d3e..2c73e26 100644 --- a/internal/net/http/middleware/x_forwarded.go +++ b/internal/net/http/middleware/x_forwarded.go @@ -2,6 +2,7 @@ package middleware import ( "net" + "strings" ) const ( @@ -15,10 +16,7 @@ const ( var SetXForwarded = &Middleware{ before: Rewrite(func(req *Request) { - req.Header.Del("Forwarded") - req.Header.Del(xForwardedFor) - req.Header.Del(xForwardedHost) - req.Header.Del(xForwardedProto) + delXForwarded(req) clientIP, _, err := net.SplitHostPort(req.RemoteAddr) if err == nil { req.Header.Set(xForwardedFor, clientIP) @@ -35,10 +33,18 @@ var SetXForwarded = &Middleware{ } var HideXForwarded = &Middleware{ - before: Rewrite(func(req *Request) { - req.Header.Del("Forwarded") - req.Header.Del(xForwardedFor) - req.Header.Del(xForwardedHost) - req.Header.Del(xForwardedProto) - }), + before: Rewrite(delXForwarded), +} + +func delXForwarded(req *Request) { + req.Header.Del("Forwarded") + toRemove := make([]string, 0) + for k := range req.Header { + if strings.HasPrefix(k, "X-Forwarded-") { + toRemove = append(toRemove, k) + } + } + for _, k := range toRemove { + req.Header.Del(k) + } } diff --git a/internal/watcher/health/monitor.go b/internal/watcher/health/monitor.go index bd98987..559c255 100644 --- a/internal/watcher/health/monitor.go +++ b/internal/watcher/health/monitor.go @@ -174,16 +174,16 @@ func (mon *monitor) checkUpdateHealth() error { logger.Debug().Msg(detail) notif.Notify(mon.service, "server is down") } - if common.PrometheusEnabled { - go func() { - m := metrics.GetRouteMetrics() - var up float64 - if healthy { - up = 1 - } - m.HealthStatus.With(metrics.HealthMetricLabels(mon.service)).Set(up) - }() - } + } + if common.PrometheusEnabled { + go func() { + m := metrics.GetRouteMetrics() + var up float64 + if healthy { + up = 1 + } + m.HealthStatus.With(metrics.HealthMetricLabels(mon.service)).Set(up) + }() } return nil