quico 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +308 -70
- package/index.cjs +45 -0
- package/index.d.ts +158 -0
- package/index.js +46 -2631
- package/package.json +69 -14
- package/src/agent.js +275 -0
- package/src/client.js +439 -0
- package/src/crypto.js +519 -0
- package/src/h3.js +911 -0
- package/src/h3_client.js +317 -0
- package/src/h3_server.js +523 -0
- package/src/quic_connection.js +1238 -0
- package/src/server.js +346 -0
- package/src/streams.js +471 -0
- package/src/tls_bridge.js +211 -0
- package/src/transport.js +651 -0
- package/src/utils.js +290 -0
- package/src/webtransport.js +708 -0
- package/CHANGELOG.md +0 -25
- package/examples/webtransport_datagram_echo_server/client.html +0 -58
- package/examples/webtransport_datagram_echo_server/server.js +0 -29
- package/libs/crypto.js +0 -2107
- package/libs/h3.js +0 -1214
- package/libs/utils.js +0 -404
- package/quico.svg +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<h1 align="center">QUICO</h1>
|
|
6
6
|
<p align="center">
|
|
7
|
-
<em
|
|
7
|
+
<em>Pure JavaScript implementation of QUIC, HTTP/3, QPACK & WebTransport for Node.js</em>
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center">
|
|
@@ -17,112 +17,350 @@
|
|
|
17
17
|
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
-
> **⚠️ Project status:
|
|
21
|
-
> APIs may change
|
|
22
|
-
> Use at your own risk and please report issues!
|
|
20
|
+
> **⚠️ Project status: Active development.**
|
|
21
|
+
> APIs may change before v1.0. Use at your own risk and please report issues!
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Table of Contents
|
|
27
|
+
1. [What is QUIC / HTTP/3?](#-what-is-quic--http3)
|
|
28
|
+
2. [Why QUICO?](#-why-quico)
|
|
29
|
+
3. [Quick Start](#-quick-start)
|
|
30
|
+
4. [Features](#-features)
|
|
31
|
+
5. [Node.js API Compatibility](#-nodejs-api-compatibility)
|
|
32
|
+
6. [WebTransport](#-webtransport)
|
|
33
|
+
7. [Testing](#-testing)
|
|
34
|
+
8. [Project Structure](#-project-structure)
|
|
35
|
+
9. [Roadmap](#-roadmap)
|
|
36
|
+
10. [Sponsors](#-sponsors)
|
|
37
|
+
11. [Contact](#-contact)
|
|
38
|
+
12. [License](#-license)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## ⚡ What is QUIC / HTTP/3?
|
|
42
|
+
|
|
43
|
+
**QUIC** is the future of Internet transport protocols. Created at Google and standardized by the IETF, QUIC powers **HTTP/3** and is already deployed at scale by the world’s largest platforms. It was specifically designed to overcome the limitations of TCP and deliver a faster, smoother, and more resilient web.
|
|
44
|
+
|
|
45
|
+
Key advantages of QUIC include:
|
|
46
|
+
|
|
47
|
+
- **Eliminating bottlenecks**: With TCP, data must arrive strictly in order. If a single packet is lost or delayed, all subsequent packets are blocked until it arrives — a phenomenon called **head-of-line blocking**. QUIC removes this bottleneck by running over UDP, so each package arrives on its own and avoids dependence on delayed pieces of data.
|
|
48
|
+
|
|
49
|
+
- **UDP efficiency**: By running over UDP, QUIC bypasses decades of kernel-level constraints, enabling lightning-fast performance even on constrained devices such as smartphones, IoT hardware, or edge gateways.
|
|
50
|
+
|
|
51
|
+
- **Security by default**: TLS 1.3 is built directly into the protocol. Every connection is encrypted — no exceptions, no downgrade paths.
|
|
52
|
+
|
|
53
|
+
- **Seamless mobility**: Connections remain stable as devices move across networks (e.g., Wi-Fi → 4G/5G) or switch IP addresses, without breaking the session.
|
|
54
|
+
|
|
55
|
+
- **Lower latency**: QUIC merges the transport and TLS handshake into fewer round-trips, significantly reducing connection setup time.
|
|
56
|
+
|
|
57
|
+
- **Smarter congestion control**: Advanced congestion control algorithms (such as BBR) continuously measure bandwidth, round-trip time, and loss in real time. They dynamically adjust to real-world network conditions.
|
|
31
58
|
|
|
32
|
-
|
|
59
|
+
|
|
60
|
+
QUIC combines **UDP speed, TCP reliability, mandatory TLS security, and adaptive multiplexing** into one powerful transport layer.
|
|
61
|
+
|
|
62
|
+
**HTTP/3** (h3 in short) is the layer on top of QUIC. This is the evolution of the web’s application layer. Instead of riding over TCP, HTTP/3 maps HTTP requests and responses directly onto QUIC streams.
|
|
63
|
+
HTTP/3 is also himself dramatically reduces overhead and improves efficiency - thats bring faster page loads and real-time applications that scale across the modern web.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## 🧠 Why QUICO?
|
|
68
|
+
|
|
69
|
+
Node.js is the backbone of countless modern web applications, but it currently lacks a native QUIC implementation. Supporting QUIC inside Node.js requires **deep access to TLS internals** that Node's existing TLS APIs don't expose. Beyond that, QUIC demands a **highly complex architecture** — intricate state machines, packet schedulers, and congestion control mechanisms.
|
|
70
|
+
|
|
71
|
+
**QUICO** brings this missing capability directly into the **Node.js ecosystem**. It is a from-scratch JavaScript implementation of QUIC, HTTP/3 and WebTransport, built without relying on OpenSSL or native code. At its core, it uses [LemonTLS](https://github.com/colocohen/lemon-tls) — a pure JavaScript TLS 1.3 library built by the same author — to provide the cryptographic layer that QUIC requires. Together, QUICO and LemonTLS form a complete, fully auditable JavaScript networking stack from UDP to HTTP/3.
|
|
72
|
+
|
|
73
|
+
**What this means for you:**
|
|
74
|
+
|
|
75
|
+
- **`npm install` and go** — no build tools, no platform-specific binaries, no gyp
|
|
76
|
+
- **Works everywhere Node.js runs** — Linux, macOS, Windows, ARM, Docker, edge
|
|
77
|
+
- **Debuggable top to bottom** — every packet, every frame, every byte is JavaScript you can step through
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## 📦 Quick Start
|
|
33
81
|
|
|
34
82
|
```bash
|
|
35
83
|
npm install quico
|
|
36
84
|
```
|
|
37
85
|
|
|
38
|
-
|
|
86
|
+
### Server
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
import quico from 'quico';
|
|
90
|
+
import fs from 'node:fs';
|
|
91
|
+
|
|
92
|
+
const server = quico.createServer({
|
|
93
|
+
key: fs.readFileSync('server.key'),
|
|
94
|
+
cert: fs.readFileSync('server.crt')
|
|
95
|
+
}, (req, res) => {
|
|
96
|
+
res.writeHead(200, { 'content-type': 'text/plain' });
|
|
97
|
+
res.end('Hello from HTTP/3!');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
server.listen(4433, () => {
|
|
101
|
+
console.log('HTTP/3 server on https://localhost:4433');
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Client
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
import quico from 'quico';
|
|
39
109
|
|
|
40
|
-
|
|
110
|
+
quico.request('https://www.google.com/', (res) => {
|
|
111
|
+
console.log('Status:', res.statusCode); // 200
|
|
112
|
+
console.log('Protocol:', res.httpVersion); // "3.0"
|
|
113
|
+
res.on('data', (chunk) => process.stdout.write(chunk));
|
|
114
|
+
});
|
|
115
|
+
```
|
|
41
116
|
|
|
42
|
-
|
|
43
|
-
Generate one using OpenSSL:
|
|
117
|
+
> 📂 For more examples, see [`examples/`](./examples)
|
|
44
118
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
119
|
+
|
|
120
|
+
## ✨ Features
|
|
121
|
+
|
|
122
|
+
### HTTP/3 Server & Client
|
|
123
|
+
- Full request/response cycle over QUIC
|
|
124
|
+
- QPACK header compression with static and dynamic tables
|
|
125
|
+
- Standard Node.js API: `req.headers`, `res.writeHead()`, `res.end()`
|
|
126
|
+
- Multiple requests per connection (stream multiplexing)
|
|
127
|
+
- Automatic `Alt-Svc` header for HTTP/3 protocol discovery
|
|
128
|
+
- H3 → H2 → H1 fallback when QUIC isn't available
|
|
129
|
+
- Tested against Google, Cloudflare, Facebook, Microsoft and nginx QUIC servers
|
|
130
|
+
|
|
131
|
+
### WebTransport
|
|
132
|
+
- Client and server implementation
|
|
133
|
+
- Bidirectional and unidirectional streams (Node.js Duplex + WHATWG Streams API)
|
|
134
|
+
- Unreliable datagrams (RFC 9297)
|
|
135
|
+
- Natural API: `req.on('stream')`, `res.sendDatagram()`, `res.writeHead(200)` to accept
|
|
136
|
+
|
|
137
|
+
### QUIC Transport
|
|
138
|
+
- Full TLS 1.3 handshake via LemonTLS (no OpenSSL)
|
|
139
|
+
- Initial, Handshake, and 1-RTT packet handling
|
|
140
|
+
- Connection ID management
|
|
141
|
+
- ACK processing and flow control
|
|
142
|
+
- Key updates (key phase bit rotation)
|
|
143
|
+
- HelloRetryRequest support (P-256, P-384, X25519)
|
|
144
|
+
- AES-128-GCM and AES-256-GCM cipher suites
|
|
145
|
+
- Handshake timeout
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
## 🔗 Node.js API Compatibility
|
|
149
|
+
|
|
150
|
+
One of the core design goals of QUICO is to provide a **familiar API**. If you already know `node:https`, working with QUICO should feel natural — `createServer`, `request`, `req`, `res` all work the same way.
|
|
151
|
+
|
|
152
|
+
This means existing frameworks work out of the box:
|
|
153
|
+
|
|
154
|
+
```js
|
|
155
|
+
import express from 'express';
|
|
156
|
+
import quico from 'quico';
|
|
157
|
+
|
|
158
|
+
const app = express();
|
|
159
|
+
app.get('/', (req, res) => res.json({ protocol: req.httpVersion }));
|
|
160
|
+
|
|
161
|
+
quico.createServer({ key: KEY, cert: CERT }, app).listen(4433);
|
|
162
|
+
// → { "protocol": "3.0" }
|
|
48
163
|
```
|
|
49
164
|
|
|
50
|
-
|
|
165
|
+
For multi-domain setups, QUICO supports `SNICallback` just like `node:https`:
|
|
51
166
|
|
|
52
167
|
```js
|
|
53
|
-
|
|
54
|
-
|
|
168
|
+
import quico from 'quico';
|
|
169
|
+
import tls from 'lemon-tls';
|
|
55
170
|
|
|
56
171
|
const server = quico.createServer({
|
|
57
172
|
SNICallback: function (servername, cb) {
|
|
58
|
-
cb(null, {
|
|
59
|
-
key: fs.readFileSync('certs/
|
|
60
|
-
cert: fs.readFileSync('certs/
|
|
61
|
-
});
|
|
173
|
+
cb(null, tls.createSecureContext({
|
|
174
|
+
key: fs.readFileSync('certs/' + servername + '.key'),
|
|
175
|
+
cert: fs.readFileSync('certs/' + servername + '.crt')
|
|
176
|
+
}));
|
|
62
177
|
}
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
res.
|
|
66
|
-
'Content-Type': 'text/html; charset=utf-8',
|
|
67
|
-
'Content-Length': data.byteLength
|
|
68
|
-
});
|
|
69
|
-
res.end(data);
|
|
178
|
+
}, (req, res) => {
|
|
179
|
+
res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });
|
|
180
|
+
res.end('Hello World from HTTP/3 on Node.js!');
|
|
70
181
|
});
|
|
71
182
|
|
|
72
|
-
server.listen(4433,
|
|
183
|
+
server.listen(4433, () => {
|
|
73
184
|
console.log('🚀 QUIC server running on https://localhost:4433');
|
|
74
185
|
});
|
|
75
186
|
```
|
|
76
187
|
|
|
77
|
-
> ✅ You must launch Chrome with `--enable-quic` and `--origin-to-force-quic-on=localhost:4433`
|
|
78
|
-
> 📂 For more examples, see [`examples/`](./examples)
|
|
79
188
|
|
|
80
|
-
##
|
|
189
|
+
## 🚀 WebTransport
|
|
81
190
|
|
|
82
|
-
|
|
83
|
-
✅ = Completed 🔄 = In progress ⏳ = Planned ❌ = Not planned
|
|
191
|
+
### Server
|
|
84
192
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
193
|
+
```js
|
|
194
|
+
import quico from 'quico';
|
|
195
|
+
|
|
196
|
+
quico.createServer({ key: KEY, cert: CERT }, (req, res) => {
|
|
197
|
+
if (req.headers[':protocol'] === 'webtransport') {
|
|
198
|
+
// Accept the WebTransport session
|
|
199
|
+
res.writeHead(200);
|
|
200
|
+
|
|
201
|
+
// Bidirectional streams from the client
|
|
202
|
+
req.on('stream', (stream) => {
|
|
203
|
+
stream.on('data', (chunk) => {
|
|
204
|
+
stream.write(chunk); // echo back
|
|
205
|
+
});
|
|
206
|
+
stream.on('end', () => stream.end());
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Unreliable datagrams
|
|
210
|
+
req.on('datagram', (data) => {
|
|
211
|
+
res.sendDatagram(data); // echo back
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Server can also create streams
|
|
215
|
+
const push = res.createBidirectionalStream();
|
|
216
|
+
push.write('server says hello');
|
|
217
|
+
push.end();
|
|
218
|
+
} else {
|
|
219
|
+
// Regular HTTP/3 request
|
|
220
|
+
res.end('Hello HTTP/3!');
|
|
221
|
+
}
|
|
222
|
+
}).listen(4433);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Client (Node.js)
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
import { WebTransport } from 'quico';
|
|
229
|
+
|
|
230
|
+
const wt = new WebTransport('https://localhost:4433/echo');
|
|
231
|
+
await wt.ready;
|
|
232
|
+
|
|
233
|
+
// Bidirectional stream
|
|
234
|
+
const stream = await wt.createBidirectionalStream();
|
|
235
|
+
stream.write('hello');
|
|
236
|
+
stream.on('data', (chunk) => console.log('echo:', chunk.toString()));
|
|
237
|
+
stream.end();
|
|
238
|
+
|
|
239
|
+
// Unidirectional stream
|
|
240
|
+
const uni = await wt.createUnidirectionalStream();
|
|
241
|
+
uni.write('fire and forget');
|
|
242
|
+
uni.end();
|
|
243
|
+
|
|
244
|
+
// Datagrams (unreliable)
|
|
245
|
+
wt.sendDatagram(Buffer.from('ping'));
|
|
246
|
+
wt.on('datagram', (data) => console.log('pong:', data.toString()));
|
|
247
|
+
|
|
248
|
+
wt.close();
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Client (Browser)
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
const wt = new WebTransport('https://yourserver.com:4433/live');
|
|
255
|
+
await wt.ready;
|
|
256
|
+
|
|
257
|
+
// Send via WHATWG Streams API
|
|
258
|
+
const writer = wt.datagrams.writable.getWriter();
|
|
259
|
+
await writer.write(new TextEncoder().encode('Hello QUIC'));
|
|
260
|
+
|
|
261
|
+
// Receive
|
|
262
|
+
const reader = wt.datagrams.readable.getReader();
|
|
263
|
+
const { value } = await reader.read();
|
|
264
|
+
console.log('Received:', new TextDecoder().decode(value));
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
## 🧪 Testing
|
|
269
|
+
|
|
270
|
+
QUIC requires TLS 1.3. Self-signed RSA certificates usually fail with QUIC clients and browsers. For local development, [mkcert](https://github.com/FiloSottile/mkcert) is the easiest option — it generates locally-trusted ECDSA certificates automatically.
|
|
271
|
+
|
|
272
|
+
This project supports ALPN `"h3"` only (not draft variants like `h3-29`). You must explicitly force h3 when testing with Chrome.
|
|
273
|
+
|
|
274
|
+
**Chrome:**
|
|
275
|
+
```bash
|
|
276
|
+
chrome --enable-quic --quic-version=h3 --ignore-certificate-errors --origin-to-force-quic-on=localhost:4433
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**curl:**
|
|
280
|
+
```bash
|
|
281
|
+
curl --http3 https://localhost:4433 --insecure
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Integration tests:**
|
|
285
|
+
```bash
|
|
286
|
+
node examples/test_integration.js # HTTP/3 client + server — 19 tests
|
|
287
|
+
node examples/test_wt_server.js # WebTransport server echo (QUICO ↔ QUICO)
|
|
288
|
+
node examples/test_webtransport.js URL # WebTransport client against any server
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
## 📁 Project Structure
|
|
293
|
+
|
|
294
|
+
```
|
|
295
|
+
quico/
|
|
296
|
+
├── index.js — Public API exports
|
|
297
|
+
└── src/
|
|
298
|
+
├── server.js — Unified server (drop-in for node:https)
|
|
299
|
+
├── client.js — Unified client with H3 → H2 → H1 fallback
|
|
300
|
+
├── agent.js — Connection pooling and Alt-Svc protocol cache
|
|
301
|
+
├── streams.js — IncomingMessage, ServerResponse, ClientRequest
|
|
302
|
+
├── h3.js — HTTP/3 framing, QPACK, WebTransport detection
|
|
303
|
+
├── h3_server.js — HTTP/3 + WebTransport server
|
|
304
|
+
├── h3_client.js — HTTP/3 client (DNS, UDP, QUIC, H3 pipeline)
|
|
305
|
+
├── quic_connection.js — QUIC state machine, burst scheduler, flow control
|
|
306
|
+
├── transport.js — Packet parsing, frame encoding/decoding
|
|
307
|
+
├── crypto.js — AEAD encryption, header protection, key derivation
|
|
308
|
+
├── tls_bridge.js — LemonTLS integration, transport parameters
|
|
309
|
+
└── webtransport.js — WebTransport client + stream classes
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
## 🛣 Roadmap
|
|
314
|
+
|
|
315
|
+
### ✅ Done
|
|
316
|
+
- QUIC v1 transport (RFC 9000) — Initial, Handshake, 1-RTT
|
|
317
|
+
- TLS 1.3 via LemonTLS — X25519, P-256, P-384, AES-128/256-GCM
|
|
318
|
+
- HTTP/3 server and client (RFC 9114)
|
|
319
|
+
- QPACK header compression with static and dynamic tables (RFC 9204)
|
|
320
|
+
- WebTransport client and server — bidi/uni streams + datagrams (RFC 9297)
|
|
321
|
+
- Node.js `https`-style API compatibility — `createServer`, `request`, `get`
|
|
322
|
+
- Express / Fastify / Koa middleware support
|
|
323
|
+
- Connection reuse and multiplexing
|
|
324
|
+
- HelloRetryRequest — client and server
|
|
325
|
+
- Key updates (key phase rotation)
|
|
326
|
+
- Interop with Google, Cloudflare, Facebook, Microsoft, nginx
|
|
327
|
+
- Handshake timeout
|
|
92
328
|
|
|
93
329
|
### 🔄 In Progress
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
| 🔄 | HTTP/3: GOAWAY & request cancellation | Partial control flow support |
|
|
99
|
-
| 🔄 | Integration with Node.js `https`-style API | Goal: seamless compatibility |
|
|
100
|
-
| 🔄 | Project modularization & internal refactoring | To improve maintainability |
|
|
330
|
+
- Loss detection and PTO timers (RFC 9002)
|
|
331
|
+
- Congestion control (BBR / CUBIC)
|
|
332
|
+
- 0-RTT and session resumption
|
|
333
|
+
- Debug log cleanup (`QUICO_DEBUG` environment flag)
|
|
101
334
|
|
|
102
335
|
### ⏳ Planned
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
| ⏳ | Fuzz testing and protocol robustness checks | To improve security and reliability |
|
|
114
|
-
| ⏳ | Developer documentation & API reference | For better onboarding |
|
|
115
|
-
| ⏳ | TypeScript typings | Type safety for IDE support |
|
|
116
|
-
|
|
117
|
-
_Note: QUICO is a work-in-progress project aiming to provide a full JavaScript implementation of QUIC + HTTP/3. Community contributions are welcome!_
|
|
118
|
-
|
|
119
|
-
_Please ⭐ star the repo to follow progress!_
|
|
336
|
+
- Connection migration (PATH_CHALLENGE / PATH_RESPONSE)
|
|
337
|
+
- ChaCha20-Poly1305 cipher suite (0x1303)
|
|
338
|
+
- GOAWAY and graceful shutdown
|
|
339
|
+
- Stream priority (RFC 9218)
|
|
340
|
+
- TypeScript type definitions
|
|
341
|
+
- Performance benchmarks
|
|
342
|
+
- Fuzz testing
|
|
343
|
+
|
|
344
|
+
_Community contributions are welcome! Please ⭐ star the repo to follow progress._
|
|
345
|
+
|
|
120
346
|
|
|
121
347
|
## 🙏 Sponsors
|
|
122
348
|
|
|
123
349
|
QUICO is an evenings-and-weekends project.
|
|
124
350
|
Support development via **GitHub Sponsors** or simply share the project.
|
|
125
351
|
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
## 💬 Contact
|
|
355
|
+
|
|
356
|
+
For feedback, ideas, or contributions,
|
|
357
|
+
contact directly at:
|
|
358
|
+
📧 **support@quicojs.dev**
|
|
359
|
+
|
|
360
|
+
For security-related issues, please see [SECURITY.md](./SECURITY.md).
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
|
|
126
364
|
## 📜 License
|
|
127
365
|
|
|
128
366
|
**Apache License 2.0**
|
package/index.cjs
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* quico — CommonJS entry point
|
|
3
|
+
*
|
|
4
|
+
* Since quico is ESM-only internally, CommonJS users
|
|
5
|
+
* must await .ready before calling any API:
|
|
6
|
+
*
|
|
7
|
+
* const quico = require('quico');
|
|
8
|
+
* await quico.ready;
|
|
9
|
+
* const server = quico.createServer({ ... }, handler);
|
|
10
|
+
*
|
|
11
|
+
* Or in one step:
|
|
12
|
+
*
|
|
13
|
+
* const quico = await require('quico').ready;
|
|
14
|
+
* const server = quico.createServer({ ... }, handler);
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
let _mod;
|
|
18
|
+
|
|
19
|
+
const proxy = new Proxy({}, {
|
|
20
|
+
get(target, key) {
|
|
21
|
+
// .ready must be accessible before module loads
|
|
22
|
+
if (key === 'ready') return target.ready;
|
|
23
|
+
// Prevent Promise auto-unwrapping when proxy is returned from async
|
|
24
|
+
if (key === 'then') return undefined;
|
|
25
|
+
if (!_mod) throw new Error(
|
|
26
|
+
'quico: module not loaded yet. Use "const quico = await require(\'quico\').ready" first.'
|
|
27
|
+
);
|
|
28
|
+
return _mod[key];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Kick off async ESM import — sets .ready on proxy target
|
|
33
|
+
proxy.ready = import('./index.js').then(function (m) {
|
|
34
|
+
// Merge default export (createServer, request, etc.)
|
|
35
|
+
// with named exports (WebTransport, etc.)
|
|
36
|
+
_mod = Object.assign({}, m.default);
|
|
37
|
+
for (var key in m) {
|
|
38
|
+
if (key !== 'default' && key !== '__esModule') {
|
|
39
|
+
_mod[key] = m[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return proxy;
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
module.exports = proxy;
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* quico — TypeScript declarations
|
|
3
|
+
* Pure JavaScript QUIC, HTTP/3, QPACK & WebTransport for Node.js
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/// <reference types="node" />
|
|
7
|
+
|
|
8
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
9
|
+
import { Duplex, Readable, Writable } from 'node:stream';
|
|
10
|
+
import { EventEmitter } from 'node:events';
|
|
11
|
+
|
|
12
|
+
// ============================================================
|
|
13
|
+
// Server
|
|
14
|
+
// ============================================================
|
|
15
|
+
|
|
16
|
+
type RequestHandler = (req: QuicoIncomingMessage, res: QuicoServerResponse) => void;
|
|
17
|
+
|
|
18
|
+
interface ServerOptions {
|
|
19
|
+
key?: string | Buffer;
|
|
20
|
+
cert?: string | Buffer;
|
|
21
|
+
ca?: string | Buffer;
|
|
22
|
+
SNICallback?: (servername: string, cb: (err: Error | null, ctx: any) => void) => void;
|
|
23
|
+
maxConnections?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface QuicoServer extends EventEmitter {
|
|
27
|
+
listen(port: number, callback?: () => void): this;
|
|
28
|
+
listen(port: number, host: string, callback?: () => void): this;
|
|
29
|
+
close(callback?: () => void): void;
|
|
30
|
+
on(event: 'request', listener: RequestHandler): this;
|
|
31
|
+
on(event: 'listening', listener: () => void): this;
|
|
32
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
33
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface QuicoIncomingMessage extends IncomingMessage {
|
|
37
|
+
/** QUIC stream ID */
|
|
38
|
+
streamId?: number;
|
|
39
|
+
|
|
40
|
+
// WebTransport events (available after res.writeHead(200) on WT sessions)
|
|
41
|
+
on(event: 'stream', listener: (stream: Duplex) => void): this;
|
|
42
|
+
on(event: 'unidirectionalStream', listener: (stream: Readable) => void): this;
|
|
43
|
+
on(event: 'datagram', listener: (data: Buffer) => void): this;
|
|
44
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface QuicoServerResponse extends ServerResponse {
|
|
48
|
+
// WebTransport methods (available after res.writeHead(200) on WT sessions)
|
|
49
|
+
sendDatagram?(data: Buffer | Uint8Array | string): void;
|
|
50
|
+
createBidirectionalStream?(): Duplex;
|
|
51
|
+
createUnidirectionalStream?(): Writable;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
// ============================================================
|
|
56
|
+
// Client
|
|
57
|
+
// ============================================================
|
|
58
|
+
|
|
59
|
+
interface RequestOptions {
|
|
60
|
+
hostname?: string;
|
|
61
|
+
host?: string;
|
|
62
|
+
port?: number;
|
|
63
|
+
path?: string;
|
|
64
|
+
method?: string;
|
|
65
|
+
headers?: Record<string, string | string[]>;
|
|
66
|
+
agent?: QuicoAgent;
|
|
67
|
+
rejectUnauthorized?: boolean;
|
|
68
|
+
timeout?: number;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface QuicoClientRequest extends Writable {
|
|
72
|
+
on(event: 'response', listener: (res: QuicoIncomingMessage) => void): this;
|
|
73
|
+
on(event: 'error', listener: (err: Error) => void): this;
|
|
74
|
+
on(event: 'timeout', listener: () => void): this;
|
|
75
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
76
|
+
end(data?: string | Buffer): this;
|
|
77
|
+
abort(): void;
|
|
78
|
+
setTimeout(ms: number, callback?: () => void): this;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
// ============================================================
|
|
83
|
+
// Agent
|
|
84
|
+
// ============================================================
|
|
85
|
+
|
|
86
|
+
interface QuicoAgent {
|
|
87
|
+
destroy(): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
// ============================================================
|
|
92
|
+
// WebTransport
|
|
93
|
+
// ============================================================
|
|
94
|
+
|
|
95
|
+
interface WebTransportOptions {
|
|
96
|
+
/** Skip certificate verification (default: true for self-signed) */
|
|
97
|
+
rejectUnauthorized?: boolean;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
declare class WebTransport extends EventEmitter {
|
|
101
|
+
constructor(url: string, options?: WebTransportOptions);
|
|
102
|
+
|
|
103
|
+
/** Resolves when the WebTransport session is established */
|
|
104
|
+
readonly ready: Promise<void>;
|
|
105
|
+
/** Current state: 'connecting' | 'connected' | 'closed' */
|
|
106
|
+
readonly state: string;
|
|
107
|
+
/** The URL of the WebTransport session */
|
|
108
|
+
readonly url: string;
|
|
109
|
+
|
|
110
|
+
/** WHATWG-style datagram interface */
|
|
111
|
+
readonly datagrams: {
|
|
112
|
+
readonly readable: ReadableStream<Uint8Array>;
|
|
113
|
+
readonly writable: WritableStream<Uint8Array>;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/** Create a bidirectional stream (Node.js Duplex + WHATWG .readable/.writable) */
|
|
117
|
+
createBidirectionalStream(): Promise<Duplex & {
|
|
118
|
+
readonly readable: ReadableStream<Uint8Array>;
|
|
119
|
+
readonly writable: WritableStream<Uint8Array>;
|
|
120
|
+
}>;
|
|
121
|
+
|
|
122
|
+
/** Create a unidirectional stream for writing */
|
|
123
|
+
createUnidirectionalStream(): Promise<Writable>;
|
|
124
|
+
|
|
125
|
+
/** Send an unreliable datagram */
|
|
126
|
+
sendDatagram(data: Buffer | Uint8Array): void;
|
|
127
|
+
|
|
128
|
+
/** Close the WebTransport session */
|
|
129
|
+
close(info?: { closeCode?: number; reason?: string }): void;
|
|
130
|
+
|
|
131
|
+
on(event: 'datagram', listener: (data: Buffer) => void): this;
|
|
132
|
+
on(event: 'close', listener: () => void): this;
|
|
133
|
+
on(event: string, listener: (...args: any[]) => void): this;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
// ============================================================
|
|
138
|
+
// Main module
|
|
139
|
+
// ============================================================
|
|
140
|
+
|
|
141
|
+
declare const quico: {
|
|
142
|
+
createServer(options: ServerOptions, handler?: RequestHandler): QuicoServer;
|
|
143
|
+
|
|
144
|
+
request(url: string | URL, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
145
|
+
request(url: string | URL, options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
146
|
+
request(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
147
|
+
|
|
148
|
+
get(url: string | URL, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
149
|
+
get(url: string | URL, options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
150
|
+
get(options: RequestOptions, callback?: (res: QuicoIncomingMessage) => void): QuicoClientRequest;
|
|
151
|
+
|
|
152
|
+
globalAgent: QuicoAgent;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export default quico;
|
|
156
|
+
export { WebTransport };
|
|
157
|
+
export { QuicoServer, QuicoIncomingMessage, QuicoServerResponse, QuicoClientRequest, QuicoAgent };
|
|
158
|
+
export { ServerOptions, RequestOptions, RequestHandler, WebTransportOptions };
|