Description
Specification
We need QUIC in order to simplify our networking stack in PK.
QUIC is a superior UDP layer that can make use of any UDP socket, and create multiplexed reliable streams. It is also capable of hole punching either just by attempting to send ping frames on the stream, or through the unreliable datagrams.
Our goals is to make use of a QUIC library, something that is compilable to desktop and mobile operating systems, expose its functionality to JS, but have the JS runtime manage the actual sockets.
On NodeJS, it can already manage the underlying UDP sockets, and by relying on NodeJS, it will also ensure that these sockets will mix well with the concurrency/parallelism used by the rest of the NodeJS system due to libuv and thus avoid creating a second IO system running in parallel.
On Mobile runtimes, they may not have a dgram module readily available. In such cases, having an IO runtime to supply the UDP sockets may be required. But it is likely there are already existing libraries that provide this like https://github.com/tradle/react-native-udp.
The underlying QUIC library there is expected to be agnostic to the socket runtime. It will give you data that you need to the UDP socket, and it will take data that comes of the UDP socket.
However it does need 2 major duties:
- The multiplexing and managing of streams.
- The encryption/decryption TLS side
Again if we want to stay cross platform, we would not want to bind into Node.js's openssl crypto. It would require instead that the library can take a callback of crypto routines to use. However I've found that this is generally not the case with most existing QUIC libraries. But let's see how we go with this.
Additional context
- Using QUIC/HTTP3 to replace utp-native for the Data Transfer Layer in the networking domain Polykey#234 - This is the main issue talking about how to replace utp-native
- https://github.com/napi-rs/napi-rs/tree/main/examples/napi - examples of how to use napi-rs
- https://nodejs.org/api/dgram.html - dgram module in NodeJS where the QUIC data will be plumbed to
- https://blogs.keysight.com/blogs/tech/nwvs.entry.html/2021/07/17/looking_into_quicpa-pUtF.html - some notes about how QUIC data can be viewed on wireshark
- https://www.sitepoint.com/rust-global-variables/
- https://deepu.tech/memory-management-in-rust/
- Support custom certificate verification cloudflare/quiche#326 (comment) - Disabling TLS verification and doing it custom
- Required options Clarify which configuration options are mandatory cloudflare/quiche#1250
- https://blog.cloudflare.com/head-start-with-quic/
- https://github.com/romain-jacotin/quic/blob/master/doc/QUIC_crypto_protocol.md
QUIC and NAPI-RS
- Why does failing to call a callback result in a panic? napi-rs/napi-rs#1373 - callback usage
- Callback usage in factory method results in macro error napi-rs/napi-rs#1374 - Callback usage in factory methods
- Support custom certificate verification cloudflare/quiche#326 (comment) - custom TLS verification in quiche
- When using `External<...>` the `...` ends up in the `index.d.ts` as an unknown type napi-rs/napi-rs#1409 - how to deal with external object types
- How to create a submodule in the rust code? napi-rs/napi-rs#1379 - napi-rs can't export submodules in rust for now
Sub issues
- Enable TLS Configuration with in-memory PEM strings #2
- TLS Rotation for QUICServer and QUICClient #3
- Allow Bidirectional Hole Punching from QUICClient and QUICServer #4
- Fixed size buffer for QUICStream instead of object stream #5
- Keepalive component after QUIC connection is established #6
- Packaging up js-quic for Linux, Windows, MacOS #7
- Create
TLS
cert arbitraries for testing #8 - Graceful connection failure for failed TLS handshake #9
- Create tests for
QUICStreams
#10 - Check if application protocols are required #13
- Test connection multiplexing across multiple servers and clients #14
- Create benchmarks for sending data #15
- Propagate connection info along side stream creation #16
- Server selects certificate based on client's support #17
- Canonical form for IP addresses #18
Tasks
- Experiment with Neon or Napi-rs
- Experiment with quiche by cloudflare
- Create bridge code plumbing UDP sockets and QUIC functions
- Create self signed TLS certificate during development - Create QUIC library that can be exposed to JS and uses the Node
dgram
module #1 (comment) Extract out the TLS configuration so that it can be set via in-memory PEM variable and key variable. - 2 day- see Enable TLS Configuration with in-memory PEM strings #2- This should be doable with https://docs.rs/quiche/latest/quiche/struct.Config.html#method.with_boring_ssl_ctx
- This needs to construct a SSL ctx with boring ssl, and pass in PEM string and key string (or Uint8Array) to be processed by the boringssl library.
Decide whether application protocols are necessary here, or abstract the quiche- see Check if application protocols are required #13Config
so the user can decide this (especially since this is not a HTTP3 library). - 0.5 day- Fix the timeout events and ensure that when a timeout occurs, that the connection gets cleaned up, and we are not inadvertently clearing the timeout due to
null
. Right now when a quiche client connects to the server, even after closing, the server side is keeping the connection alive. - 1 day - We need lifecycle events for
QUICConnection
andQUICStream
andQUICServer
andQUICSocket
. This will allow users to hook into the destruction of the object, and perhaps remove their event listeners. These events must be post-facto events. - 0.5 day [ ] Test the- see Fixed size buffer for QUICStream instead of object stream #5.QUICStream
and change to BYOB style, so that way there can be a byte buffer for it. Testing code should be able generator functions similar to our RPC handlers. - 1 day- Complete the
QUICClient
with the shared socketQUICSocket
. - 3 day Test the multiplexing/demultipexing of the UDP socket with multiple- See Test connection multiplexing across multiple servers and clients #14QUICClient
and a singleQUICServer
. - 1 day[ ] Test the error handling of the QUIC stream, and life cycle and destruction routines. - 1 day- Create tests forQUICStreams
#10Benchmark this system, by sending lots of data through. - 1 day- See Create benchmarks for sending data #15Propagate the- See Propagate connection info along side stream creation #16rinfo
from the UDP datagram into theconn.recv()
so that the streams (either during construction or otherwise) can have itsrinfo
updated. Perhaps we can just "set" therinfo
properties of the connection every time we do aconn.recv()
. Or... we just mutate theconn
parameters every time we receive a UDP packet.Ensure that when a user asks- See Propagate connection info along side stream creation #16stream.connection
they can acquire the remote information and also all the remote peer's certificate chain.[ ] Integrate dgram sending and recving for hole punching logic.- see Allow Bidirectional Hole Punching from QUICClient and QUICServer #4[ ] Integrate the- see Packaging up js-quic for Linux, Windows, MacOS #7napi
program into thescripts/prebuild.js
so we can actually build the package in a similar to other native packages we are using likejs-db