76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package middleware
|
||
|
||
import (
|
||
"context"
|
||
"log/slog"
|
||
"net"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"yobbly-gateway-go/internal/logger"
|
||
)
|
||
|
||
type contextKey string
|
||
|
||
const realIPContextKey contextKey = "realIP"
|
||
|
||
// RealIPMiddleware извлекает реальный IP-адрес клиента из заголовков и устанавливает его в контекст запроса.
|
||
func RealIPMiddleware(next http.Handler) http.Handler {
|
||
op := "middleware.RealIPMiddleware"
|
||
log := logger.NewLoggerWithOp(op)
|
||
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
realIP := r.Header.Get("X-Real-IP")
|
||
if realIP == "" {
|
||
forwardedFor := r.Header.Get("X-Forwarded-For")
|
||
if forwardedFor != "" {
|
||
parts := strings.Split(forwardedFor, ",")
|
||
realIP = strings.TrimSpace(parts[0])
|
||
}
|
||
}
|
||
|
||
if realIP != "" {
|
||
_, port, err := net.SplitHostPort(r.RemoteAddr)
|
||
if err == nil {
|
||
r.RemoteAddr = net.JoinHostPort(realIP, port)
|
||
} else {
|
||
r.RemoteAddr = realIP
|
||
}
|
||
ctx := context.WithValue(r.Context(), realIPContextKey, realIP)
|
||
r = r.WithContext(ctx)
|
||
log.Debug("real IP extracted", slog.String("ip", realIP))
|
||
}
|
||
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|
||
|
||
// GetRealIP извлекает реальный IP из контекста запроса.
|
||
func GetRealIP(r *http.Request) string {
|
||
if ip, ok := r.Context().Value(realIPContextKey).(string); ok {
|
||
return ip
|
||
}
|
||
return r.RemoteAddr
|
||
}
|
||
|
||
// RemoveTrailingSlashMiddleware перенаправляет запросы с завершающим слэшем (если это не просто '/')
|
||
// на тот же путь без завершающего слэша.
|
||
func RemoveTrailingSlashMiddleware(next http.Handler) http.Handler {
|
||
op := "middleware.RemoveTrailingSlashMiddleware"
|
||
log := logger.NewLoggerWithOp(op)
|
||
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
urlPath := r.URL.Path
|
||
|
||
if urlPath != "" && urlPath != "/" && strings.HasSuffix(urlPath, "/") {
|
||
newPath := strings.TrimSuffix(urlPath, "/")
|
||
newURL := *r.URL
|
||
newURL.Path = newPath
|
||
log.Debug("redirecting trailing slash", slog.String("old_path", urlPath), slog.String("new_path", newPath))
|
||
http.Redirect(w, r, newURL.String(), http.StatusMovedPermanently)
|
||
return
|
||
}
|
||
next.ServeHTTP(w, r)
|
||
})
|
||
}
|