@@ -72,6 +72,8 @@ const {
72
72
ERR_INVALID_ARG_VALUE ,
73
73
ERR_MULTIPLE_CALLBACK ,
74
74
ERR_SOCKET_CLOSED ,
75
+ ERR_TLS_ALPN_CALLBACK_INVALID_RESULT ,
76
+ ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS ,
75
77
ERR_TLS_DH_PARAM_SIZE ,
76
78
ERR_TLS_HANDSHAKE_TIMEOUT ,
77
79
ERR_TLS_INVALID_CONTEXT ,
@@ -108,6 +110,7 @@ const kErrorEmitted = Symbol('error-emitted');
108
110
const kHandshakeTimeout = Symbol ( 'handshake-timeout' ) ;
109
111
const kRes = Symbol ( 'res' ) ;
110
112
const kSNICallback = Symbol ( 'snicallback' ) ;
113
+ const kALPNCallback = Symbol ( 'alpncallback' ) ;
111
114
const kEnableTrace = Symbol ( 'enableTrace' ) ;
112
115
const kPskCallback = Symbol ( 'pskcallback' ) ;
113
116
const kPskIdentityHint = Symbol ( 'pskidentityhint' ) ;
@@ -234,6 +237,45 @@ function loadSNI(info) {
234
237
}
235
238
236
239
240
+ function callALPNCallback ( protocolsBuffer ) {
241
+ const handle = this ;
242
+ const socket = handle [ owner_symbol ] ;
243
+
244
+ const servername = handle . getServername ( ) ;
245
+
246
+ // Collect all the protocols from the given buffer:
247
+ const protocols = [ ] ;
248
+ let offset = 0 ;
249
+ while ( offset < protocolsBuffer . length ) {
250
+ const protocolLen = protocolsBuffer [ offset ] ;
251
+ offset += 1 ;
252
+
253
+ const protocol = protocolsBuffer . slice ( offset , offset + protocolLen ) ;
254
+ offset += protocolLen ;
255
+
256
+ protocols . push ( protocol . toString ( 'ascii' ) ) ;
257
+ }
258
+
259
+ const selectedProtocol = socket [ kALPNCallback ] ( {
260
+ servername,
261
+ protocols,
262
+ } ) ;
263
+
264
+ // Undefined -> all proposed protocols rejected
265
+ if ( selectedProtocol === undefined ) return undefined ;
266
+
267
+ const protocolIndex = protocols . indexOf ( selectedProtocol ) ;
268
+ if ( protocolIndex === - 1 ) {
269
+ throw new ERR_TLS_ALPN_CALLBACK_INVALID_RESULT ( selectedProtocol , protocols ) ;
270
+ }
271
+ let protocolOffset = 0 ;
272
+ for ( let i = 0 ; i < protocolIndex ; i ++ ) {
273
+ protocolOffset += 1 + protocols [ i ] . length ;
274
+ }
275
+
276
+ return protocolOffset ;
277
+ }
278
+
237
279
function requestOCSP ( socket , info ) {
238
280
if ( ! info . OCSPRequest || ! socket . server )
239
281
return requestOCSPDone ( socket ) ;
@@ -493,6 +535,7 @@ function TLSSocket(socket, opts) {
493
535
this . _controlReleased = false ;
494
536
this . secureConnecting = true ;
495
537
this . _SNICallback = null ;
538
+ this [ kALPNCallback ] = null ;
496
539
this . servername = null ;
497
540
this . alpnProtocol = null ;
498
541
this . authorized = false ;
@@ -787,6 +830,16 @@ TLSSocket.prototype._init = function(socket, wrap) {
787
830
ssl . lastHandshakeTime = 0 ;
788
831
ssl . handshakes = 0 ;
789
832
833
+ if ( options . ALPNCallback ) {
834
+ if ( typeof options . ALPNCallback !== 'function' ) {
835
+ throw new ERR_INVALID_ARG_TYPE ( 'options.ALPNCallback' , 'Function' , options . ALPNCallback ) ;
836
+ }
837
+ assert ( typeof options . ALPNCallback === 'function' ) ;
838
+ this [ kALPNCallback ] = options . ALPNCallback ;
839
+ ssl . ALPNCallback = callALPNCallback ;
840
+ ssl . enableALPNCb ( ) ;
841
+ }
842
+
790
843
if ( this . server ) {
791
844
if ( this . server . listenerCount ( 'resumeSession' ) > 0 ||
792
845
this . server . listenerCount ( 'newSession' ) > 0 ) {
@@ -1165,6 +1218,7 @@ function tlsConnectionListener(rawSocket) {
1165
1218
rejectUnauthorized : this . rejectUnauthorized ,
1166
1219
handshakeTimeout : this [ kHandshakeTimeout ] ,
1167
1220
ALPNProtocols : this . ALPNProtocols ,
1221
+ ALPNCallback : this . ALPNCallback ,
1168
1222
SNICallback : this [ kSNICallback ] || SNICallback ,
1169
1223
enableTrace : this [ kEnableTrace ] ,
1170
1224
pauseOnConnect : this . pauseOnConnect ,
@@ -1264,6 +1318,11 @@ function Server(options, listener) {
1264
1318
this . requestCert = options . requestCert === true ;
1265
1319
this . rejectUnauthorized = options . rejectUnauthorized !== false ;
1266
1320
1321
+ this . ALPNCallback = options . ALPNCallback ;
1322
+ if ( this . ALPNCallback && options . ALPNProtocols ) {
1323
+ throw new ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS ( ) ;
1324
+ }
1325
+
1267
1326
if ( options . sessionTimeout )
1268
1327
this . sessionTimeout = options . sessionTimeout ;
1269
1328
0 commit comments