| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 00705a4 commit f697c55
3 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -204,6 +204,12 @@ added: v23.8.0 | |||
| 204 | 204 | ||
| 205 | 205 | True if `endpoint.destroy()` has been called. Read only. | |
| 206 | 206 | ||
| 207 | + ### `endpoint.listening` | ||
| 208 | + | ||
| 209 | + * Type: {boolean} | ||
| 210 | + | ||
| 211 | + True if the endpoint is actively listening for incoming connections. Read only. | ||
| 212 | + | ||
| 207 | 213 | ### `endpoint.setSNIContexts(entries[, options])` | |
| 208 | 214 | ||
| 209 | 215 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -720,7 +720,25 @@ class QuicStream { | |||
| 720 | 720 | } | |
| 721 | 721 | ||
| 722 | 722 | /** | |
| 723 | - * @param {ArrayBuffer|ArrayBufferView|Blob} outbound | ||
| 723 | + * Immediately destroys the stream. Any queued data is discarded. If an | ||
| 724 | + * error is given, the closed promise will be rejected with that error. | ||
| 725 | + * If no error is given, the closed promise will be resolved. | ||
| 726 | + * @param {any} error | ||
| 727 | + */ | ||
| 728 | + destroy(error) { | ||
| 729 | + if (this.destroyed) return; | ||
| 730 | + const handle = this.#handle; | ||
| 731 | + this[kFinishClose](error); | ||
| 732 | + handle.destroy(); | ||
| 733 | + } | ||
| 734 | + | ||
| 735 | + /** | ||
| 736 | + * Sets the outbound data source for the stream. This can only be called | ||
| 737 | + * once and must be called before any data will be sent. The body can be | ||
| 738 | + * an ArrayBuffer, a TypedArray or DataView, or a Blob. If the stream | ||
| 739 | + * is destroyed or already has an outbound data source, an error will | ||
| 740 | + * be thrown. | ||
| 741 | + * @param {ArrayBuffer|SharedArrayBuffer|ArrayBufferView|Blob} outbound | ||
| 724 | 742 | */ | |
| 725 | 743 | setOutbound(outbound) { | |
| 726 | 744 | if (this.destroyed) { | |
@@ -1748,7 +1766,14 @@ class QuicEndpoint { | |||
| 1748 | 1766 | get closing() { return this.#isPendingClose; } | |
| 1749 | 1767 | ||
| 1750 | 1768 | /** @type {boolean} */ | |
| 1751 | - get destroyed() { return this.#handle === undefined; } | ||
| 1769 | + get listening() { | ||
| 1770 | + return this.#listening; | ||
| 1771 | + } | ||
| 1772 | + | ||
| 1773 | + /** @type {boolean} */ | ||
| 1774 | + get destroyed() { | ||
| 1775 | + return this.#handle === undefined; | ||
| 1776 | + } | ||
| 1752 | 1777 | ||
| 1753 | 1778 | /** | |
| 1754 | 1779 | * Forcefully terminates the endpoint by immediately destroying all sessions | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,98 @@ | |||
| 1 | + // Flags: --experimental-quic --no-warnings | ||
| 2 | + | ||
| 3 | + import { hasQuic, skip, mustCall } from '../common/index.mjs'; | ||
| 4 | + import assert from 'node:assert'; | ||
| 5 | + import * as fixtures from '../common/fixtures.mjs'; | ||
| 6 | + | ||
| 7 | + if (!hasQuic) { | ||
| 8 | + skip('QUIC is not enabled'); | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + // Import after the hasQuic check | ||
| 12 | + const quic = await import('node:quic'); | ||
| 13 | + const { createPrivateKey } = await import('node:crypto'); | ||
| 14 | + | ||
| 15 | + const keys = createPrivateKey(fixtures.readKey('agent1-key.pem')); | ||
| 16 | + const certs = fixtures.readKey('agent1-cert.pem'); | ||
| 17 | + | ||
| 18 | + const serverDone = Promise.withResolvers(); | ||
| 19 | + const clientDone = Promise.withResolvers(); | ||
| 20 | + | ||
| 21 | + // Create a server endpoint | ||
| 22 | + const serverEndpoint = await quic.listen(mustCall((serverSession) => { | ||
| 23 | + serverSession.opened.then((info) => { | ||
| 24 | + assert.ok(serverSession.endpoint !== null); | ||
| 25 | + assert.strictEqual(serverSession.destroyed, false); | ||
| 26 | + | ||
| 27 | + const stats = serverSession.stats; | ||
| 28 | + assert.strictEqual(stats.isConnected, true); | ||
| 29 | + assert.ok(stats.handshakeCompletedAt > 0n); | ||
| 30 | + assert.ok(stats.handshakeConfirmedAt > 0n); | ||
| 31 | + assert.strictEqual(stats.closingAt, 0n); | ||
| 32 | + | ||
| 33 | + serverDone.resolve(); | ||
| 34 | + serverSession.close(); | ||
| 35 | + }).then(mustCall()); | ||
| 36 | + }), { sni: { '*': { keys, certs } } }); | ||
| 37 | + | ||
| 38 | + assert.strictEqual(serverEndpoint.busy, false); | ||
| 39 | + assert.strictEqual(serverEndpoint.closing, false); | ||
| 40 | + assert.strictEqual(serverEndpoint.destroyed, false); | ||
| 41 | + assert.strictEqual(serverEndpoint.listening, true); | ||
| 42 | + | ||
| 43 | + assert.ok(serverEndpoint.address !== undefined); | ||
| 44 | + assert.strictEqual(serverEndpoint.address.family, 'ipv4'); | ||
| 45 | + assert.strictEqual(serverEndpoint.address.address, '127.0.0.1'); | ||
| 46 | + assert.ok(typeof serverEndpoint.address.port === 'number'); | ||
| 47 | + assert.ok(serverEndpoint.address.port > 0); | ||
| 48 | + | ||
| 49 | + const epStats = serverEndpoint.stats; | ||
| 50 | + assert.strictEqual(epStats.isConnected, true); | ||
| 51 | + assert.ok(epStats.createdAt > 0n); | ||
| 52 | + | ||
| 53 | + // Connect with a client | ||
| 54 | + const clientSession = await quic.connect(serverEndpoint.address); | ||
| 55 | + | ||
| 56 | + assert.strictEqual(clientSession.destroyed, false); | ||
| 57 | + assert.ok(clientSession.endpoint !== null); | ||
| 58 | + assert.strictEqual(clientSession.stats.isConnected, true); | ||
| 59 | + | ||
| 60 | + clientSession.opened.then((clientInfo) => { | ||
| 61 | + assert.strictEqual(clientInfo.servername, 'localhost'); | ||
| 62 | + assert.strictEqual(clientInfo.protocol, 'h3'); | ||
| 63 | + assert.strictEqual(clientInfo.cipherVersion, 'TLSv1.3'); | ||
| 64 | + assert.ok(clientInfo.local !== undefined); | ||
| 65 | + assert.ok(clientInfo.remote !== undefined); | ||
| 66 | + | ||
| 67 | + const cStats = clientSession.stats; | ||
| 68 | + assert.strictEqual(cStats.isConnected, true); | ||
| 69 | + assert.ok(cStats.handshakeCompletedAt > 0n); | ||
| 70 | + assert.ok(cStats.bytesSent > 0n, 'Expected bytesSent > 0 after handshake'); | ||
| 71 | + | ||
| 72 | + clientDone.resolve(); | ||
| 73 | + }).then(mustCall()); | ||
| 74 | + | ||
| 75 | + await Promise.all([serverDone.promise, clientDone.promise]); | ||
| 76 | + | ||
| 77 | + // Open a bidirectional stream. | ||
| 78 | + const stream = await clientSession.createBidirectionalStream(); | ||
| 79 | + | ||
| 80 | + assert.strictEqual(stream.destroyed, false); | ||
| 81 | + assert.strictEqual(stream.direction, 'bidi'); | ||
| 82 | + assert.strictEqual(stream.session, clientSession); | ||
| 83 | + assert.ok(stream.id !== null, 'Non-pending stream should have an id'); | ||
| 84 | + assert.strictEqual(typeof stream.id, 'bigint'); | ||
| 85 | + assert.strictEqual(stream.pending, false); | ||
| 86 | + assert.strictEqual(stream.stats.isConnected, true); | ||
| 87 | + assert.ok(stream.readable instanceof ReadableStream); | ||
| 88 | + | ||
| 89 | + // Destroying the session should destroy it and the stream, and clear its properties. | ||
| 90 | + clientSession.destroy(); | ||
| 91 | + assert.strictEqual(clientSession.destroyed, true); | ||
| 92 | + assert.strictEqual(clientSession.endpoint, null); | ||
| 93 | + assert.strictEqual(clientSession.stats.isConnected, false); | ||
| 94 | + | ||
| 95 | + assert.strictEqual(stream.destroyed, true); | ||
| 96 | + assert.strictEqual(stream.session, null); | ||
| 97 | + assert.strictEqual(stream.id, null); | ||
| 98 | + assert.strictEqual(stream.direction, null); | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments