@webprotocol/nwep
v0.2.5
Published
@webprotocol/nwep ==================
Readme
@webprotocol/nwep
Node.js bindings for quiche-nwep, providing QUIC transport and NWEP (New Web Exchange Protocol) support.
Installation
npm install @webprotocol/nwepOfficial High-Level Packages
For most use cases, we recommend using one of the official high-level packages built on top of @webprotocol/nwep:
webfetch - NWEP Client Library
A fetch-like API for making NWEP requests:
import { read, write, modify, del } from 'webfetch';
// READ request
const res = await read('web://[::1]:4433/users/123');
console.log(res.json());
// WRITE request
await write('web://[::1]:4433/users', {
name: 'alice',
email: '[email protected]'
});
// MODIFY request
await modify('web://[::1]:4433/users/123', { name: 'Alice Cooper' });
// DELETE request
await del('web://[::1]:4433/users/123');Repository: webfetch
server - NWEP Server Framework
An Express-like framework for building NWEP servers:
import { Server } from 'server';
const server = new Server();
server.read('/', ctx => {
ctx.send({ message: 'hello world' });
});
server.write('/users', ctx => {
const data = ctx.parse();
ctx.status('created');
ctx.send({ id: 123, ...data });
});
server.read('/users/:id', ctx => {
ctx.send({ id: ctx.id, name: 'alice' });
});
server.listen(4433);Repository: server
Low-Level API
The following documentation covers the low-level QUIC and NWEP APIs. Most developers should use webfetch or server instead, which handle connection management, error handling, and provide idiomatic JavaScript APIs.
Use the low-level API only if you need:
- Custom QUIC connection configuration
- Direct control over packet handling
- Integration with existing QUIC applications
- Protocol-level debugging or experimentation
Error Handling
N-API functions in this package can return either a successful result or an Error object. You must check the result before using it:
// Helper function to check for errors
function isNapiError(value) {
return value instanceof Error;
}
// Example: Creating a connection
const conn = Connection.connect(scid, local, peer, config);
if (isNapiError(conn)) {
console.error('Failed to connect:', conn.message);
return;
}
// Now safe to use conn
conn.send(buffer);Common pattern for all N-API calls:
const result = someFunctionCall();
if (isNapiError(result)) {
// Handle error - result.message contains the error description
return;
}
// Use result safelyNote: Some operations like conn.recv() may return a "Done" error, which is not a failure but indicates no more data is available:
const recvResult = conn.recv(msg, peer);
if (isNapiError(recvResult) && recvResult.message !== 'Done') {
console.error('Error receiving:', recvResult.message);
return;
}
// Continue processingThis pattern is used throughout the low-level API. The high-level packages (webfetch and server) handle this automatically.
Requirements
- Node.js 12.22+ / 14.17+ / 15.12+ / 16.0+
- Rust 1.85+ (install from rustup.rs)
- CMake 3.18+
- C++ compiler (GCC, Clang, or MSVC)
On Ubuntu/Debian:
sudo apt-get install build-essential cmakeOn macOS:
brew install cmakeLow-Level Quick Start
The examples below demonstrate direct usage of the QUIC and NWEP APIs. For production applications, consider using webfetch or server instead.
NWEP Client (Low-Level)
const dgram = require('dgram');
const { Config, Connection, H3Connection } = require('@webprotocol/nwep');
// Create UDP socket
const socket = dgram.createSocket('udp4');
// Create config
const config = new Config();
config.setApplicationProtos(['nwep/1']);
config.setMaxIdleTimeout(30000);
config.setInitialMaxData(10_000_000);
config.setInitialMaxStreamDataBidiLocal(1_000_000);
config.setInitialMaxStreamDataBidiRemote(1_000_000);
config.setInitialMaxStreamsBidi(100);
// Create QUIC connection
const connId = 0n;
const local = { address: '0.0.0.0', family: 'IPv4', port: 0 };
const peer = { address: '127.0.0.1', family: 'IPv4', port: 4433 };
const conn = Connection.connect('example.com', connId, local, peer, config);
// Create NWEP connection
const h3 = new H3Connection(conn);
// Send NWEP READ request
const headers = [
[':method', 'READ'],
[':scheme', 'web'],
[':authority', 'example.com'],
[':path', '/api/data'],
];
const streamId = h3.sendRequest(headers, true);
// Handle socket events
socket.on('message', (msg, rinfo) => {
const recvInfo = {
from: { address: rinfo.address, family: rinfo.family, port: rinfo.port },
to: local,
};
conn.recv(msg, recvInfo);
// Poll for NWEP events
for (const event of h3.poll(conn)) {
if (event.type === 'headers') {
console.log('Response:', event.headers);
} else if (event.type === 'data') {
console.log('Body:', event.data);
}
}
// Send outgoing packets
const buf = Buffer.allocUnsafe(1200);
while (true) {
const [written, sendInfo] = conn.send(buf);
if (written === 0) break;
socket.send(buf.slice(0, written), sendInfo.to.port, sendInfo.to.address);
}
});
socket.bind(local.port);NWEP Server (Low-Level)
const dgram = require('dgram');
const { Config, Connection, H3Connection } = require('@webprotocol/nwep');
const socket = dgram.createSocket('udp4');
const connections = new Map();
// Create server config
const config = new Config();
config.setApplicationProtos(['nwep/1']);
config.loadCertChainFromPemFile('cert.crt');
config.loadPrivKeyFromPemFile('cert.key');
config.setMaxIdleTimeout(30000);
config.setInitialMaxData(10_000_000);
config.setInitialMaxStreamDataBidiLocal(1_000_000);
config.setInitialMaxStreamDataBidiRemote(1_000_000);
config.setInitialMaxStreamsBidi(100);
socket.on('message', (msg, rinfo) => {
const connId = getConnectionId(msg); // Your connection ID logic
let conn = connections.get(connId);
if (!conn) {
const local = { address: '127.0.0.1', family: 'IPv4', port: 4433 };
const peer = { address: rinfo.address, family: rinfo.family, port: rinfo.port };
conn = Connection.accept(connId, local, peer, config);
connections.set(connId, conn);
}
const recvInfo = {
from: { address: rinfo.address, family: rinfo.family, port: rinfo.port },
to: { address: '127.0.0.1', family: 'IPv4', port: 4433 },
};
conn.recv(msg, recvInfo);
if (conn.isEstablished() && !conn.h3) {
conn.h3 = new H3Connection(conn);
}
if (conn.h3) {
// Poll for NWEP requests
for (const event of conn.h3.poll(conn)) {
if (event.type === 'headers') {
// Send NWEP response
const responseHeaders = [
[':status', 'ok'],
['content-type', 'text/plain'],
];
conn.h3.sendResponse(event.streamId, responseHeaders, false);
conn.h3.sendBody(event.streamId, Buffer.from('Hello NWEP!'), true);
}
}
}
// Send outgoing packets
const buf = Buffer.allocUnsafe(1200);
while (true) {
const [written, sendInfo] = conn.send(buf);
if (written === 0) break;
socket.send(buf.slice(0, written), sendInfo.to.port, sendInfo.to.address);
}
});
socket.bind(4433);API Reference
Config
Create and configure a QUIC connection:
const config = new Config();Methods:
setApplicationProtos(protos: string[])- Set ALPN protocols (e.g.,['nwep/1'])loadCertChainFromPemFile(path: string)- Load certificate chain (server)loadPrivKeyFromPemFile(path: string)- Load private key (server)setMaxIdleTimeout(ms: number)- Set idle timeout in millisecondssetInitialMaxData(bytes: number)- Set connection-level flow controlsetInitialMaxStreamDataBidiLocal(bytes: number)- Set stream flow controlsetInitialMaxStreamDataBidiRemote(bytes: number)- Set stream flow controlsetInitialMaxStreamsBidi(streams: number)- Set maximum bidirectional streamssetCongestionControl(algo: string)- Set congestion control ('cubic', 'reno', 'bbr')
Connection
Create a QUIC connection:
// Client
const conn = Connection.connect(serverName, connId, local, peer, config);
// Server
const conn = Connection.accept(connId, local, peer, config);Methods:
recv(buf: Buffer, info: RecvInfo)- Process incoming packetsend(buf: Buffer): [number, SendInfo]- Generate outgoing packetstreamSend(id: bigint, data: Buffer, fin: boolean)- Send data on streamstreamRecv(id: bigint, buf: Buffer)- Receive data from streamclose(app: boolean, err: number, reason: string)- Close connectiontimeout(): number- Get next timeout in millisecondsonTimeout()- Handle timeout eventisEstablished(): boolean- Check if handshake completeisClosed(): boolean- Check if connection closed
H3Connection
Create an NWEP/HTTP/3 connection:
const h3 = new H3Connection(conn);Methods:
sendRequest(headers: [string, string][], fin: boolean): bigint- Send NWEP requestsendResponse(streamId: bigint, headers: [string, string][], fin: boolean)- Send NWEP responsesendBody(streamId: bigint, data: Buffer, fin: boolean)- Send body datapoll(conn: Connection): Event[]- Poll for eventsisNwep(conn: Connection): boolean- Check if using NWEP protocol
Events:
{ type: 'headers', streamId: bigint, headers: [string, string][] }{ type: 'data', streamId: bigint, data: Buffer }{ type: 'finished', streamId: bigint }{ type: 'datagram', data: Buffer }
NWEP Headers
Request:
const headers = [
[':method', 'READ'], // READ, WRITE, MODIFY, DELETE, PROBE
[':scheme', 'web'], // web:// scheme
[':authority', 'example.com'],
[':path', '/api/resource'],
['accept', 'application/json'],
];Response:
const headers = [
[':status', 'ok'], // ok, created, not_found, internal_error, etc.
['content-type', 'application/json'],
];Examples
High-Level Examples
Recommended for most users:
- webfetch - Fetch-like NWEP client library with convenience methods
- server - Express-like NWEP server framework
Low-Level Examples
Complete low-level examples are available in the examples/ directory:
- nwep-server.js - Full NWEP server implementation
- nwep-client.js - NWEP client with request handling
- server.js - Basic QUIC server
- client.js - Basic QUIC client
- migration-demo.js - Connection migration example
- stats-demo.js - Statistics monitoring
Run a low-level example:
# NWEP server
node examples/nwep-server.js
# NWEP client
node examples/nwep-client.js web://127.0.0.1:4433/Building from Source
The package uses napi-rs to build native Node.js addons from Rust:
# Development build
npm run build
# Release build
npm run build:release
# Debug build with symbols
npm run build:debugThe build process:
- Checks for Rust installation
- Compiles Rust code to native addon
- Places
.nodefile in package directory - Generates TypeScript definitions
Testing
npm testTests cover:
- QUIC connection establishment
- NWEP request/response handling
- Stream management
- Connection migration
- Error handling
TypeScript
TypeScript definitions are included in index.d.ts:
import { Config, Connection, H3Connection } from '@webprotocol/nwep';
const config: Config = new Config();
const conn: Connection = Connection.connect('example.com', 0n, local, peer, config);
const h3: H3Connection = new H3Connection(conn);Architecture
The NWEP ecosystem consists of multiple layers:
High-Level Libraries
webfetch (client) / server (framework)
↓
@webprotocol/nwep
(Low-Level QUIC/NWEP API)
↓
N-API Bindings (napi-rs)
↓
Rust quiche Library
↓
QUIC/NWEP Protocol
↓
UDP SocketLayer Responsibilities:
- webfetch / server - High-level, application-friendly APIs (recommended)
- @webprotocol/nwep - Low-level QUIC/NWEP primitives (advanced usage)
- napi-rs bindings - Native performance bridge to Rust
- quiche - Core protocol implementation
This architecture provides:
- Multiple abstraction levels for different use cases
- Native performance for QUIC packet processing
- Zero-copy operations where possible
- Cross-platform compatibility
License
MIT
The underlying quiche library is licensed under BSD-2-Clause. See COPYING for details.
