Skip to content

Commit

Permalink
src: optimize ALPN callback
Browse files Browse the repository at this point in the history
It doesn't make sense from a performance perspective to retain an
arraybuffer with the ALPN byte string and look it up as a property on
the JS context object for every TLS handshake.

Store the byte string in the C++ TLSWrap object instead. That's both
a lot faster and a lot simpler.

PR-URL: #44875
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
  • Loading branch information
bnoordhuis authored and danielleadams committed Jan 3, 2023
1 parent d433d34 commit bf17f8d
Show file tree
Hide file tree
Showing 5 changed files with 19 additions and 35 deletions.
7 changes: 2 additions & 5 deletions lib/_tls_wrap.js
Expand Up @@ -786,11 +786,8 @@ TLSSocket.prototype._init = function(socket, wrap) {
ssl.enableCertCb();
}

if (options.ALPNProtocols) {
// Keep reference in secureContext not to be GC-ed
ssl._secureContext.alpnBuffer = options.ALPNProtocols;
ssl.setALPNProtocols(ssl._secureContext.alpnBuffer);
}
if (options.ALPNProtocols)
ssl.setALPNProtocols(options.ALPNProtocols);

if (options.pskCallback && ssl.enablePskCallback) {
validateFunction(options.pskCallback, 'pskCallback');
Expand Down
41 changes: 13 additions & 28 deletions src/crypto/crypto_tls.cc
Expand Up @@ -225,26 +225,17 @@ int SelectALPNCallback(
const unsigned char* in,
unsigned int inlen,
void* arg) {
TLSWrap* w = static_cast<TLSWrap*>(SSL_get_app_data(s));
Environment* env = w->env();
HandleScope handle_scope(env->isolate());
Context::Scope context_scope(env->context());
TLSWrap* w = static_cast<TLSWrap*>(arg);
const std::vector<unsigned char>& alpn_protos = w->alpn_protos_;

Local<Value> alpn_buffer =
w->object()->GetPrivate(
env->context(),
env->alpn_buffer_private_symbol()).FromMaybe(Local<Value>());
if (UNLIKELY(alpn_buffer.IsEmpty()) || !alpn_buffer->IsArrayBufferView())
return SSL_TLSEXT_ERR_NOACK;
if (alpn_protos.empty()) return SSL_TLSEXT_ERR_NOACK;

ArrayBufferViewContents<unsigned char> alpn_protos(alpn_buffer);
int status = SSL_select_next_proto(
const_cast<unsigned char**>(out),
outlen,
alpn_protos.data(),
alpn_protos.length(),
in,
inlen);
int status = SSL_select_next_proto(const_cast<unsigned char**>(out),
outlen,
alpn_protos.data(),
alpn_protos.size(),
in,
inlen);

// According to 3.2. Protocol Selection of RFC7301, fatal
// no_application_protocol alert shall be sent but OpenSSL 1.0.2 does not
Expand Down Expand Up @@ -1529,20 +1520,14 @@ void TLSWrap::SetALPNProtocols(const FunctionCallbackInfo<Value>& args) {
if (args.Length() < 1 || !Buffer::HasInstance(args[0]))
return env->ThrowTypeError("Must give a Buffer as first argument");

ArrayBufferViewContents<uint8_t> protos(args[0].As<ArrayBufferView>());
SSL* ssl = w->ssl_.get();
if (w->is_client()) {
ArrayBufferViewContents<uint8_t> protos(args[0].As<ArrayBufferView>());
CHECK_EQ(0, SSL_set_alpn_protos(ssl, protos.data(), protos.length()));
} else {
CHECK(
w->object()->SetPrivate(
env->context(),
env->alpn_buffer_private_symbol(),
args[0]).FromJust());
// Server should select ALPN protocol from list of advertised by client
SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl),
SelectALPNCallback,
nullptr);
w->alpn_protos_ = std::vector<unsigned char>(
protos.data(), protos.data() + protos.length());
SSL_CTX_set_alpn_select_cb(SSL_get_SSL_CTX(ssl), SelectALPNCallback, w);
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/crypto/crypto_tls.h
Expand Up @@ -34,6 +34,7 @@
#include <openssl/ssl.h>

#include <string>
#include <vector>

namespace node {
namespace crypto {
Expand Down Expand Up @@ -283,6 +284,9 @@ class TLSWrap : public AsyncWrap,
void* cert_cb_arg_ = nullptr;

BIOPointer bio_trace_;

public:
std::vector<unsigned char> alpn_protos_; // Accessed by SelectALPNCallback.
};

} // namespace crypto
Expand Down
1 change: 0 additions & 1 deletion src/env_properties.h
Expand Up @@ -18,7 +18,6 @@
// for the sake of convenience. Strings should be ASCII-only and have a
// "node:" prefix to avoid name clashes with third-party code.
#define PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES(V) \
V(alpn_buffer_private_symbol, "node:alpnBuffer") \
V(arrow_message_private_symbol, "node:arrowMessage") \
V(contextify_context_private_symbol, "node:contextify:context") \
V(contextify_global_private_symbol, "node:contextify:global") \
Expand Down
1 change: 0 additions & 1 deletion typings/internalBinding/util.d.ts
Expand Up @@ -9,7 +9,6 @@ declare namespace InternalUtilBinding {

declare function InternalBinding(binding: 'util'): {
// PER_ISOLATE_PRIVATE_SYMBOL_PROPERTIES, defined in src/env.h
alpn_buffer_private_symbol: 0;
arrow_message_private_symbol: 1;
contextify_context_private_symbol: 2;
contextify_global_private_symbol: 3;
Expand Down

0 comments on commit bf17f8d

Please sign in to comment.