package server import ( "context" "errors" "fmt" "net/http" "proxy-pool/internal/gateway/policy" "proxy-pool/internal/platform/httpsecurity" ) type HTTPError struct { StatusCode int Header http.Header Cause error } func (err *HTTPError) Error() string { if err.Cause != nil { return err.Cause.Error() } return http.StatusText(err.StatusCode) } func (err *HTTPError) Unwrap() error { return err.Cause } type ClientIPResolver = httpsecurity.ClientIPResolver var NewClientIPResolver = httpsecurity.NewClientIPResolver type AccessGuard struct { resolver *ClientIPResolver allow policy.CIDRMatcher unrestricted bool } func NewAccessGuard(resolver *ClientIPResolver, allowCIDRs []string) (*AccessGuard, error) { if resolver == nil { return nil, errors.New("create access guard: client IP resolver is required") } allow, err := policy.NewCIDRMatcher(allowCIDRs) if err != nil { return nil, fmt.Errorf("create access guard: %w", err) } return &AccessGuard{resolver: resolver, allow: allow, unrestricted: len(allowCIDRs) == 0}, nil } func (guard *AccessGuard) Check(_ context.Context, request *http.Request) error { address, err := guard.resolver.Resolve(request) if err != nil { return &HTTPError{StatusCode: http.StatusBadRequest, Cause: err} } if guard.unrestricted || guard.allow.Match(address) { return nil } return &HTTPError{StatusCode: http.StatusForbidden, Cause: fmt.Errorf("client address %s is not allowed", address)} } type Admitter interface { Admit(context.Context, string) error } type AdmissionGuard struct { resolver *ClientIPResolver admitter Admitter } func NewAdmissionGuard(resolver *ClientIPResolver, admitter Admitter) *AdmissionGuard { return &AdmissionGuard{resolver: resolver, admitter: admitter} } func (guard *AdmissionGuard) Check(ctx context.Context, request *http.Request) error { if guard == nil || guard.resolver == nil || guard.admitter == nil { return &HTTPError{StatusCode: http.StatusInternalServerError, Cause: errors.New("gateway admission is not configured")} } address, err := guard.resolver.Resolve(request) if err != nil { return &HTTPError{StatusCode: http.StatusBadRequest, Cause: err} } if err := guard.admitter.Admit(ctx, address.String()); err != nil { return &HTTPError{StatusCode: http.StatusTooManyRequests, Cause: err} } return nil }