GeezerVison is showing github.com — stream: expose DuplexPair API · nodejs/node@15816bd
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/stream.md +30-2Lines changed: 30 additions & 2 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -45,8 +45,11 @@ There are four fundamental stream types within Node.js: | 45 | 45 | is written and read (for example, [`zlib.createDeflate()`][]). | 46 | 46 |
| 47 | 47 | Additionally, this module includes the utility functions | 48 | | -[`stream.pipeline()`][], [`stream.finished()`][], [`stream.Readable.from()`][] | 49 | | -and [`stream.addAbortSignal()`][]. | | 48 | +[`stream.duplexPair()`][], | | 49 | +[`stream.pipeline()`][], | | 50 | +[`stream.finished()`][] | | 51 | +[`stream.Readable.from()`][], and | | 52 | +[`stream.addAbortSignal()`][]. | 50 | 53 |
| 51 | 54 | ### Streams Promises API | 52 | 55 |
| @@ -2675,6 +2678,30 @@ unless `emitClose` is set in false. | 2675 | 2678 | Once `destroy()` has been called, any further calls will be a no-op and no | 2676 | 2679 | further errors except from `_destroy()` may be emitted as `'error'`. | 2677 | 2680 |
| | 2681 | +#### `stream.duplexPair([options])` | | 2682 | + | | 2683 | +<!-- YAML | | 2684 | +added: REPLACEME | | 2685 | +--> | | 2686 | + | | 2687 | +* `options` {Object} A value to pass to both [`Duplex`][] constructors, | | 2688 | + to set options such as buffering. | | 2689 | +* Returns: {Array} of two [`Duplex`][] instances. | | 2690 | + | | 2691 | +The utility function `duplexPair` returns an Array with two items, | | 2692 | +each being a `Duplex` stream connected to the other side: | | 2693 | + | | 2694 | +```js | | 2695 | +const [ sideA, sideB ] = duplexPair(); | | 2696 | +``` | | 2697 | + | | 2698 | +Whatever is written to one stream is made readable on the other. It provides | | 2699 | +behavior analogous to a network connection, where the data written by the client | | 2700 | +becomes readable by the server, and vice-versa. | | 2701 | + | | 2702 | +The Duplex streams are symmetrical; one or the other may be used without any | | 2703 | +difference in behavior. | | 2704 | + | 2678 | 2705 | ### `stream.finished(stream[, options], callback)` | 2679 | 2706 |
| 2680 | 2707 | <!-- YAML | @@ -4865,6 +4892,7 @@ contain multi-byte characters. | 4865 | 4892 | [`stream.addAbortSignal()`]: #streamaddabortsignalsignal-stream | 4866 | 4893 | [`stream.compose`]: #streamcomposestreams | 4867 | 4894 | [`stream.cork()`]: #writablecork | | 4895 | +[`stream.duplexPair()`]: #streamduplexpairoptions | 4868 | 4896 | [`stream.finished()`]: #streamfinishedstream-options-callback | 4869 | 4897 | [`stream.pipe()`]: #readablepipedestination-options | 4870 | 4898 | [`stream.pipeline()`]: #streampipelinesource-transforms-destination-callback | |
Collapse file Copy file name to clipboard +62Lines changed: 62 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +const { | | 3 | + Symbol, | | 4 | +} = primordials; | | 5 | + | | 6 | +const { Duplex } = require('stream'); | | 7 | +const assert = require('internal/assert'); | | 8 | + | | 9 | +const kCallback = Symbol('Callback'); | | 10 | +const kInitOtherSide = Symbol('InitOtherSide'); | | 11 | + | | 12 | +class DuplexSide extends Duplex { | | 13 | + #otherSide = null; | | 14 | + | | 15 | + constructor(options) { | | 16 | + super(options); | | 17 | + this[kCallback] = null; | | 18 | + this.#otherSide = null; | | 19 | + } | | 20 | + | | 21 | + [kInitOtherSide](otherSide) { | | 22 | + // Ensure this can only be set once, to enforce encapsulation. | | 23 | + if (this.#otherSide === null) { | | 24 | + this.#otherSide = otherSide; | | 25 | + } else { | | 26 | + assert(this.#otherSide === null); | | 27 | + } | | 28 | + } | | 29 | + | | 30 | + _read() { | | 31 | + const callback = this[kCallback]; | | 32 | + if (callback) { | | 33 | + this[kCallback] = null; | | 34 | + callback(); | | 35 | + } | | 36 | + } | | 37 | + | | 38 | + _write(chunk, encoding, callback) { | | 39 | + assert(this.#otherSide !== null); | | 40 | + assert(this.#otherSide[kCallback] === null); | | 41 | + if (chunk.length === 0) { | | 42 | + process.nextTick(callback); | | 43 | + } else { | | 44 | + this.#otherSide.push(chunk); | | 45 | + this.#otherSide[kCallback] = callback; | | 46 | + } | | 47 | + } | | 48 | + | | 49 | + _final(callback) { | | 50 | + this.#otherSide.on('end', callback); | | 51 | + this.#otherSide.push(null); | | 52 | + } | | 53 | +} | | 54 | + | | 55 | +function duplexPair(options) { | | 56 | + const side0 = new DuplexSide(options); | | 57 | + const side1 = new DuplexSide(options); | | 58 | + side0[kInitOtherSide](side1); | | 59 | + side1[kInitOtherSide](side0); | | 60 | + return [ side0, side1 ]; | | 61 | +} | | 62 | +module.exports = duplexPair; |
Collapse file Copy file name to clipboardExpand all lines: lib/stream.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -101,6 +101,7 @@ Stream.Writable = require('internal/streams/writable'); | 101 | 101 | Stream.Duplex = require('internal/streams/duplex'); | 102 | 102 | Stream.Transform = require('internal/streams/transform'); | 103 | 103 | Stream.PassThrough = require('internal/streams/passthrough'); | | 104 | +Stream.duplexPair = require('internal/streams/duplexpair'); | 104 | 105 | Stream.pipeline = pipeline; | 105 | 106 | const { addAbortSignal } = require('internal/streams/add-abort-signal'); | 106 | 107 | Stream.addAbortSignal = addAbortSignal; | |
Collapse file Copy file name to clipboardExpand all lines: test/common/README.md -9Lines changed: 0 additions & 9 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -12,7 +12,6 @@ This directory contains modules used to test the Node.js implementation. | 12 | 12 | * [CPU Profiler module](#cpu-profiler-module) | 13 | 13 | * [Debugger module](#debugger-module) | 14 | 14 | * [DNS module](#dns-module) | 15 | | -* [Duplex pair helper](#duplex-pair-helper) | 16 | 15 | * [Environment variables](#environment-variables) | 17 | 16 | * [Fixtures module](#fixtures-module) | 18 | 17 | * [Heap dump checker module](#heap-dump-checker-module) | @@ -669,14 +668,6 @@ Reads a Domain String and returns a Buffer containing the domain. | 669 | 668 | Takes in a parsed Object and writes its fields to a DNS packet as a Buffer | 670 | 669 | object. | 671 | 670 |
| 672 | | -## Duplex pair helper | 673 | | - | 674 | | -The `common/duplexpair` module exports a single function `makeDuplexPair`, | 675 | | -which returns an object `{ clientSide, serverSide }` where each side is a | 676 | | -`Duplex` stream connected to the other side. | 677 | | - | 678 | | -There is no difference between client or server side beyond their names. | 679 | | - | 680 | 671 | ## Environment variables | 681 | 672 |
| 682 | 673 | The behavior of the Node.js test suite can be altered using the following | |
Collapse file Copy file name to clipboardExpand all lines: test/common/duplexpair.js -48Lines changed: 0 additions & 48 deletions Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-bootstrap-modules.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -128,6 +128,7 @@ if (common.isMainThread) { | 128 | 128 | 'NativeModule internal/streams/compose', | 129 | 129 | 'NativeModule internal/streams/destroy', | 130 | 130 | 'NativeModule internal/streams/duplex', | | 131 | + 'NativeModule internal/streams/duplexpair', | 131 | 132 | 'NativeModule internal/streams/end-of-stream', | 132 | 133 | 'NativeModule internal/streams/from', | 133 | 134 | 'NativeModule internal/streams/legacy', | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-gc-tls-external-memory.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -8,7 +8,7 @@ const common = require('../common'); | 8 | 8 | if (!common.hasCrypto) | 9 | 9 | common.skip('missing crypto'); | 10 | 10 |
| 11 | | -const makeDuplexPair = require('../common/duplexpair'); | | 11 | +const { duplexPair } = require('stream'); | 12 | 12 | const onGC = require('../common/ongc'); | 13 | 13 | const assert = require('assert'); | 14 | 14 | const tls = require('tls'); | @@ -37,7 +37,7 @@ function connect() { | 37 | 37 | return; | 38 | 38 | } | 39 | 39 |
| 40 | | - const { clientSide, serverSide } = makeDuplexPair(); | | 40 | + const [ clientSide, serverSide ] = duplexPair(); | 41 | 41 |
| 42 | 42 | const tlsSocket = tls.connect({ socket: clientSide }); | 43 | 43 | tlsSocket.on('error', common.mustCall(connect)); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-http-agent-domain-reused-gc.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 3 | 3 | const common = require('../common'); | 4 | 4 | const http = require('http'); | 5 | 5 | const async_hooks = require('async_hooks'); | 6 | | -const makeDuplexPair = require('../common/duplexpair'); | | 6 | +const { duplexPair } = require('stream'); | 7 | 7 |
| 8 | 8 | // Regression test for https://github.com/nodejs/node/issues/30122 | 9 | 9 | // When a domain is attached to an http Agent’s ReusedHandle object, that | @@ -36,7 +36,7 @@ async_hooks.createHook({ | 36 | 36 | // attached to too many objects that use strong references (timers, the network | 37 | 37 | // socket handle, etc.) and wrap the client side in a JSStreamSocket so we don’t | 38 | 38 | // have to implement the whole _handle API ourselves. | 39 | | -const { serverSide, clientSide } = makeDuplexPair(); | | 39 | +const [ serverSide, clientSide ] = duplexPair(); | 40 | 40 | const JSStreamSocket = require('internal/js_stream_socket'); | 41 | 41 | const wrappedClientSide = new JSStreamSocket(clientSide); | 42 | 42 |
| |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-http-generic-streams.js +6-6Lines changed: 6 additions & 6 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 | const common = require('../common'); | 3 | 3 | const assert = require('assert'); | 4 | 4 | const http = require('http'); | 5 | | -const MakeDuplexPair = require('../common/duplexpair'); | | 5 | +const { duplexPair } = require('stream'); | 6 | 6 |
| 7 | 7 | // Test 1: Simple HTTP test, no keep-alive. | 8 | 8 | { | @@ -13,7 +13,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 13 | 13 | res.end(testData); | 14 | 14 | })); | 15 | 15 |
| 16 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 16 | + const [ clientSide, serverSide ] = duplexPair(); | 17 | 17 | server.emit('connection', serverSide); | 18 | 18 |
| 19 | 19 | const req = http.request({ | @@ -37,7 +37,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 37 | 37 | res.end(testData); | 38 | 38 | }, 2)); | 39 | 39 |
| 40 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 40 | + const [ clientSide, serverSide ] = duplexPair(); | 41 | 41 | server.emit('connection', serverSide); | 42 | 42 |
| 43 | 43 | function doRequest(cb) { | @@ -77,7 +77,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 77 | 77 | }); | 78 | 78 | })); | 79 | 79 |
| 80 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 80 | + const [ clientSide, serverSide ] = duplexPair(); | 81 | 81 | server.emit('connection', serverSide); | 82 | 82 | clientSide.on('end', common.mustCall()); | 83 | 83 | serverSide.on('end', common.mustCall()); | @@ -117,7 +117,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 117 | 117 |
| 118 | 118 | })); | 119 | 119 |
| 120 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 120 | + const [ clientSide, serverSide ] = duplexPair(); | 121 | 121 | server.emit('connection', serverSide); | 122 | 122 | clientSide.on('end', common.mustCall()); | 123 | 123 | serverSide.on('end', common.mustCall()); | @@ -143,7 +143,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 143 | 143 | { | 144 | 144 | const server = http.createServer(common.mustNotCall()); | 145 | 145 |
| 146 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 146 | + const [ clientSide, serverSide ] = duplexPair(); | 147 | 147 | server.emit('connection', serverSide); | 148 | 148 |
| 149 | 149 | server.on('clientError', common.mustCall()); | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-http-insecure-parser-per-stream.js +5-5Lines changed: 5 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 2 | 2 | const common = require('../common'); | 3 | 3 | const assert = require('assert'); | 4 | 4 | const http = require('http'); | 5 | | -const MakeDuplexPair = require('../common/duplexpair'); | | 5 | +const { duplexPair } = require('stream'); | 6 | 6 |
| 7 | 7 | // Test that setting the `maxHeaderSize` option works on a per-stream-basis. | 8 | 8 |
| 9 | 9 | // Test 1: The server sends an invalid header. | 10 | 10 | { | 11 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 11 | + const [ clientSide, serverSide ] = duplexPair(); | 12 | 12 |
| 13 | 13 | const req = http.request({ | 14 | 14 | createConnection: common.mustCall(() => clientSide), | @@ -30,7 +30,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 30 | 30 |
| 31 | 31 | // Test 2: The same as Test 1 except without the option, to make sure it fails. | 32 | 32 | { | 33 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 33 | + const [ clientSide, serverSide ] = duplexPair(); | 34 | 34 |
| 35 | 35 | const req = http.request({ | 36 | 36 | createConnection: common.mustCall(() => clientSide) | @@ -59,7 +59,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 59 | 59 |
| 60 | 60 | server.on('clientError', common.mustNotCall()); | 61 | 61 |
| 62 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 62 | + const [ clientSide, serverSide ] = duplexPair(); | 63 | 63 | serverSide.server = server; | 64 | 64 | server.emit('connection', serverSide); | 65 | 65 |
| @@ -75,7 +75,7 @@ const MakeDuplexPair = require('../common/duplexpair'); | 75 | 75 |
| 76 | 76 | server.on('clientError', common.mustCall()); | 77 | 77 |
| 78 | | - const { clientSide, serverSide } = MakeDuplexPair(); | | 78 | + const [ clientSide, serverSide ] = duplexPair(); | 79 | 79 | serverSide.server = server; | 80 | 80 | server.emit('connection', serverSide); | 81 | 81 |
| |
|