@@ -24,6 +24,7 @@ const { format, parse } = require('./extension');
2424const { toBuffer } = require ( './buffer-util' ) ;
2525
2626const readyStates = [ 'CONNECTING' , 'OPEN' , 'CLOSING' , 'CLOSED' ] ;
27+ const subprotocolRegex = / ^ [ ! # $ % & ' * + \- . 0 - 9 A - Z ^ _ ` | a - z ~ ] + $ / ;
2728const protocolVersions = [ 8 , 13 ] ;
2829const closeTimeout = 30 * 1000 ;
2930
@@ -61,11 +62,15 @@ class WebSocket extends EventEmitter {
6162 this . _isServer = false ;
6263 this . _redirects = 0 ;
6364
64- if ( Array . isArray ( protocols ) ) {
65- protocols = protocols . join ( ', ' ) ;
66- } else if ( typeof protocols === 'object' && protocols !== null ) {
67- options = protocols ;
68- protocols = undefined ;
65+ if ( protocols === undefined ) {
66+ protocols = [ ] ;
67+ } else if ( ! Array . isArray ( protocols ) ) {
68+ if ( typeof protocols === 'object' && protocols !== null ) {
69+ options = protocols ;
70+ protocols = [ ] ;
71+ } else {
72+ protocols = [ protocols ] ;
73+ }
6974 }
7075
7176 initAsClient ( this , address , protocols , options ) ;
@@ -558,7 +563,7 @@ module.exports = WebSocket;
558563 *
559564 * @param {WebSocket } websocket The client to initialize
560565 * @param {(String|URL) } address The URL to which to connect
561- * @param {String } [ protocols] The subprotocols
566+ * @param {Array } protocols The subprotocols
562567 * @param {Object } [options] Connection options
563568 * @param {(Boolean|Object) } [options.perMessageDeflate=true] Enable/disable
564569 * permessage-deflate
@@ -623,6 +628,7 @@ function initAsClient(websocket, address, protocols, options) {
623628 const defaultPort = isSecure ? 443 : 80 ;
624629 const key = randomBytes ( 16 ) . toString ( 'base64' ) ;
625630 const get = isSecure ? https . get : http . get ;
631+ const protocolSet = new Set ( ) ;
626632 let perMessageDeflate ;
627633
628634 opts . createConnection = isSecure ? tlsConnect : netConnect ;
@@ -651,8 +657,22 @@ function initAsClient(websocket, address, protocols, options) {
651657 [ PerMessageDeflate . extensionName ] : perMessageDeflate . offer ( )
652658 } ) ;
653659 }
654- if ( protocols ) {
655- opts . headers [ 'Sec-WebSocket-Protocol' ] = protocols ;
660+ if ( protocols . length ) {
661+ for ( const protocol of protocols ) {
662+ if (
663+ typeof protocol !== 'string' ||
664+ ! subprotocolRegex . test ( protocol ) ||
665+ protocolSet . has ( protocol )
666+ ) {
667+ throw new SyntaxError (
668+ 'An invalid or duplicated subprotocol was specified'
669+ ) ;
670+ }
671+
672+ protocolSet . add ( protocol ) ;
673+ }
674+
675+ opts . headers [ 'Sec-WebSocket-Protocol' ] = protocols . join ( ',' ) ;
656676 }
657677 if ( opts . origin ) {
658678 if ( opts . protocolVersion < 13 ) {
@@ -739,15 +759,16 @@ function initAsClient(websocket, address, protocols, options) {
739759 }
740760
741761 const serverProt = res . headers [ 'sec-websocket-protocol' ] ;
742- const protList = ( protocols || '' ) . split ( / , * / ) ;
743762 let protError ;
744763
745- if ( ! protocols && serverProt ) {
746- protError = 'Server sent a subprotocol but none was requested' ;
747- } else if ( protocols && ! serverProt ) {
764+ if ( serverProt ) {
765+ if ( ! protocolSet . size ) {
766+ protError = 'Server sent a subprotocol but none was requested' ;
767+ } else if ( ! protocolSet . has ( serverProt ) ) {
768+ protError = 'Server sent an invalid subprotocol' ;
769+ }
770+ } else if ( protocolSet . size ) {
748771 protError = 'Server sent no subprotocol' ;
749- } else if ( serverProt && ! protList . includes ( serverProt ) ) {
750- protError = 'Server sent an invalid subprotocol' ;
751772 }
752773
753774 if ( protError ) {
0 commit comments