Skip to content

Commit 6f613d8

Browse files
ronagTrott
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 e4bbbcc commit 6f613d8

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
@@ -1436,6 +1457,17 @@ response.statusMessage = 'Not found';
14361457
After response header was sent to the client, this property indicates the
14371458
status message which was sent out.
14381459

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

doc/api/http2.md

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

32723272
The [`Http2Stream`][] object backing the response.
32733273

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

doc/api/stream.md

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

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

497+
##### writable.writableEnded
498+
<!-- YAML
499+
added: REPLACEME
500+
-->
501+
502+
* {boolean}
503+
504+
Is `true` after [`writable.end()`][] has been called. This property
505+
does not indicate whether the data has been flushed, for this use
506+
[`writable.writableFinished`][] instead.
507+
497508
##### writable.writableFinished
498509
<!-- YAML
499510
added: v12.6.0
@@ -2707,7 +2718,9 @@ contain multi-byte characters.
27072718
[`stream.unpipe()`]: #stream_readable_unpipe_destination
27082719
[`stream.wrap()`]: #stream_readable_wrap_stream
27092720
[`writable.cork()`]: #stream_writable_cork
2721+
[`writable.end()`]: #stream_writable_end_chunk_encoding_callback
27102722
[`writable.uncork()`]: #stream_writable_uncork
2723+
[`writable.writableFinished`]: #stream_writable_writablefinished
27112724
[`zlib.createDeflate()`]: zlib.html#zlib_zlib_createdeflate_options
27122725
[API for Stream Consumers]: #stream_api_for_stream_consumers
27132726
[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

@@ -711,7 +721,7 @@ Object.defineProperty(Writable.prototype, 'writableFinished', {
711721
// userland will fail
712722
enumerable: false,
713723
get() {
714-
return this._writableState.finished;
724+
return this._writableState ? this._writableState.finished : false;
715725
}
716726
});
717727

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)