Files
traefikoidc/internal/middleware/request_handler.go
T
lukaszraczylo ae59a5e88a 0.7.10 (#80)
* Add ability to disable replay protection. - This is useful for runs with multiple traefik replicas to avoid false positives and tokens re-creation.
* Enhance the CI/CD pipelines
* Increase test coverage.
* Update vendored dependencies.
* Update behaviour on forceHTTPS as per issue #82
2025-10-16 10:56:28 +01:00

123 lines
3.4 KiB
Go

package middleware
import (
"fmt"
"net/http"
"strings"
"time"
)
// RequestContext holds request processing context
type RequestContext struct {
Writer http.ResponseWriter
Request *http.Request
RedirectURL string
Scheme string
Host string
}
// RequestProcessor handles common request processing operations
type RequestProcessor struct {
logger Logger
}
// Logger interface for logging operations
type Logger interface {
Debug(msg string)
Debugf(format string, args ...interface{})
Error(msg string)
Errorf(format string, args ...interface{})
Info(msg string)
Infof(format string, args ...interface{})
}
// NewRequestProcessor creates a new request processor
func NewRequestProcessor(logger Logger) *RequestProcessor {
return &RequestProcessor{
logger: logger,
}
}
// BuildRequestContext creates a request context with scheme and host detection
func (rp *RequestProcessor) BuildRequestContext(rw http.ResponseWriter, req *http.Request, redirectPath string) *RequestContext {
scheme := rp.determineScheme(req)
host := rp.determineHost(req)
redirectURL := buildFullURL(scheme, host, redirectPath)
return &RequestContext{
Writer: rw,
Request: req,
RedirectURL: redirectURL,
Scheme: scheme,
Host: host,
}
}
// IsHealthCheckRequest checks if request is a health check
func (rp *RequestProcessor) IsHealthCheckRequest(req *http.Request) bool {
return strings.HasPrefix(req.URL.Path, "/health")
}
// IsEventStreamRequest checks if request expects event stream
func (rp *RequestProcessor) IsEventStreamRequest(req *http.Request) bool {
acceptHeader := req.Header.Get("Accept")
return strings.Contains(acceptHeader, "text/event-stream")
}
// IsAjaxRequest determines if this is an AJAX request
func (rp *RequestProcessor) IsAjaxRequest(req *http.Request) bool {
xhr := req.Header.Get("X-Requested-With")
contentType := req.Header.Get("Content-Type")
accept := req.Header.Get("Accept")
return xhr == "XMLHttpRequest" ||
strings.Contains(contentType, "application/json") ||
strings.Contains(accept, "application/json")
}
// WaitForInitialization waits for OIDC provider initialization with timeout
func (rp *RequestProcessor) WaitForInitialization(req *http.Request, initComplete <-chan struct{}) error {
select {
case <-initComplete:
return nil
case <-req.Context().Done():
rp.logger.Debug("Request canceled while waiting for OIDC initialization")
return fmt.Errorf("request canceled")
case <-time.After(30 * time.Second):
rp.logger.Error("Timeout waiting for OIDC initialization")
return fmt.Errorf("timeout waiting for OIDC provider initialization")
}
}
// determineScheme determines the URL scheme for building redirect URLs
func (rp *RequestProcessor) determineScheme(req *http.Request) string {
if scheme := req.Header.Get("X-Forwarded-Proto"); scheme != "" {
return scheme
}
if req.TLS != nil {
return "https"
}
return "http"
}
// determineHost determines the host for building redirect URLs
func (rp *RequestProcessor) determineHost(req *http.Request) string {
if host := req.Header.Get("X-Forwarded-Host"); host != "" {
return host
}
return req.Host
}
// buildFullURL constructs a complete URL from scheme, host, and path components
func buildFullURL(scheme, host, path string) string {
if strings.HasPrefix(path, "http://") || strings.HasPrefix(path, "https://") {
return path
}
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
return fmt.Sprintf("%s://%s%s", scheme, host, path)
}