This documentation is part of the "Projects with Books" initiative at zenOSmosis.
The source code for this project is available on GitHub.
Network Layer & SecClient
Relevant source files
Purpose and Scope
This page documents the network layer of the Rust sec-fetcher application, specifically the SecClient HTTP client and its associated infrastructure. The SecClient provides the foundational HTTP communication layer for all SEC EDGAR API interactions, implementing throttling, caching, and retry logic to ensure reliable and compliant data fetching.
This page covers:
- The
SecClientstructure and initialization - Throttling and rate limiting policies
- HTTP caching mechanisms
- Request/response handling
- User-Agent management and email validation
For information about the specific network fetching functions that use SecClient (such as fetch_company_tickers, fetch_us_gaap_fundamentals, etc.), see Data Fetching Functions. For details on the caching system architecture, see Caching & Storage System.
SecClient Architecture Overview
Component Diagram
Sources: src/network/sec_client.rs:1-181
SecClient Structure and Initialization
SecClient Fields
The SecClient struct maintains four core components:
| Field | Type | Purpose |
|---|---|---|
email | String | Contact email for SEC User-Agent header |
http_client | ClientWithMiddleware | reqwest client with middleware stack |
cache_policy | Arc<CachePolicy> | Shared cache configuration |
throttle_policy | Arc<ThrottlePolicy> | Shared throttle configuration |
Sources: src/network/sec_client.rs:14-19
Construction from ConfigManager
The from_config_manager() constructor performs the following initialization sequence:
- Extract Configuration : Reads
email,max_concurrent,min_delay_ms, andmax_retriesfromAppConfig - Create CachePolicy : Configures cache with 1-week TTL and disables header respect
- Create ThrottlePolicy : Configures rate limiting with adaptive jitter
- Initialize HTTP Cache : Retrieves the shared HTTP cache store from
Caches - Build Middleware Stack : Combines cache and throttle layers
- Construct Client : Creates
ClientWithMiddlewarewith full middleware stack
Sources: src/network/sec_client.rs:23-89
Configuration Parameters
The following table details the configuration parameters required by SecClient:
| Parameter | AppConfig Field | Required | Default | Purpose |
|---|---|---|---|---|
email | Yes | None | SEC User-Agent compliance | |
| Max Concurrent | max_concurrent | Yes | None | Concurrent request limit |
| Min Delay (ms) | min_delay_ms | Yes | None | Base throttle delay |
| Max Retries | max_retries | Yes | None | Retry attempt limit |
Sources: src/network/sec_client.rs:28-43
Throttle Policy Configuration
ThrottlePolicy Structure
The ThrottlePolicy from reqwest_drive controls request rate limiting with the following parameters:
| Field | Type | Source | Purpose |
|---|---|---|---|
base_delay_ms | u64 | AppConfig.min_delay_ms | Minimum delay between requests |
max_concurrent | u64 | AppConfig.max_concurrent | Maximum concurrent requests |
max_retries | u64 | AppConfig.max_retries | Maximum retry attempts |
adaptive_jitter_ms | u64 | Hardcoded: 500 | Randomized delay for retry backoff |
Sources: src/network/sec_client.rs:52-59
Request Throttling Mechanism
The throttle mechanism operates as follows:
- Concurrent Limit : Enforces
max_concurrentsimultaneous requests - Base Delay : Applies
base_delay_msbetween sequential requests - Retry Logic : Retries failed requests up to
max_retriestimes - Adaptive Jitter : Adds randomized delay of up to
adaptive_jitter_mson retries to prevent thundering herd
The ThrottlePolicy can be overridden on a per-request basis by passing a custom policy to raw_request():
Sources: src/network/sec_client.rs:140-165
Cache Policy Configuration
CachePolicy Structure
The CachePolicy configuration defines HTTP caching behavior:
| Field | Value | Purpose |
|---|---|---|
default_ttl | Duration::from_secs(60 * 60 * 24 * 7) | 1 week cache lifetime |
respect_headers | false | Ignore HTTP cache control headers |
cache_status_override | None | No status code override |
Note : The 1-week TTL is currently hardcoded and marked with a TODO comment for future configurability.
Sources: src/network/sec_client.rs:45-50
Cache Storage Integration
The cache storage uses the following integration:
- HTTP Cache Store : Retrieved via
Caches::get_http_cache_store(), a staticOnceLocksingleton - Drive Integration : Uses
init_cache_with_drive_and_throttle()fromreqwest_drive - Persistent Storage : Backed by
simd-r-driveWebSocket server for cross-session persistence
Sources: src/network/sec_client.rs:73-81
User-Agent Management
User-Agent Format
The get_user_agent() method generates a compliant User-Agent string for SEC EDGAR API requests:
Format: <package_name>/<version> (+<email>)
Example: sec-fetcher/0.1.0 (+contact@example.com)
Sources: src/network/sec_client.rs:91-108
Email Validation
The email validation occurs at User-Agent generation time rather than during instantiation. This design ensures that:
- Every network request validates the email format
- Invalid configurations fail fast on first network call
- The email is validated even if the
SecClientis constructed through different paths
Sources: src/network/sec_client.rs:91-99
Request Methods
raw_request Method
The raw_request() method provides low-level HTTP request functionality:
Parameters:
method: HTTP method (GET, POST, etc.)url: Target URLheaders: Optional additional headers as key-value tuplescustom_throttle_policy: Optional per-request throttle override
Behavior:
- Constructs request with User-Agent header
- Applies optional custom headers
- Injects custom throttle policy if provided (via request extensions)
- Executes request through middleware stack
- Returns raw
reqwest::Response
Sources: src/network/sec_client.rs:140-165
fetch_json Method
The fetch_json() method is a convenience wrapper for JSON API requests:
Flow:
- Calls
raw_request()with GET method - Awaits response
- Deserializes response body to
serde_json::Value - Returns parsed JSON
Sources: src/network/sec_client.rs:167-179
Request Flow Diagram
Sources: src/network/sec_client.rs:140-179
Testing Infrastructure
Test Organization
The SecClient tests use the mockito crate for HTTP mocking:
Sources: tests/sec_client_tests.rs:1-159
Test Coverage
The test suite covers the following scenarios:
| Test | Purpose | Mock Behavior | Assertion |
|---|---|---|---|
test_user_agent | User-Agent formatting | N/A | Matches expected format |
test_invalid_email_panic | Email validation | N/A | Panics with expected message |
test_fetch_json_without_retry_success | Successful request | 200 JSON response | JSON parsed correctly |
test_fetch_json_with_retry_success | Retry not needed | 200 JSON response | JSON parsed correctly |
test_fetch_json_with_retry_failure | Retry exhaustion | 500 error (3x) | Returns error |
test_fetch_json_with_retry_backoff | Retry with recovery | 500 → 200 | JSON parsed correctly |
Key Testing Patterns:
- Mock Server :
mockito::Server::new_async()creates isolated HTTP endpoints - Configuration : Tests use
AppConfig::default()with overrides - Async Execution : All network tests use
#[tokio::test] - Expectations :
mockitoverifies request counts with.expect(n)
Sources: tests/sec_client_tests.rs:7-158
Dependencies and Integration
External Dependencies
| Crate | Purpose | Usage in SecClient |
|---|---|---|
reqwest | HTTP client | Core HTTP functionality |
reqwest_drive | Middleware stack | Cache and throttle integration |
tokio | Async runtime | All async operations |
serde_json | JSON parsing | Response deserialization |
email_address | Email validation | User-Agent email checking |
Sources: src/network/sec_client.rs:1-12 Cargo.lock:1-100
Internal Dependencies
The SecClient integrates with:
- ConfigManager : Provides configuration for initialization (Configuration System)
- Caches : Supplies HTTP cache storage singleton (Caching & Storage System)
- Network Module : Exposed via
src/network.rsmodule - Fetch Functions : Used by all data fetching operations (Data Fetching Functions)
Sources: src/network.rs:1-23 src/network/sec_client.rs:1-12
Error Handling
Error Types
SecClient methods return Result<T, Box<dyn Error>>, allowing propagation of:
- reqwest::Error : HTTP client errors (connection, timeout, etc.)
- serde_json::Error : JSON deserialization errors
- Custom Errors : From configuration or validation
Panic Conditions
The only panic condition is invalid email format detected in get_user_agent():
This is intentional as an invalid email violates SEC API requirements.
Sources: src/network/sec_client.rs:95-98
Usage Example
The following example demonstrates typical SecClient usage:
Sources: tests/sec_client_tests.rs:36-62