diff --git a/lib/XMLHttpRequest.js b/lib/XMLHttpRequest.js index 3223020..50f6158 100644 --- a/lib/XMLHttpRequest.js +++ b/lib/XMLHttpRequest.js @@ -102,6 +102,7 @@ function XMLHttpRequest(opts) { var sendFlag = false; // Error flag, used when errors occur or abort is called var errorFlag = false; + var abortedFlag = false; // Event listeners var listeners = {}; @@ -172,10 +173,11 @@ function XMLHttpRequest(opts) { this.open = function(method, url, async, user, password) { this.abort(); errorFlag = false; + abortedFlag = false; // Check for valid request method if (!isAllowedHttpMethod(method)) { - throw "SecurityError: Request method not allowed"; + throw new Error("SecurityError: Request method not allowed"); } settings = { @@ -208,16 +210,14 @@ function XMLHttpRequest(opts) { */ this.setRequestHeader = function(header, value) { if (this.readyState != this.OPENED) { - throw "INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN"; - return false; + throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN"); } if (!isAllowedHttpHeader(header)) { console.warn('Refused to set unsafe header "' + header + '"'); return false; } if (sendFlag) { - throw "INVALID_STATE_ERR: send flag is true"; - return false; + throw new Error("INVALID_STATE_ERR: send flag is true"); } headers[header] = value; return true; @@ -283,11 +283,11 @@ function XMLHttpRequest(opts) { */ this.send = function(data) { if (this.readyState != this.OPENED) { - throw "INVALID_STATE_ERR: connection must be opened before send() is called"; + throw new Error("INVALID_STATE_ERR: connection must be opened before send() is called"); } if (sendFlag) { - throw "INVALID_STATE_ERR: send has already been called"; + throw new Error("INVALID_STATE_ERR: send has already been called"); } var ssl = false, local = false; @@ -312,13 +312,13 @@ function XMLHttpRequest(opts) { break; default: - throw "Protocol not supported."; + throw new Error("Protocol not supported."); } // Load files off the local filesystem (file://) if (local) { if (settings.method !== "GET") { - throw "XMLHttpRequest: Only GET method is supported"; + throw new Error("XMLHttpRequest: Only GET method is supported"); } if (settings.async) { @@ -402,7 +402,6 @@ function XMLHttpRequest(opts) { // Reset error flag errorFlag = false; - // Handle async requests if (settings.async) { // Use the proper protocol @@ -545,7 +544,7 @@ function XMLHttpRequest(opts) { if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) { // If the file returned an error, handle it var errorObj = self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ""); - self.handleError(errorObj); + self.handleError(errorObj, 503); } else { // If the file returned okay, parse its data and move to the DONE state self.status = self.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/, "$1"); @@ -557,9 +556,10 @@ function XMLHttpRequest(opts) { /** * Called when an error is encountered to deal with it. + * @param status {number} HTTP status code to use rather than the default (0) for XHR errors. */ - this.handleError = function(error) { - this.status = 503; + this.handleError = function(error, status) { + this.status = +status || 0; this.statusText = error; this.responseText = error.stack; errorFlag = true; @@ -579,8 +579,7 @@ function XMLHttpRequest(opts) { this.responseText = ""; this.responseXML = ""; - errorFlag = true; - + errorFlag = abortedFlag = true if (this.readyState !== this.UNSENT && (this.readyState !== this.OPENED || sendFlag) && this.readyState !== this.DONE) { @@ -619,11 +618,17 @@ function XMLHttpRequest(opts) { */ this.dispatchEvent = function(event) { if (typeof self["on" + event] === "function") { - self["on" + event](); + if (this.readyState === this.DONE) + setImmediate(function() { self["on" + event]() }) + else + self["on" + event]() } if (event in listeners) { - for (var i = 0, len = listeners[event].length; i < len; i++) { - listeners[event][i].call(self); + for (let i = 0, len = listeners[event].length; i < len; i++) { + if (this.readyState === this.DONE) + setImmediate(function() { listeners[event][i].call(self) }) + else + listeners[event][i].call(self) } } }; @@ -634,18 +639,29 @@ function XMLHttpRequest(opts) { * @param int state New state */ var setState = function(state) { - if (self.readyState !== state) { - self.readyState = state; + if ((self.readyState === state) || (self.readyState === self.UNSENT && abortedFlag)) + return - if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) { - self.dispatchEvent("readystatechange"); - } + self.readyState = state; - if (self.readyState === self.DONE && !errorFlag) { - self.dispatchEvent("load"); - // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie) - self.dispatchEvent("loadend"); - } + if (settings.async || self.readyState < self.OPENED || self.readyState === self.DONE) { + self.dispatchEvent("readystatechange"); + } + + if (self.readyState === self.DONE) { + let fire + + if (abortedFlag) + fire = "abort" + else if (errorFlag) + fire = "error" + else + fire = "load" + + self.dispatchEvent(fire) + + // @TODO figure out InspectorInstrumentation::didLoadXHR(cookie) + self.dispatchEvent("loadend"); } }; }; diff --git a/package.json b/package.json index 1738267..55ab8fd 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xmlhttprequest-ssl", "description": "XMLHttpRequest for Node", - "version": "1.5.5", + "version": "1.5.6", "author": { "name": "Michael de Wit" },