Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions modules/angular2/src/http/backends/xhr_backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ 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';
import {isPresent} from 'angular2/src/facade/lang';
import {Observable} from 'angular2/angular2';
import {isSuccess} from '../http_utils';
/**
* Creates connections using `XMLHttpRequest`. Given a fully-qualified
* request, an `XHRConnection` will immediately create an `XMLHttpRequest` object and send the
Expand All @@ -33,24 +35,31 @@ 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 response = 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 = _xhr.status === 1223 ? 204 : _xhr.status;
let status: number = _xhr.status === 1223 ? 204 : _xhr.status;

// fix status code when it is 0 (0 status is undocumented).
// Occurs when accessing file resources or on Android 4.1 stock browser
// while retrieving files from application cache.
if (status === 0) {
status = response ? 200 : 0;
status = body ? 200 : 0;
}
var responseOptions = new ResponseOptions({body: response, status: status});
var responseOptions = new ResponseOptions({body, status, headers});
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.next(new Response(responseOptions));
// TODO(gdi2290): defer complete if array buffer until done
responseObserver.complete();
let response = new Response(responseOptions);
if (isSuccess(status)) {
responseObserver.next(response);
// TODO(gdi2290): defer complete if array buffer until done
responseObserver.complete();
return;
}
responseObserver.error(response);
};
// error event handler
let onError = (err) => {
Expand Down
12 changes: 12 additions & 0 deletions modules/angular2/src/http/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ export class Headers {
headers, (v, k) => { this._headersMap.set(k, isListLikeIterable(v) ? v : [v]); });
}

static fromResponseHeaderString(headersString: string): Headers {
let responseHeaders = new Headers();
let headerPairs = headersString.trim().split('\n');
headerPairs.forEach((header) => {
let parts = header.trim().split(':');
let key = parts.shift().trim();
var value = parts.join(':').trim();
responseHeaders.append(key, value);
});
return responseHeaders;
}

/**
* Appends a header to existing list of header values for a given header name.
*/
Expand Down
3 changes: 3 additions & 0 deletions modules/angular2/src/http/http_utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {isString} from 'angular2/src/facade/lang';
import {RequestMethods} from './enums';
import {makeTypeError} from 'angular2/src/facade/exceptions';
import {Response} from './static_response';

export function normalizeMethodName(method): RequestMethods {
if (isString(method)) {
Expand All @@ -14,4 +15,6 @@ export function normalizeMethodName(method): RequestMethods {
return method;
}

export const isSuccess = (status: number): boolean => (status >= 200 && status < 300);

export {isJsObject} from 'angular2/src/facade/lang';
85 changes: 79 additions & 6 deletions modules/angular2/test/http/backends/xhr_backend_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class MockBrowserXHR extends BrowserXhr {
setRequestHeader: any;
callbacks = new Map<string, Function>();
status: number;
responseHeaders: string;
constructor() {
super();
var spy = new SpyObject();
Expand All @@ -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); }
Expand Down Expand Up @@ -99,6 +104,7 @@ export function main() {
expect(res.type).toBe(ResponseTypes.Error);
async.done();
});
existingXHRs[0].setStatusCode(200);
existingXHRs[0].dispatchEvent('load');
}));

Expand All @@ -107,7 +113,7 @@ export function main() {
new ResponseOptions({type: ResponseTypes.Error}));
connection.response.subscribe(res => { expect(res.type).toBe(ResponseTypes.Error); },
null, () => { async.done(); });

existingXHRs[0].setStatusCode(200);
existingXHRs[0].dispatchEvent('load');
}));

Expand Down Expand Up @@ -164,15 +170,57 @@ export function main() {
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
new ResponseOptions({status: statusCode}));

connection.response.subscribe(res => {
expect(res.status).toBe(statusCode);
async.done();
});
connection.response.subscribe(
res => {

},
errRes => {
expect(errRes.status).toBe(statusCode);
async.done();
});

existingXHRs[0].setStatusCode(statusCode);
existingXHRs[0].dispatchEvent('load');
}));

it('should call next and complete on 200 codes', inject([AsyncTestCompleter], async => {
var nextCalled = false;
var errorCalled = false;
var statusCode = 200;
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
new ResponseOptions({status: statusCode}));

connection.response.subscribe(
res => {
nextCalled = true;
expect(res.status).toBe(statusCode);
},
errRes => { errorCalled = true; }, () => {
expect(nextCalled).toBe(true);
expect(errorCalled).toBe(false);
async.done();
});

existingXHRs[0].setStatusCode(statusCode);
existingXHRs[0].dispatchEvent('load');
}));

it('should call error and not complete on 300+ codes', inject([AsyncTestCompleter], async => {
var nextCalled = false;
var errorCalled = false;
var statusCode = 301;
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
new ResponseOptions({status: statusCode}));

connection.response.subscribe(res => { nextCalled = true; }, errRes => {
expect(errRes.status).toBe(statusCode);
expect(nextCalled).toBe(false);
async.done();
}, () => { throw 'should not be called'; });

existingXHRs[0].setStatusCode(statusCode);
existingXHRs[0].dispatchEvent('load');
}));
it('should normalize IE\'s 1223 status code into 204', inject([AsyncTestCompleter], async => {
var statusCode = 1223;
var normalizedCode = 204;
Expand Down Expand Up @@ -204,14 +252,39 @@ export function main() {
expect(ress.text()).toBe(responseBody);
async.done();
});
existingXHRs[1].setStatusCode(200);
existingXHRs[1].setResponse(responseBody);
existingXHRs[1].dispatchEvent('load');
});

existingXHRs[0].setStatusCode(200);
existingXHRs[0].setResponseText(responseBody);
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');
}));
});
});
}
19 changes: 19 additions & 0 deletions modules/angular2/test/http/headers_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');

});
});
}