import { Transport } from "../transport.js" ; import parseqs from "parseqs" ; import yeast from "yeast" ; import { pick } from "../util.js" ; import { defaultBinaryType , nextTick , usingBrowserWebSocket , WebSocket } from "./websocket-constructor.js" ; import debugModule from "debug" ; import { encodePacket } from "engine.io-parser" ; const debug = debugModule ( "engine.io-client:websocket" ) ; const isReactNative = typeof navigator !== "undefined" && typeof navigator . product === "string" && navigator . product . toLowerCase ( ) === "reactnative" ; export class WS extends Transport { private ws : any ; api api constructor ( opts ) { super ( opts ) ; this . supportsBinary = ! opts . forceBase64 ; } api get name ( ) { return "websocket" ; } api doOpen ( ) { if ( ! this . check ( ) ) { return ; } const uri = this . uri ( ) ; const protocols = this . opts . protocols ; const opts = isReactNative ? { } : pick ( this . opts , "agent" , "perMessageDeflate" , "pfx" , "key" , "passphrase" , "cert" , "ca" , "ciphers" , "rejectUnauthorized" , "localAddress" , "protocolVersion" , "origin" , "maxPayload" , "family" , "checkServerIdentity" ) ; if ( this . opts . extraHeaders ) { opts . headers = this . opts . extraHeaders ; } try { this . ws = usingBrowserWebSocket && ! isReactNative ? protocols ? new WebSocket ( uri , protocols ) : new WebSocket ( uri ) : new WebSocket ( uri , protocols , opts ) ; } catch ( err ) { return this . emit ( "error" , err ) ; } this . ws . binaryType = this . socket . binaryType || defaultBinaryType ; this . addEventListeners ( ) ; } api addEventListeners ( ) { this . ws . onopen = ( ) => { if ( this . opts . autoUnref ) { this . ws . _socket . unref ( ) ; } this . onOpen ( ) ; } ; this . ws . onclose = this . onClose . bind ( this ) ; this . ws . onmessage = ev => this . onData ( ev . data ) ; this . ws . onerror = e => this . onError ( "websocket error" , e ) ; } param { Array } array api write ( packets ) { this . writable = false ; for ( let i = 0 ; i < packets . length ; i ++ ) { const packet = packets [ i ] ; const lastPacket = i === packets . length - 1 ; encodePacket ( packet , this . supportsBinary , data => { const opts = { } ; if ( ! usingBrowserWebSocket ) { if ( packet . options ) { opts . compress = packet . options . compress ; } if ( this . opts . perMessageDeflate ) { const len = "string" === typeof data ? Buffer . byteLength ( data ) : data . length ; if ( len < this . opts . perMessageDeflate . threshold ) { opts . compress = false ; } } } try { if ( usingBrowserWebSocket ) { this . ws . send ( data ) ; } else { this . ws . send ( data , opts ) ; } } catch ( e ) { debug ( "websocket closed before onclose event" ) ; } if ( lastPacket ) { nextTick ( ( ) => { this . writable = true ; this . emit ( "drain" ) ; } , this . setTimeoutFn ) ; } } ) ; } } api doClose ( ) { if ( typeof this . ws !== "undefined" ) { this . ws . close ( ) ; this . ws = null ; } } api uri ( ) { let query = this . query || { } ; const schema = this . opts . secure ? "wss" : "ws" ; let port = "" ; if ( this . opts . port && ( ( "wss" === schema && Number ( this . opts . port ) !== 443 ) || ( "ws" === schema && Number ( this . opts . port ) !== 80 ) ) ) { port = ":" + this . opts . port ; } if ( this . opts . timestampRequests ) { query [ this . opts . timestampParam ] = yeast ( ) ; } if ( ! this . supportsBinary ) { query . b64 = 1 ; } const encodedQuery = parseqs . encode ( query ) ; const ipv6 = this . opts . hostname . indexOf ( ":" ) !== - 1 ; return ( schema + "://" + ( ipv6 ? "[" + this . opts . hostname + "]" : this . opts . hostname ) + port + this . opts . path + ( encodedQuery . length ? "?" + encodedQuery : "" ) ) ; } return { Boolean } api check ( ) { return ( ! ! WebSocket && ! ( "__initialize" in WebSocket && this . name === WS . prototype . name ) ) ; } }