Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Events Refactoring (integrating js-events) #53

Merged
merged 122 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
9209cff
wip: introducing `Evented` and push flow abstractions
CMCDragonkai Aug 27, 2023
bd77a12
chore: structure diagram for explaining how js-quic works
CMCDragonkai Sep 4, 2023
3903d69
fix: `QUICStream.destroy()` now has a force option and awaits destruc…
tegefaulkes Sep 4, 2023
2e6e6e4
wip: prototyping push flow abstractions and connection and stream li…
tegefaulkes Sep 4, 2023
081286d
wip: expanding native tls tests to cover more lifecycle conditions
CMCDragonkai Sep 6, 2023
284859a
fix: secure establishment logic moved to send
tegefaulkes Sep 7, 2023
582521a
fix: refactoring connection flow
tegefaulkes Sep 7, 2023
9278ac4
wip: prototyping push flow abstractions and updating tests for `QUICC…
tegefaulkes Sep 7, 2023
b608bcb
wip: experiments with swc, @marixai/errors, node 20 types and @peculi…
CMCDragonkai Sep 12, 2023
6548ca1
wip: cancel and destroy now throws errors if it failed, but async des…
CMCDragonkai Sep 13, 2023
c1bee37
wip: use `Buffer.allocUnsafe` no need for non-pooling
CMCDragonkai Sep 13, 2023
0a250ae
wip: `QUICConnection` now dispatches `EventQUICConnectionSend` event …
CMCDragonkai Sep 13, 2023
281a801
wip: supporting unidirectional streams
CMCDragonkai Sep 13, 2023
5f5a35c
wip: finalised overall evented architecture
CMCDragonkai Sep 13, 2023
7919571
wip: can still use `Host` and `Port` opaque types, only input types n…
CMCDragonkai Sep 14, 2023
cc954eb
wip: format the errors
CMCDragonkai Sep 14, 2023
9a8c4b2
wip: tested `QUICSocket.test.ts` with fast check
CMCDragonkai Sep 14, 2023
31ef0f6
wip: updated README to remove inaccurate evented usage
CMCDragonkai Sep 14, 2023
dbb37cb
fix: small logic error with secure establishment when not verifying peer
tegefaulkes Sep 15, 2023
37ec3a1
wip: `EventAll` for `QUICStream` should be registered to the `QUICStr…
CMCDragonkai Sep 15, 2023
0cb0a41
wip: event listeners are added to the beginning of `QUICConnection.st…
CMCDragonkai Sep 15, 2023
a1fba6b
wip: secure established optimisation
CMCDragonkai Sep 15, 2023
17c74b4
wip: remove `EventAll` from `QUICStream` when `QUICStream` is destroyed
CMCDragonkai Sep 15, 2023
d806057
wip: for connection start/stop, you cannot start it again after it is…
CMCDragonkai Sep 15, 2023
7fbd9d3
wip: minor `QUICClient.test.ts` fixes
tegefaulkes Sep 15, 2023
cb9a62a
wip: connection events have to be bridged to the socket via the client
CMCDragonkai Sep 15, 2023
64b5d80
wip: added failed send is not a domain error
CMCDragonkai Sep 15, 2023
1838b4a
wip: QUICClient `localHost` and `localPort` should be optional if not…
CMCDragonkai Sep 15, 2023
aee40c8
wip: non-working benchmarks
CMCDragonkai Sep 15, 2023
47dd9a4
fix: prevent socket leak if `QUICConnection` construction fails
tegefaulkes Sep 15, 2023
3b0b31a
fix: fixed logic bug with timeout timer
tegefaulkes Sep 15, 2023
ab04d99
fix: connection creation start timeout or abortion throws original error
tegefaulkes Sep 15, 2023
2da0624
wip: error handler only logs out errors that are non-graceful
CMCDragonkai Sep 15, 2023
d179221
wip: fixing the fact that `ErrorQUICConnectionPeer` wasn't even used
CMCDragonkai Sep 15, 2023
dd1d80d
wip: no need to delete the timer, timer is already settled
CMCDragonkai Sep 15, 2023
9aca10b
wip: some what working benchmark
CMCDragonkai Sep 15, 2023
833d487
wip: removed repeat error event from `QUICConnection` to `QUICClient`…
CMCDragonkai Sep 15, 2023
0deef70
wip: getting nicer error and info messages
CMCDragonkai Sep 15, 2023
b5e5dd5
wip: fixes a situation where receiving a peer error results in us sen…
CMCDragonkai Sep 15, 2023
42dba62
wip: removed `Monitor` in favour of a single `recv` lock
CMCDragonkai Sep 16, 2023
c883c47
wip: added `QUICSocket.send_` as an internal send that does not do an…
CMCDragonkai Sep 16, 2023
adc0d06
wip: up to date bench
CMCDragonkai Sep 16, 2023
efd6583
wip: make both max recv and max send to be 1350 `MAX_DATAGRAM_SIZE`, …
CMCDragonkai Sep 16, 2023
17448e2
wip: buffer allocation benchmarks, ended up using subarray
CMCDragonkai Sep 16, 2023
7a2ad4c
wip: completely removed asynchonicity in processing streams, quic con…
CMCDragonkai Sep 17, 2023
ae000d5
bench: created a bench for native only quiche with simple runtime
tegefaulkes Sep 18, 2023
40dd1a6
wip: brought back async `verifyCallback` so that way `recv` and `send…
CMCDragonkai Sep 18, 2023
e820a52
wip: no need to stub out benches
CMCDragonkai Sep 18, 2023
705cd7d
wip: fixed the case where the peer cancels the readable side even whe…
CMCDragonkai Sep 18, 2023
8fb3783
wip: added branch for comparing minimal work and simple runtime
tegefaulkes Sep 18, 2023
f386195
wip: fix to `try_from` for `SocketAddr`
tegefaulkes Sep 18, 2023
0fb8276
wip: added null to `streamShutdown` and `close` and cleaned up `sendO…
CMCDragonkai Sep 18, 2023
3e02cfb
wip: native exposes `setMaxStreamWindow` and `setMaxConnectionWindow`
CMCDragonkai Sep 18, 2023
02fdd37
wip: exposed `maxConnectionWindow` and `maxStreamWindow` to config
CMCDragonkai Sep 18, 2023
6218d8f
wip: `quiche::stream` is a private module, have to duplicate constants
CMCDragonkai Sep 18, 2023
0f2b694
wip: we are now using `null` to mean `Done` instead of exceptions
CMCDragonkai Sep 18, 2023
f88ef70
wip: fix a bad deconstructor
CMCDragonkai Sep 18, 2023
1090a40
wip: adding native rust simple test
tegefaulkes Sep 18, 2023
f63a384
wip: working rs example simple bench
tegefaulkes Sep 19, 2023
abc186d
wip: moved native bench to an example file and tested with `flamegraph`
tegefaulkes Sep 19, 2023
a713b6b
wip: fixing up client tests
tegefaulkes Sep 19, 2023
4c56951
wip: updated `@matrixai/async-init` and events to bring in `EventErro…
CMCDragonkai Sep 19, 2023
547107c
wip: handling domain error should be sync, and also keep alive timer …
CMCDragonkai Sep 19, 2023
86c9046
wip: quic streams now report a reason when the quic connection is for…
CMCDragonkai Sep 19, 2023
22651b6
wip: fixing tests
tegefaulkes Sep 20, 2023
18997b9
wip: fixing stream tests
tegefaulkes Sep 20, 2023
8c6ff6e
wip: graceful connection fail fix
tegefaulkes Sep 20, 2023
a4c9eb9
wip: temp fix for when socket stops before server does
tegefaulkes Sep 20, 2023
176fb01
wip: temp fix for streams throwing original errors
tegefaulkes Sep 20, 2023
30d8e1c
wip: fixed bug with accepting a new connection
tegefaulkes Sep 20, 2023
cd5ea8d
wip: fixed address utilisation in start/stop and create/destroy
CMCDragonkai Sep 20, 2023
eda047c
wip: switched `QUICStream` to use the new error to closed flow
CMCDragonkai Sep 20, 2023
dc50bb5
wip: all architecture changed to using error to close flow
CMCDragonkai Sep 20, 2023
35d6f48
wip: temp fix for proactively handling write stream errors
tegefaulkes Sep 21, 2023
a89549a
wip: test fixes
tegefaulkes Sep 21, 2023
ded6d6e
wip: temp fix for socket send errors when socket ends first
tegefaulkes Sep 21, 2023
123b04e
wip: temp fix for server not stopping due to error
tegefaulkes Sep 21, 2023
444c1c4
wip: temp fix for `connection.send` while stopped
tegefaulkes Sep 21, 2023
4268fb7
fix: small test fix
tegefaulkes Sep 21, 2023
a8f3e60
wip: concurrency tests working
tegefaulkes Sep 21, 2023
b23e287
wip: more concurrency test fixes
tegefaulkes Sep 21, 2023
d48897f
dep: updated `@matrixai/async-init` to `^1.10.0`
tegefaulkes Sep 21, 2023
76c7d1f
wip: `verifyCallback` now uses Uint8Array DER buffers to align with W…
CMCDragonkai Sep 22, 2023
9c805aa
wip: updated `QUICConnection` to completely change to using the DER c…
CMCDragonkai Sep 22, 2023
d335dc6
wip: fixing native tests
tegefaulkes Sep 22, 2023
0353c74
wip: temp fix for readable stream proactively handling errors
tegefaulkes Sep 22, 2023
890ae8f
wip: temp fix for writable not handling peer cancel
tegefaulkes Sep 22, 2023
61ec945
wip: `verifyCallback` now returns the `CryptoError` code, and we are …
CMCDragonkai Sep 22, 2023
891691a
wip: specialise `ErrorQUICConnectionPeerTLS`
CMCDragonkai Sep 22, 2023
588bb58
wip: all stream tests working now
tegefaulkes Sep 22, 2023
353e25f
wip: native test fixes
tegefaulkes Sep 22, 2023
0bf368b
fix: created `checkReadableClosed()` for symmetry with `checkWritable…
tegefaulkes Sep 22, 2023
09f239b
wip: all tests working
tegefaulkes Sep 22, 2023
f7856df
wip: fixed native tests
tegefaulkes Sep 22, 2023
6b5f71f
wip: verification fixes
tegefaulkes Sep 22, 2023
ca03e8d
wip: removed scaffolding
tegefaulkes Sep 22, 2023
0c1685a
wip: removed `generateConfig` and `generateCertificate` is now aligne…
CMCDragonkai Sep 22, 2023
53087aa
wip: expose `CRYPTO_ERROR_START` and `CRYPTO_ERROR_STOP` from quiche
CMCDragonkai Sep 22, 2023
33d88ab
wip: fixed `generateCertificate`, for now hacking in DNS SANs
CMCDragonkai Sep 22, 2023
322f524
wip: removed `streamStats` since it is not needed, and if needed shou…
CMCDragonkai Sep 22, 2023
049957e
wip: usability on `QUICSocket` has been loosened
CMCDragonkai Sep 23, 2023
f06b36b
wip: now both QUICServer and QUICClient are loosened up
CMCDragonkai Sep 23, 2023
2b1c1af
wip: renamed `toCanonicalIp` to `toCanonicalIP`
CMCDragonkai Sep 23, 2023
69aa800
wip: cleaning up commentary and lint fixing src and some benches and …
CMCDragonkai Sep 24, 2023
dfe7c4c
wip: fixing tests and linting
tegefaulkes Sep 25, 2023
74a18a7
wip: updating `quiche` to `0.1.0` and `boring ` to `3`
tegefaulkes Sep 25, 2023
5d190e2
wip: fixes for client tests
tegefaulkes Sep 25, 2023
166ea37
wip: processing readable and writable independently and dealing with …
CMCDragonkai Sep 25, 2023
9155ade
wip: small fix
tegefaulkes Sep 25, 2023
3266b17
wip: lintfixing and cleaning
CMCDragonkai Sep 25, 2023
01608c0
wip: fixed `QUICStream` being re-created after destroy
tegefaulkes Sep 25, 2023
442ae1e
wip: removed usage of `sleep` from tests
tegefaulkes Sep 25, 2023
36be662
wip: made `closedP` public readonly
tegefaulkes Sep 25, 2023
5c90460
wip: fixed native tls rsa tests
tegefaulkes Sep 25, 2023
d0180c1
wip: linting
tegefaulkes Sep 25, 2023
1eeef62
wip: fixed up benches, combined stream bench files
tegefaulkes Sep 25, 2023
e99960c
wip: removed stub
tegefaulkes Sep 25, 2023
0eb9f7d
wip: fixed repeated start stop of `QUICServer` and `QUICSocket`
CMCDragonkai Sep 25, 2023
a24b278
wip: remove scaffolding
CMCDragonkai Sep 25, 2023
c9fab57
wip: confirmed that lazy timers do require deletion because their sta…
CMCDragonkai Sep 25, 2023
8f0c8cb
test: added test for consecutive start stops on server
tegefaulkes Sep 25, 2023
0a5f4bd
bench: only 2 benchmarks for stream 1KiB now
CMCDragonkai Sep 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@
"message": "Use `globalThis` instead"
}
],
"prefer-rest-params": 0,
"prefer-const": [
"error", {
"destructuring": "all"
}
],
"require-yield": 0,
"eqeqeq": ["error", "smart"],
"spaced-comment": [
Expand Down
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
/tests
/tmp
/docs
/images
/benches
/build
/builds
Expand Down
85 changes: 68 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ path = "src/native/napi/lib.rs"
napi = { version = "2", features = ["async", "napi6", "serde-json"] }
serde = { version = "1.0", features = ["derive"] }
napi-derive = { version = "2", default-features = false, features = ["strict", "compat-mode"] }
quiche = { version = "0.17.1", features = ["boringssl-boring-crate", "boringssl-vendored"] }
boring = "2.1.0"
quiche = { version = "0.18.0", features = ["boringssl-boring-crate", "boringssl-vendored"] }
boring = "3"

[build-dependencies]
napi-build = "2"
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,57 @@ x86_64-apple-darwin

The available target list is in `rustc --print target-list`.

### Structure

It is possible to structure the QUIC system in the encapsulated way or the injected way.

When using the encapsulated way, the `QUICSocket` is separated between client and server.

When using the injected way, the `QUICSocket` is shared between client and server.

![](/images/quic_structure_encapsulated.svg)

If you are building a peer to peer network, you must use the injected way. This is the only way to ensure that hole-punching works because both the client and server for any given peer must share the same UDP socket and thus share the `QUICSocket`. When done in this way, the `QUICSocket` lifecycle is managed outside of both the `QUICClient` and `QUICServer`.

![](/images/quic_structure_injected.svg)

This also means both `QUICClient` and `QUICServer` must share the same connection map. In order to allow the `QUICSocket` to dispatch data into the correct connection, the connection map is constructed in the `QUICSocket`, however setting and unsetting connections is managed by `QUICClient` and `QUICServer`.

## Dataflow

The data flow of the QUIC system is a bidirectional graph.

Data received from the outside world is received on the UDP socket. It is parsed and then dispatched to each `QUICConnection`. Each connection further parses the data and then dispatches to the `QUICStream`. Each `QUICStream` presents the data on the `ReadableStream` interface, which can be read by a caller.

Data sent to the outside world is written to a `WritableStream` interface of a `QUICStream`. This data is buffered up in the underlying Quiche stream. A send procedure is triggered on the associated `QUICConnection` which takes all the buffered data to be sent for that connection, and sends it to the `QUICSocket`, which then sends it to the underlying UDP socket.

![](/images/quic_dataflow.svg)

Buffering occurs at the connection level and at the stream level. Each connection has a global buffer for all streams, and each stream has its own buffer. Note that connection buffering and stream buffering all occur within the Quiche library. The web streams `ReadableStream` and `WritableStream` do not do any buffering at all.

### Connection Negotiation

The connection negotiation process involves several exchanges of QUIC packets before the `QUICConnection` is constructed.

The primary reason to do this is for both sides to determine their respective connection IDs.

![](/images/quic_connection_negotiation.svg)

### Push & Pull

The `QUICSocket`, `QUICClient`, `QUICServer`, `QUICConnection` and `QUICStream` are independent state machines that exposes methods that can be called as well as events that may be emitted between them.

This creates a concurrent decentralised state machine system where there are multiple entrypoints of change.

Users may call methods which causes state transitions internally that trigger event emissions. However some methods are considered internal to the library, this means these methods are not intended to be called by the end user. They are however public relative to the other components in the system. These methods should be marked with `@internal` documentation annotation.

External events may also trigger event handlers that will call methods which perform state transitions and event emission.

Keeping track of how the system works is therefore quite complex and must follow a set of rules.

* Pull methods - these are either synchronous or asynchronous methods that may throw exceptions.
* Push handlers - these are event handlers that can initiate pull methods, if these pull handlers throw exceptions, these exceptions must be caught, and expected runtime exceptions are to be converted to error events, all other exceptions will be considered to be software bugs and will be bubbled up to the program boundary as unhandled exceptions or unhandled promise rejections. Generally the only exceptions that are expected runtime exceptions are those that arise from perform IO with the operating system.

## Benchmarks

```sh
Expand Down
56 changes: 34 additions & 22 deletions benches/index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
#!/usr/bin/env ts-node

import type { Summary } from 'benny/lib/internal/common-types';
import fs from 'fs';
import path from 'path';
import si from 'systeminformation';
import Stream1KB from './stream_1KB';
import { fsWalk, resultsPath, suitesPath } from './utils';

async function main(): Promise<void> {
await fs.promises.mkdir(path.join(__dirname, 'results'), { recursive: true });
// Running benches
await Stream1KB();
const resultFilenames = await fs.promises.readdir(
path.join(__dirname, 'results'),
);
const metricsFile = await fs.promises.open(
path.join(__dirname, 'results', 'metrics.txt'),
'w',
);
// Running all suites
for await (const suitePath of fsWalk(suitesPath)) {
// Skip over non-ts and non-js files
const ext = path.extname(suitePath);
if (ext !== '.ts' && ext !== '.js') {
continue;
}
const suite: () => Promise<Summary> = (await import(suitePath)).default;
// Skip default exports that are not functions and are not called "main"
// They might be utility files
if (typeof suite === 'function' && suite.name === 'main') {
await suite();
}
}
// Concatenating metrics
const metricsPath = path.join(resultsPath, 'metrics.txt');
await fs.promises.rm(metricsPath, { force: true });
let concatenating = false;
for (const resultFilename of resultFilenames) {
if (/.+_metrics\.txt$/.test(resultFilename)) {
const metricsData = await fs.promises.readFile(
path.join(__dirname, 'results', resultFilename),
);
if (concatenating) {
await metricsFile.write('\n');
}
await metricsFile.write(metricsData);
concatenating = true;
for await (const metricPath of fsWalk(resultsPath)) {
// Skip over non-metrics files
if (!metricPath.endsWith('_metrics.txt')) {
continue;
}
const metricData = await fs.promises.readFile(metricPath);
if (concatenating) {
await fs.promises.appendFile(metricsPath, '\n');
}
await fs.promises.appendFile(metricsPath, metricData);
concatenating = true;
}
await metricsFile.close();
const systemData = await si.get({
cpu: '*',
osInfo: 'platform, distro, release, kernel, arch',
Expand All @@ -41,4 +49,8 @@ async function main(): Promise<void> {
);
}

void main();
if (require.main === module) {
void main();
}

export default main;
Loading