From 95829be4ea17d7c4deaff62be8fef1e797ca1b28 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 16:38:14 +0000 Subject: [PATCH 1/6] Fix waiting for loading quiche --- main/lib/index.node.js | 2 +- main/lib/index.types.js | 2 +- main/lib/webtransport.node.js | 11 ++++++++++- main/lib/webtransportbase.js | 2 +- main/package.json | 1 + main/test/bidirectional-streams.spec.js | 12 ++++++++++++ main/test/datagrams.spec.js | 12 ++++++++++++ main/test/fixtures/quiche.browser.js | 2 ++ main/test/fixtures/quiche.js | 1 + main/test/session.spec.js | 12 ++++++++++++ main/test/stream-limits.spec.js | 12 ++++++++++++ main/test/unidirectional-streams.spec.js | 12 ++++++++++++ 12 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 main/test/fixtures/quiche.browser.js create mode 100644 main/test/fixtures/quiche.js diff --git a/main/lib/index.node.js b/main/lib/index.node.js index 254fada4..5b4d36bc 100644 --- a/main/lib/index.node.js +++ b/main/lib/index.node.js @@ -25,4 +25,4 @@ // also edit index.types.js export { HttpServer, Http3Server, Http2Server } from './server.js' -export { WebTransport } from './webtransport.node.js' +export { WebTransport, quicheLoaded } from './webtransport.node.js' diff --git a/main/lib/index.types.js b/main/lib/index.types.js index e5576cb3..83e546e4 100644 --- a/main/lib/index.types.js +++ b/main/lib/index.types.js @@ -25,7 +25,7 @@ // both imports from the browser side and for nodes to generate a joint type file export { HttpServer, Http3Server, Http2Server } from './server.js' -export { WebTransport } from './webtransport.node.js' +export { WebTransport, quicheLoaded } from './webtransport.node.js' export { WebTransportPonyfill, diff --git a/main/lib/webtransport.node.js b/main/lib/webtransport.node.js index fe3f48fc..09751923 100644 --- a/main/lib/webtransport.node.js +++ b/main/lib/webtransport.node.js @@ -18,9 +18,13 @@ let Http3WebTransportClientSocket * @type {new (arg0: import("./types").HttpWebTransportInit) => any} */ let Http3WebTransportClient +/** + * @type {boolean} + */ +let quicheLoadedReady // @ts-ignore // eslint-disable-next-line no-unused-vars -const quicheLoaded = new Promise((resolve, reject) => { +export const quicheLoaded = new Promise((resolve, reject) => { // @ts-ignore import('@fails-components/webtransport-transport-http3-quiche') .then( @@ -39,6 +43,9 @@ const quicheLoaded = new Promise((resolve, reject) => { resolve(undefined) } ) + .finally(() => { + quicheLoadedReady = true + }) .catch((error) => { log('Problem loading http3-quiche transport', error) reject(error) @@ -102,6 +109,8 @@ export class WebTransport extends WebTransportBase { */ createClient(args) { let createUnreliableClient + if (!quicheLoadedReady) + throw new Error('Lib quiche loading attempt did not end') if (checkQuicheInit) { createUnreliableClient = function (/** @type {any} */ _client) { if ( diff --git a/main/lib/webtransportbase.js b/main/lib/webtransportbase.js index abd60738..69032222 100644 --- a/main/lib/webtransportbase.js +++ b/main/lib/webtransportbase.js @@ -68,7 +68,7 @@ export class WebTransportBase { * @abstract */ startUpConnection({ client, sessionint, ourl }) { - throw new Error('Implement createClient') + throw new Error('Implement startUpConnection') } get reliability() { diff --git a/main/package.json b/main/package.json index 4edbd84f..e6697233 100644 --- a/main/package.json +++ b/main/package.json @@ -83,6 +83,7 @@ }, "browser": { "./test/fixtures/webtransport.js": "./test/fixtures/webtransport.browser.js", + "./test/fixtures/quiche.js": "./test/fixtures/quiche.browser.js", "./lib/webstreams.js": "./lib/webstreams.browser.js" } } diff --git a/main/test/bidirectional-streams.spec.js b/main/test/bidirectional-streams.spec.js index 2db1a2c7..d26766b0 100644 --- a/main/test/bidirectional-streams.spec.js +++ b/main/test/bidirectional-streams.spec.js @@ -6,6 +6,7 @@ import { readStream } from './fixtures/read-stream.js' import { writeStream } from './fixtures/write-stream.js' import { readCertHash } from './fixtures/read-cert-hash.js' import WebTransport from './fixtures/webtransport.js' +import { quicheLoaded } from './fixtures/quiche.js' import * as ui8 from 'uint8arrays' import { KNOWN_BYTES, @@ -38,6 +39,17 @@ describe('bidirectional streams', function () { // @ts-ignore delete wtOptions.serverCertificateHashes + // @ts-ignore + beforeEach(async () => { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + }) + // @ts-ignore afterEach(async () => { if (client != null) { diff --git a/main/test/datagrams.spec.js b/main/test/datagrams.spec.js index 3961515f..149e3792 100644 --- a/main/test/datagrams.spec.js +++ b/main/test/datagrams.spec.js @@ -5,6 +5,7 @@ import { expect } from './fixtures/chai.js' import { readStream } from './fixtures/read-stream.js' import { readCertHash } from './fixtures/read-cert-hash.js' import { pTimeout } from './fixtures/p-timeout.js' +import { quicheLoaded } from './fixtures/quiche.js' /** * @template T @@ -32,6 +33,17 @@ describe('datagrams', function () { // @ts-ignore delete wtOptions.serverCertificateHashes + // @ts-ignore + beforeEach(async () => { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + }) + // @ts-ignore afterEach(async () => { if (client != null) { diff --git a/main/test/fixtures/quiche.browser.js b/main/test/fixtures/quiche.browser.js new file mode 100644 index 00000000..0e398852 --- /dev/null +++ b/main/test/fixtures/quiche.browser.js @@ -0,0 +1,2 @@ +const quicheLoaded = Promise.resolve() +export { quicheLoaded } diff --git a/main/test/fixtures/quiche.js b/main/test/fixtures/quiche.js new file mode 100644 index 00000000..162c4a5e --- /dev/null +++ b/main/test/fixtures/quiche.js @@ -0,0 +1 @@ +export { quicheLoaded } from '../../lib/index.node.js' diff --git a/main/test/session.spec.js b/main/test/session.spec.js index 30b9bc2c..b1f8e40b 100644 --- a/main/test/session.spec.js +++ b/main/test/session.spec.js @@ -3,6 +3,7 @@ import { readCertHash } from './fixtures/read-cert-hash.js' import WebTransport from './fixtures/webtransport.js' import { expect } from './fixtures/chai.js' +import { quicheLoaded } from './fixtures/quiche.js' /** * @template T @@ -38,6 +39,17 @@ describe('session', function () { // @ts-ignore delete wtOptions.serverCertificateHashes + // @ts-ignore + beforeEach(async () => { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + }) + // @ts-ignore afterEach(async () => { if (client != null) { diff --git a/main/test/stream-limits.spec.js b/main/test/stream-limits.spec.js index fe2eb09d..29ca46c6 100644 --- a/main/test/stream-limits.spec.js +++ b/main/test/stream-limits.spec.js @@ -3,6 +3,7 @@ import { readCertHash } from './fixtures/read-cert-hash.js' import WebTransport from './fixtures/webtransport.js' import { expect } from './fixtures/chai.js' +import { quicheLoaded } from './fixtures/quiche.js' /** * @template T @@ -44,6 +45,17 @@ describe('streamlimits', function () { // @ts-ignore delete wtOptions.serverCertificateHashes + // @ts-ignore + beforeEach(async () => { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + }) + // @ts-ignore afterEach(async () => { if (client != null) { diff --git a/main/test/unidirectional-streams.spec.js b/main/test/unidirectional-streams.spec.js index 16f20717..6bc0bb7d 100644 --- a/main/test/unidirectional-streams.spec.js +++ b/main/test/unidirectional-streams.spec.js @@ -8,6 +8,7 @@ import { writeStream } from './fixtures/write-stream.js' import { readCertHash } from './fixtures/read-cert-hash.js' import * as ui8 from 'uint8arrays' import { KNOWN_BYTES, KNOWN_BYTES_LENGTH } from './fixtures/known-bytes.js' +import { quicheLoaded } from './fixtures/quiche.js' describe('unidirectional streams', function () { /** @type {import('../lib/dom').WebTransport | undefined} */ @@ -29,6 +30,17 @@ describe('unidirectional streams', function () { // @ts-ignore delete wtOptions.serverCertificateHashes + // @ts-ignore + beforeEach(async () => { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + }) + // @ts-ignore afterEach(async () => { if (client != null) { From 5793a1229d7b84a336a89de18d8077102d7256d2 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 17:12:17 +0000 Subject: [PATCH 2/6] Fix http3 client connection (together with achingbrain) --- transports/http3-quiche/src/http3client.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/transports/http3-quiche/src/http3client.cc b/transports/http3-quiche/src/http3client.cc index 37d6d7a7..7b2e71b3 100644 --- a/transports/http3-quiche/src/http3client.cc +++ b/transports/http3-quiche/src/http3client.cc @@ -532,7 +532,7 @@ namespace quic if (wait_for_encryption_) { if (EncryptionBeingEstablished()) - return false; + return true; wait_for_encryption_ = false; ParsedQuicVersion version = UnsupportedQuicVersion(); if (session_ != nullptr && !CanReconnectWithDifferentVersion(&version) && !session_->connection()->connected()) @@ -826,7 +826,7 @@ namespace quic if (client_->connectionrecheck_) { - client_->connectionrecheck_ = !client_->handleConnecting(); + client_->connectionrecheck_ = client_->handleConnecting(); } } @@ -834,7 +834,7 @@ namespace quic { if (client_->connectionrecheck_) { - client_->connectionrecheck_ = !client_->handleConnecting(); + client_->connectionrecheck_ = client_->handleConnecting(); } client_->OnCanWrite(); } From 3e317314e896b8526b1f6d067789ef7385a930ed Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 17:36:46 +0000 Subject: [PATCH 3/6] Separate check for session --- transports/http3-quiche/src/http3client.cc | 7 ++----- transports/http3-quiche/src/http3client.h | 4 ++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/transports/http3-quiche/src/http3client.cc b/transports/http3-quiche/src/http3client.cc index 7b2e71b3..7a1b0b80 100644 --- a/transports/http3-quiche/src/http3client.cc +++ b/transports/http3-quiche/src/http3client.cc @@ -569,11 +569,6 @@ namespace quic recheck = true; } - if (checkSession()) - { - recheck = true; - } - return recheck; } @@ -828,6 +823,7 @@ namespace quic { client_->connectionrecheck_ = client_->handleConnecting(); } + if (client_->needsToCheckForSession()) client_->checkSession(); } void Http3ClientJS::onCanWrite(const Napi::CallbackInfo &info) @@ -836,6 +832,7 @@ namespace quic { client_->connectionrecheck_ = client_->handleConnecting(); } + if (client_->needsToCheckForSession()) client_->checkSession(); client_->OnCanWrite(); } diff --git a/transports/http3-quiche/src/http3client.h b/transports/http3-quiche/src/http3client.h index ff09c961..db8ab378 100644 --- a/transports/http3-quiche/src/http3client.h +++ b/transports/http3-quiche/src/http3client.h @@ -349,6 +349,10 @@ namespace quic bool checkSession(); + bool needsToCheckForSession() { + return finish_stream_open_.size() > 0 && session_->CanOpenNextOutgoingBidirectionalStream(); + } + // Read oldest received response and remove it from closed_stream_states_. // void ReadNextResponse(); From e9f027e7d55daed4579ed9e9c9218ee0f90726f3 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 17:55:59 +0000 Subject: [PATCH 4/6] Add read.me change --- main/readme.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main/readme.md b/main/readme.md index fb852919..3878edf2 100644 --- a/main/readme.md +++ b/main/readme.md @@ -126,6 +126,9 @@ The `WebTransportSession` should behave most like a WebTransport object on the b ### Using a client The `WebTransport` object for node or the `WebTransportPonyfill`, `WebTransportPolyfill` objects for the browser, behave like the `WebTransport` client object for the browser with few additions and some missing features. +As the http/3 package is loaded dynamically and the WebTransport object is created synchronously, you may want to make sure, that the modules are already loaded. You can do so, by waiting for the promise `quicheLoaded` exported by the package. + + ## Specification divergence This module implements parts of the [WebTransport spec](https://datatracker.ietf.org/doc/html/draft-vvv-webtransport-quic-00) but not all of it. From 94598da7a4df8f39809329ff3fcd32ef5b7c69c7 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 18:01:36 +0000 Subject: [PATCH 5/6] Fix oldtest --- main/old_test/test.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/main/old_test/test.js b/main/old_test/test.js index dc854df7..7d4f88f1 100644 --- a/main/old_test/test.js +++ b/main/old_test/test.js @@ -5,7 +5,12 @@ // this file runs various tests import { generateWebTransportCertificate } from '../test/fixtures/certificate.js' -import { Http3Server, Http2Server, WebTransport } from '../lib/index.node.js' +import { + Http3Server, + Http2Server, + WebTransport, + quicheLoaded +} from '../lib/index.node.js' import { echoTestsConnection, runEchoServer } from './testsuite.js' let http2 = false @@ -85,6 +90,14 @@ async function run() { const url = 'https://127.0.0.1:8080/echo' + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } + /** @type {import('../lib/dom').WebTransport | null} */ let client = new WebTransport(url, { serverCertificateHashes: [{ algorithm: 'sha-256', value: certificate.hash }] From 6b6a1e085010655bad36d14a581be7d97826d155 Mon Sep 17 00:00:00 2001 From: Marten Richter Date: Fri, 8 Mar 2024 18:32:20 +0000 Subject: [PATCH 6/6] Fix oldtest again --- main/old_test/test.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/main/old_test/test.js b/main/old_test/test.js index 7d4f88f1..f273f76d 100644 --- a/main/old_test/test.js +++ b/main/old_test/test.js @@ -20,6 +20,13 @@ if (process.argv.some((el) => el === 'http2')) { } async function run() { + if ( + process.env.USE_HTTP2 !== 'true' && + process.env.USE_PONYFILL !== 'true' && + process.env.USE_POLYFILL !== 'true' + ) { + await quicheLoaded + } console.log('try connecting to server that does not exist') const badClient = new WebTransport('https://127.0.0.1:49823/echo', { serverCertificateHashes: [ @@ -90,14 +97,6 @@ async function run() { const url = 'https://127.0.0.1:8080/echo' - if ( - process.env.USE_HTTP2 !== 'true' && - process.env.USE_PONYFILL !== 'true' && - process.env.USE_POLYFILL !== 'true' - ) { - await quicheLoaded - } - /** @type {import('../lib/dom').WebTransport | null} */ let client = new WebTransport(url, { serverCertificateHashes: [{ algorithm: 'sha-256', value: certificate.hash }]