Node.js server implementation of the WebTransport protocol, using the WTransport Rust library.
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).
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 connectionsidle
: a promise that resolves when all connections are shut downaddress
: the local address the underlying socket is bound toopenConnections
: the number of currently open connectionspatchConfig(config)
: updates and reloads the server config; all properties ofconfig
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
Common properties:
identity
: aWtIdentity
maxIdleTimeout?
: duration of inactivity to accept before timing out the connection; defaults to an infinite timeoutkeepAliveInterval?
: period of inactivity before sending a keep-alive packet; must be set lower thanmaxIdleTimeout
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 portbindStack?
: either'v4'
,'v6'
or'dual'
; default isdual
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 addressbindV6Dual?
: IPv6 dual stack config; either'osDefault'
,'deny'
or'allow'
; default is'osDefault'
: the associated port
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 contentsWtIdentityFromPemFiles(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
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
.
Accepts an object of the following shape:
address
: an UDP address of the peer
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 validationundefined
: to accept this connection attempt
Accepts an object of the following shape:
address
: an UDP address of the peerauthority
: the:authority
field of the requestpath
: the:path
field of the requestorigin
: theorigin
field of the request if presentuserAgent
: theuser-agent
field of the request if presentheaders
: 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
Accepts an object of the following shape:
address
: an UDP address of the peer; note that this may change during the session's lifetimeopenUni()
: opens a new outgoing unidirectional streamopenBi()
: opens a new outgoing bidirectional streamacceptUni()
: waits for an incoming unidirectional stream to be available and returns itacceptBi()
: waits for an incoming bidirectional stream to be available and returns itdatagramRecieve()
: waits for the next incoming datagram and returns its payloaddatagramSend(payload)
: sends a datagram, suddenlydatagramMaxSize
: 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 limitidUnstable
: an unstable WebTransport session identifierid
: a stable identifier for this connectionrtt
: current best estimate of this connection’s latency (round-trip-time) in millisecondsclosed
: whether the session is closedclose()
: immediately closes the connection
onSession
handler is expected to return nothing.
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 exactlycount
bytes from the streamreadTo(buffer)
: reads data contiguously from the stream tobuffer
and returns the number of bytes readreadNext(limit)
: reads the next segment of data not exceedinglimit
bytes and returns it; returnsnull
if the stream was finishedreadChunk(limit)
: reads the next segment of data not exceedinglimit
bytes and returns a stream chunk; returnsnull
if the stream was finishedstop()
: 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)
: sendbytes
to the peerpriority
: 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 performancefinish()
: shuts down the stream gracefullyreset(errorCode?)
: closes the write stream immediatelystopped()
: waits for the stream to be stopped for any reason, which is returned:'closed'
,'notConnected'
,'quicProtocolError'
or'stopped'
An address is an object of the following shape:
toString()
ip
: an IPv4 or IPv6 address depending onstack
stack
:'v4
or'v6'
port
A stream chunk is an object of the following shape:
bytes
: data of the chunkoffset
: absolute offset of the chunk in the stream
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()
} } )
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) ) )
}
}
} } )
AGPL-3.0-or-later