proxywhirl.api.core¶
FastAPI REST API for ProxyWhirl proxy rotation service.
This module provides HTTP endpoints for: - Making proxied HTTP requests - Managing proxy pool (CRUD operations) - Monitoring health and status - Configuring runtime settings
The API uses: - FastAPI for async request handling and auto-generated OpenAPI docs - slowapi for rate limiting - Optional API key authentication - Singleton ProxyWhirl for proxy management
Classes¶
Structured audit logging for API operations. |
|
Add request ID correlation for request tracing. |
|
Log all HTTP requests with structured JSON logging. |
|
Add security headers to all responses. |
Functions¶
|
Add a new proxy to the pool. |
|
Remove a proxy from the pool. |
|
Get circuit breaker state for a specific proxy. |
|
Get circuit breaker state change events. |
|
Get circuit breaker states and events in JSON or Prometheus format. |
Get current API configuration. |
|
|
Get current API configuration. |
|
Get details of a specific proxy. |
|
Extract rate limit key from request. |
|
Get aggregated retry metrics. |
|
Get retry statistics in JSON or Prometheus format. |
|
Get the current global retry policy configuration. |
|
Get retry statistics grouped by proxy. |
|
Get hourly retry metrics for the specified time range. |
Get the singleton ProxyWhirl instance. |
|
|
Get API performance statistics (general aggregate metrics). |
|
Get detailed system status including pool stats. |
Get the optional SQLiteStorage instance. |
|
|
Check API health status. |
|
Run health checks on specified proxies. |
|
Run health checks on specified proxies. |
|
Handle 500 Internal Server errors. |
|
Application lifespan manager for startup and shutdown. |
|
Get circuit breaker states for all proxies. |
|
List all proxies in the pool with pagination and filtering. |
|
Make an HTTP request through a rotating proxy. |
|
Expose Prometheus metrics in text format. |
|
Handle 404 Not Found errors. |
|
Handle ProxyWhirlError exceptions with enhanced error details. |
|
Check if API is ready to serve requests. |
|
Manually reset a circuit breaker to CLOSED state. |
|
Root endpoint - redirect to docs. |
|
Update API configuration at runtime. |
Update Prometheus metrics for proxy pool and circuit breakers. |
|
|
Update the global retry policy configuration. |
|
Dependency to validate target URL for SSRF protection. |
|
Handle 422 Validation errors. |
|
Verify API key if authentication is required. |
Module Contents¶
- class proxywhirl.api.core.AuditLoggingMiddleware(app, dispatch=None)[source]¶
Bases:
starlette.middleware.base.BaseHTTPMiddlewareStructured audit logging for API operations.
Provides security audit trail including: - Authentication context (API key used, redacted) - Operation classification (read, write, admin, auth) - Resource identification for mutating operations - Request body logging for writes (with sensitive data redaction)
All logs use structured JSON format for easy parsing by SIEM tools.
- Parameters:
app (starlette.types.ASGIApp)
dispatch (DispatchFunction | None)
- async dispatch(request, call_next)[source]¶
Process request and emit audit log for sensitive operations.
- Parameters:
request (fastapi.Request)
call_next (Any)
- Return type:
Any
- class proxywhirl.api.core.RequestIDMiddleware(app, dispatch=None)[source]¶
Bases:
starlette.middleware.base.BaseHTTPMiddlewareAdd request ID correlation for request tracing.
This middleware ensures every request has a unique identifier that can be used for tracing requests through the retry/cache/circuit-breaker chain.
The request ID is: - Taken from the X-Request-ID header if provided by the client - Generated as a new UUID v4 if not provided - Added to loguru context for all downstream logging - Included in the response X-Request-ID header
- Parameters:
app (starlette.types.ASGIApp)
dispatch (DispatchFunction | None)
- async dispatch(request, call_next)[source]¶
Process request and add request ID correlation.
- Parameters:
request (fastapi.Request)
call_next (Any)
- Return type:
Any
- class proxywhirl.api.core.RequestLoggingMiddleware(app, dispatch=None)[source]¶
Bases:
starlette.middleware.base.BaseHTTPMiddlewareLog all HTTP requests with structured JSON logging.
Logs: - Request method, path, and query parameters (redacted) - Client IP address - Request duration in milliseconds - Response status code - Sensitive data redaction (passwords, tokens, API keys)
- Parameters:
app (starlette.types.ASGIApp)
dispatch (DispatchFunction | None)
- async dispatch(request, call_next)[source]¶
Process request and log details.
- Parameters:
request (fastapi.Request)
call_next (Any)
- Return type:
Any
- class proxywhirl.api.core.SecurityHeadersMiddleware(app, dispatch=None)[source]¶
Bases:
starlette.middleware.base.BaseHTTPMiddlewareAdd security headers to all responses.
- Parameters:
app (starlette.types.ASGIApp)
dispatch (DispatchFunction | None)
- async proxywhirl.api.core.add_proxy(request, proxy_data, rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Add a new proxy to the pool.
- Parameters:
proxy_data (proxywhirl.api.models.CreateProxyRequest) – Proxy URL and optional credentials
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
api_key (None) – API key verification
request (fastapi.Request)
- Returns:
Created proxy resource
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ProxyResource]
- async proxywhirl.api.core.delete_proxy(proxy_id, rotator=Depends(get_rotator), storage=Depends(get_storage), api_key=Depends(verify_api_key))[source]¶
Remove a proxy from the pool.
- Parameters:
proxy_id (str) – Proxy identifier
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
storage (proxywhirl.storage.SQLiteStorage | None) – Optional storage dependency
api_key (None) – API key verification
- Return type:
None
- async proxywhirl.api.core.get_circuit_breaker(proxy_id, api_key=Depends(verify_api_key))[source]¶
Get circuit breaker state for a specific proxy.
- Parameters:
proxy_id (str) – Proxy ID to get circuit breaker for
api_key (None)
- Returns:
Circuit breaker state
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.CircuitBreakerResponse]
- async proxywhirl.api.core.get_circuit_breaker_metrics(hours=24, api_key=Depends(verify_api_key))[source]¶
Get circuit breaker state change events.
- Parameters:
hours (int) – Number of hours to retrieve (default: 24)
api_key (None)
- Returns:
List of circuit breaker events
- Return type:
proxywhirl.api.models.APIResponse[list[proxywhirl.api.models.CircuitBreakerEventResponse]]
- async proxywhirl.api.core.get_circuit_breaker_metrics_endpoint(format=None, hours=24, api_key=Depends(verify_api_key))[source]¶
Get circuit breaker states and events in JSON or Prometheus format.
This endpoint provides circuit breaker information including: - Current state of all circuit breakers - State change events (history) - Failure counts and thresholds
- Parameters:
- Returns:
Circuit breaker metrics in requested format
- Return type:
- Example Prometheus format:
# HELP proxywhirl_circuit_breaker_state Circuit breaker state (0=closed, 1=open, 2=half_open) # TYPE proxywhirl_circuit_breaker_state gauge proxywhirl_circuit_breaker_state{proxy_id=”proxy1:8080”} 0
# HELP proxywhirl_circuit_breaker_failure_count Current failure count # TYPE proxywhirl_circuit_breaker_failure_count gauge proxywhirl_circuit_breaker_failure_count{proxy_id=”proxy1:8080”} 2
- async proxywhirl.api.core.get_configuration(config=Depends(get_config), api_key=Depends(verify_api_key))[source]¶
Get current API configuration.
- Parameters:
- Returns:
Current configuration settings
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ConfigurationSettings]
- async proxywhirl.api.core.get_proxy(proxy_id, rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Get details of a specific proxy.
- Parameters:
proxy_id (str) – Proxy identifier
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
api_key (None) – API key verification
- Returns:
Proxy resource
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ProxyResource]
- proxywhirl.api.core.get_rate_limit_key(request)[source]¶
Extract rate limit key from request.
SECURITY: This function is designed to prevent rate limit bypass attacks.
- For authenticated requests (with API key):
Uses hashed API key as rate limit key
This ensures rate limiting is per-API-key, not per-IP
- For unauthenticated requests:
Uses ONLY direct client IP (request.client.host)
NEVER trusts X-Forwarded-For header to prevent spoofing attacks
Attackers cannot bypass rate limits by sending fake X-Forwarded-For headers
Note: If you need to trust X-Forwarded-For (e.g., behind a reverse proxy), configure your reverse proxy to set the real client IP in request.client, or use a trusted proxy middleware that validates the header chain.
- Parameters:
request (fastapi.Request) – FastAPI Request object
- Returns:
Rate limit key in the form
apikey:{hash}orip:{address}.- Return type:
- async proxywhirl.api.core.get_retry_metrics(api_key=Depends(verify_api_key))[source]¶
Get aggregated retry metrics.
- Returns:
Retry metrics summary
- Parameters:
api_key (None)
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.RetryMetricsResponse]
- async proxywhirl.api.core.get_retry_metrics_endpoint(format=None, hours=24, api_key=Depends(verify_api_key))[source]¶
Get retry statistics in JSON or Prometheus format.
This endpoint provides comprehensive retry metrics including: - Total retry attempts - Success rate by attempt number - Time-series data (hourly aggregates) - Per-proxy statistics
- Parameters:
- Returns:
Retry metrics in requested format
- Return type:
- Example Prometheus format:
# HELP proxywhirl_retry_total Total number of retry attempts # TYPE proxywhirl_retry_total counter proxywhirl_retry_total 1250
# HELP proxywhirl_retry_success_by_attempt Successful requests by attempt number # TYPE proxywhirl_retry_success_by_attempt gauge proxywhirl_retry_success_by_attempt{attempt=”0”} 850 proxywhirl_retry_success_by_attempt{attempt=”1”} 300
- async proxywhirl.api.core.get_retry_policy(api_key=Depends(verify_api_key))[source]¶
Get the current global retry policy configuration.
- Returns:
Current retry policy settings
- Parameters:
api_key (None)
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.RetryPolicyResponse]
- async proxywhirl.api.core.get_retry_stats_by_proxy(hours=24, api_key=Depends(verify_api_key))[source]¶
Get retry statistics grouped by proxy.
- Parameters:
hours (int) – Number of hours to analyze (default: 24)
api_key (None)
- Returns:
Per-proxy retry statistics
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ProxyRetryStatsResponse]
- async proxywhirl.api.core.get_retry_timeseries(hours=24, api_key=Depends(verify_api_key))[source]¶
Get hourly retry metrics for the specified time range.
- Parameters:
hours (int) – Number of hours to retrieve (default: 24)
api_key (None)
- Returns:
Time-series retry data
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.TimeSeriesResponse]
- proxywhirl.api.core.get_rotator()[source]¶
Get the singleton ProxyWhirl instance.
- Returns:
ProxyWhirl instance
- Raises:
HTTPException – If rotator not initialized
- Return type:
proxywhirl.rotator.ProxyWhirl
- async proxywhirl.api.core.get_stats(rotator=Depends(get_rotator))[source]¶
Get API performance statistics (general aggregate metrics).
This endpoint provides high-level performance statistics for the API, distinct from the detailed Prometheus metrics available at /metrics.
- Parameters:
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
- Returns:
Performance statistics response
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.MetricsResponse]
Note
This endpoint provides aggregate metrics. For detailed metrics, use: - /metrics - Prometheus format metrics - /api/v1/metrics/retries - Retry-specific metrics - /metrics/retry - Comprehensive retry metrics with JSON/Prometheus format
- async proxywhirl.api.core.get_status(rotator=Depends(get_rotator), storage=Depends(get_storage), config=Depends(get_config))[source]¶
Get detailed system status including pool stats.
- Parameters:
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
storage (proxywhirl.storage.SQLiteStorage | None) – Optional storage dependency
- Returns:
System status response
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.StatusResponse]
- proxywhirl.api.core.get_storage()[source]¶
Get the optional SQLiteStorage instance.
- Returns:
SQLiteStorage instance or None if not configured
- Return type:
- async proxywhirl.api.core.health_check(rotator=Depends(get_rotator))[source]¶
Check API health status.
Returns 200 if healthy, 503 if unhealthy.
- Parameters:
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
- Returns:
Health status response
- Return type:
- async proxywhirl.api.core.health_check_proxies(request_data, rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Run health checks on specified proxies.
- Parameters:
request_data (proxywhirl.api.models.HealthCheckRequest) – Optional list of proxy IDs to check
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
api_key (None) – API key verification
- Returns:
List of health check results
- Return type:
proxywhirl.api.models.APIResponse[list[proxywhirl.api.models.HealthCheckResult]]
- async proxywhirl.api.core.health_check_proxies_deprecated(request_data, rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Run health checks on specified proxies.
DEPRECATED: Use /api/v1/proxies/health-check instead. This endpoint is kept for backward compatibility and will be removed in a future version.
- Parameters:
request_data (proxywhirl.api.models.HealthCheckRequest) – Optional list of proxy IDs to check
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
api_key (None) – API key verification
- Returns:
List of health check results
- Return type:
proxywhirl.api.models.APIResponse[list[proxywhirl.api.models.HealthCheckResult]]
- async proxywhirl.api.core.internal_error_handler(request, exc)[source]¶
Handle 500 Internal Server errors.
- Parameters:
request (fastapi.Request)
exc (Exception)
- Return type:
- async proxywhirl.api.core.lifespan(app)[source]¶
Application lifespan manager for startup and shutdown.
Handles: - ProxyWhirl initialization on startup - Optional SQLiteStorage initialization - Graceful cleanup on shutdown
- Parameters:
app (fastapi.FastAPI)
- Return type:
- async proxywhirl.api.core.list_circuit_breakers(api_key=Depends(verify_api_key))[source]¶
Get circuit breaker states for all proxies.
- Returns:
List of circuit breaker states
- Parameters:
api_key (None)
- Return type:
proxywhirl.api.models.APIResponse[list[proxywhirl.api.models.CircuitBreakerResponse]]
- async proxywhirl.api.core.list_proxies(page=1, page_size=50, status_filter=None, rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
List all proxies in the pool with pagination and filtering.
- Parameters:
- Returns:
Paginated list of proxy resources
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.PaginatedResponse[proxywhirl.api.models.ProxyResource]]
- async proxywhirl.api.core.make_proxied_request(request, request_data=Depends(validate_proxied_request_url), rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Make an HTTP request through a rotating proxy.
This endpoint routes your HTTP request through the proxy pool, automatically handling rotation and failover.
SECURITY: All target URLs are validated to prevent SSRF attacks. The following are blocked by default: - Localhost and loopback addresses (127.0.0.0/8, ::1) - Private IP ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) - Link-local addresses (169.254.0.0/16) - Internal domain names (.local, .internal, .lan, .corp) - Non-HTTP/HTTPS schemes (file://, data://, etc.)
- Parameters:
request_data (proxywhirl.api.models.ProxiedRequest) – Request details (URL, method, headers, body, timeout) - validated for SSRF
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency injection
api_key (None) – API key verification dependency
request (fastapi.Request)
- Returns:
APIResponse with proxied response data
- Raises:
HTTPException – For various error conditions including SSRF protection
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ProxiedResponse]
- async proxywhirl.api.core.metrics()[source]¶
Expose Prometheus metrics in text format.
This endpoint returns metrics in Prometheus exposition format, including: - proxywhirl_requests_total: Total HTTP requests by endpoint, method, and status - proxywhirl_request_duration_seconds: Request duration histogram - proxywhirl_proxies_total: Total proxies in pool - proxywhirl_proxies_healthy: Number of healthy proxies - proxywhirl_circuit_breaker_state: Circuit breaker states (0=closed, 1=open, 2=half-open)
- Returns:
Prometheus metrics in text format
- Return type:
- async proxywhirl.api.core.not_found_handler(request, exc)[source]¶
Handle 404 Not Found errors.
- Parameters:
request (fastapi.Request)
exc (Any)
- Return type:
- async proxywhirl.api.core.proxy_error_handler(request, exc)[source]¶
Handle ProxyWhirlError exceptions with enhanced error details.
- Parameters:
request (fastapi.Request)
- Return type:
- async proxywhirl.api.core.readiness_check(rotator=Depends(get_rotator), storage=Depends(get_storage))[source]¶
Check if API is ready to serve requests.
Returns 200 if ready, 503 if not ready.
- Parameters:
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
storage (proxywhirl.storage.SQLiteStorage | None) – Optional storage dependency
- Returns:
Readiness status response
- Return type:
- async proxywhirl.api.core.reset_circuit_breaker(request, proxy_id, api_key=Depends(verify_api_key))[source]¶
Manually reset a circuit breaker to CLOSED state.
- Parameters:
proxy_id (str) – Proxy ID whose circuit breaker to reset
request (fastapi.Request)
api_key (None)
- Returns:
Updated circuit breaker state
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.CircuitBreakerResponse]
- async proxywhirl.api.core.update_configuration(request, update_data, config=Depends(get_config), rotator=Depends(get_rotator), api_key=Depends(verify_api_key))[source]¶
Update API configuration at runtime.
- Parameters:
update_data (proxywhirl.api.models.UpdateConfigRequest) – Configuration updates (partial)
rotator (proxywhirl.rotator.ProxyWhirl) – ProxyWhirl dependency
api_key (None) – API key verification
request (fastapi.Request)
- Returns:
Updated configuration settings
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.ConfigurationSettings]
- proxywhirl.api.core.update_prometheus_metrics()[source]¶
Update Prometheus metrics for proxy pool and circuit breakers.
- Return type:
None
- async proxywhirl.api.core.update_retry_policy(policy_request, api_key=Depends(verify_api_key))[source]¶
Update the global retry policy configuration.
- Parameters:
policy_request (proxywhirl.api.models.RetryPolicyRequest) – New retry policy settings
api_key (None)
- Returns:
Updated retry policy
- Return type:
proxywhirl.api.models.APIResponse[proxywhirl.api.models.RetryPolicyResponse]
- proxywhirl.api.core.validate_proxied_request_url(request_data)[source]¶
Dependency to validate target URL for SSRF protection.
This dependency runs BEFORE other dependencies to ensure SSRF validation happens first, preventing malicious URLs from being processed.
- Parameters:
request_data (proxywhirl.api.models.ProxiedRequest) – The proxied request data
- Returns:
The validated request data
- Raises:
HTTPException – If URL is invalid or blocked for security reasons
- Return type:
- async proxywhirl.api.core.validation_error_handler(request, exc)[source]¶
Handle 422 Validation errors.
Returns 400 Bad Request for client input validation failures, which is more semantically appropriate than 422 Unprocessable Entity.
- Parameters:
request (fastapi.Request)
exc (Any)
- Return type: