Components Overview

This page provides an overview of the major components in Request Forge and how they interact.

System Architecture

High-Level Architecture

┌─────────────────────────────────────────────────────────────┐
│                      Application Code                       │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ↓
┌─────────────────────────────────────────────────────────────┐
│                      Request Forge                            │
│  ┌────────────────┐  ┌────────────────┐  ┌──────────────┐   │
│  │  Request       │→ │  Hook          │→ │  Auth        │   │
│  │  Processing    │  │  Pipeline      │  │  Injection   │   │
│  └────────────────┘  └────────────────┘  └──────────────┘   │
│           ↓                   ↓                    ↓        │
│  ┌────────────────┐  ┌────────────────┐  ┌──────────────┐   │
│  │  Retry         │  │  HTTP          │  │  Response    │   │
│  │  Logic         │  │  Execution     │  │  Processing  │   │
│  └────────────────┘  └────────────────┘  └──────────────┘   │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ↓
┌─────────────────────────────────────────────────────────────┐
│                   Token Management Layer                    │
│  ┌────────────────┐  ┌────────────────┐  ┌──────────────┐   │
│  │  Token         │  │  Token         │  │  Token       │   │
│  │  Manager       │  │  Provider      │  │  Storage     │   │
│  └────────────────┘  └────────────────┘  └──────────────┘   │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ↓
┌─────────────────────────────────────────────────────────────┐
│              Multi-Step Authentication Pipeline             │
│  ┌────────┐      ┌────────┐      ┌────────┐                 │
│  │ Step 1 │  →   │ Step 2 │  →   │ Step 3 │                 │
│  └────────┘      └────────┘      └────────┘                 │
└────────────────────────┬────────────────────────────────────┘
                         │
                         ↓
┌─────────────────────────────────────────────────────────────┐
│                    External Services                        │
│        (Auth Servers, APIs, Token Endpoints)                │
└─────────────────────────────────────────────────────────────┘

Core Components

HttpClient

Responsibility: Execute HTTP requests with retry logic and lifecycle hooks.

Key Features:

  • Thread-safe session management

  • Request/response/error hook execution

  • Authentication injection

  • Retry logic with multiple strategies

  • Concurrent request execution

  • Resource management

Dependencies:

  • HttpClientConfig - Configuration

  • RetryStrategyInterface - Retry logic

  • AuthHookInterface - Authentication

  • RequestHookInterface - Request hooks

  • ResponseHookInterface - Response hooks

  • ErrorHookInterface - Error hooks

Interaction Diagram:

Application
    ↓
HttpClient.get('/users')
    ↓
┌─────────────────────────────────────┐
│ 1. Execute request hooks            │
│ 2. Execute auth hook (inject token) │
│ 3. Execute HTTP request             │
│ 4. Check for auth error (401)       │
│    ├─ Yes → Refresh token & retry   │
│    └─ No → Continue                 │
│ 5. Check for retryable error        │
│    ├─ Yes → Retry with backoff      │
│    └─ No → Continue                 │
│ 6. Execute response hooks           │
│ 7. Return response                  │
└─────────────────────────────────────┘

Example:

config = HttpClientConfigBuilder().build()
client = HttpClient(config)
response = client.get('/users')

HttpClientConfig

Responsibility: Hold immutable configuration data.

Key Features:

  • Immutable (frozen dataclass)

  • Validation on creation

  • Default values

  • Type-safe attributes

Attributes:

  • base_url - Base URL for requests

  • default_timeout - Default timeout

  • default_headers - Default headers

  • verify_ssl - SSL verification

  • retry_strategy - Retry strategy instance

  • auth_hook - Authentication hook

  • request_hooks - Request hooks tuple

  • response_hooks - Response hooks tuple

  • error_hooks - Error hooks tuple

Created via: HttpClientConfigBuilder

HttpClientConfigBuilder

Responsibility: Build HttpClientConfig using fluent interface.

Key Features:

  • Fluent API (method chaining)

  • Validation at build time

  • Sensible defaults

  • Helper methods for common configurations

Example:

config = (
    HttpClientConfigBuilder()
    .with_base_url('https://api.example.com')
    .with_timeout(30.0)
    .with_retry(max_retries=3)
    .with_bearer_token('token')
    .build()
)

Retry Components

RetryStrategyInterface

Responsibility: Define retry behavior.

Key Methods:

  • should_retry(context, exception) - Determine if should retry

  • get_delay(context) - Calculate delay before retry

  • max_retries - Maximum retry attempts

Implementations:

  • NoRetryStrategy - No retries

  • SimpleRetryStrategy - Fixed delay

  • ExponentialBackoffRetryStrategy - Exponential backoff with jitter

  • CircuitBreakerRetryStrategy - Circuit breaker pattern

Interaction:

Request fails
    ↓
RetryStrategy.should_retry(context, exception)
    ↓
if True:
    delay = RetryStrategy.get_delay(context)
    sleep(delay)
    retry request
else:
    raise MaxRetryException

Hook Components

Hook Pipeline

Responsibility: Execute hooks in order during request lifecycle.

Hook Types:

  1. RequestHookInterface - Modify requests before sending

  2. ResponseHookInterface - Process responses after receiving

  3. ErrorHookInterface - Handle errors during lifecycle

  4. AuthHookInterface - Manage authentication

Execution Flow:

Request initiated
    ↓
Execute request hooks (in order)
    hook1.before_request()
    hook2.before_request()
    hook3.before_request()
    ↓
Execute auth hook
    auth_hook.before_request()
    ↓
HTTP request sent
    ↓
Response received
    ↓
Execute response hooks (in order)
    hook1.after_response()
    hook2.after_response()
    hook3.after_response()
    ↓
Return response

(On error)
    ↓
Execute error hooks (in order)
    hook1.on_error()
    hook2.on_error()
    hook3.on_error()

Built-in Hooks

Request Hooks:

  • LoggingRequestHook - Log outgoing requests

  • CorrelationIdHook - Add correlation IDs

  • AuthorizationHook - Dynamic authorization

Response Hooks:

  • LoggingResponseHook - Log incoming responses

  • RateLimitResponseHook - Parse rate limit headers

Error Hooks:

  • LoggingErrorHook - Log errors

Auth Hooks:

  • TokenAuthHook - Token-based auth with refresh

  • ApiKeyAuthHook - API key auth

  • BasicAuthHook - HTTP Basic auth

  • CompositeAuthHook - Combine multiple auth methods

Token Management Components

TokenManager

Responsibility: Manage token lifecycle with caching.

Key Features:

  • Automatic token caching

  • Thread-safe token refresh

  • Prevents concurrent fetches

  • Expiration handling

Dependencies:

  • TokenProviderInterface - Fetches tokens

  • TokenStorageInterface - Stores tokens

Flow:

get_token()
    ↓
Check storage for cached token
    ↓
if cached and not expired:
    return cached token
else:
    ↓
    Lock (prevent concurrent fetches)
        ↓
    Double-check cache
        ↓
    if still need new token:
        ↓
        provider.fetch_token() or provider.refresh_token()
            ↓
        Store in cache
            ↓
        Return new token

TokenProviderInterface

Responsibility: Fetch and refresh tokens.

Key Methods:

  • fetch_token() - Fetch new token

  • refresh_token(current) - Refresh existing token

  • service_name - Service identifier

Implementations:

  • ClientCredentialsTokenProvider - OAuth2 client credentials

  • PasswordGrantTokenProvider - OAuth2 password grant

  • PipelineTokenProvider - Wraps multi-step pipeline

  • Custom implementations

TokenStorageInterface

Responsibility: Store and retrieve tokens.

Key Methods:

  • get(key) - Retrieve token

  • set(key, token) - Store token

  • delete(key) - Delete token

  • exists(key) - Check existence

Implementations:

  • InMemoryTokenStorage - In-memory storage (single process)

  • DjangoCacheTokenStorage - Django cache (multi-process)

  • Custom implementations (Redis, Memcached, etc.)

Storage Comparison:

Storage

Scope

Persistence

Use Case

InMemoryTokenStorage

Single process

Lost on restart

Development, single-instance

DjangoCacheTokenStorage

Multi-process

Depends on backend

Production, multi-instance

RedisTokenStorage

Multi-process

Persistent

Production, high availability

Multi-Step Authentication Components

TokenFetchPipeline

Responsibility: Execute multi-step token fetching with dependencies.

Key Features:

  • Dependency resolution

  • Per-step caching

  • Selective invalidation

  • Cascading invalidation

Dependencies:

  • TokenFetcherInterface - Individual fetch steps

  • TokenStorageInterface - Cache storage

Execution Flow:

pipeline.execute()
    ↓
For each step in order:
    ↓
    Check cache
        ↓
    if cached and valid:
        Use cached token
    else:
        ↓
        Verify dependencies available
            ↓
        Fetch new token (passing dependency tokens)
            ↓
        Cache with step's TTL
            ↓
        Add to context for next steps
    ↓
Return final token

Example:

Step 1: device_token (no deps)
    → Fetch → Cache (30 days)
        ↓
Step 2: app_token (depends on device_token)
    → Fetch using device_token → Cache (1 hour)
        ↓
Step 3: user_token (depends on app_token)
    → Fetch using app_token → Cache (15 min)
        ↓
Return user_token

TokenFetcherInterface

Responsibility: Fetch a single token in a pipeline step.

Key Methods:

  • fetch(context) - Fetch token with dependency context

  • name - Step name

  • depends_on - Dependency step names

  • ttl - Cache TTL for this step

Implementations:

  • HeaderTokenFetcher - Extract from response headers

  • BodyTokenFetcher - Extract from response body (JSON)

  • Custom implementations

PipelineTokenProvider

Responsibility: Wrap TokenFetchPipeline as TokenProviderInterface.

Purpose: Allow pipeline to be used with TokenManager.

Example:

pipeline = TokenFetchPipeline(steps=[...], storage=storage)
provider = PipelineTokenProvider(pipeline, 'myapp')
token_manager = TokenManager(provider)

Data Models

HttpRequest

Responsibility: Represent an HTTP request (immutable).

Key Attributes:

  • method - HTTP method

  • url - Request URL

  • headers - Request headers

  • params - Query parameters

  • json_data - JSON body

  • data - Form data

  • timeout - Request timeout

Methods:

  • with_headers(headers) - Create copy with additional headers

  • to_dict() - Convert to dictionary

  • to_curl() - Convert to cURL command

HttpResponse

Responsibility: Represent an HTTP response (immutable).

Key Attributes:

  • status_code - HTTP status code

  • headers - Response headers

  • content - Raw bytes

  • elapsed_ms - Request duration

  • url - Final URL

  • request - Original request

Properties:

  • text - Decoded content

  • is_success - 2xx status

  • is_client_error - 4xx status

  • is_server_error - 5xx status

Methods:

  • json() - Parse JSON

  • json_or_none() - Safe JSON parse

RequestContext

Responsibility: Track request lifecycle state (mutable).

Key Attributes:

  • request - The HTTP request

  • attempt - Current attempt number

  • max_retries - Maximum retries

  • metadata - Additional context data

Methods:

  • increment_attempt() - Increment attempt counter

  • is_retry - Check if retry

TokenData

Responsibility: Represent token data (immutable).

Key Attributes:

  • access_token - Token string

  • token_type - Token type

  • expires_at - Expiration datetime

  • refresh_token - Refresh token

  • scope - Token scope

Properties:

  • is_expired - Check expiration

  • authorization_header - Get header value

Methods:

  • from_response(data) - Create from API response

Component Interactions

Request Flow

Application
    ↓
client.get('/users')
    ↓
HttpClient
    ├─→ Request Hooks
    │   └─→ Modify request
    ├─→ Auth Hook
    │   ├─→ TokenManager.get_token()
    │   │   ├─→ Check TokenStorage
    │   │   └─→ TokenProvider.fetch_token() if needed
    │   └─→ Inject token into headers
    ├─→ Execute HTTP request
    ├─→ Check response
    │   ├─→ If 401: Refresh token & retry
    │   └─→ If retryable: RetryStrategy determines retry
    ├─→ Response Hooks
    │   └─→ Process response
    └─→ Return HttpResponse

Token Management Flow

TokenAuthHook.before_request()
    ↓
TokenManager.get_token()
    ↓
Check TokenStorage
    ↓
if cached and valid:
    return cached
else:
    ↓
    TokenProvider.fetch_token()
        ↓
    (If PipelineTokenProvider)
        ↓
    TokenFetchPipeline.execute()
        ↓
    For each step:
        ↓
        TokenFetcher.fetch(context)
            ↓
        HTTP request to auth endpoint
            ↓
        Parse response
            ↓
        Return TokenData
            ↓
        Store in TokenStorage
            ↓
    Return final token
        ↓
    Store in TokenStorage
        ↓
    Return token

Thread Safety

Thread-Safe Components:

  • HttpClient - Thread-local sessions

  • TokenManager - Locking for token refresh

  • InMemoryTokenStorage - RLock for dictionary access

  • TokenFetchPipeline - Lock-free (relies on storage locking)

Not Thread-Safe (by design):

  • HttpRequest - Immutable (inherently thread-safe)

  • HttpResponse - Immutable (inherently thread-safe)

  • HttpClientConfig - Immutable (inherently thread-safe)

  • TokenData - Immutable (inherently thread-safe)

See Thread Safety for detailed thread-safety analysis.

Extension Points

Applications can extend Request Forge by:

  1. Custom Retry Strategies

    class CustomRetryStrategy(RetryStrategyInterface):
        def should_retry(self, context, exception): ...
        def get_delay(self, context): ...
    
  2. Custom Hooks

    class CustomRequestHook(RequestHookInterface):
        def before_request(self, request, context): ...
    
  3. Custom Token Providers

    class CustomTokenProvider(TokenProviderInterface):
        def fetch_token(self): ...
        def refresh_token(self, current): ...
    
  4. Custom Token Storage

    class CustomTokenStorage(TokenStorageInterface):
        def get(self, key): ...
        def set(self, key, token): ...
    
  5. Custom Token Fetchers

    class CustomTokenFetcher(TokenFetcherInterface):
        def fetch(self, context): ...
    

See Also