diff --git a/modules/angular2/src/http/backends/xhr_backend.ts b/modules/angular2/src/http/backends/xhr_backend.ts index 897a5259c9..66bf375390 100644 --- a/modules/angular2/src/http/backends/xhr_backend.ts +++ b/modules/angular2/src/http/backends/xhr_backend.ts @@ -2,6 +2,7 @@ import {ConnectionBackend, Connection} from '../interfaces'; import {ReadyStates, RequestMethods, ResponseTypes} from '../enums'; import {Request} from '../static_request'; import {Response} from '../static_response'; +import {Headers} from '../headers'; import {ResponseOptions, BaseResponseOptions} from '../base_response_options'; import {Injectable} from 'angular2/angular2'; import {BrowserXhr} from './browser_xhr'; @@ -34,8 +35,9 @@ export class XHRConnection implements Connection { // 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) - let xhrResponse = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText; + let body = isPresent(_xhr.response) ? _xhr.response : _xhr.responseText; + let headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders()); // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) let status: number = _xhr.status === 1223 ? 204 : _xhr.status; @@ -44,9 +46,9 @@ export class XHRConnection implements Connection { // Occurs when accessing file resources or on Android 4.1 stock browser // while retrieving files from application cache. if (status === 0) { - status = xhrResponse ? 200 : 0; + status = body ? 200 : 0; } - var responseOptions = new ResponseOptions({body: xhrResponse, status: status}); + var responseOptions = new ResponseOptions({body, status, headers}); if (isPresent(baseResponseOptions)) { responseOptions = baseResponseOptions.merge(responseOptions); } diff --git a/modules/angular2/src/http/headers.ts b/modules/angular2/src/http/headers.ts index 03c45aae6e..b6740e642b 100644 --- a/modules/angular2/src/http/headers.ts +++ b/modules/angular2/src/http/headers.ts @@ -54,6 +54,17 @@ export class Headers { headers, (v, k) => { this._headersMap.set(k, isListLikeIterable(v) ? v : [v]); }); } + /** + * Returns a new Headers instance from the given DOMString of Response Headers + */ + static fromResponseHeaderString(headersString: string): Headers { + return headersString.trim() + .split('\n') + .map(val => val.split(':')) + .map(([key, ...parts]) => ([key.trim(), parts.join(':').trim()])) + .reduce((headers, [key, value]) => !headers.set(key, value) && headers, new Headers()); + } + /** * Appends a header to existing list of header values for a given header name. */ diff --git a/modules/angular2/test/http/backends/xhr_backend_spec.ts b/modules/angular2/test/http/backends/xhr_backend_spec.ts index 2ae0206205..eac46518cf 100644 --- a/modules/angular2/test/http/backends/xhr_backend_spec.ts +++ b/modules/angular2/test/http/backends/xhr_backend_spec.ts @@ -40,6 +40,7 @@ class MockBrowserXHR extends BrowserXhr { setRequestHeader: any; callbacks = new Map(); status: number; + responseHeaders: string; constructor() { super(); var spy = new SpyObject(); @@ -55,6 +56,10 @@ class MockBrowserXHR extends BrowserXhr { setResponseText(value) { this.responseText = value; } + setResponseHeaders(value) { this.responseHeaders = value; } + + getAllResponseHeaders() { return this.responseHeaders || ''; } + addEventListener(type: string, cb: Function) { this.callbacks.set(type, cb); } removeEventListener(type: string, cb: Function) { this.callbacks.delete(type); } @@ -256,6 +261,30 @@ export function main() { existingXHRs[0].dispatchEvent('load'); })); + it('should parse response headers and add them to the response', + inject([AsyncTestCompleter], async => { + var statusCode = 200; + var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(), + new ResponseOptions({status: statusCode})); + + let responseHeaderString = + `Date: Fri, 20 Nov 2015 01:45:26 GMT + Content-Type: application/json; charset=utf-8 + Transfer-Encoding: chunked + Connection: keep-alive` + + connection.response.subscribe(res => { + expect(res.headers.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT'); + expect(res.headers.get('Content-Type')).toEqual('application/json; charset=utf-8'); + expect(res.headers.get('Transfer-Encoding')).toEqual('chunked'); + expect(res.headers.get('Connection')).toEqual('keep-alive'); + async.done(); + }); + + existingXHRs[0].setResponseHeaders(responseHeaderString); + existingXHRs[0].setStatusCode(statusCode); + existingXHRs[0].dispatchEvent('load'); + })); }); }); } diff --git a/modules/angular2/test/http/headers_spec.ts b/modules/angular2/test/http/headers_spec.ts index 156ab65b30..0102164f4a 100644 --- a/modules/angular2/test/http/headers_spec.ts +++ b/modules/angular2/test/http/headers_spec.ts @@ -63,4 +63,23 @@ export function main() { }); }); }); + + describe('.fromResponseHeaderString()', () => { + + it('should parse a response header string', () => { + + let responseHeaderString = `Date: Fri, 20 Nov 2015 01:45:26 GMT + Content-Type: application/json; charset=utf-8 + Transfer-Encoding: chunked + Connection: keep-alive`; + + let responseHeaders = Headers.fromResponseHeaderString(responseHeaderString); + + expect(responseHeaders.get('Date')).toEqual('Fri, 20 Nov 2015 01:45:26 GMT'); + expect(responseHeaders.get('Content-Type')).toEqual('application/json; charset=utf-8'); + expect(responseHeaders.get('Transfer-Encoding')).toEqual('chunked'); + expect(responseHeaders.get('Connection')).toEqual('keep-alive'); + + }); + }); }