Skip to content

Commit 5b892c4

Browse files
lpincatargos
authored andcommitted
tls: allow client-side sockets to be half-opened
Make `tls.connect()` support an `allowHalfOpen` option which specifies whether or not to allow the connection to be half-opened when the `socket` option is not specified. PR-URL: #27836 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Ouyang Yadong <[email protected]> Reviewed-By: Sam Roberts <[email protected]>
1 parent e474c67 commit 5b892c4

4 files changed

+120
-1
lines changed

doc/api/tls.md

+7
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,9 @@ being issued by trusted CA (`options.ca`).
11771177
<!-- YAML
11781178
added: v0.11.3
11791179
changes:
1180+
- version: REPLACEME
1181+
pr-url: https://github.com/nodejs/node/pull/27836
1182+
description: Support the `allowHalfOpen` option.
11801183
- version: v12.4.0
11811184
pr-url: https://github.com/nodejs/node/pull/27816
11821185
description: The `hints` option is now supported.
@@ -1217,6 +1220,10 @@ changes:
12171220
Connection/disconnection/destruction of `socket` is the user's
12181221
responsibility; calling `tls.connect()` will not cause `net.connect()` to be
12191222
called.
1223+
* `allowHalfOpen` {boolean} If the `socket` option is missing, indicates
1224+
whether or not to allow the internally created socket to be half-open,
1225+
otherwise the option is ignored. See the `allowHalfOpen` option of
1226+
[`net.Socket`][] for details. **Default:** `false`.
12201227
* `rejectUnauthorized` {boolean} If not `false`, the server certificate is
12211228
verified against the list of supplied CAs. An `'error'` event is emitted if
12221229
verification fails; `err.code` contains the OpenSSL error code. **Default:**

lib/_tls_wrap.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ function TLSSocket(socket, opts) {
410410

411411
net.Socket.call(this, {
412412
handle: this._wrapHandle(wrap),
413-
allowHalfOpen: socket && socket.allowHalfOpen,
413+
allowHalfOpen: socket ? socket.allowHalfOpen : tlsOptions.allowHalfOpen,
414414
readable: false,
415415
writable: false
416416
});
@@ -1403,6 +1403,7 @@ exports.connect = function connect(...args) {
14031403
const context = options.secureContext || tls.createSecureContext(options);
14041404

14051405
const tlssock = new TLSSocket(options.socket, {
1406+
allowHalfOpen: options.allowHalfOpen,
14061407
pipe: !!options.path,
14071408
secureContext: context,
14081409
isServer: false,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
// This test verifies that `tls.connect()` honors the `allowHalfOpen` option.
6+
7+
if (!common.hasCrypto)
8+
common.skip('missing crypto');
9+
10+
const assert = require('assert');
11+
const fixtures = require('../common/fixtures');
12+
const tls = require('tls');
13+
14+
{
15+
const socket = tls.connect({ lookup() {} });
16+
assert.strictEqual(socket.allowHalfOpen, false);
17+
}
18+
19+
{
20+
const socket = tls.connect({ allowHalfOpen: false, lookup() {} });
21+
assert.strictEqual(socket.allowHalfOpen, false);
22+
}
23+
24+
const server = tls.createServer({
25+
key: fixtures.readKey('agent1-key.pem'),
26+
cert: fixtures.readKey('agent1-cert.pem'),
27+
}, common.mustCall((socket) => {
28+
server.close();
29+
30+
let message = '';
31+
32+
socket.setEncoding('utf8');
33+
socket.on('data', (chunk) => {
34+
message += chunk;
35+
36+
if (message === 'Hello') {
37+
socket.end(message);
38+
message = '';
39+
}
40+
});
41+
42+
socket.on('end', common.mustCall(() => {
43+
assert.strictEqual(message, 'Bye');
44+
}));
45+
}));
46+
47+
server.listen(0, common.mustCall(() => {
48+
const socket = tls.connect({
49+
port: server.address().port,
50+
rejectUnauthorized: false,
51+
allowHalfOpen: true,
52+
}, common.mustCall(() => {
53+
let message = '';
54+
55+
socket.on('data', (chunk) => {
56+
message += chunk;
57+
});
58+
59+
socket.on('end', common.mustCall(() => {
60+
assert.strictEqual(message, 'Hello');
61+
62+
setTimeout(() => {
63+
assert(socket.writable);
64+
assert(socket.write('Bye'));
65+
socket.end();
66+
}, 50);
67+
}));
68+
69+
socket.write('Hello');
70+
}));
71+
72+
socket.setEncoding('utf8');
73+
}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
5+
// Test the `allowHalfOpen` option of the `tls.TLSSocket` constructor.
6+
7+
if (!common.hasCrypto)
8+
common.skip('missing crypto');
9+
10+
const assert = require('assert');
11+
const net = require('net');
12+
const stream = require('stream');
13+
const tls = require('tls');
14+
15+
{
16+
// The option is ignored when the `socket` argument is a `net.Socket`.
17+
const socket = new tls.TLSSocket(new net.Socket(), { allowHalfOpen: true });
18+
assert.strictEqual(socket.allowHalfOpen, false);
19+
}
20+
21+
{
22+
// The option is ignored when the `socket` argument is a generic
23+
// `stream.Duplex`.
24+
const duplex = new stream.Duplex({ allowHalfOpen: false });
25+
const socket = new tls.TLSSocket(duplex, { allowHalfOpen: true });
26+
assert.strictEqual(socket.allowHalfOpen, false);
27+
}
28+
29+
{
30+
const socket = new tls.TLSSocket();
31+
assert.strictEqual(socket.allowHalfOpen, false);
32+
}
33+
34+
{
35+
// The option is honored when the `socket` argument is not specified.
36+
const socket = new tls.TLSSocket(undefined, { allowHalfOpen: true });
37+
assert.strictEqual(socket.allowHalfOpen, true);
38+
}

0 commit comments

Comments
 (0)