Skip to content

krulod/node-wtransport

Repository files navigation

node-wtransport

Node.js server implementation of the WebTransport protocol, using the WTransport Rust library.

Installation

pnpm add wtransport # or whichever package manager you are using

This library is ESM-only.

Currently, it only works on Linux OS on x86-64 and ARM CPUs (support for other platforms is coming soon).

Usage

Server

To create a server, instantiate new WtServer(config, handler) with config being the server configuration object and handler being the handler object.

Members:

  • close(errorCode, reason): closes all connections immediately and ceases accepting new connections
  • idle: a promise that resolves when all connections are shut down
  • address: the local address the underlying socket is bound to
  • openConnections: the number of currently open connections
  • patchConfig(config): updates and reloads the server config; all properties of config are optional
import {WtServer, WtIdentityFromPemFiles} from 'wtransport'

const server = new WtServer( {
	identity: WtIdentityFromPemFiles('certificate_chain.pem', 'private_key.pem'),
	bind: 7777,
}, {} )

server.address() //=> {address: '::', port: 7777, stack: 'v6'} or something like that

Config

Common properties:

  • identity: a WtIdentity
  • maxIdleTimeout?: duration of inactivity to accept before timing out the connection; defaults to an infinite timeout
  • keepAliveInterval?: period of inactivity before sending a keep-alive packet; must be set lower than maxIdleTimeout of both peers to be effective; defaults to not sending keep-alive packets

You also must supply the binding address through one of these mutually exclusive property groups:

  • bind to a local socket
    • bind: the port
    • bindStack?: either 'v4', 'v6' or 'dual'; default is dual
    • bindTo?: either 'local' (LOCALHOST) or 'any' (INADDR_ANY, listen on all local interfaces); default is 'any'
  • bind to an IPv4 socket specified by the address
    • bindV4: the address
  • bind to an IPv6 socket specified by the address
    • bindV6: the address
    • bindV6Dual?: IPv6 dual stack config; either 'osDefault', 'deny' or 'allow'; default is 'osDefault' : the associated port

Identity

Identity represents a TLS identity consisting of a certificate chain and a private key. There are several ways to generate one:

  • WtIdentityFromPem(certificateChainPem, privateKeyPem): from .pem file contents
  • WtIdentityFromPemFiles(certificateChainPath, privateKeyPath): from .pem file paths

Note that it may be tricky to get the self-signed development certificates accepted by your browser. For instance, mkcert doesn't seem to work with WebTransport. Consider reading this project's local development docs

Usage: Handler

The handler is an object through the members of which the client processing pipeline is configured.

  • request: immediately after recieving a request
  • connection: when a QUIC connection is established
  • session: when a WebTransport session is established

The member you are most likely interested in is onSession.

onRequest

Accepts an object of the following shape:

Valid return values are:

  • 'refuse': to reject this connection attempt
  • 'ignore': to ignore this connection attempt, not sending any packet in response
  • 'retry': to respond with a retry packet, requiring the client to retry with address validation
  • undefined: to accept this connection attempt

onConnection

Accepts an object of the following shape:

  • address: an UDP address of the peer
  • authority: the :authority field of the request
  • path: the :path field of the request
  • origin: the origin field of the request if present
  • userAgent: the user-agent field of the request if present
  • headers: all header fields associated with the request

Valid return values are:

  • 'forbidden': rejects the request by replying with 403 status code
  • 'notFound': rejects the client request by replying with 404 status code.
  • 'tooManyRequests': rejects the client request by replying with 429 status code.
  • undefined: to establish the session

onSession

Accepts an object of the following shape:

  • address: an UDP address of the peer; note that this may change during the session's lifetime
  • openUni(): opens a new outgoing unidirectional stream
  • openBi(): opens a new outgoing bidirectional stream
  • acceptUni(): waits for an incoming unidirectional stream to be available and returns it
  • acceptBi(): waits for an incoming bidirectional stream to be available and returns it
  • datagramRecieve(): waits for the next incoming datagram and returns its payload
  • datagramSend(payload): sends a datagram, suddenly
  • datagramMaxSize: the maximum size of datagrams that can be sent; undefined if datagrams are unsupported; may change according to variation in the path MTU estimate; is at least a little over a kilobyte unless the peer specifies a lesser limit
  • idUnstable: an unstable WebTransport session identifier
  • id: a stable identifier for this connection
  • rtt: current best estimate of this connection’s latency (round-trip-time) in milliseconds
  • closed: whether the session is closed
  • close(): immediately closes the connection

onSession handler is expected to return nothing.

Streams

All streams have these members:

  • id: identifier of the stream

If a stream is readable (either incoming unidirectional or bidirectional), the following members are available:

  • readExact(count): reads exactly count bytes from the stream
  • readTo(buffer): reads data contiguously from the stream to buffer and returns the number of bytes read
  • readNext(limit): reads the next segment of data not exceeding limit bytes and returns it; returns null if the stream was finished
  • readChunk(limit): reads the next segment of data not exceeding limit bytes and returns a stream chunk; returns null if the stream was finished
  • stop(): discards unread data and notifies the peer to stop transmitting; the stream stops being readable after calling this

If a stream is writable (either outgoing unidirectional or bidirectional), the following members are available:

  • write(bytes): send bytes to the peer
  • priority: a getter/setter; data from streams with higher priority will be transmitted before data from streams with lower priority; changing the priority of a stream with pending data may only take effect after that data has been transmitted; using many different priority levels per connection may have a negative impact on performance
  • finish(): shuts down the stream gracefully
  • reset(errorCode?): closes the write stream immediately
  • stopped(): waits for the stream to be stopped for any reason, which is returned: 'closed', 'notConnected', 'quicProtocolError' or 'stopped'

Data types

Address

An address is an object of the following shape:

  • toString()
  • ip: an IPv4 or IPv6 address depending on stack
  • stack: 'v4 or 'v6'
  • port

Stream Chunk

A stream chunk is an object of the following shape:

  • bytes: data of the chunk
  • offset: absolute offset of the chunk in the stream

Examples

Example: Echo Server

This example implements a WebTransport server that accepts a bidirectional stream and echoes back any data received from the client until the stream is closed.

import {WtIdentityFromPemFiles, WtServer} from './src/index.js'

new WtServer( {
	identity: WtIdentityFromPemFiles('certificate_chain.pem', 'private_key.key'),
	bind: 7777,
}, { async onSession(session) {
	const stream = await session.acceptBi()

	do {
		var chunk = await stream.readNext(1024)
		chunk && await stream.write(chunk)
	} while(chunk)

	session.close()
} } )

Example: Datagram Echo Server

import {WtIdentityFromPemFiles, WtServer} from './src/index.js'

new WtServer( {
	identity: WtIdentityFromPemFiles('certificate_chain.pem', 'private_key.key'),
	bind: 7777,
}, { async onSession(session) {
	for(;;) {
		const limit = session.datagramMaxSize
		if(!limit) {
			session.close()
			break
		}

		const datagram = await session.datagramRecieve()

		for(
			let start = 0, end = limit;
			end < datagram.length;
			start = end, end += limit
		) {
			await session.datagramSend( datagram.slice(start, end) ) )
		}
	}
} } )

LICENSE

AGPL-3.0-or-later

About

WebTransport server for Node

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published