mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-23 09:50:24 +00:00
A workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=608735 In FF getAllResponseHeaders() returns null if the request is the result of CORS. Tried to format the code so that when a FF patch is released and gains enough traction it can easily be selected and deleted. Heavily inspired by jQuery's patch for the same bug. This patch falls short of passing through custom headers but covers all of the "simple response headers" in the spec at http://www.w3.org/TR/cors/ This commit should get reverted once Firefox 21 gets out. Closes #1468
152 lines
5.2 KiB
JavaScript
152 lines
5.2 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.
|
|
|
|
completeRequest(callback, status || xhr.status, 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);
|
|
}
|
|
}
|