@@ -27,6 +27,8 @@ var stream = require('stream');
2727var END_OF_FILE = 42 ;
2828var assert = require ( 'assert' ) . ok ;
2929
30+ var NPN_ENABLED = process . binding ( 'constants' ) . NPN_ENABLED ;
31+
3032var debug ;
3133if ( process . env . NODE_DEBUG && / t l s / . test ( process . env . NODE_DEBUG ) ) {
3234 debug = function ( a ) { console . error ( 'TLS:' , a ) ; } ;
@@ -38,10 +40,36 @@ if (process.env.NODE_DEBUG && /tls/.test(process.env.NODE_DEBUG)) {
3840var Connection = null ;
3941try {
4042 Connection = process . binding ( 'crypto' ) . Connection ;
43+ exports . NPN_ENABLED = NPN_ENABLED ;
4144} catch ( e ) {
4245 throw new Error ( 'node.js not compiled with openssl crypto support.' ) ;
4346}
4447
48+ // Convert protocols array into valid OpenSSL protocols list
49+ // ("\x06spdy/2\x08http/1.1\x08http/1.0")
50+ function convertNPNProtocols ( NPNProtocols , out ) {
51+ // If NPNProtocols is Array - translate it into buffer
52+ if ( Array . isArray ( NPNProtocols ) ) {
53+ var buff = new Buffer ( NPNProtocols . reduce ( function ( p , c ) {
54+ return p + 1 + Buffer . byteLength ( c ) ;
55+ } , 0 ) ) ;
56+
57+ NPNProtocols . reduce ( function ( offset , c ) {
58+ var clen = Buffer . byteLength ( c ) ;
59+ buff [ offset ] = clen ;
60+ buff . write ( c , offset + 1 ) ;
61+
62+ return offset + 1 + clen ;
63+ } , 0 ) ;
64+
65+ NPNProtocols = buff ;
66+ }
67+
68+ // If it's already a Buffer - store it
69+ if ( Buffer . isBuffer ( NPNProtocols ) ) {
70+ out . NPNProtocols = NPNProtocols ;
71+ }
72+ } ;
4573
4674// Base class of both CleartextStream and EncryptedStream
4775function CryptoStream ( pair ) {
@@ -437,12 +465,14 @@ EncryptedStream.prototype._pusher = function(pool, offset, length) {
437465 * Provides a pair of streams to do encrypted communication.
438466 */
439467
440- function SecurePair ( credentials , isServer , requestCert , rejectUnauthorized ) {
468+ function SecurePair ( credentials , isServer , requestCert , rejectUnauthorized ,
469+ NPNProtocols ) {
441470 if ( ! ( this instanceof SecurePair ) ) {
442471 return new SecurePair ( credentials ,
443472 isServer ,
444473 requestCert ,
445- rejectUnauthorized ) ;
474+ rejectUnauthorized ,
475+ NPNProtocols ) ;
446476 }
447477
448478 var self = this ;
@@ -478,6 +508,10 @@ function SecurePair(credentials, isServer, requestCert, rejectUnauthorized) {
478508 this . _requestCert ,
479509 this . _rejectUnauthorized ) ;
480510
511+ if ( NPN_ENABLED && NPNProtocols ) {
512+ this . _ssl . setNPNProtocols ( NPNProtocols ) ;
513+ this . npnProtocol = null ;
514+ }
481515
482516 /* Acts as a r/w stream to the cleartext side of the stream. */
483517 this . cleartext = new CleartextStream ( this ) ;
@@ -588,6 +622,10 @@ SecurePair.prototype._cycle = function(depth) {
588622
589623SecurePair . prototype . _maybeInitFinished = function ( ) {
590624 if ( this . _ssl && ! this . _secureEstablished && this . _ssl . isInitFinished ( ) ) {
625+ if ( NPN_ENABLED ) {
626+ this . npnProtocol = this . _ssl . getNegotiatedProtocol ( ) ;
627+ }
628+
591629 this . _secureEstablished = true ;
592630 debug ( 'secure established' ) ;
593631 this . emit ( 'secure' ) ;
@@ -745,13 +783,15 @@ function Server(/* [options], listener */) {
745783 var pair = new SecurePair ( creds ,
746784 true ,
747785 self . requestCert ,
748- self . rejectUnauthorized ) ;
786+ self . rejectUnauthorized ,
787+ self . NPNProtocols ) ;
749788
750789 var cleartext = pipe ( pair , socket ) ;
751790 cleartext . _controlReleased = false ;
752791
753792 pair . on ( 'secure' , function ( ) {
754793 pair . cleartext . authorized = false ;
794+ pair . cleartext . npnProtocol = pair . npnProtocol ;
755795 if ( ! self . requestCert ) {
756796 cleartext . _controlReleased = true ;
757797 self . emit ( 'secureConnection' , pair . cleartext , pair . encrypted ) ;
@@ -812,6 +852,7 @@ Server.prototype.setOptions = function(options) {
812852 if ( options . ciphers ) this . ciphers = options . ciphers ;
813853 if ( options . secureProtocol ) this . secureProtocol = options . secureProtocol ;
814854 if ( options . secureOptions ) this . secureOptions = options . secureOptions ;
855+ if ( options . NPNProtocols ) convertNPNProtocols ( options . NPNProtocols , this ) ;
815856} ;
816857
817858
@@ -854,7 +895,9 @@ exports.connect = function(port /* host, options, cb */) {
854895 var sslcontext = crypto . createCredentials ( options ) ;
855896 //sslcontext.context.setCiphers('RC4-SHA:AES128-SHA:AES256-SHA');
856897
857- var pair = new SecurePair ( sslcontext , false ) ;
898+ convertNPNProtocols ( options . NPNProtocols , this ) ;
899+ var pair = new SecurePair ( sslcontext , false , true , false ,
900+ this . NPNProtocols ) ;
858901
859902 var cleartext = pipe ( pair , socket ) ;
860903
@@ -863,6 +906,8 @@ exports.connect = function(port /* host, options, cb */) {
863906 pair . on ( 'secure' , function ( ) {
864907 var verifyError = pair . _ssl . verifyError ( ) ;
865908
909+ cleartext . npnProtocol = pair . npnProtocol ;
910+
866911 if ( verifyError ) {
867912 cleartext . authorized = false ;
868913 cleartext . authorizationError = verifyError ;
0 commit comments