Skip to content

Commit f9b61d2

Browse files
ronagtargos
authored andcommitted
http,stream: add writableEnded
This is work towards resolving the response.finished confusion and future deprecation. Note that implementation-wise, streams have both an ending and ended state. However, in this case (in order to avoid confusion in user space) writableEnded is equal to writable.ending. The ending vs ended situation is internal state required for internal stream logic. PR-URL: #28934 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Trivikram Kamat <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent bb19d82 commit f9b61d2

11 files changed

+103
-5
lines changed

doc/api/http.md

+34
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,27 @@ req.once('response', (res) => {
764764
});
765765
```
766766

767+
### request.writableEnded
768+
<!-- YAML
769+
added: REPLACEME
770+
-->
771+
772+
* {boolean}
773+
774+
Is `true` after [`request.end()`][] has been called. This property
775+
does not indicate whether the data has been flushed, for this use
776+
[`request.writableFinished`][] instead.
777+
778+
### request.writableFinished
779+
<!-- YAML
780+
added: v12.7.0
781+
-->
782+
783+
* {boolean}
784+
785+
Is `true` if all data has been flushed to the underlying system, immediately
786+
before the [`'finish'`][] event is emitted.
787+
767788
### request.write(chunk[, encoding][, callback])
768789
<!-- YAML
769790
added: v0.1.29
@@ -1434,6 +1455,17 @@ response.statusMessage = 'Not found';
14341455
After response header was sent to the client, this property indicates the
14351456
status message which was sent out.
14361457

1458+
### response.writableEnded
1459+
<!-- YAML
1460+
added: REPLACEME
1461+
-->
1462+
1463+
* {boolean}
1464+
1465+
Is `true` after [`response.end()`][] has been called. This property
1466+
does not indicate whether the data has been flushed, for this use
1467+
[`response.writableFinished`][] instead.
1468+
14371469
### response.writableFinished
14381470
<!-- YAML
14391471
added: v12.7.0
@@ -2221,11 +2253,13 @@ not abort the request or do anything besides add a `'timeout'` event.
22212253
[`request.setTimeout()`]: #http_request_settimeout_timeout_callback
22222254
[`request.socket.getPeerCertificate()`]: tls.html#tls_tlssocket_getpeercertificate_detailed
22232255
[`request.socket`]: #http_request_socket
2256+
[`request.writableFinished`]: #http_request_writablefinished
22242257
[`request.write(data, encoding)`]: #http_request_write_chunk_encoding_callback
22252258
[`response.end()`]: #http_response_end_data_encoding_callback
22262259
[`response.getHeader()`]: #http_response_getheader_name
22272260
[`response.setHeader()`]: #http_response_setheader_name_value
22282261
[`response.socket`]: #http_response_socket
2262+
[`response.writableFinished`]: #http_response_writablefinished
22292263
[`response.write()`]: #http_response_write_chunk_encoding_callback
22302264
[`response.write(data, encoding)`]: #http_response_write_chunk_encoding_callback
22312265
[`response.writeContinue()`]: #http_response_writecontinue

doc/api/http2.md

+12
Original file line numberDiff line numberDiff line change
@@ -3269,6 +3269,17 @@ added: v8.4.0
32693269

32703270
The [`Http2Stream`][] object backing the response.
32713271

3272+
#### response.writableEnded
3273+
<!-- YAML
3274+
added: REPLACEME
3275+
-->
3276+
3277+
* {boolean}
3278+
3279+
Is `true` after [`response.end()`][] has been called. This property
3280+
does not indicate whether the data has been flushed, for this use
3281+
[`writable.writableFinished`][] instead.
3282+
32723283
#### response.write(chunk[, encoding][, callback])
32733284
<!-- YAML
32743285
added: v8.4.0
@@ -3509,3 +3520,4 @@ following additional properties:
35093520
[`tls.connect()`]: tls.html#tls_tls_connect_options_callback
35103521
[`tls.createServer()`]: tls.html#tls_tls_createserver_options_secureconnectionlistener
35113522
[error code]: #error_codes
3523+
[`writable.writableFinished`]: stream.html#stream_writable_writablefinished

doc/api/stream.md

+13
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,17 @@ added: v11.4.0
491491

492492
Is `true` if it is safe to call [`writable.write()`][stream-write].
493493

494+
##### writable.writableEnded
495+
<!-- YAML
496+
added: REPLACEME
497+
-->
498+
499+
* {boolean}
500+
501+
Is `true` after [`writable.end()`][] has been called. This property
502+
does not indicate whether the data has been flushed, for this use
503+
[`writable.writableFinished`][] instead.
504+
494505
##### writable.writableFinished
495506
<!-- YAML
496507
added: v12.6.0
@@ -2704,7 +2715,9 @@ contain multi-byte characters.
27042715
[`stream.unpipe()`]: #stream_readable_unpipe_destination
27052716
[`stream.wrap()`]: #stream_readable_wrap_stream
27062717
[`writable.cork()`]: #stream_writable_cork
2718+
[`writable.end()`]: #stream_writable_end_chunk_encoding_callback
27072719
[`writable.uncork()`]: #stream_writable_uncork
2720+
[`writable.writableFinished`]: #stream_writable_writablefinished
27082721
[`zlib.createDeflate()`]: zlib.html#zlib_zlib_createdeflate_options
27092722
[API for Stream Consumers]: #stream_api_for_stream_consumers
27102723
[API for Stream Implementers]: #stream_api_for_stream_implementers

lib/_http_outgoing.js

+4
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,10 @@ Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
575575
get: function() { return !!this._header; }
576576
});
577577

578+
Object.defineProperty(OutgoingMessage.prototype, 'writableEnded', {
579+
get: function() { return this.finished; }
580+
});
581+
578582

579583
const crlf_buf = Buffer.from('\r\n');
580584
OutgoingMessage.prototype.write = function write(chunk, encoding, callback) {

lib/_stream_duplex.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', {
7474
// userland will fail
7575
enumerable: false,
7676
get() {
77-
return this._writableState.highWaterMark;
77+
return this._writableState && this._writableState.highWaterMark;
7878
}
7979
});
8080

@@ -94,7 +94,7 @@ Object.defineProperty(Duplex.prototype, 'writableLength', {
9494
// userland will fail
9595
enumerable: false,
9696
get() {
97-
return this._writableState.length;
97+
return this._writableState && this._writableState.length;
9898
}
9999
});
100100

@@ -104,7 +104,17 @@ Object.defineProperty(Duplex.prototype, 'writableFinished', {
104104
// userland will fail
105105
enumerable: false,
106106
get() {
107-
return this._writableState.finished;
107+
return this._writableState ? this._writableState.finished : false;
108+
}
109+
});
110+
111+
Object.defineProperty(Duplex.prototype, 'writableEnded', {
112+
// Making it explicit this property is not enumerable
113+
// because otherwise some prototype manipulation in
114+
// userland will fail
115+
enumerable: false,
116+
get() {
117+
return this._writableState ? this._writableState.ending : false;
108118
}
109119
});
110120

lib/_stream_writable.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,23 @@ function decodeChunk(state, chunk, encoding) {
352352
return chunk;
353353
}
354354

355+
Object.defineProperty(Writable.prototype, 'writableEnded', {
356+
// Making it explicit this property is not enumerable
357+
// because otherwise some prototype manipulation in
358+
// userland will fail
359+
enumerable: false,
360+
get: function() {
361+
return this._writableState ? this._writableState.ending : false;
362+
}
363+
});
364+
355365
Object.defineProperty(Writable.prototype, 'writableHighWaterMark', {
356366
// Making it explicit this property is not enumerable
357367
// because otherwise some prototype manipulation in
358368
// userland will fail
359369
enumerable: false,
360370
get: function() {
361-
return this._writableState.highWaterMark;
371+
return this._writableState && this._writableState.highWaterMark;
362372
}
363373
});
364374

@@ -713,7 +723,7 @@ Object.defineProperty(Writable.prototype, 'writableFinished', {
713723
// userland will fail
714724
enumerable: false,
715725
get() {
716-
return this._writableState.finished;
726+
return this._writableState ? this._writableState.finished : false;
717727
}
718728
});
719729

lib/internal/http2/compat.js

+5
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,11 @@ class Http2ServerResponse extends Stream {
454454
return this.headersSent;
455455
}
456456

457+
get writableEnded() {
458+
const state = this[kState];
459+
return state.ending;
460+
}
461+
457462
get finished() {
458463
const stream = this[kStream];
459464
return stream.destroyed ||

test/parallel/test-http-outgoing-finish-writable.js

+2
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ const http = require('http');
99
const server = http.createServer(common.mustCall(function(req, res) {
1010
assert.strictEqual(res.writable, true);
1111
assert.strictEqual(res.finished, false);
12+
assert.strictEqual(res.writableEnded, false);
1213
res.end();
1314

1415
// res.writable is set to false after it has finished sending
1516
// Ref: https://github.com/nodejs/node/issues/15029
1617
assert.strictEqual(res.writable, true);
1718
assert.strictEqual(res.finished, true);
19+
assert.strictEqual(res.writableEnded, true);
1820

1921
server.close();
2022
}));

test/parallel/test-http2-compat-serverresponse-end.js

+2
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,10 @@ const {
150150
// for http1 compatibility
151151
const server = createServer(mustCall((request, response) => {
152152
strictEqual(response.finished, true);
153+
strictEqual(response.writableEnded, false);
153154
response.writeHead(HTTP_STATUS_OK, { foo: 'bar' });
154155
response.end('data', mustCall());
156+
strictEqual(response.writableEnded, true);
155157
}));
156158
server.listen(0, mustCall(() => {
157159
const { port } = server.address();

test/parallel/test-http2-compat-serverresponse-finished.js

+2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ server.listen(0, common.mustCall(() => {
2525
}));
2626
}));
2727
assert.strictEqual(response.finished, false);
28+
assert.strictEqual(response.writableEnded, false);
2829
response.end();
2930
assert.strictEqual(response.finished, true);
31+
assert.strictEqual(response.writableEnded, true);
3032
}));
3133

3234
const url = `http://localhost:${port}`;

test/parallel/test-stream-writable-ended-state.js

+4
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,17 @@ const writable = new stream.Writable();
99

1010
writable._write = (chunk, encoding, cb) => {
1111
assert.strictEqual(writable._writableState.ended, false);
12+
assert.strictEqual(writable.writableEnded, false);
1213
cb();
1314
};
1415

1516
assert.strictEqual(writable._writableState.ended, false);
17+
assert.strictEqual(writable.writableEnded, false);
1618

1719
writable.end('testing ended state', common.mustCall(() => {
1820
assert.strictEqual(writable._writableState.ended, true);
21+
assert.strictEqual(writable.writableEnded, true);
1922
}));
2023

2124
assert.strictEqual(writable._writableState.ended, true);
25+
assert.strictEqual(writable.writableEnded, true);

0 commit comments

Comments
 (0)