Skip to content

Commit 9a83a24

Browse files
tls: allow empty ALPNProtocols and keep C++ CHECK_EQ
Empty ALPN buffer/array means skip ALPN (same as historical behavior for []). Zero-length protocol entries and malformed wire buffers still throw from convertALPNProtocols. Revert the C++ THROW_ERR_INVALID_ARG_VALUE back to CHECK_EQ: after JS validation, a non-zero SSL_set_alpn_protos return is an internal invariant failure, not user-facing input. Refs: #65076
1 parent 10909d9 commit 9a83a24

3 files changed

Lines changed: 26 additions & 13 deletions

File tree

lib/tls.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,11 +276,8 @@ function convertProtocols(protocols) {
276276
function validateALPNBuffer(buffer) {
277277
// Wire format: sequence of <len><proto> where len is 1 byte (1-255) and
278278
// exactly len bytes follow, no trailing bytes, no zero-length entries.
279+
// Empty buffer is allowed and means skip ALPN (same as []).
279280
let offset = 0;
280-
if (buffer.length === 0) {
281-
throw new ERR_INVALID_ARG_VALUE('ALPNProtocols', buffer,
282-
'must not be empty');
283-
}
284281
while (offset < buffer.length) {
285282
const len = buffer[offset];
286283
if (len === 0) {

src/crypto/crypto_tls.cc

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,9 +1699,7 @@ void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
16991699
ArrayBufferViewContents<uint8_t> protos(args[0].As<ArrayBufferView>());
17001700
SSL* ssl = w->ssl_.get();
17011701
if (w->is_client()) {
1702-
if (SSL_set_alpn_protos(ssl, protos.data(), protos.length()) != 0) {
1703-
return THROW_ERR_INVALID_ARG_VALUE(env, "Invalid ALPNProtocols value");
1704-
}
1702+
CHECK_EQ(0, SSL_set_alpn_protos(ssl, protos.data(), protos.length()));
17051703
} else {
17061704
w->alpn_protos_ = std::vector<unsigned char>(
17071705
protos.data(), protos.data() + protos.length());

test/parallel/test-tls-alpn-protocols-validation.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ if (!common.hasCrypto)
66
const assert = require('assert');
77
const tls = require('tls');
88

9-
// Array with empty string should throw (client and server paths via convertALPNProtocols)
9+
// Array with empty string should throw (zero-length protocol entry)
1010
assert.throws(() => {
1111
const out = {};
1212
tls.convertALPNProtocols([''], out);
@@ -46,22 +46,40 @@ assert.throws(() => {
4646
code: 'ERR_INVALID_ARG_VALUE',
4747
});
4848

49-
// Empty buffer should throw
50-
assert.throws(() => {
49+
// Empty array means skip ALPN (allowed)
50+
{
51+
const out = {};
52+
tls.convertALPNProtocols([], out);
53+
assert.ok(Buffer.isBuffer(out.ALPNProtocols));
54+
assert.strictEqual(out.ALPNProtocols.length, 0);
55+
}
56+
57+
// Empty buffer means skip ALPN (allowed; same as [])
58+
{
5159
const out = {};
5260
tls.convertALPNProtocols(Buffer.alloc(0), out);
53-
}, {
54-
code: 'ERR_INVALID_ARG_VALUE',
55-
});
61+
assert.ok(Buffer.isBuffer(out.ALPNProtocols));
62+
assert.strictEqual(out.ALPNProtocols.length, 0);
63+
}
64+
65+
// Empty Uint8Array means skip ALPN
66+
{
67+
const out = {};
68+
tls.convertALPNProtocols(new Uint8Array(0), out);
69+
assert.ok(Buffer.isBuffer(out.ALPNProtocols));
70+
assert.strictEqual(out.ALPNProtocols.length, 0);
71+
}
5672

5773
// Valid inputs should not throw
5874
{
5975
const out = {};
6076
tls.convertALPNProtocols(['h2', 'http/1.1'], out);
77+
assert.ok(out.ALPNProtocols.length > 0);
6178
}
6279
{
6380
const out = {};
6481
tls.convertALPNProtocols(Buffer.from([
6582
2, 0x61, 0x62, 8, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31,
6683
]), out);
84+
assert.strictEqual(out.ALPNProtocols.length, 12);
6785
}

0 commit comments

Comments
 (0)