X Tutup
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
4 changes: 2 additions & 2 deletions modules/angular2/src/http/backends/jsonp_backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ export class JSONPConnection_ extends JSONPConnection {
_dom.cleanup(script);
if (!this._finished) {
let responseOptions =
new ResponseOptions({body: JSONP_ERR_NO_CALLBACK, type: ResponseTypes.Error});
new ResponseOptions({body: JSONP_ERR_NO_CALLBACK, type: ResponseTypes.Error, url});
if (isPresent(baseResponseOptions)) {
responseOptions = baseResponseOptions.merge(responseOptions);
}
responseObserver.error(new Response(responseOptions));
return;
}

let responseOptions = new ResponseOptions({body: this._responseData});
let responseOptions = new ResponseOptions({body: this._responseData, url});
if (isPresent(this.baseResponseOptions)) {
responseOptions = this.baseResponseOptions.merge(responseOptions);
}
Expand Down
25 changes: 18 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, getResponseURL} 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,33 @@ 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());

let url = getResponseURL(_xhr);

// 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, url});
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
11 changes: 11 additions & 0 deletions modules/angular2/src/http/headers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/src/http/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ function mergeOptions(defaultOpts, providedOpts, method, url): RequestOptions {
var newOptions = defaultOpts;
if (isPresent(providedOpts)) {
// Hack so Dart can used named parameters
newOptions = newOptions.merge(new RequestOptions({
method: providedOpts.method,
url: providedOpts.url,
return newOptions.merge(new RequestOptions({
method: providedOpts.method || method,
url: providedOpts.url || url,
search: providedOpts.search,
headers: providedOpts.headers,
body: providedOpts.body
Expand Down
13 changes: 13 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,16 @@ export function normalizeMethodName(method): RequestMethods {
return method;
}

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

export function getResponseURL(xhr: any): string {
if ('responseURL' in xhr) {
return xhr.responseURL;
}
if (/^X-Request-URL:/m.test(xhr.getAllResponseHeaders())) {
return xhr.getResponseHeader('X-Request-URL');
}
return;
}

export {isJsObject} from 'angular2/src/facade/lang';
123 changes: 117 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,8 @@ class MockBrowserXHR extends BrowserXhr {
setRequestHeader: any;
callbacks = new Map<string, Function>();
status: number;
responseHeaders: string;
responseURL: string;
constructor() {
super();
var spy = new SpyObject();
Expand All @@ -55,6 +57,14 @@ class MockBrowserXHR extends BrowserXhr {

setResponseText(value) { this.responseText = value; }

setResponseURL(value) { this.responseURL = value; }

setResponseHeaders(value) { this.responseHeaders = value; }

getAllResponseHeaders() { return this.responseHeaders || ''; }

getResponseHeader(key) { return Headers.fromResponseHeaderString(this.responseHeaders).get(key); }

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 +109,7 @@ export function main() {
expect(res.type).toBe(ResponseTypes.Error);
async.done();
});
existingXHRs[0].setStatusCode(200);
existingXHRs[0].dispatchEvent('load');
}));

Expand All @@ -107,7 +118,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 +175,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 +257,72 @@ 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');
}));

it('should add the responseURL to the response', inject([AsyncTestCompleter], async => {
var statusCode = 200;
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
new ResponseOptions({status: statusCode}));

connection.response.subscribe(res => {
expect(res.url).toEqual('http://google.com');
async.done();
});

existingXHRs[0].setResponseURL('http://google.com');
existingXHRs[0].setStatusCode(statusCode);
existingXHRs[0].dispatchEvent('load');
}));

it('should add use the X-Request-URL in CORS situations',
inject([AsyncTestCompleter], async => {
var statusCode = 200;
var connection = new XHRConnection(sampleRequest, new MockBrowserXHR(),
new ResponseOptions({status: statusCode}));
var responseHeaders = `X-Request-URL: http://somedomain.com
Foo: Bar`

connection.response.subscribe(res => {
expect(res.url).toEqual('http://somedomain.com');
async.done();
});

existingXHRs[0].setResponseHeaders(responseHeaders);
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');

});
});
}
Loading
X Tutup