private _fire()

in src/SimpleWebRequest.ts [302:497]


    private _fire(): void {
        this._xhr = new XMLHttpRequest();
        this._xhrRequestHeaders = {};

        // xhr.open() can throw an exception for a CSP violation.
        const openError = attempt(() => {
            // Apparently you're supposed to open the connection before adding events to it.  If you don't, the node.js implementation
            // of XHR actually calls this.abort() at the start of open()...  Bad implementations, hooray.
            this._xhr!!!.open(this._action, this._url, true);
        });

        if (openError) {
            this._respond(openError.toString());
            return;
        }

        if (this._options.timeout) {
            const timeoutSupported = timeoutSupportStatus;
            // Use manual timer if we don't know about timeout support
            if (timeoutSupported !== FeatureSupportStatus.Supported) {
                this._assertAndClean(!this._requestTimeoutTimer, 'Double-fired requestTimeoutTimer');
                this._requestTimeoutTimer = SimpleWebRequestOptions.setTimeout(() => {
                    this._requestTimeoutTimer = undefined;

                    this._timedOut = true;
                    this.abort();
                }, this._options.timeout);
            }

            // This is our first completed request. Use it for feature detection
            if (timeoutSupported === FeatureSupportStatus.Supported || timeoutSupported <= FeatureSupportStatus.Detecting) {
                // timeout and ontimeout are part of the XMLHttpRequest Level 2 spec, should be supported in most modern browsers
                this._xhr.timeout = this._options.timeout;
                this._xhr.ontimeout = () => {
                    timeoutSupportStatus = FeatureSupportStatus.Supported;
                    if (timeoutSupported !== FeatureSupportStatus.Supported) {
                    // When this request initially fired we didn't know about support, bail & let the fallback method handle this
                        return;
                    }
                    this._timedOut = true;
                    // Set aborted flag to match simple timer approach, which aborts the request and results in an _respond call
                    this._aborted = true;
                    this._respond('TimedOut');
                };
            }
        }

        const onLoadErrorSupported = onLoadErrorSupportStatus;

        // Use onreadystatechange if we don't know about onload support or it onload is not supported
        if (onLoadErrorSupported !== FeatureSupportStatus.Supported) {
            if (onLoadErrorSupported === FeatureSupportStatus.Unknown) {
                // Set global status to detecting, leave local state so we can set a timer on finish
                onLoadErrorSupportStatus = FeatureSupportStatus.Detecting;
            }
            this._xhr.onreadystatechange = () => {
                if (!this._xhr) {
                    return;
                }

                if (this._xhr.readyState === 3 && this._options.streamingDownloadProgress && !this._aborted) {
                    // This callback may result in cancelling the connection, so keep that in mind with any handling after it
                    // if we decide to stop using the return after this someday down the line.  i.e. this._xhr may be undefined
                    // when we come back from this call.
                    this._options.streamingDownloadProgress(this._xhr.responseText);
                    return;
                }

                if (this._xhr.readyState !== 4) {
                    // Wait for it to finish
                    return;
                }

                // This is the first request completed (unknown status when fired, detecting now), use it for detection
                if (onLoadErrorSupported === FeatureSupportStatus.Unknown &&
                        onLoadErrorSupportStatus === FeatureSupportStatus.Detecting) {
                    // If onload hasn't fired within 10 seconds of completion, detect as not supported
                    SimpleWebRequestOptions.setTimeout(() => {
                        if (onLoadErrorSupportStatus !== FeatureSupportStatus.Supported) {
                            onLoadErrorSupportStatus = FeatureSupportStatus.NotSupported;
                        }
                    }, 10000);
                }

                this._respond();
            };
        } else if (this._options.streamingDownloadProgress) {
            // If we support onload and such, but have a streaming download handler, still trap the oRSC.
            this._xhr.onreadystatechange = () => {
                if (!this._xhr) {
                    return;
                }

                if (this._xhr.readyState === 3 && this._options.streamingDownloadProgress && !this._aborted) {
                    // This callback may result in cancelling the connection, so keep that in mind with any handling after it
                    // if we decide to stop using the return after this someday down the line.  i.e. this._xhr may be undefined
                    // when we come back from this call.
                    this._options.streamingDownloadProgress(this._xhr.responseText);
                }
            };
        }

        if (onLoadErrorSupported !== FeatureSupportStatus.NotSupported) {
            // onLoad and onError are part of the XMLHttpRequest Level 2 spec, should be supported in most modern browsers
            this._xhr.onload = () => {
                onLoadErrorSupportStatus = FeatureSupportStatus.Supported;
                if (onLoadErrorSupported !== FeatureSupportStatus.Supported) {
                    // When this request initially fired we didn't know about support, bail & let the fallback method handle this
                    return;
                }
                this._respond();
            };
            this._xhr.onerror = () => {
                onLoadErrorSupportStatus = FeatureSupportStatus.Supported;
                if (onLoadErrorSupported !== FeatureSupportStatus.Supported) {
                    // When this request initially fired we didn't know about support, bail & let the fallback method handle this
                    return;
                }
                this._respond();
            };
        }

        this._xhr.onabort = () => {
            // If the browser cancels us (page navigation or whatever), it sometimes calls both the readystatechange and this,
            // so make sure we know that this is an abort.
            this._aborted = true;
            this._respond('Aborted');
        };

        if (this._xhr.upload && this._options.onProgress) {
            this._xhr.upload.onprogress = this._options.onProgress as (ev: ProgressEvent) => void;
        }

        const acceptType = this._options.acceptType || 'json';
        const responseType = this._options.customResponseType || SimpleWebRequestBase._getResponseType(acceptType);
        const responseTypeError = attempt(() => {
            this._xhr!!!.responseType = responseType;
        });

        if (responseTypeError) {
            // WebKit added support for the json responseType value on 09/03/2013
            // https://bugs.webkit.org/show_bug.cgi?id=73648.
            // Versions of Safari prior to 7 and Android 4 Samsung borwsers are
            // known to throw an Error when setting the value "json" as the response type.
            //
            // The json response type can be ignored if not supported, because JSON payloads
            // are handled by mapBody anyway
            if (responseType !== 'json') {
                throw responseTypeError;
            }
        }

        SimpleWebRequest._setRequestHeader(this._xhr, this._xhrRequestHeaders, 'Accept', SimpleWebRequestBase.mapContentType(acceptType));

        this._xhr.withCredentials = !!this._options.withCredentials;

        const nextHeaders = this.getRequestHeaders();
        // check/process headers
        let headersCheck: Dictionary<boolean> = {};

        Object.keys(nextHeaders).forEach(key => {
            const value = nextHeaders[key];
            const headerLower = key.toLowerCase();

            if (headerLower === 'content-type') {
                this._assertAndClean(false, `Don't set Content-Type with options.headers -- use it with the options.contentType property`);
                return;
            }

            if (headerLower === 'accept') {
                this._assertAndClean(false, `Don't set Accept with options.headers -- use it with the options.acceptType property`);
                return;
            }

            this._assertAndClean(!headersCheck[headerLower], `Setting duplicate header key: ${ headersCheck[headerLower] } and ${ key }`);

            if (value === undefined || value === null) {
                console.warn(`Tried to set header "${ key }" on request with "${ value }" value, header will be dropped`);
                return;
            }

            headersCheck[headerLower] = true;

            SimpleWebRequest._setRequestHeader(this._xhr!!!, this._xhrRequestHeaders!!!, key, value);
        });

        if (this._options.sendData) {
            const contentType = SimpleWebRequestBase.mapContentType(this._options.contentType || 'json');
            SimpleWebRequest._setRequestHeader(this._xhr, this._xhrRequestHeaders, 'Content-Type', contentType);

            const sendData = SimpleWebRequestBase.mapBody(this._options.sendData, contentType);
            this._xhr.send(sendData as BodyInit);
        } else {
            this._xhr.send();
        }
    }