@@ -72,6 +72,8 @@ const {
7272 ERR_INVALID_ARG_VALUE ,
7373 ERR_MULTIPLE_CALLBACK ,
7474 ERR_SOCKET_CLOSED ,
75+ ERR_TLS_ALPN_CALLBACK_INVALID_RESULT ,
76+ ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS ,
7577 ERR_TLS_DH_PARAM_SIZE ,
7678 ERR_TLS_HANDSHAKE_TIMEOUT ,
7779 ERR_TLS_INVALID_CONTEXT ,
@@ -108,6 +110,7 @@ const kErrorEmitted = Symbol('error-emitted');
108110const kHandshakeTimeout = Symbol ( 'handshake-timeout' ) ;
109111const kRes = Symbol ( 'res' ) ;
110112const kSNICallback = Symbol ( 'snicallback' ) ;
113+ const kALPNCallback = Symbol ( 'alpncallback' ) ;
111114const kEnableTrace = Symbol ( 'enableTrace' ) ;
112115const kPskCallback = Symbol ( 'pskcallback' ) ;
113116const kPskIdentityHint = Symbol ( 'pskidentityhint' ) ;
@@ -234,6 +237,45 @@ function loadSNI(info) {
234237}
235238
236239
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+
237279function requestOCSP ( socket , info ) {
238280 if ( ! info . OCSPRequest || ! socket . server )
239281 return requestOCSPDone ( socket ) ;
@@ -493,6 +535,7 @@ function TLSSocket(socket, opts) {
493535 this . _controlReleased = false ;
494536 this . secureConnecting = true ;
495537 this . _SNICallback = null ;
538+ this [ kALPNCallback ] = null ;
496539 this . servername = null ;
497540 this . alpnProtocol = null ;
498541 this . authorized = false ;
@@ -787,6 +830,16 @@ TLSSocket.prototype._init = function(socket, wrap) {
787830 ssl . lastHandshakeTime = 0 ;
788831 ssl . handshakes = 0 ;
789832
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+
790843 if ( this . server ) {
791844 if ( this . server . listenerCount ( 'resumeSession' ) > 0 ||
792845 this . server . listenerCount ( 'newSession' ) > 0 ) {
@@ -1165,6 +1218,7 @@ function tlsConnectionListener(rawSocket) {
11651218 rejectUnauthorized : this . rejectUnauthorized ,
11661219 handshakeTimeout : this [ kHandshakeTimeout ] ,
11671220 ALPNProtocols : this . ALPNProtocols ,
1221+ ALPNCallback : this . ALPNCallback ,
11681222 SNICallback : this [ kSNICallback ] || SNICallback ,
11691223 enableTrace : this [ kEnableTrace ] ,
11701224 pauseOnConnect : this . pauseOnConnect ,
@@ -1264,6 +1318,11 @@ function Server(options, listener) {
12641318 this . requestCert = options . requestCert === true ;
12651319 this . rejectUnauthorized = options . rejectUnauthorized !== false ;
12661320
1321+ this . ALPNCallback = options . ALPNCallback ;
1322+ if ( this . ALPNCallback && options . ALPNProtocols ) {
1323+ throw new ERR_TLS_ALPN_CALLBACK_WITH_PROTOCOLS ( ) ;
1324+ }
1325+
12671326 if ( options . sessionTimeout )
12681327 this . sessionTimeout = options . sessionTimeout ;
12691328
0 commit comments