2013-10-22 21:41:21 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
2013-12-13 09:49:37 +00:00
|
|
|
function createXhr(method) {
|
|
|
|
|
// IE8 doesn't support PATCH method, but the ActiveX object does
|
2013-10-22 21:41:21 +00:00
|
|
|
/* global ActiveXObject */
|
2013-12-13 09:49:37 +00:00
|
|
|
return (msie <= 8 && lowercase(method) === 'patch')
|
|
|
|
|
? new ActiveXObject('Microsoft.XMLHTTP')
|
|
|
|
|
: new window.XMLHttpRequest();
|
|
|
|
|
}
|
2011-08-23 20:19:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @ngdoc object
|
2012-06-12 06:49:24 +00:00
|
|
|
* @name ng.$httpBackend
|
2011-08-23 20:19:36 +00:00
|
|
|
* @requires $browser
|
|
|
|
|
* @requires $window
|
|
|
|
|
* @requires $document
|
|
|
|
|
*
|
|
|
|
|
* @description
|
2012-06-12 06:49:24 +00:00
|
|
|
* HTTP backend used by the {@link ng.$http service} that delegates to
|
2012-01-11 03:16:33 +00:00
|
|
|
* XMLHttpRequest object or JSONP and deals with browser incompatibilities.
|
|
|
|
|
*
|
|
|
|
|
* You should never need to use this service directly, instead use the higher-level abstractions:
|
2012-06-12 06:49:24 +00:00
|
|
|
* {@link ng.$http $http} or {@link ngResource.$resource $resource}.
|
2011-12-28 17:26:22 +00:00
|
|
|
*
|
2012-06-12 06:49:24 +00:00
|
|
|
* During testing this implementation is swapped with {@link ngMock.$httpBackend mock
|
2011-12-28 17:26:22 +00:00
|
|
|
* $httpBackend} which can be trained with responses.
|
2011-08-23 20:19:36 +00:00
|
|
|
*/
|
2011-08-16 19:24:53 +00:00
|
|
|
function $HttpBackendProvider() {
|
2011-08-23 20:19:36 +00:00
|
|
|
this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
|
2013-12-13 09:49:37 +00:00
|
|
|
return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
|
2011-08-16 19:24:53 +00:00
|
|
|
}];
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-13 09:49:37 +00:00
|
|
|
function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
|
2013-11-13 20:24:15 +00:00
|
|
|
var ABORTED = -1;
|
|
|
|
|
|
2011-08-23 20:19:36 +00:00
|
|
|
// TODO(vojta): fix the signature
|
2012-08-04 19:11:00 +00:00
|
|
|
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
|
2013-04-24 17:33:08 +00:00
|
|
|
var status;
|
2011-08-23 20:19:36 +00:00
|
|
|
$browser.$$incOutstandingRequestCount();
|
2012-02-24 06:50:02 +00:00
|
|
|
url = url || $browser.url();
|
2011-08-23 20:19:36 +00:00
|
|
|
|
|
|
|
|
if (lowercase(method) == 'jsonp') {
|
2011-12-07 15:53:57 +00:00
|
|
|
var callbackId = '_' + (callbacks.counter++).toString(36);
|
|
|
|
|
callbacks[callbackId] = function(data) {
|
|
|
|
|
callbacks[callbackId].data = data;
|
2011-08-23 20:19:36 +00:00
|
|
|
};
|
|
|
|
|
|
2013-04-24 17:33:08 +00:00
|
|
|
var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
|
2012-01-09 18:36:51 +00:00
|
|
|
function() {
|
2011-12-07 15:53:57 +00:00
|
|
|
if (callbacks[callbackId].data) {
|
|
|
|
|
completeRequest(callback, 200, callbacks[callbackId].data);
|
2011-08-23 20:19:36 +00:00
|
|
|
} else {
|
2013-04-24 17:33:08 +00:00
|
|
|
completeRequest(callback, status || -2);
|
2011-08-23 20:19:36 +00:00
|
|
|
}
|
2014-01-03 15:22:55 +00:00
|
|
|
callbacks[callbackId] = angular.noop;
|
2011-08-23 20:19:36 +00:00
|
|
|
});
|
|
|
|
|
} else {
|
2013-12-13 09:49:37 +00:00
|
|
|
|
|
|
|
|
var xhr = createXhr(method);
|
|
|
|
|
|
2011-08-23 20:19:36 +00:00
|
|
|
xhr.open(method, url, true);
|
|
|
|
|
forEach(headers, function(value, key) {
|
2013-09-03 21:19:38 +00:00
|
|
|
if (isDefined(value)) {
|
|
|
|
|
xhr.setRequestHeader(key, value);
|
|
|
|
|
}
|
2011-08-23 20:19:36 +00:00
|
|
|
});
|
|
|
|
|
|
2011-12-28 17:26:22 +00:00
|
|
|
// In IE6 and 7, this might be called synchronously when xhr.send below is called and the
|
2012-01-07 03:17:31 +00:00
|
|
|
// response is in the cache. the promise api will ensure that to the app code the api is
|
|
|
|
|
// always async
|
2011-12-28 17:26:22 +00:00
|
|
|
xhr.onreadystatechange = function() {
|
2014-01-03 21:19:37 +00:00
|
|
|
// onreadystatechange might get called multiple times with readyState === 4 on mobile webkit caused by
|
2014-01-03 17:23:51 +00:00
|
|
|
// xhrs that are resolved while the app is in the background (see #5426).
|
|
|
|
|
// since calling completeRequest sets the `xhr` variable to null, we just check if it's not null before
|
|
|
|
|
// continuing
|
|
|
|
|
//
|
|
|
|
|
// we can't set xhr.onreadystatechange to undefined or delete it because that breaks IE8 (method=PATCH) and
|
|
|
|
|
// Safari respectively.
|
|
|
|
|
if (xhr && xhr.readyState == 4) {
|
2013-11-13 20:24:15 +00:00
|
|
|
var responseHeaders = null,
|
|
|
|
|
response = null;
|
|
|
|
|
|
|
|
|
|
if(status !== ABORTED) {
|
|
|
|
|
responseHeaders = xhr.getAllResponseHeaders();
|
2014-01-05 07:42:44 +00:00
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
response = ('response' in xhr) ? xhr.response : xhr.responseText;
|
2013-11-13 20:24:15 +00:00
|
|
|
}
|
2013-01-18 22:09:19 +00:00
|
|
|
|
2013-02-24 06:16:35 +00:00
|
|
|
completeRequest(callback,
|
|
|
|
|
status || xhr.status,
|
2013-11-13 20:24:15 +00:00
|
|
|
response,
|
2013-02-24 06:16:35 +00:00
|
|
|
responseHeaders);
|
2011-12-28 17:26:22 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2012-04-03 18:00:52 +00:00
|
|
|
if (withCredentials) {
|
|
|
|
|
xhr.withCredentials = true;
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-04 19:11:00 +00:00
|
|
|
if (responseType) {
|
|
|
|
|
xhr.responseType = responseType;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-17 04:24:35 +00:00
|
|
|
xhr.send(post || null);
|
2013-04-24 17:33:08 +00:00
|
|
|
}
|
2011-08-23 20:19:36 +00:00
|
|
|
|
2013-04-24 17:33:08 +00:00
|
|
|
if (timeout > 0) {
|
2013-04-27 15:22:03 +00:00
|
|
|
var timeoutId = $browserDefer(timeoutRequest, timeout);
|
|
|
|
|
} else if (timeout && timeout.then) {
|
|
|
|
|
timeout.then(timeoutRequest);
|
2011-08-23 20:19:36 +00:00
|
|
|
}
|
2011-11-04 00:14:03 +00:00
|
|
|
|
2011-12-28 17:26:22 +00:00
|
|
|
|
2013-04-27 15:22:03 +00:00
|
|
|
function timeoutRequest() {
|
2013-11-13 20:24:15 +00:00
|
|
|
status = ABORTED;
|
2013-04-27 15:22:03 +00:00
|
|
|
jsonpDone && jsonpDone();
|
|
|
|
|
xhr && xhr.abort();
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-28 17:26:22 +00:00
|
|
|
function completeRequest(callback, status, response, headersString) {
|
2013-04-27 15:22:03 +00:00
|
|
|
// cancel timeout and subsequent timeout promise resolution
|
2013-04-24 17:33:08 +00:00
|
|
|
timeoutId && $browserDefer.cancel(timeoutId);
|
2013-04-27 15:22:03 +00:00
|
|
|
jsonpDone = xhr = null;
|
2013-04-24 17:33:08 +00:00
|
|
|
|
2013-12-27 11:55:02 +00:00
|
|
|
// fix status code when it is 0 (0 status is undocumented).
|
|
|
|
|
// Occurs when accessing file resources.
|
|
|
|
|
// On Android 4.1 stock browser it occurs while retrieving files from application cache.
|
|
|
|
|
status = (status === 0) ? (response ? 200 : 404) : status;
|
2011-11-04 00:14:03 +00:00
|
|
|
|
|
|
|
|
// normalize IE bug (http://bugs.jquery.com/ticket/1450)
|
|
|
|
|
status = status == 1223 ? 204 : status;
|
|
|
|
|
|
2011-12-28 17:26:22 +00:00
|
|
|
callback(status, response, headersString);
|
2011-11-04 00:14:03 +00:00
|
|
|
$browser.$$completeOutstandingRequest(noop);
|
|
|
|
|
}
|
2011-08-23 20:19:36 +00:00
|
|
|
};
|
2012-04-09 21:27:14 +00:00
|
|
|
|
|
|
|
|
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() {
|
2013-11-22 13:57:13 +00:00
|
|
|
script.onreadystatechange = script.onload = script.onerror = null;
|
2012-04-09 21:27:14 +00:00
|
|
|
rawDocument.body.removeChild(script);
|
|
|
|
|
if (done) done();
|
2012-04-10 21:29:49 +00:00
|
|
|
};
|
2012-04-09 21:27:14 +00:00
|
|
|
|
|
|
|
|
script.type = 'text/javascript';
|
|
|
|
|
script.src = url;
|
|
|
|
|
|
2013-11-22 13:22:41 +00:00
|
|
|
if (msie && msie <= 8) {
|
2012-04-09 21:27:14 +00:00
|
|
|
script.onreadystatechange = function() {
|
2013-11-22 13:22:41 +00:00
|
|
|
if (/loaded|complete/.test(script.readyState)) {
|
|
|
|
|
doneWrapper();
|
|
|
|
|
}
|
2012-04-09 21:27:14 +00:00
|
|
|
};
|
|
|
|
|
} else {
|
2013-11-22 13:22:41 +00:00
|
|
|
script.onload = script.onerror = function() {
|
|
|
|
|
doneWrapper();
|
|
|
|
|
};
|
2012-04-09 21:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rawDocument.body.appendChild(script);
|
2013-04-24 17:33:08 +00:00
|
|
|
return doneWrapper;
|
2012-04-10 21:29:49 +00:00
|
|
|
}
|
2011-08-23 20:19:36 +00:00
|
|
|
}
|