Skip to content

Commit db5d657

Browse files
committed
net: move createPipe onto net
1 parent 4d06c46 commit db5d657

18 files changed

Lines changed: 139 additions & 184 deletions

doc/api/child_process.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1055,8 +1055,8 @@ pipes between the parent and child. The value is one of the following:
10551055
file descriptor is duplicated in the child process to the fd that
10561056
corresponds to the index in the `stdio` array. The stream must have an
10571057
underlying descriptor (file streams do not start until the `'open'` event has
1058-
occurred). Pipe endpoints returned by [`pipe.createPipe()`][] may be passed
1059-
here. A readable pipe endpoint returned by [`pipe.createPipe()`][] must not
1058+
occurred). Pipe endpoints returned by [`net.createPipe()`][] may be passed
1059+
here. A readable pipe endpoint returned by [`net.createPipe()`][] must not
10601060
be flowing when it is passed here.
10611061
**NOTE:** While it is technically possible to pass `stdin` as a writable or
10621062
`stdout`/`stderr` as readable, it is not recommended.
@@ -1445,7 +1445,7 @@ streams. The `'close'` event will always emit after [`'exit'`][] was
14451445
already emitted, or [`'error'`][] if the child process failed to spawn.
14461446
Readable stdio streams created by Node.js are resumed after the child process
14471447
exits so they can be fully consumed and closed before the `'close'` event is
1448-
emitted. Endpoints created by [`pipe.createPipe()`][] are an exception to this
1448+
emitted. Endpoints created by [`net.createPipe()`][] are an exception to this
14491449
rule and are not resumed by the child process. Their stream lifecycle remains
14501450
owned by the parent process, and consequently the child process `'close'` event
14511451
does not wait for such streams to close.
@@ -2382,7 +2382,7 @@ or [`child_process.fork()`][].
23822382
[`maxBuffer` and Unicode]: #maxbuffer-and-unicode
23832383
[`net.Server`]: net.md#class-netserver
23842384
[`net.Socket`]: net.md#class-netsocket
2385-
[`pipe.createPipe()`]: pipe.md#pipecreatepipe
2385+
[`net.createPipe()`]: net.md#netcreatepipe
23862386
[`options.detached`]: #optionsdetached
23872387
[`process.disconnect()`]: process.md#processdisconnect
23882388
[`process.env`]: process.md#processenv

doc/api/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
* [Net](net.md)
4545
* [OS](os.md)
4646
* [Path](path.md)
47-
* [Pipe](pipe.md)
4847
* [Performance hooks](perf_hooks.md)
4948
* [Permissions](permissions.md)
5049
* [Process](process.md)

doc/api/net.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2138,6 +2138,90 @@ Use `nc` to connect to a Unix domain socket server:
21382138
nc -U /tmp/echo.sock
21392139
```
21402140

2141+
## `net.createPipe()`
2142+
2143+
<!-- YAML
2144+
added: REPLACEME
2145+
-->
2146+
2147+
* Returns: {Object}
2148+
* `readable` {net.Socket} The readable end of the pipe.
2149+
* `writable` {net.Socket} The writable end of the pipe.
2150+
2151+
The `net.createPipe()` method creates an operating system pipe pair. The
2152+
returned `readable` and `writable` streams are owned by the current process and
2153+
may be passed to [`child_process.spawn()`][] using the [`stdio`][] option.
2154+
2155+
When a `readable` endpoint is passed as child stdin or as another child fd, the
2156+
child leases a readable handle. When a `writable` endpoint is passed as child
2157+
stdout, stderr, or another child fd, the child leases a writable handle. A
2158+
`readable` endpoint may not be passed as child stdout or stderr, and a
2159+
`writable` endpoint may not be passed as child stdin. An endpoint may be leased
2160+
to only one child process at a time. After the child process exits, endpoints
2161+
created by [`net.createPipe()`][] are released from their lease and may be
2162+
passed to another [`child_process.spawn()`][] call. Endpoints created by
2163+
[`net.createPipe()`][] are not supported by synchronous child process APIs such
2164+
as [`child_process.spawnSync()`][].
2165+
2166+
A `readable` endpoint created by [`net.createPipe()`][] must not be flowing
2167+
when it is passed to [`child_process.spawn()`][]. The child process
2168+
[`'close'` event][child-process-close] does not wait for such an endpoint to
2169+
close and does not resume it after the child process exits.
2170+
2171+
The current process is responsible for the endpoint streams. Use normal stream
2172+
idioms such as `end()` to finish writing and stream consumption to drain a
2173+
readable endpoint. Use `resume()` when an unread readable endpoint should be
2174+
drained without observing its data, and use `destroy()` when an endpoint is no
2175+
longer needed without being naturally ended or drained.
2176+
2177+
```cjs
2178+
const { spawn } = require('node:child_process');
2179+
const { createPipe } = require('node:net');
2180+
const { text } = require('node:stream/consumers');
2181+
2182+
const { readable, writable } = createPipe();
2183+
const child = spawn(process.execPath, ['-e', `
2184+
const fs = require('node:fs');
2185+
const buffer = Buffer.alloc(1);
2186+
const count = fs.readSync(0, buffer, 0, 1, null);
2187+
fs.writeSync(1, buffer.subarray(0, count));
2188+
`], {
2189+
stdio: [readable, 'pipe', 'inherit'],
2190+
});
2191+
2192+
const output = text(child.stdout);
2193+
writable.end('abc');
2194+
2195+
child.on('close', async () => {
2196+
console.log(await output); // Prints: a
2197+
console.log(await text(readable)); // Prints: bc
2198+
});
2199+
```
2200+
2201+
```mjs
2202+
import { spawn } from 'node:child_process';
2203+
import { createPipe } from 'node:net';
2204+
import { text } from 'node:stream/consumers';
2205+
2206+
const { readable, writable } = createPipe();
2207+
const child = spawn(process.execPath, ['-e', `
2208+
const fs = require('node:fs');
2209+
const buffer = Buffer.alloc(1);
2210+
const count = fs.readSync(0, buffer, 0, 1, null);
2211+
fs.writeSync(1, buffer.subarray(0, count));
2212+
`], {
2213+
stdio: [readable, 'pipe', 'inherit'],
2214+
});
2215+
2216+
const output = text(child.stdout);
2217+
writable.end('abc');
2218+
2219+
child.on('close', async () => {
2220+
console.log(await output); // Prints: a
2221+
console.log(await text(readable)); // Prints: bc
2222+
});
2223+
```
2224+
21412225
## `net.getDefaultAutoSelectFamily()`
21422226

21432227
<!-- YAML
@@ -2264,6 +2348,8 @@ net.isIPv6('fhqwhgads'); // returns false
22642348
[`ERR_SOCKET_HANDLE_ADOPTED`]: errors.md#err_socket_handle_adopted
22652349
[`EventEmitter`]: events.md#class-eventemitter
22662350
[`child_process.fork()`]: child_process.md#child_processforkmodulepath-args-options
2351+
[`child_process.spawn()`]: child_process.md#child_processspawncommand-args-options
2352+
[`child_process.spawnSync()`]: child_process.md#child_processspawnsynccommand-args-options
22672353
[`dns.lookup()`]: dns.md#dnslookuphostname-options-callback
22682354
[`dns.lookup()` hints]: dns.md#supported-getaddrinfo-flags
22692355
[`net.Server`]: #class-netserver
@@ -2276,6 +2362,7 @@ net.isIPv6('fhqwhgads'); // returns false
22762362
[`net.createConnection(options)`]: #netcreateconnectionoptions-connectlistener
22772363
[`net.createConnection(path)`]: #netcreateconnectionpath-connectlistener
22782364
[`net.createConnection(port, host)`]: #netcreateconnectionport-host-connectlistener
2365+
[`net.createPipe()`]: #netcreatepipe
22792366
[`net.createServer()`]: #netcreateserveroptions-connectionlistener
22802367
[`net.getDefaultAutoSelectFamily()`]: #netgetdefaultautoselectfamily
22812368
[`net.getDefaultAutoSelectFamilyAttemptTimeout()`]: #netgetdefaultautoselectfamilyattempttimeout
@@ -2308,13 +2395,15 @@ net.isIPv6('fhqwhgads'); // returns false
23082395
[`socket.setTimeout()`]: #socketsettimeouttimeout-callback
23092396
[`socket.setTimeout(timeout)`]: #socketsettimeouttimeout-callback
23102397
[`stream.getDefaultHighWaterMark()`]: stream.md#streamgetdefaulthighwatermarkobjectmode
2398+
[`stdio`]: child_process.md#optionsstdio
23112399
[`worker_threads`]: worker_threads.md
23122400
[`writable.destroy()`]: stream.md#writabledestroyerror
23132401
[`writable.destroyed`]: stream.md#writabledestroyed
23142402
[`writable.end()`]: stream.md#writableendchunk-encoding-callback
23152403
[`writable.writableLength`]: stream.md#writablewritablelength
23162404
[dot-decimal notation]: https://en.wikipedia.org/wiki/Dot-decimal_notation
23172405
[half-closed]: https://tools.ietf.org/html/rfc1122
2406+
[child-process-close]: child_process.md#event-close
23182407
[stream_writable_write]: stream.md#writablewritechunk-encoding-callback
23192408
[unspecified IPv4 address]: https://en.wikipedia.org/wiki/0.0.0.0
23202409
[unspecified IPv6 address]: https://en.wikipedia.org/wiki/IPv6_address#Unspecified_address

doc/api/pipe.md

Lines changed: 0 additions & 111 deletions
This file was deleted.

lib/internal/child_process.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ const {
7777
} = internalBinding('uv');
7878

7979
const { SocketListSend, SocketListReceive } = SocketList;
80-
const { kReaderOfPair, kWriterOfPair } = require('internal/pipe');
80+
const { kReaderOfPair, kWriterOfPair } = require('internal/net');
8181
const kLeasedTo = Symbol('kLeasedTo');
8282
const kStreamLeaseInUseMessage =
8383
'Stream is already in use by a child process';

lib/internal/net.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,14 @@ function isLoopback(host) {
9999
}
100100

101101
module.exports = {
102+
kReaderOfPair: Symbol('kReaderOfPair'),
102103
kReinitializeHandle: Symbol('kReinitializeHandle'),
103104
kSetNoDelay: Symbol('kSetNoDelay'),
104105
kSetKeepAlive: Symbol('kSetKeepAlive'),
105106
kSetKeepAliveInitialDelay: Symbol('kSetKeepAliveInitialDelay'),
106107
kSetKeepAliveInterval: Symbol('kSetKeepAliveInterval'),
107108
kSetKeepAliveCount: Symbol('kSetKeepAliveCount'),
109+
kWriterOfPair: Symbol('kWriterOfPair'),
108110
isIP,
109111
isIPv4,
110112
isIPv6,

lib/internal/pipe.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

lib/net.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ let debug = require('internal/util/debuglog').debuglog('net', (fn) => {
4747
debug = fn;
4848
});
4949
const {
50+
kReaderOfPair,
5051
kReinitializeHandle,
5152
kSetNoDelay,
5253
kSetKeepAlive,
5354
kSetKeepAliveInitialDelay,
5455
kSetKeepAliveInterval,
5556
kSetKeepAliveCount,
57+
kWriterOfPair,
5658
isIP,
5759
isIPv4,
5860
isIPv6,
@@ -80,6 +82,7 @@ const {
8082
const {
8183
Pipe,
8284
PipeConnectWrap,
85+
pairPipes,
8386
constants: PipeConstants,
8487
} = internalBinding('pipe_wrap');
8588
const {
@@ -211,6 +214,29 @@ function createHandle(fd, is_server) {
211214
throw new ERR_INVALID_FD_TYPE(type);
212215
}
213216

217+
function createPipe() {
218+
const readHandle = new Pipe(PipeConstants.SOCKET);
219+
const writeHandle = new Pipe(PipeConstants.SOCKET);
220+
pairPipes(readHandle, writeHandle);
221+
222+
const readable = new Socket({
223+
handle: readHandle,
224+
pauseOnCreate: true,
225+
readable: true,
226+
writable: false,
227+
});
228+
const writable = new Socket({
229+
handle: writeHandle,
230+
readable: false,
231+
writable: true,
232+
});
233+
234+
readable[kReaderOfPair] = true;
235+
writable[kWriterOfPair] = true;
236+
237+
return { readable, writable };
238+
}
239+
214240

215241
function getNewAsyncId(handle) {
216242
return (!handle || typeof handle.getAsyncId !== 'function') ?
@@ -2863,6 +2889,7 @@ module.exports = {
28632889
BoundSocket,
28642890
connect,
28652891
createConnection: connect,
2892+
createPipe,
28662893
createServer,
28672894
isIP: isIP,
28682895
isIPv4: isIPv4,

0 commit comments

Comments
 (0)