mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-28 04:00:23 +00:00
Previously if an app was running from file:// origin we would always return either http 200 or 404 depending on whether the response was present. This changes the behavior so that we do this only if the protocol of the request (not the origin) is file:// and only if the status code is 0. Closes #4436 Closes #4587 Closes #4514
161 lines
5.2 KiB
JavaScript
161 lines
5.2 KiB
JavaScript
'use strict';
|
|
|
|
var XHR = window.XMLHttpRequest || function() {
|
|
/* global ActiveXObject */
|
|
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 minErr('$httpBackend')('noxhr', "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]);
|
|
}];
|
|
}
|
|
|
|
function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument) {
|
|
var ABORTED = -1;
|
|
|
|
// TODO(vojta): fix the signature
|
|
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
|
var status;
|
|
$browser.$$incOutstandingRequestCount();
|
|
url = url || $browser.url();
|
|
|
|
if (lowercase(method) == 'jsonp') {
|
|
var callbackId = '_' + (callbacks.counter++).toString(36);
|
|
callbacks[callbackId] = function(data) {
|
|
callbacks[callbackId].data = data;
|
|
};
|
|
|
|
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
|
function() {
|
|
if (callbacks[callbackId].data) {
|
|
completeRequest(callback, 200, callbacks[callbackId].data);
|
|
} else {
|
|
completeRequest(callback, status || -2);
|
|
}
|
|
delete callbacks[callbackId];
|
|
});
|
|
} else {
|
|
var xhr = new XHR();
|
|
xhr.open(method, url, true);
|
|
forEach(headers, function(value, key) {
|
|
if (isDefined(value)) {
|
|
xhr.setRequestHeader(key, value);
|
|
}
|
|
});
|
|
|
|
// 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 = null,
|
|
response = null;
|
|
|
|
if(status !== ABORTED) {
|
|
responseHeaders = xhr.getAllResponseHeaders();
|
|
response = xhr.responseType ? xhr.response : xhr.responseText;
|
|
}
|
|
|
|
// responseText is the old-school way of retrieving response (supported by IE8 & 9)
|
|
// response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
|
|
completeRequest(callback,
|
|
status || xhr.status,
|
|
response,
|
|
responseHeaders);
|
|
}
|
|
};
|
|
|
|
if (withCredentials) {
|
|
xhr.withCredentials = true;
|
|
}
|
|
|
|
if (responseType) {
|
|
xhr.responseType = responseType;
|
|
}
|
|
|
|
xhr.send(post || null);
|
|
}
|
|
|
|
if (timeout > 0) {
|
|
var timeoutId = $browserDefer(timeoutRequest, timeout);
|
|
} else if (timeout && timeout.then) {
|
|
timeout.then(timeoutRequest);
|
|
}
|
|
|
|
|
|
function timeoutRequest() {
|
|
status = ABORTED;
|
|
jsonpDone && jsonpDone();
|
|
xhr && xhr.abort();
|
|
}
|
|
|
|
function completeRequest(callback, status, response, headersString) {
|
|
var protocol = urlResolve(url).protocol;
|
|
|
|
// cancel timeout and subsequent timeout promise resolution
|
|
timeoutId && $browserDefer.cancel(timeoutId);
|
|
jsonpDone = xhr = null;
|
|
|
|
// fix status code for file protocol (it's always 0)
|
|
status = (protocol == 'file' && status === 0) ? (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() {
|
|
script.onreadystatechange = script.onload = script.onerror = null;
|
|
rawDocument.body.removeChild(script);
|
|
if (done) done();
|
|
};
|
|
|
|
script.type = 'text/javascript';
|
|
script.src = url;
|
|
|
|
if (msie && msie <= 8) {
|
|
script.onreadystatechange = function() {
|
|
if (/loaded|complete/.test(script.readyState)) {
|
|
doneWrapper();
|
|
}
|
|
};
|
|
} else {
|
|
script.onload = script.onerror = function() {
|
|
doneWrapper();
|
|
};
|
|
}
|
|
|
|
rawDocument.body.appendChild(script);
|
|
return doneWrapper;
|
|
}
|
|
}
|