|
| 1 | +--- |
| 2 | +title: "WebTransport over HTTP/3" |
| 3 | +description: "Bidirectional streaming via QUIC — first Java framework with WebTransport" |
| 4 | +--- |
| 5 | + |
| 6 | +# WebTransport over HTTP/3 |
| 7 | + |
| 8 | +Bidirectional streaming transport for Atmosphere using WebTransport over HTTP/3 (QUIC). The first Java framework with WebTransport support. Browsers connect over QUIC with multiplexed streams, built-in congestion control, and sub-millisecond latency — with automatic fallback to WebSocket. |
| 9 | + |
| 10 | +## How It Works |
| 11 | + |
| 12 | +The WebTransport server runs as a **Spring-managed sidecar** alongside the servlet container on a separate UDP port. Both transports feed into the same Atmosphere framework — your application code doesn't change. An `Alt-Svc` header tells browsers that HTTP/3 is available. |
| 13 | + |
| 14 | +``` |
| 15 | +Spring Boot App |
| 16 | ++----------------------------------+----------------------------+ |
| 17 | +| Servlet Container (Jetty/Tomcat) | Netty HTTP/3 (UDP sidecar) | |
| 18 | +| HTTP/1.1, HTTP/2, WebSocket | HTTP/3, WebTransport | |
| 19 | ++----------------+-----------------+-------------+--------------+ |
| 20 | + | | |
| 21 | + v v |
| 22 | + AtmosphereFramework (doCometSupport / WebSocketProcessor) |
| 23 | + | |
| 24 | + @Agent / @Prompt / @ManagedService / Handlers |
| 25 | +``` |
| 26 | + |
| 27 | +## Quick Start |
| 28 | + |
| 29 | +### 1. Add the dependency |
| 30 | + |
| 31 | +```xml |
| 32 | +<dependency> |
| 33 | + <groupId>io.projectreactor.netty</groupId> |
| 34 | + <artifactId>reactor-netty-http</artifactId> |
| 35 | +</dependency> |
| 36 | +``` |
| 37 | + |
| 38 | +This is managed by Spring Boot's BOM — no version needed. |
| 39 | + |
| 40 | +### 2. Enable in application.yml |
| 41 | + |
| 42 | +```yaml |
| 43 | +atmosphere: |
| 44 | + web-transport: |
| 45 | + enabled: true |
| 46 | + port: 4443 |
| 47 | +``` |
| 48 | +
|
| 49 | +The HTTP/3 server starts automatically with a self-signed ECDSA certificate for development. The certificate hash is exposed at `/api/webtransport-info`. |
| 50 | + |
| 51 | +### 3. Connect from JavaScript |
| 52 | + |
| 53 | +```typescript |
| 54 | +import { Atmosphere } from 'atmosphere.js'; |
| 55 | +
|
| 56 | +const atmosphere = new Atmosphere(); |
| 57 | +
|
| 58 | +// Fetch cert hash for dev self-signed cert |
| 59 | +const info = await (await fetch('/api/webtransport-info')).json(); |
| 60 | +
|
| 61 | +const subscription = await atmosphere.subscribe({ |
| 62 | + url: '/atmosphere/chat', |
| 63 | + transport: 'webtransport', |
| 64 | + fallbackTransport: 'websocket', |
| 65 | + webTransportUrl: `https://${location.hostname}:${info.port}/atmosphere/chat`, |
| 66 | + serverCertificateHashes: [info.certificateHash], |
| 67 | +}, { |
| 68 | + open: () => console.log('Connected via WebTransport!'), |
| 69 | + message: (response) => console.log(response.responseBody), |
| 70 | +}); |
| 71 | +``` |
| 72 | + |
| 73 | +In production behind a reverse proxy with real TLS, omit `webTransportUrl` and `serverCertificateHashes` — both protocols share the same origin. |
| 74 | + |
| 75 | +## Configuration |
| 76 | + |
| 77 | +| Property | Default | Description | |
| 78 | +|----------|---------|-------------| |
| 79 | +| `atmosphere.web-transport.enabled` | `false` | Enable the HTTP/3 sidecar server | |
| 80 | +| `atmosphere.web-transport.port` | `4443` | UDP port for QUIC/HTTP/3 | |
| 81 | +| `atmosphere.web-transport.host` | `0.0.0.0` | Bind address | |
| 82 | +| `atmosphere.web-transport.add-alt-svc` | `true` | Add `Alt-Svc` header to servlet responses | |
| 83 | +| `atmosphere.web-transport.ssl.certificate` | (auto) | Path to PEM certificate file | |
| 84 | +| `atmosphere.web-transport.ssl.private-key` | (auto) | Path to PEM private key file | |
| 85 | + |
| 86 | +## Production Deployment |
| 87 | + |
| 88 | +Use a reverse proxy (nginx, Cloudflare, Caddy) that terminates both HTTP/2 (TCP) and HTTP/3 (QUIC) on the same hostname:port: |
| 89 | + |
| 90 | +```nginx |
| 91 | +server { |
| 92 | + listen 443 ssl http2; |
| 93 | + listen 443 quic reuseport; |
| 94 | +
|
| 95 | + ssl_certificate /path/to/cert.pem; |
| 96 | + ssl_certificate_key /path/to/key.pem; |
| 97 | +
|
| 98 | + add_header Alt-Svc 'h3=":443"; ma=86400'; |
| 99 | +
|
| 100 | + location / { |
| 101 | + proxy_pass http://localhost:8080; |
| 102 | + proxy_http_version 1.1; |
| 103 | + proxy_set_header Upgrade $http_upgrade; |
| 104 | + proxy_set_header Connection "upgrade"; |
| 105 | + } |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +## Browser Support |
| 110 | + |
| 111 | +| Browser | Version | Coverage | |
| 112 | +|---------|---------|----------| |
| 113 | +| Chrome | 97+ | Jan 2022 | |
| 114 | +| Edge | 98+ | Feb 2022 | |
| 115 | +| Firefox | 114+ | Jun 2023 | |
| 116 | +| Safari | 26.4+ | 2026 | |
| 117 | +| Opera | 83+ | Mar 2022 | |
| 118 | + |
| 119 | +~80% global coverage. The `fallbackTransport: 'websocket'` setting ensures all browsers work. |
| 120 | + |
| 121 | +## Architecture |
| 122 | + |
| 123 | +The server uses raw Netty HTTP/3 codec (not Reactor Netty's HttpServer) because WebTransport requires protocol features not exposed by Reactor Netty: |
| 124 | + |
| 125 | +- **`ENABLE_CONNECT_PROTOCOL`** (RFC 9220) — extended CONNECT method |
| 126 | +- **`H3_DATAGRAM`** (RFC 9297) — QUIC datagram support |
| 127 | +- **`WEBTRANSPORT_MAX_SESSIONS`** — session negotiation (draft-02 + draft-07) |
| 128 | +- **WebTransport frame type `0x41`** — bidirectional data streams |
| 129 | + |
| 130 | +Sessions bridge into Atmosphere via `WebSocketProcessor.open()`, the same pattern used by JSR 356 WebSocket. |
| 131 | + |
| 132 | +## Client API |
| 133 | + |
| 134 | +The `WebTransportTransport` in atmosphere.js supports all standard `BaseTransport` features: |
| 135 | + |
| 136 | +- Automatic reconnection with exponential backoff |
| 137 | +- Protocol handshake and heartbeat |
| 138 | +- Message tracking and length-delimited framing |
| 139 | +- Outgoing/incoming interceptors |
| 140 | +- Offline queue with drain-on-reconnect |
| 141 | +- `suspend()` / `resume()` for pausing without disconnect |
| 142 | + |
| 143 | +New request options: |
| 144 | + |
| 145 | +| Option | Type | Description | |
| 146 | +|--------|------|-------------| |
| 147 | +| `webTransportUrl` | `string` | Explicit URL for the HTTP/3 server (dev mode) | |
| 148 | +| `serverCertificateHashes` | `string[]` | Base64 SHA-256 cert hashes for self-signed certs | |
| 149 | + |
| 150 | +## SPI |
| 151 | + |
| 152 | +The `org.atmosphere.webtransport` package provides a transport-agnostic SPI: |
| 153 | + |
| 154 | +| Class | Purpose | |
| 155 | +|-------|---------| |
| 156 | +| `WebTransportSession` | Abstract bidirectional stream session | |
| 157 | +| `WebTransportHandler` | Application-level callbacks | |
| 158 | +| `WebTransportProcessor` | Processing SPI | |
| 159 | +| `WebTransportProtocol` | Message-to-request conversion | |
| 160 | +| `WebTransportEventListener` | Lifecycle events | |
| 161 | + |
| 162 | +## Limitations (v1) |
| 163 | + |
| 164 | +- Single bidirectional stream per session (WebSocket-equivalent semantics) |
| 165 | +- QUIC datagrams not yet exposed to application code |
| 166 | +- Multiplexed streams deferred to v2 |
| 167 | +- Chrome strips query params from WebTransport CONNECT `:path` — auth tokens must use header-based authentication, not query parameters |
0 commit comments