import XMLHttpRequest from "./xmlhttprequest.js" ; import debugModule from "debug" ; import globalThis from "../globalThis.js" ; import { installTimerFunctions , pick } from "../util.js" ; import { DefaultEventsMap , Emitter } from "@socket.io/component-emitter" ; import { Polling } from "./polling.js" ; import { SocketOptions } from "../socket.js" ; const debug = debugModule ( "engine.io-client:polling-xhr" ) ; /**
 * Empty function
 */ function empty ( ) { } const hasXHR2 = ( function ( ) { const xhr = new XMLHttpRequest ( { xdomain : false } ) ; return null != xhr . responseType ; } ) ( ) ; export class XHR extends Polling { private readonly xd : boolean ; private readonly xs : boolean ; private pollXhr : any ; param { Object } opts api constructor ( opts ) { super ( opts ) ; if ( typeof location !== "undefined" ) { const isSSL = "https:" === location . protocol ; let port = location . port ; if ( ! port ) { port = isSSL ? "443" : "80" ; } this . xd = ( typeof location !== "undefined" && opts . hostname !== location . hostname ) || port !== opts . port ; this . xs = opts . secure !== isSSL ; } /**
         * XHR supports binary
         */ const forceBase64 = opts && opts . forceBase64 ; this . supportsBinary = hasXHR2 && ! forceBase64 ; } param { String } method api request ( opts = { } ) { Object . assign ( opts , { xd : this . xd , xs : this . xs } , this . opts ) ; return new Request ( this . uri ( ) , opts ) ; } param { String } data param { Function } called api doWrite ( data , fn ) { const req = this . request ( { method : "POST" , data : data } ) ; req . on ( "success" , fn ) ; req . on ( "error" , err => { this . onError ( "xhr post error" , err ) ; } ) ; } api doPoll ( ) { debug ( "xhr poll" ) ; const req = this . request ( ) ; req . on ( "data" , this . onData . bind ( this ) ) ; req . on ( "error" , err => { this . onError ( "xhr poll error" , err ) ; } ) ; this . pollXhr = req ; } } export class Request extends Emitter < DefaultEventsMap , DefaultEventsMap > { private readonly opts : { xd ; xs ; } & SocketOptions ; private readonly method : string ; private readonly uri : string ; private readonly async : boolean ; private readonly data : string | ArrayBuffer ; private xhr : any ; private setTimeoutFn : typeof setTimeout ; private index : number ; static requestsCount = 0 ; static requests = { } ; param { Object } options api constructor ( uri , opts ) { super ( ) ; installTimerFunctions ( this , opts ) ; this . opts = opts ; this . method = opts . method || "GET" ; this . uri = uri ; this . async = false !== opts . async ; this . data = undefined !== opts . data ? opts . data : null ; this . create ( ) ; } api create ( ) { const opts = pick ( this . opts , "agent" , "pfx" , "key" , "passphrase" , "cert" , "ca" , "ciphers" , "rejectUnauthorized" , "autoUnref" ) ; opts . xdomain = ! ! this . opts . xd ; opts . xscheme = ! ! this . opts . xs ; const xhr = ( this . xhr = new XMLHttpRequest ( opts ) ) ; try { debug ( "xhr open %s: %s" , this . method , this . uri ) ; xhr . open ( this . method , this . uri , this . async ) ; try { if ( this . opts . extraHeaders ) { xhr . setDisableHeaderCheck && xhr . setDisableHeaderCheck ( true ) ; for ( let i in this . opts . extraHeaders ) { if ( this . opts . extraHeaders . hasOwnProperty ( i ) ) { xhr . setRequestHeader ( i , this . opts . extraHeaders [ i ] ) ; } } } } catch ( e ) { } if ( "POST" === this . method ) { try { xhr . setRequestHeader ( "Content-type" , "text/plain;charset=UTF-8" ) ; } catch ( e ) { } } try { xhr . setRequestHeader ( "Accept" , "*/*" ) ; } catch ( e ) { } if ( "withCredentials" in xhr ) { xhr . withCredentials = this . opts . withCredentials ; } if ( this . opts . requestTimeout ) { xhr . timeout = this . opts . requestTimeout ; } xhr . onreadystatechange = ( ) => { if ( 4 !== xhr . readyState ) return ; if ( 200 === xhr . status || 1223 === xhr . status ) { this . onLoad ( ) ; } else { this . setTimeoutFn ( ( ) => { this . onError ( typeof xhr . status === "number" ? xhr . status : 0 ) ; } , 0 ) ; } } ; debug ( "xhr data %s" , this . data ) ; xhr . send ( this . data ) ; } catch ( e ) { this . setTimeoutFn ( ( ) => { this . onError ( e ) ; } , 0 ) ; return ; } if ( typeof document !== "undefined" ) { this . index = Request . requestsCount ++ ; Request . requests [ this . index ] = this ; } } api onSuccess ( ) { this . emit ( "success" ) ; this . cleanup ( ) ; } api onData ( data ) { this . emit ( "data" , data ) ; this . onSuccess ( ) ; } api onError ( err ) { this . emit ( "error" , err ) ; this . cleanup ( true ) ; } api cleanup ( fromError ? ) { if ( "undefined" === typeof this . xhr || null === this . xhr ) { return ; } this . xhr . onreadystatechange = empty ; if ( fromError ) { try { this . xhr . abort ( ) ; } catch ( e ) { } } if ( typeof document !== "undefined" ) { delete Request . requests [ this . index ] ; } this . xhr = null ; } api onLoad ( ) { const data = this . xhr . responseText ; if ( data !== null ) { this . onData ( data ) ; } } api abort ( ) { this . cleanup ( ) ; } } /**
 * Aborts pending requests when unloading the window. This is needed to prevent
 * memory leaks (e.g. when using IE) and to ensure that no spurious error is
 * emitted.
 */ if ( typeof document !== "undefined" ) { if ( typeof attachEvent === "function" ) { attachEvent ( "onunload" , unloadHandler ) ; } else if ( typeof addEventListener === "function" ) { const terminationEvent = "onpagehide" in globalThis ? "pagehide" : "unload" ; addEventListener ( terminationEvent , unloadHandler , false ) ; } } function unloadHandler ( ) { for ( let i in Request . requests ) { if ( Request . requests . hasOwnProperty ( i ) ) { Request . requests [ i ] . abort ( ) ; } } }