diff --git a/src/ng/http.js b/src/ng/http.js index fbdc89a50cf0..2b9c7475e40e 100644 --- a/src/ng/http.js +++ b/src/ng/http.js @@ -649,17 +649,12 @@ function $HttpProvider() { transformRequest: defaults.transformRequest, transformResponse: defaults.transformResponse }; - var headers = {}; + var headers = mergeHeaders(requestConfig); extend(config, requestConfig); config.headers = headers; config.method = uppercase(config.method); - extend(headers, - defaults.headers.common, - defaults.headers[lowercase(config.method)], - requestConfig.headers); - var xsrfValue = isSameDomain(config.url, $browser.url()) ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName] : undefined; @@ -673,7 +668,11 @@ function $HttpProvider() { // strip content-type if data is undefined if (isUndefined(config.data)) { - delete headers['Content-Type']; + forEach(headers, function(value, header) { + if (lowercase(header) === 'content-type') { + delete headers[header]; + } + }); } if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) { @@ -729,6 +728,30 @@ function $HttpProvider() { ? resp : $q.reject(resp); } + + function mergeHeaders(config) { + var defHeaders = defaults.headers, + reqHeaders = extend({}, config.headers), + defHeaderName, lowercaseDefHeaderName, reqHeaderName; + + defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]); + + // using for-in instead of forEach to avoid unecessary iteration after header has been found + defaultHeadersIteration: + for (defHeaderName in defHeaders) { + lowercaseDefHeaderName = lowercase(defHeaderName); + + for (reqHeaderName in reqHeaders) { + if (lowercase(reqHeaderName) === lowercaseDefHeaderName) { + continue defaultHeadersIteration; + } + } + + reqHeaders[defHeaderName] = defHeaders[defHeaderName]; + } + + return reqHeaders; + } } $http.pendingRequests = []; diff --git a/test/ng/httpSpec.js b/test/ng/httpSpec.js index 4ddb36611ab0..8f2642f33573 100644 --- a/test/ng/httpSpec.js +++ b/test/ng/httpSpec.js @@ -717,6 +717,21 @@ describe('$http', function() { $httpBackend.flush(); }); + it('should override default headers with custom in a case insensitive manner', function() { + $httpBackend.expect('POST', '/url', 'messageBody', function(headers) { + return headers['accept'] == 'Rewritten' && + headers['content-type'] == 'Content-Type Rewritten' && + headers['Accept'] === undefined && + headers['Content-Type'] === undefined; + }).respond(''); + + $http({url: '/url', method: 'POST', data: 'messageBody', headers: { + 'accept': 'Rewritten', + 'content-type': 'Content-Type Rewritten' + }}); + $httpBackend.flush(); + }); + it('should not set XSRF cookie for cross-domain requests', inject(function($browser) { $browser.cookies('XSRF-TOKEN', 'secret'); $browser.url('http://host.com/base'); @@ -734,7 +749,12 @@ describe('$http', function() { return !headers.hasOwnProperty('Content-Type'); }).respond(''); + $httpBackend.expect('POST', '/url2', undefined, function(headers) { + return !headers.hasOwnProperty('content-type'); + }).respond(''); + $http({url: '/url', method: 'POST'}); + $http({url: '/url2', method: 'POST', headers: {'content-type': 'Rewritten'}}); $httpBackend.flush(); });