Skip to content

tunnix v0.4.1

Latest

Choose a tag to compare

@aeroxy aeroxy released this 09 Jul 17:01

1. High-Level Summary (TL;DR)

  • Impact: [Medium] - Significantly improves the stability of SSE tunnel connections and fixes stdout stream separation (logs/status moved to stderr where they belong).
  • Key Changes:
    • Logging Refactor: Switched tracing logs and CLI completion messages (push complete, pull complete) to use stderr instead of stdout, keeping stdout as a clean data channel for piping (e.g. tunnix exec remote -- 'cat file' | gzip > file.gz).
    • Connection Timeout: Introduced a strict 15-second timeout (SSE_CONNECT_TIMEOUT) for establishing SSE connections to prevent indefinite hangs.
    • Stabilized Recovery: Eliminated session ID rotation on HTTP 503 "unknown session" errors. It now forces an SSE reconnect to stabilize recovery and prevent rotation "death spirals."
    • Race Condition Fix: Fixed a synchronization race condition in the reconnect signaling flow by registering interest before firing the signal.
    • Architecture Docs: Updated the internal wiki to accurately reflect the new connection recovery and session handling logic.

2. Visual Overview (Code & Logic Map)

graph TD
    classDef method fill:#bbdefb,color:#0d47a1,stroke:#0d47a1;
    classDef action fill:#fff3e0,color:#e65100,stroke:#e65100;

    subgraph "src/tunnel.rs"
        A["send_message()"]:::method --> B{"try_post()"}:::method
        B -- "Success" --> C["Return Ok"]:::action
        B -- "Failure (e.g. 503)" --> D["reconnect_signal.notify_one()"]:::action
        
        D --> E["Wait for sse_ready"]:::action
        E --> F["try_post() retry"]:::method
        
        G["sse_read_loop()"]:::method --> H["GET /stream/{sid}"]:::action
        H -- "SSE_CONNECT_TIMEOUT (15s)" --> I["Drain reconnect_signal"]:::action
        I --> J["sse_ready.notify_waiters()"]:::action
    end
    
    D -. "Triggers" .-> G
    J -. "Wakes up" .-> E

3. Detailed Change Analysis

🔄 Tunnel Connection & Recovery

  • What Changed: Modified the recovery strategy in send_message() when a POST request fails. Previously, a 503 error caused the client to generate a new session ID and clear response channels. Now, the session ID remains stable, and the client forces an SSE reconnect. This allows the server to recreate the session on the next GET /stream/{sid} and announce freshness via a Reset event. (Source: src/tunnel.rs)
  • What Changed: Added a 15-second timeout (SSE_CONNECT_TIMEOUT) to the SSE GET request. Because the http_client has no global timeout, a half-open connection could wedge send().await forever. (Source: src/tunnel.rs)
  • What Changed: Added logic to securely drain any reconnect_signal permits that were buffered during connection setup. This ensures that a stale reconnect signal doesn't immediately tear down a freshly established stream. (Source: src/tunnel.rs)
  • What Changed: Fixed a race condition in send_message() by calling ready.as_mut().enable() before notifying the reconnect signal, guaranteeing the notification isn't missed by the event loop. (Source: src/tunnel.rs)
  • What Changed: Changed try_post to take bytes::Bytes instead of &[u8], eliminating unnecessary copying when a message retry occurs. (Source: src/tunnel.rs)

Configuration Updates

Constant Old Value New Value Description
RECONNECT_WAIT 10s 20s Time send_message waits for the SSE stream to become ready after forcing a reconnect.
SSE_CONNECT_TIMEOUT N/A 15s Strict bound on establishing the SSE GET connection.

📝 CLI & Logging

  • What Changed: Changed the default tracing_subscriber output from stdout to stderr. Also updated "push complete" and "pull complete" messages to use eprintln! instead of println!. Rationale: tunnix exec streams remote process output through stdout (src/exec.rs). If tracing logs were interleaved on stdout, piped output like tunnix exec remote -- 'cat file' | gzip > file.gz would be corrupted. Moving logs and status banners to stderr follows the Unix convention of reserving stdout for data and stderr for diagnostics. (Source: src/main.rs, src/exec.rs)

📚 Documentation

  • What Changed: Updated wiki/architecture.md to document the new session_id handling, explaining why it is deliberately not rotated on 503 errors and documenting the new timeout parameters. (Source: wiki/architecture.md)

4. Impact & Risk Assessment

  • Breaking Changes: None. Status messages and logging now correctly go to stderr, reserving stdout exclusively for data output (e.g. remote process streams from tunnix exec). This was a bug — status messages never belonged on stdout and could corrupt piped output.