mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-20 00:10:26 +00:00
If responseType is defined and the request fails for one reason or another the .response property returned falsy value which caused dereferencing of .responseText. If the responseType was a blob or document then an error was thrown. To prevent this, I'm checking for responseType first and based on that dereferencing .response or .responseText. We need to keep on checking .responseText because that's the original XHR response api that is still needed for IE8 and 9. Closes #1922
156 lines
5.4 KiB
JavaScript
156 lines
5.4 KiB
JavaScript
var XHR = window.XMLHttpRequest || function() {
|
|
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
|
|
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
|
|
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
|
|
throw new Error("This browser does not support XMLHttpRequest.");
|
|
};
|
|
|
|
|
|
/**
|
|
* @ngdoc object
|
|
* @name ng.$httpBackend
|
|
* @requires $browser
|
|
* @requires $window
|
|
* @requires $document
|
|
*
|
|
* @description
|
|
* HTTP backend used by the {@link ng.$http service} that delegates to
|
|
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
|
|
*
|
|
* You should never need to use this service directly, instead use the higher-level abstractions:
|
|
* {@link ng.$http $http} or {@link ngResource.$resource $resource}.
|
|
*
|
|
* During testing this implementation is swapped with {@link ngMock.$httpBackend mock
|
|
* $httpBackend} which can be trained with responses.
|
|
*/
|
|
function $HttpBackendProvider() {
|
|
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
|
return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks,
|
|
$document[0], $window.location.protocol.replace(':', ''));
|
|
}];
|
|
}
|
|
|
|
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) {
|
|
// TODO(vojta): fix the signature
|
|
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
|
$browser.$$incOutstandingRequestCount();
|
|
url = url || $browser.url();
|
|
|
|
if (lowercase(method) == 'jsonp') {
|
|
var callbackId = '_' + (callbacks.counter++).toString(36);
|
|
callbacks[callbackId] = function(data) {
|
|
callbacks[callbackId].data = data;
|
|
};
|
|
|
|
jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
|
function() {
|
|
if (callbacks[callbackId].data) {
|
|
completeRequest(callback, 200, callbacks[callbackId].data);
|
|
} else {
|
|
completeRequest(callback, -2);
|
|
}
|
|
delete callbacks[callbackId];
|
|
});
|
|
} else {
|
|
var xhr = new XHR();
|
|
xhr.open(method, url, true);
|
|
forEach(headers, function(value, key) {
|
|
if (value) xhr.setRequestHeader(key, value);
|
|
});
|
|
|
|
var status;
|
|
|
|
// In IE6 and 7, this might be called synchronously when xhr.send below is called and the
|
|
// response is in the cache. the promise api will ensure that to the app code the api is
|
|
// always async
|
|
xhr.onreadystatechange = function() {
|
|
if (xhr.readyState == 4) {
|
|
var responseHeaders = xhr.getAllResponseHeaders();
|
|
|
|
// TODO(vojta): remove once Firefox 21 gets released.
|
|
// begin: workaround to overcome Firefox CORS http response headers bug
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=608735
|
|
// Firefox already patched in nightly. Should land in Firefox 21.
|
|
|
|
// CORS "simple response headers" http://www.w3.org/TR/cors/
|
|
var value,
|
|
simpleHeaders = ["Cache-Control", "Content-Language", "Content-Type",
|
|
"Expires", "Last-Modified", "Pragma"];
|
|
if (!responseHeaders) {
|
|
responseHeaders = "";
|
|
forEach(simpleHeaders, function (header) {
|
|
var value = xhr.getResponseHeader(header);
|
|
if (value) {
|
|
responseHeaders += header + ": " + value + "\n";
|
|
}
|
|
});
|
|
}
|
|
// end of the workaround.
|
|
|
|
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
|
// response and responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
|
completeRequest(callback,
|
|
status || xhr.status,
|
|
(xhr.responseType ? xhr.response : xhr.responseText),
|
|
responseHeaders);
|
|
}
|
|
};
|
|
|
|
if (withCredentials) {
|
|
xhr.withCredentials = true;
|
|
}
|
|
|
|
if (responseType) {
|
|
xhr.responseType = responseType;
|
|
}
|
|
|
|
xhr.send(post || '');
|
|
|
|
if (timeout > 0) {
|
|
$browserDefer(function() {
|
|
status = -1;
|
|
xhr.abort();
|
|
}, timeout);
|
|
}
|
|
}
|
|
|
|
|
|
function completeRequest(callback, status, response, headersString) {
|
|
// URL_MATCH is defined in src/service/location.js
|
|
var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1];
|
|
|
|
// fix status code for file protocol (it's always 0)
|
|
status = (protocol == 'file') ? (response ? 200 : 404) : status;
|
|
|
|
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
|
status = status == 1223 ? 204 : status;
|
|
|
|
callback(status, response, headersString);
|
|
$browser.$$completeOutstandingRequest(noop);
|
|
}
|
|
};
|
|
|
|
function jsonpReq(url, done) {
|
|
// we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
|
|
// - fetches local scripts via XHR and evals them
|
|
// - adds and immediately removes script elements from the document
|
|
var script = rawDocument.createElement('script'),
|
|
doneWrapper = function() {
|
|
rawDocument.body.removeChild(script);
|
|
if (done) done();
|
|
};
|
|
|
|
script.type = 'text/javascript';
|
|
script.src = url;
|
|
|
|
if (msie) {
|
|
script.onreadystatechange = function() {
|
|
if (/loaded|complete/.test(script.readyState)) doneWrapper();
|
|
};
|
|
} else {
|
|
script.onload = script.onerror = doneWrapper;
|
|
}
|
|
|
|
rawDocument.body.appendChild(script);
|
|
}
|
|
}
|