Connection Strategies

Connection strategies determine how TRECO establishes and manages TCP/TLS connections for HTTP requests. The choice of connection strategy significantly impacts race window timing and overall performance.


Overview

TRECO provides four connection strategies:

  1. Preconnect (Recommended): Establish connections before synchronization point

  2. Lazy: Connect on-demand when sending requests

  3. Pooled: Share connection pool across threads

  4. Multiplexed: HTTP/2 multiplexing over single connection

Each strategy has different performance characteristics and use cases.



Lazy Strategy

The lazy strategy connects on-demand when sending each request, without pre-establishing connections.

How It Works

  1. Threads synchronize at barrier/latch

  2. Threads are released simultaneously

  3. Each thread establishes TCP/TLS connection after release

  4. Connection overhead included in race window

  5. Requests sent after connection completes

race:
  threads: 20
  sync_mechanism: barrier
  connection_strategy: lazy  # Not recommended for races

Performance Characteristics

  • Race Window: 50-500ms+ (includes connection time)

  • Connection Overhead: Included in race window

  • Memory Usage: Lower (connects only when needed)

  • CPU Usage: Lower overall

  • Best For: Testing connection timing, sequential requests

Visual Representation

Timeline:

[Synchronization Phase]
All threads: [Wait at barrier] → [Release] → Connect → Send requests
                                           ↑
                                 Race window starts here
                                 (includes connection overhead!)

Thread 1: TCP → TLS → Request (total: 100ms)
Thread 2: TCP → TLS → Request (total: 105ms)
Thread 3: TCP → TLS → Request (total: 98ms)
Thread 4: TCP → TLS → Request (total: 110ms)

Race window: ~12ms (variation in connection times)

Advantages

✅ Lower Memory Usage

Connections created only when needed

✅ Simpler Implementation

No pre-connection management required

✅ Tests Real Conditions

Includes connection overhead in timing

✅ Fewer Socket Descriptors

Suitable for very high thread counts

Disadvantages

❌ Poor Timing Precision

Race window typically 50-500ms or more

❌ Variable Timing

Connection times introduce significant variance

❌ Lower Success Rate

Difficult to trigger race conditions reliably

❌ Not Suitable for Races

Should not be used for race condition testing

Use Cases

Suitable for:

  • Testing connection timing vulnerabilities

  • Sequential request testing

  • Low-resource environments

  • Scenarios where connection timing matters

NOT suitable for:

  • ❌ Race condition testing

  • ❌ Double-spending attacks

  • ❌ Inventory manipulation

  • ❌ Time-sensitive exploits

Example: Connection Timing Test

states:
  test_connection:
    description: "Test authentication during connection"
    request: |
      GET /api/resource HTTP/1.1
      Authorization: Bearer {{ token }}

    race:
      threads: 10
      sync_mechanism: barrier
      connection_strategy: lazy  # Include connection in timing

    logger:
      on_thread_leave: |
        [Thread {{ thread.id }}] Total time: {{ response_time }}ms
        (includes connection establishment)

When to Use

Use lazy strategy when:

  • Testing scenarios where connection timing is relevant

  • Memory is severely constrained

  • You need many threads (>100) with socket descriptor limits

  • Not testing race conditions

Don’t use lazy strategy when:

  • Testing race conditions (use preconnect)

  • Need sub-100ms timing precision (use preconnect)

  • Testing double-spending or similar (use preconnect)


Pooled Strategy

The pooled strategy shares a connection pool across threads, reusing connections for multiple requests.

How It Works

  1. Connection pool created with configurable size

  2. Threads acquire connection from pool

  3. Requests serialized through pool

  4. Connection released back to pool after use

  5. Next thread acquires same connection

race:
  threads: 20
  sync_mechanism: barrier
  connection_strategy: pooled
  pool_size: 5  # Share 5 connections among 20 threads

Performance Characteristics

  • Race Window: Variable, depends on pool size and contention

  • Connection Overhead: Mixed (first request per connection, then reuse)

  • Memory Usage: Low (limited by pool size)

  • CPU Usage: Low

  • Best For: Sequential testing, connection reuse scenarios

Visual Representation

Connection Pool: [C1][C2][C3][C4][C5]

Thread 1: Acquire C1 → Request → Release C1
Thread 2: Acquire C2 → Request → Release C2
Thread 3: Acquire C3 → Request → Release C3
Thread 4: Acquire C4 → Request → Release C4
Thread 5: Acquire C5 → Request → Release C5
Thread 6: Wait for C1 → Acquire C1 → Request → Release C1
...

Serialization: Threads wait for available connections

Advantages

✅ Resource Efficient

Limited number of connections regardless of thread count

✅ Connection Reuse

Amortizes connection overhead across requests

✅ Predictable Resource Usage

Won’t exceed pool size connections

✅ Server-Friendly

Limits connection burst to target

Disadvantages

❌ Serializes Requests

Threads wait for available connections, destroying race timing

❌ Unpredictable Timing

Pool contention introduces significant variance

❌ NOT for Race Conditions

Should never be used for race condition testing

❌ Complex Behavior

Pool dynamics can be difficult to reason about

Use Cases

Suitable for:

  • Connection reuse testing

  • Sequential request patterns

  • Resource-constrained environments

  • Testing keep-alive behavior

NOT suitable for:

  • ❌ Race condition testing (destroys timing)

  • ❌ Concurrent request scenarios

  • ❌ Double-spending or inventory races

  • ❌ Any scenario requiring simultaneous execution

Example: Sequential API Testing

states:
  sequential_test:
    description: "Test API with connection reuse"
    request: |
      GET /api/data/{{ thread.id }} HTTP/1.1
      Authorization: Bearer {{ token }}

    race:
      threads: 50
      sync_mechanism: semaphore
      connection_strategy: pooled
      pool_size: 10  # Reuse 10 connections

    logger:
      on_thread_leave: |
        [Thread {{ thread.id }}] Completed using pooled connection

When to Use

Use pooled strategy when:

  • Testing connection reuse behavior

  • Making sequential requests

  • Need to limit connections to server

  • Testing keep-alive functionality

Don’t use pooled strategy when:

  • Testing race conditions (use preconnect)

  • Need concurrent execution (use preconnect)

  • Timing precision matters (use preconnect)


Multiplexed Strategy

The multiplexed strategy uses HTTP/2 multiplexing to send multiple requests over a single connection.

How It Works

  1. Single HTTP/2 connection established

  2. Multiple streams created over connection

  3. Requests multiplexed over streams

  4. Server processes streams concurrently (if supported)

  5. Responses received asynchronously

race:
  threads: 20
  sync_mechanism: barrier
  connection_strategy: multiplexed
  http_version: "2"  # Required

Performance Characteristics

  • Race Window: Variable, depends on HTTP/2 support and implementation

  • Connection Overhead: Single connection for all requests

  • Memory Usage: Low (single connection)

  • CPU Usage: Moderate (stream management)

  • Best For: HTTP/2-specific testing

Visual Representation

Single HTTP/2 Connection
│
├─ Stream 1: Request 1 → Response 1
├─ Stream 2: Request 2 → Response 2
├─ Stream 3: Request 3 → Response 3
├─ Stream 4: Request 4 → Response 4
└─ ...

All streams over single TCP connection

Advantages

✅ Single Connection

Minimal connection overhead and resource usage

✅ HTTP/2 Features

Access to header compression, server push, etc.

✅ Server-Friendly

Single connection to server

✅ Efficient for High Thread Counts

No socket descriptor limits

Disadvantages

❌ Requires HTTP/2

Not all servers support HTTP/2

❌ Complex Implementation

HTTP/2 adds significant complexity

❌ Stream Ordering

Server may process streams sequentially despite concurrency

❌ Unpredictable Timing

Difficult to achieve precise race windows

Use Cases

Suitable for:

  • HTTP/2-specific vulnerability testing

  • Testing stream-based race conditions

  • Server push vulnerabilities

  • Header compression attacks

NOT typically suitable for:

  • Traditional race condition testing (use preconnect)

  • Double-spending attacks (use preconnect)

  • Most common vulnerabilities (use preconnect)

Example: HTTP/2 Stream Race

states:
  http2_race:
    description: "Test HTTP/2 stream handling"
    request: |
      POST /api/resource HTTP/2
      Authorization: Bearer {{ token }}
      {"action": "process"}

    race:
      threads: 10
      sync_mechanism: barrier
      connection_strategy: multiplexed

    logger:
      on_state_leave: |
        Tested HTTP/2 multiplexing with {{ threads }} streams

When to Use

Use multiplexed strategy when:

  • Specifically testing HTTP/2 vulnerabilities

  • Target only supports HTTP/2

  • Testing stream-based race conditions

  • Testing server push behavior

Don’t use multiplexed strategy when:

  • Target doesn’t support HTTP/2

  • Testing traditional race conditions (use preconnect)

  • Need predictable timing (use preconnect)

  • HTTP/1.1 is sufficient (use preconnect)


Comparison Table

Feature

Preconnect

Lazy

Pooled

Multiplexed

Race Window

< 1-10μs

50-500ms+

Variable

Variable

Precision

Excellent

Poor

Poor

Moderate

Memory Usage

High

Low

Low

Very Low

Connection Overhead

Before race

In race

Mixed

Single conn

Best For

Race conditions

Connection timing

Sequential testing

HTTP/2 testing

Recommended

✅ Yes

⚠️ Rarely

⚠️ Rarely

⚠️ Specific cases

Race Testing

✅ Excellent

❌ Poor

❌ Poor

⚠️ Limited


Choosing the Right Strategy

Decision Tree

Testing race condition?
│
├─ Yes → Use PRECONNECT
│        (99% of cases)
│
└─ No → Testing HTTP/2?
        │
        ├─ Yes → Use MULTIPLEXED
        │
        └─ No → Need connection reuse?
                │
                ├─ Yes → Use POOLED
                │
                └─ No → Testing connection timing?
                        │
                        ├─ Yes → Use LAZY
                        │
                        └─ No → Use PRECONNECT
                                (safest default)

Quick Reference

For Race Conditions (99% of cases):

race:
  connection_strategy: preconnect  # Always use this
  sync_mechanism: barrier
  threads: 20

For HTTP/2 Testing:

race:
  connection_strategy: multiplexed
  http_version: "2"

For Connection Reuse:

race:
  connection_strategy: pooled
  pool_size: 10

For Connection Timing:

race:
  connection_strategy: lazy

Performance Optimization

Achieving Optimal Timing

To achieve the best possible race window with preconnect:

1. Use Python 3.14t

uv python install 3.14t
uv sync

2. Configure for optimal performance

race:
  threads: 20                    # Reasonable count
  sync_mechanism: barrier         # Best timing
  connection_strategy: preconnect # Essential!
  thread_propagation: single

3. Test on low-latency network

  • Localhost: Best (< 1ms latency)

  • Same datacenter: Excellent (< 10ms)

  • Same region: Good (< 50ms)

  • Different regions: Poor (> 100ms)

4. Monitor and tune

logger:
  on_state_leave: |
    Race window: {{ race_window }}μs

    {% if race_window < 1 %}
    ✓ Excellent: Sub-microsecond precision
    {% elif race_window < 10 %}
    ✓ Very Good: Optimal for most races
    {% elif race_window < 100 %}
    ⚠ Good: May need optimization
    {% else %}
    ❌ Poor: Check configuration
    {% endif %}

Troubleshooting Connection Issues

Issue: Connection timeouts

Solution:

target:
  timeout: 60  # Increase timeout

race:
  threads: 10  # Reduce thread count

Issue: Too many open files

Solution:

# Check limit
ulimit -n

# Increase limit
ulimit -n 4096

Issue: Connection refused

Solution:

  • Check target is accessible

  • Verify firewall rules

  • Confirm port is correct

  • Reduce thread count


Examples

Example 1: Optimal Configuration

metadata:
  name: "Optimal Race Configuration"

target:
  host: "api.example.com"
  port: 443
  tls:
    enabled: true
    verify_cert: true

states:
  race_attack:
    request: |
      POST /api/vulnerable HTTP/1.1
      Authorization: Bearer {{ token }}

    race:
      threads: 20
      sync_mechanism: barrier
      connection_strategy: preconnect  # Optimal!

    logger:
      on_state_leave: |
        Configuration: OPTIMAL
        - Strategy: preconnect ✓
        - Sync: barrier ✓
        - Threads: {{ threads }}
        - Race window: {{ race_window }}μs

Example 2: HTTP/2 Testing

states:
  http2_test:
    request: |
      POST /api/resource HTTP/2
      Authorization: Bearer {{ token }}

    race:
      threads: 10
      sync_mechanism: barrier
      connection_strategy: multiplexed
      http_version: "2"

Example 3: Connection Reuse

states:
  reuse_test:
    request: |
      GET /api/data HTTP/1.1
      Connection: keep-alive

    race:
      threads: 50
      sync_mechanism: semaphore
      connection_strategy: pooled
      pool_size: 10

Best Practices

General Guidelines

  1. Default to preconnect for all race condition testing

  2. Use barrier sync with preconnect for best results

  3. Monitor race window and optimize based on results

  4. Test on low-latency network for best timing

  5. Start with moderate thread counts (10-20)

Configuration Checklist

Before running a race attack:

  • ✅ Using preconnect strategy

  • ✅ Using barrier or countdown_latch sync

  • ✅ Thread count is reasonable (10-30 typical)

  • ✅ Python 3.14t installed (for best performance)

  • ✅ Network latency is low (< 50ms)

  • ✅ Target system is accessible

Common Mistakes to Avoid

❌ Using lazy for race conditions

# DON'T DO THIS for race testing:
race:
  connection_strategy: lazy  # Will fail!

❌ Using pooled for race conditions

# DON'T DO THIS for race testing:
race:
  connection_strategy: pooled  # Serializes requests!

❌ Too many threads with preconnect

# May cause issues:
race:
  threads: 500  # Too many connections
  connection_strategy: preconnect

✅ Correct configuration

# DO THIS for race testing:
race:
  threads: 20
  sync_mechanism: barrier
  connection_strategy: preconnect

See Also