Skip to content

Commit a77cae1

Browse files
vtjnashmmomtchev
authored andcommitted
tls: improve handling of shutdown
RFC 5246 section-7.2.1 requires that the implementation must immediately stop reading from the stream, as it is no longer TLS-encrypted. The underlying stream is permitted to still pump events (and errors) to other users, but those are now unencrypted, so we should not process them here. But therefore, we do not want to stop the underlying stream, as there could be another user of it, but we do need to remove ourselves as a listener. Per TLS v1.2, we should have also destroy the TLS state entirely here (including the writing side), but this was revised in TLS v1.3 to permit the stream to continue to flush output. There appears to be some inconsistencies in the way nodejs handles ownership of the underlying stream, with `TLS.close()` on the write side also calling shutdown on the underlying stream (thus assuming other users of the underlying stream are not permitted), while receiving EOF on the read side leaves the underlying channel open. These inconsistencies are left for a later person to resolve, if the extra functionality is needed (as described in #35904). The current goal here is to the fix the occasional CI exceptions depending on the timing of these kernel messages through the TCP stack. PR-URL: #36111 Fixes: #35946 Refs: libuv/libuv#3036 Refs: #35904 Co-authored-by: Momtchil Momtchev <[email protected]> Reviewed-By: Robert Nagy <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Luigi Pinca <[email protected]>
1 parent 40a773a commit a77cae1

File tree

3 files changed

+13
-23
lines changed

3 files changed

+13
-23
lines changed

lib/_http_client.js

-5
Original file line numberDiff line numberDiff line change
@@ -390,11 +390,6 @@ function socketCloseListener() {
390390
const req = socket._httpMessage;
391391
debug('HTTP socket close');
392392

393-
// Pull through final chunk, if anything is buffered.
394-
// the ondata function will handle it properly, and this
395-
// is a no-op if no final chunk remains.
396-
socket.read();
397-
398393
// NOTE: It's important to get parser here, because it could be freed by
399394
// the `socketOnData`.
400395
const parser = socket.parser;

lib/internal/stream_base_commons.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,12 @@ function onStreamRead(arrayBuffer) {
205205
return;
206206
}
207207

208+
// After seeing EOF, most streams will be closed permanently,
209+
// and will not deliver any more read events after this point.
210+
// (equivalently, it should have called readStop on itself already).
211+
// Some streams may be reset and explicitly started again with a call
212+
// to readStart, such as TTY.
213+
208214
if (nread !== UV_EOF) {
209215
// CallJSOnreadMethod expects the return value to be a buffer.
210216
// Ref: https://github.com/nodejs/node/pull/34375
@@ -220,20 +226,6 @@ function onStreamRead(arrayBuffer) {
220226
if (stream[kMaybeDestroy])
221227
stream.on('end', stream[kMaybeDestroy]);
222228

223-
// TODO(ronag): Without this `readStop`, `onStreamRead`
224-
// will be called once more (i.e. after Readable.ended)
225-
// on Windows causing a ECONNRESET, failing the
226-
// test-https-truncate test.
227-
if (handle.readStop) {
228-
const err = handle.readStop();
229-
if (err) {
230-
// CallJSOnreadMethod expects the return value to be a buffer.
231-
// Ref: https://github.com/nodejs/node/pull/34375
232-
stream.destroy(errnoException(err, 'read'));
233-
return;
234-
}
235-
}
236-
237229
// Push a null to signal the end of data.
238230
// Do it before `maybeDestroy` for correct order of events:
239231
// `end` -> `close`

src/crypto/crypto_tls.cc

+7-4
Original file line numberDiff line numberDiff line change
@@ -886,7 +886,7 @@ bool TLSWrap::IsClosing() {
886886

887887
int TLSWrap::ReadStart() {
888888
Debug(this, "ReadStart()");
889-
if (underlying_stream() != nullptr)
889+
if (underlying_stream() != nullptr && !eof_)
890890
return underlying_stream()->ReadStart();
891891
return 0;
892892
}
@@ -1049,14 +1049,17 @@ uv_buf_t TLSWrap::OnStreamAlloc(size_t suggested_size) {
10491049

10501050
void TLSWrap::OnStreamRead(ssize_t nread, const uv_buf_t& buf) {
10511051
Debug(this, "Read %zd bytes from underlying stream", nread);
1052+
1053+
// Ignore everything after close_notify (rfc5246#section-7.2.1)
1054+
if (eof_)
1055+
return;
1056+
10521057
if (nread < 0) {
10531058
// Error should be emitted only after all data was read
10541059
ClearOut();
10551060

1056-
// Ignore EOF if received close_notify
10571061
if (nread == UV_EOF) {
1058-
if (eof_)
1059-
return;
1062+
// underlying stream already should have also called ReadStop on itself
10601063
eof_ = true;
10611064
}
10621065

0 commit comments

Comments
 (0)