From 4c00ecfea6785ad32b08bbbfe6b9c71b844549be Mon Sep 17 00:00:00 2001 From: Felix Gateru Date: Wed, 18 Jun 2025 17:06:00 +0300 Subject: [PATCH] feat(http): add interceptor for http requests Signed-off-by: Felix Gateru --- .env | 1 - cmd/main.go | 6 +++--- pkg/http/http.go | 38 +++++++++++++++++++++++++------------- pkg/http/interceptor.go | 18 ++++++++++++++++++ 4 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 pkg/http/interceptor.go diff --git a/.env b/.env index 2b735a68..afa6b2f4 100644 --- a/.env +++ b/.env @@ -65,7 +65,6 @@ MGATE_HTTP_WITHOUT_TLS_TARGET_HOST=localhost MGATE_HTTP_WITHOUT_TLS_TARGET_PORT=8888 MGATE_HTTP_WITHOUT_TLS_TARGET_PATH=/messages - MGATE_HTTP_WITH_TLS_PORT=8087 MGATE_HTTP_WITH_TLS_PATH_PREFIX=/mgate-http MGATE_HTTP_WITH_TLS_TARGET_PROTOCOL=http diff --git a/cmd/main.go b/cmd/main.go index 17425733..38cae246 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -134,7 +134,7 @@ func main() { } // mGate server for HTTP without TLS - httpProxy, err := http.NewProxy(httpConfig, handler, logger, []string{}, []string{}) + httpProxy, err := http.NewProxy(httpConfig, handler, logger, []string{}, []string{}, nil) if err != nil { panic(err) } @@ -149,7 +149,7 @@ func main() { } // mGate server for HTTP with TLS - httpTLSProxy, err := http.NewProxy(httpTLSConfig, handler, logger, []string{}, []string{}) + httpTLSProxy, err := http.NewProxy(httpTLSConfig, handler, logger, []string{}, []string{}, nil) if err != nil { panic(err) } @@ -164,7 +164,7 @@ func main() { } // mGate server for HTTP with mTLS - httpMTLSProxy, err := http.NewProxy(httpMTLSConfig, handler, logger, []string{}, []string{}) + httpMTLSProxy, err := http.NewProxy(httpMTLSConfig, handler, logger, []string{}, []string{}, nil) if err != nil { panic(err) } diff --git a/pkg/http/http.go b/pkg/http/http.go index a7f522aa..1fcb9bc0 100644 --- a/pkg/http/http.go +++ b/pkg/http/http.go @@ -78,6 +78,16 @@ func (p Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { Username: username, } + if p.interceptor != nil { + var err error + r, err = p.interceptor.Intercept(r.Context(), r) + if err != nil { + encodeError(w, http.StatusBadRequest, err) + p.logger.Error("failed to intercept request", slog.Any("error", err)) + return + } + } + if isWebSocketRequest(r) { //nolint:contextcheck // handleWebSocket does not need context p.handleWebSocket(w, r, s) @@ -140,15 +150,16 @@ func encodeError(w http.ResponseWriter, defStatusCode int, err error) { // Proxy represents HTTP Proxy. type Proxy struct { - config mgate.Config - target *httputil.ReverseProxy - session session.Handler - logger *slog.Logger - wsUpgrader websocket.Upgrader - bypass Checker + config mgate.Config + target *httputil.ReverseProxy + session session.Handler + logger *slog.Logger + wsUpgrader websocket.Upgrader + bypass Checker + interceptor Interceptor } -func NewProxy(config mgate.Config, handler session.Handler, logger *slog.Logger, allowedOrigins []string, bypassPaths []string) (Proxy, error) { +func NewProxy(config mgate.Config, handler session.Handler, logger *slog.Logger, allowedOrigins []string, bypassPaths []string, interceptor Interceptor) (Proxy, error) { targetUrl := &url.URL{ Scheme: config.TargetProtocol, Host: net.JoinHostPort(config.TargetHost, config.TargetPort), @@ -162,12 +173,13 @@ func NewProxy(config mgate.Config, handler session.Handler, logger *slog.Logger, wsUpgrader := websocket.Upgrader{CheckOrigin: checkOrigin(allowedOrigins)} return Proxy{ - config: config, - target: httputil.NewSingleHostReverseProxy(targetUrl), - session: handler, - logger: logger, - wsUpgrader: wsUpgrader, - bypass: bpc, + config: config, + target: httputil.NewSingleHostReverseProxy(targetUrl), + session: handler, + logger: logger, + wsUpgrader: wsUpgrader, + bypass: bpc, + interceptor: interceptor, }, nil } diff --git a/pkg/http/interceptor.go b/pkg/http/interceptor.go new file mode 100644 index 00000000..d55c98eb --- /dev/null +++ b/pkg/http/interceptor.go @@ -0,0 +1,18 @@ +// Copyright (c) Abstract Machines +// SPDX-License-Identifier: Apache-2.0 + +package http + +import ( + "context" + "net/http" +) + +// Interceptor is an interface for mGate intercept hook. +type Interceptor interface { + // Intercept is called on every request flowing through the Proxy. + // Requests can be modified before being sent to the broker or the client. + // If the interceptor returns a non-nil request, the modified request is sent. + // The error indicates unsuccessful interception and mGate is cancelling the request. + Intercept(ctx context.Context, r *http.Request) (*http.Request, error) +}