Skip to content

Allow custom reqwest::Client or expose HTTP client configuration #308

@SP-l33t

Description

@SP-l33t

The internal reqwest::Client in ClientInner is created with minimal configuration:

let client = ReqwestClient::builder().default_headers(headers).build()?;

This means no TCP_NODELAY, no connection pool tuning, no HTTP/2 window sizing, and no keep-alive configuration. The field is private with no way to inject a custom client.

For latency-sensitive use cases (market making, automated trading), this leaves significant performance on the table:

  • No TCP_NODELAY: Nagle's algorithm can add up to ~40ms delay on small packets like order submissions
  • Default HTTP/2 stream window (~64KB): Suboptimal for typical market data responses (~469KB), requiring extra round-trips
  • No keep-alive-while-idle: Idle connections go stale, causing ~200ms reconnection penalties
  • Default connection pool limits: Insufficient for burst order placement (batch cancel + batch place per tick)
  • Compression enabled by default: Adds CPU overhead that isn't worth it in colocated/low-latency deployments

Proposed Solution

Either:

  1. Accept a custom reqwest::Client in Config — lets users bring their own tuned client:
  Config {
      custom_client: Option<reqwest::Client>,
      // ...existing fields
  }
  2. Or expose builder options in Config for the most impactful settings:
  Config {
      tcp_nodelay: bool,                          // default: true
      pool_max_idle_per_host: usize,              // default: 10
      pool_idle_timeout: Option<Duration>,         // default: 90s
      http2_initial_stream_window_size: Option<u32>, // default: 524_288 (512KB)
      tcp_keepalive: Option<Duration>,             // default: 30s
      http2_keep_alive_interval: Option<Duration>, // default: 10s
      disable_compression: bool,                   // default: false
      // ...existing fields
  }

Option 1 is simpler and more flexible. Option 2 is more ergonomic for users who don't want to construct a full reqwest client.

Impact

In benchmarking with an optimized reqwest configuration vs the current defaults, the tuned client shows ~15-45ms improvement per HTTP request in colocated environments, which compounds across the multiple API calls needed per trading cycle (get_orders + cancel + place).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions