# Binary Data

> **Platform Reference** — describes implemented, production behavior.

Route handlers can return binary bodies directly — the system encodes them for transport automatically. On the receiving side, binary requests are normalized just as transparently.

```javascript
// Return a Blob, ArrayBuffer, or TypedArray — encoding is automatic
const blob = await captureImage()
return {
    status: 200,
    headers: { 'content-type': blob.type },
    body: blob,
}
```

---

## Route Responses

When a route handler returns a binary body (Blob, ArrayBuffer, or TypedArray), the system handles encoding automatically. The mechanism depends on the transport:

- **WebSocket relay** — `normalizeResponse()` base64-encodes the body and sets `bodyEncoding: 'base64'` before `JSON.stringify()`. The receiving end (service worker or discovery server proxy) decodes back to binary transparently.
- **WebRTC** — `peer.sendBig()` uses MessagePack, which handles binary natively. No encoding overhead.
- **postMessage** — structured cloning passes binary types directly between page and service worker.

### What you return

```javascript
// Any of these binary types work:
return { status: 200, headers: { 'content-type': 'image/jpeg' }, body: blob }          // Blob
return { status: 200, headers: { 'content-type': 'image/png' }, body: arrayBuffer }     // ArrayBuffer
return { status: 200, headers: { 'content-type': 'application/pdf' }, body: uint8Array } // TypedArray
```

### What happens automatically

1. `normalizeResponse()` detects the binary body type
2. Converts to base64 using chunked encoding (avoids stack overflow on large payloads)
3. Sets `bodyEncoding: 'base64'` on the response
4. Service worker / discovery proxy decodes base64 back to binary for the HTTP response

### Manual encoding still works

If you set `bodyEncoding` explicitly, the normalizer passes through unchanged. Existing code with manual base64 encoding continues to work:

```javascript
// Explicit bodyEncoding is always respected
return {
    status: 200,
    headers: { 'content-type': 'image/jpeg' },
    body: manuallyEncodedBase64,
    bodyEncoding: 'base64',
}
```

---

## Route Requests (Incoming Binary)

On the request side, `normalizeBody()` handles incoming binary automatically. Binary content-types arrive as `ArrayBuffer` in `request.body`:

```javascript
group.registerRoute('/upload', async (request) => {
    // request.body is ArrayBuffer for binary content-types
    const imageData = request.body  // ArrayBuffer
    await saveToWebDAV(imageData)
    return { status: 200, body: 'OK' }
})
```

No manual decoding needed — the normalization layer (from [request body normalization](../projects/completed/normalize-request-body.md)) handles all transport formats.

---

## Direct WebRTC (sendBig)

For large payloads **outside the route request/response model**, `peer.sendBig()` sends data directly over WebRTC data channels. It auto-chunks at 16KB via MessagePack — no base64 encoding needed.

```javascript
const peers = acequia.webrtc.getPeers()
for (const [peerId, peer] of Object.entries(peers)) {
    peer.sendBig({ type: 'largeData', payload: bigArrayBuffer })
}
```

**When to use `sendBig()` vs routes:**

| | Route response | `sendBig()` |
|---|---|---|
| **Use case** | Request/response pattern (`fetch()` compatible) | Direct peer messaging, streaming |
| **Binary handling** | Automatic (base64 over WS relay, native over WebRTC) | Native MessagePack chunking |
| **Accessible via** | `fetch()`, service worker, discovery proxy | WebRTC data channel only |
| **Size** | Any (base64 adds ~33% overhead on WS relay; no overhead on WebRTC) | Any (16KB chunks, no overhead) |

**Best practice for large data:** Store on WebDAV and send a URL reference through the route response, rather than encoding the entire payload:

```javascript
// Store the file, return a reference
await fetch('/path/to/large-file.bin', { method: 'PUT', body: data })
return {
    status: 200,
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ url: '/path/to/large-file.bin' }),
}
```

---

## How It Works

### Response encoding pipeline

```
Handler returns { body: Blob }
    │
    ▼
Transport selection
    │
    ├─ WebSocket relay path:
    │     normalizeResponse() detects binary type
    │     Blob/ArrayBuffer/TypedArray → chunked btoa() → base64
    │     Sets bodyEncoding: 'base64'
    │     JSON.stringify → WebSocket → discovery server
    │     Receiver: atob() → Uint8Array → Blob → Response
    │
    ├─ WebRTC path:
    │     peer.sendBig() → MessagePack (handles binary natively)
    │     No encoding overhead
    │
    └─ postMessage path:
          Structured cloning (handles binary natively)
          No encoding overhead
```

### Request normalization pipeline

```
Binary request arrives (any transport)
    │
    ▼
normalizeBody() converts to ArrayBuffer
    │
    ├─ ArrayBuffer → as-is
    ├─ TypedArray → .buffer.slice()
    ├─ Blob descriptor → .view.buffer.slice()
    └─ String → TextEncoder
    │
    ▼
Decode by content-type:
    ├─ application/json → parsed object
    ├─ text/* → string
    └─ binary types → ArrayBuffer
    │
    ▼
Handler receives { body: <decoded>, rawBody: <ArrayBuffer> }
```

---

## Gotchas

**Explicit `bodyEncoding` is always respected.** If your response already sets `bodyEncoding: 'base64'`, the normalizer skips encoding. This ensures full backward compatibility with existing code.

**String bodies are not encoded.** Only Blob, ArrayBuffer, and TypedArray trigger automatic encoding. String and object bodies pass through unchanged — set `content-type` appropriately.

**Set `Content-Type` on responses.** The receiving end uses `Content-Type` to construct the proper Blob type. Without it, binary responses default to `application/octet-stream`.

**`sendBig()` requires a WebRTC connection.** If peers haven't established a WebRTC data channel, `sendBig()` won't work. Routes with WebSocket relay fallback are more reliable for cross-network scenarios.

**Very large data (>10MB).** For very large files, prefer storing on WebDAV and sending a URL reference. Base64 adds ~33% size overhead, and large payloads hold the WebSocket/WebRTC channel busy.
