# Request Flow Reference

This document describes how requests flow through the Acequia system, from initial fetch to final response. There are three transport paths, chosen by the service worker based on where the route handler lives.

## Overview

```
Client makes fetch('/groups/mygroup/abc123/status')
    ↓
Service Worker intercepts
    ↓
handleGroupRouteRequest()
    ↓
Match route pattern in groupRoutes
    ↓
Find owning instance
    ↓
┌─────────────────────────┐
│ Is instance local?      │
│ (tab controlled by SW)  │
├───Yes────────────┬──No──┤
│                  │      │
│ postMessage      │ Is there a WebRTC
│ to local tab     │ peer connection?
│                  ├─Yes──┬──No──┐
│                  │      │      │
│                  │ Send via    │ HTTP proxy
│                  │ WebRTC     │ to discovery
│                  │ dataBig    │ server
└──────────────────┴──────┴──────┘
```

## Flow 1: Local postMessage (Same Browser)

The fastest path. Used when the route handler runs in a tab controlled by the same service worker.

```
1. Browser Tab A: fetch('/groups/mygroup/abc123/status')
         ↓
2. Service Worker: handleGroupRouteRequest()
   - Parse groupId='mygroup', path='abc123/status'
   - Look up groupRoutes['mygroup']
   - Match pattern '/:instanceId/status' → instance='abc123'
   - Check instanceMap['abc123'] → has client reference
         ↓
3. Service Worker: Verify client is alive
   - clients.matchAll() → find client with matching ID
   - If not found → skip to Flow 2 or 3
         ↓
4. Service Worker → Tab (via postMessage):
   {
       type: 'proxiedRequest',
       requestId: 'req_abc123',
       method: 'GET',
       path: '/abc123/status',
       pathParams: { instanceId: 'abc123' },
       headers: { ... },
       body: null
   }
         ↓
5. Tab: Route handler executes
   - Router.matchRoute() finds handler
   - Handler returns { status: 200, body: '...', headers: {...} }
         ↓
6. Tab → Service Worker (via postMessage):
   {
       type: 'proxiedResponse',
       requestId: 'req_abc123',
       data: { status: 200, body: '...', headers: {...} }
   }
         ↓
7. Service Worker: Construct Response object
   - new Response(body, { status, headers })
   - Return to original fetch event
         ↓
8. Browser Tab A: receives response
```

**Timeout:** 30 seconds. If the tab doesn't respond, falls back to WebRTC or HTTP proxy.

## Flow 2: WebRTC Relay (Cross-Browser)

Used when the route handler is in a different browser with an established WebRTC connection.

```
Browser A                           Browser B
    │                                   │
1.  fetch('/groups/mygroup/xyz/data')   │
    │                                   │
2.  SW: Match route → instance='xyz'    │
    SW: Not a local instance            │
    SW: Check for WebRTC peer           │
    │                                   │
3.  SW → Tab A (via postMessage):       │
    "Send webrtcProxiedRequest          │
     to peer xyz"                       │
    │                                   │
4.  Tab A: peer.sendBig({              │
        type: 'webrtcProxiedRequest',   │
        requestId: 'req_456',           │
        method: 'GET',                  │
        path: '/xyz/data',             │
        ...                             │
    })                                  │
    │───── WebRTC Data Channel ────────→│
    │                                   │
5.  │                     Tab B: Receive request
    │                     Router.matchRoute()
    │                     Handler executes
    │                                   │
6.  │                     Tab B: peer.sendBig({
    │                         type: 'webrtcProxiedResponse',
    │                         requestId: 'req_456',
    │                         data: { status: 200, body: '...' }
    │                     })
    │←──── WebRTC Data Channel ────────│
    │                                   │
7.  Tab A → SW (via postMessage):       │
    { type: 'webrtcProxiedResponse',    │
      requestId: 'req_456', data: ... } │
    │                                   │
8.  SW: Construct Response, return      │
    │                                   │
9.  Browser A: receives response        │
```

**Timeout:** 30 seconds for the full round-trip.

## Flow 3: HTTP Proxy via Discovery Server

The fallback path. Used when the peer is only reachable via WebSocket through the discovery server.

```
External Client                Discovery Server            Browser (Peer)
    │                              │                           │
1.  GET /groups/mygroup/xyz/data   │                           │
    │─────── HTTP ────────────────→│                           │
    │                              │                           │
2.  │                 Match route → instance='xyz'             │
    │                 Find client WebSocket                    │
    │                              │                           │
3.  │                 WS → Peer:   │                           │
    │                 { type: 'proxiedRequest',                │
    │                   requestId: 'req_789',                  │
    │                   method: 'GET',                         │
    │                   path: '/xyz/data', ... }               │
    │                              │──── WebSocket ───────────→│
    │                              │                           │
4.  │                              │              Route handler
    │                              │              executes
    │                              │                           │
5.  │                              │              WS → Server:
    │                              │              { type: 'proxiedResponse',
    │                              │                requestId: 'req_789',
    │                              │                response: { data: {...} } }
    │                              │←─── WebSocket ───────────│
    │                              │                           │
6.  │                 Resolve pending request                   │
    │                 Build HTTP response                       │
    │←────── HTTP ────────────────│                           │
    │                              │                           │
7.  Client receives response       │                           │
```

**Timeout:** 30 seconds. Discovery server tracks pending requests and cleans them up.

## Route Handler Request Format

The request object passed to route handlers:

```javascript
{
    // From the HTTP request or proxied message
    method: 'GET',                    // HTTP method
    path: '/abc123/status',           // Full matched path
    pathParams: { instanceId: 'abc123' }, // Extracted from pattern
    remainingPath: '',                // Path after pattern match
    query: { key: 'value' },         // Query parameters
    queryString: 'key=value',         // Raw query string
    headers: { 'content-type': '...' }, // Request headers
    body: null | string | number[],   // Request body
    cookies: {},                       // Parsed cookies
    contentType: 'application/json',   // Content-Type header
    contentLength: 0,                  // Content-Length header

    // Request metadata
    requestId: 'req_abc123',           // Unique request ID
    rawUrl: '/groups/mygroup/abc123/status', // Original URL
    originalUrl: '/groups/mygroup/abc123/status',
}
```

## Route Handler Response Format

Handlers must return:

```javascript
{
    status: 200,                      // HTTP status code (required)
    statusText: 'OK',                 // Optional status text
    headers: {                        // Response headers
        'content-type': 'application/json',
        'Content-Length': '42',
    },
    body: '{"key": "value"}',         // Response body (string)
    bodyEncoding: undefined,           // Set to 'base64' for binary data
}
```

### Binary Responses

For binary data (images, files), base64-encode the body:

```javascript
{
    status: 200,
    headers: { 'content-type': 'image/jpeg' },
    body: 'base64encodeddata...',
    bodyEncoding: 'base64',
}
```

The receiving end (SW or discovery server proxy) decodes base64 back to binary before sending the HTTP response.

## Transport Decision Diagram

```
Request for /groups/{groupId}/{path}
    │
    ▼
Match route in groupRoutes[groupId]
    │
    ├─ No match → 404 Not Found
    │
    ▼
Get owning instanceId
    │
    ▼
Is instance in local instanceMap?
    │
    ├─ No ──────────────────────────────────┐
    │                                       │
    ▼                                       │
Is client still alive? (clients.matchAll)   │
    │                                       │
    ├─ No ──────────────────────────────────┤
    │                                       │
    ▼                                       ▼
Send via postMessage              Is WebRTC peer available?
    │                                       │
    ├─ Timeout (30s) ──────────────────────┤├─ No → HTTP proxy to
    │                                       │        discovery server
    ▼                                       │
Return response                  ├─ Yes → Send via WebRTC
                                            │     dataBig
                                            │
                                            ├─ Timeout → HTTP proxy
                                            │
                                            ▼
                                  Return response
```