From a0c58a6b5ca7e7f444ce778bdcb2bd3355f87ada Mon Sep 17 00:00:00 2001 From: Dzmitry Shylovich Date: Sat, 12 Nov 2016 12:15:12 +0300 Subject: [PATCH] fix(http): correctly handle response body for 204 status code closes #12830 fixes #12393 --- .../@angular/http/src/backends/xhr_backend.ts | 39 ++++++++++++------- .../http/test/backends/xhr_backend_spec.ts | 18 ++++++++- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/modules/@angular/http/src/backends/xhr_backend.ts b/modules/@angular/http/src/backends/xhr_backend.ts index b4171c1852..7fedc3def3 100644 --- a/modules/@angular/http/src/backends/xhr_backend.ts +++ b/modules/@angular/http/src/backends/xhr_backend.ts @@ -8,6 +8,8 @@ import {Injectable} from '@angular/core'; import {__platform_browser_private__} from '@angular/platform-browser'; +import {Observable} from 'rxjs/Observable'; +import {Observer} from 'rxjs/Observer'; import {ResponseOptions} from '../base_response_options'; import {ContentType, ReadyState, RequestMethod, ResponseContentType, ResponseType} from '../enums'; import {Headers} from '../headers'; @@ -16,8 +18,6 @@ import {Connection, ConnectionBackend, XSRFStrategy} from '../interfaces'; import {Request} from '../static_request'; import {Response} from '../static_response'; import {BrowserXhr} from './browser_xhr'; -import {Observable} from 'rxjs/Observable'; -import {Observer} from 'rxjs/Observer'; const XSSI_PREFIX = /^\)\]\}',?\n/; @@ -49,19 +49,24 @@ export class XHRConnection implements Connection { } // load event handler const onLoad = () => { - // responseText is the old-school way of retrieving response (supported by IE8 & 9) - // response/responseType properties were introduced in ResourceLoader Level2 spec (supported - // by IE10) - let body = _xhr.response === undefined ? _xhr.responseText : _xhr.response; - // Implicitly strip a potential XSSI prefix. - if (typeof body === 'string') body = body.replace(XSSI_PREFIX, ''); - const headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders()); - - const url = getResponseURL(_xhr); - // normalize IE9 bug (http://bugs.jquery.com/ticket/1450) let status: number = _xhr.status === 1223 ? 204 : _xhr.status; + let body: any = null; + + // HTTP 204 means no content + if (status !== 204) { + // responseText is the old-school way of retrieving response (supported by IE8 & 9) + // response/responseType properties were introduced in ResourceLoader Level2 spec + // (supported by IE10) + body = _xhr.response == null ? _xhr.responseText : _xhr.response; + + // Implicitly strip a potential XSSI prefix. + if (typeof body === 'string') { + body = body.replace(XSSI_PREFIX, ''); + } + } + // 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. @@ -69,7 +74,11 @@ export class XHRConnection implements Connection { status = body ? 200 : 0; } - const statusText = _xhr.statusText || 'OK'; + const headers: Headers = Headers.fromResponseHeaderString(_xhr.getAllResponseHeaders()); + + const url: string = getResponseURL(_xhr); + + const statusText: string = _xhr.statusText || 'OK'; let responseOptions = new ResponseOptions({body, status, headers, statusText, url}); if (baseResponseOptions != null) { @@ -86,7 +95,7 @@ export class XHRConnection implements Connection { responseObserver.error(response); }; // error event handler - const onError = (err: any) => { + const onError = (err: ErrorEvent) => { let responseOptions = new ResponseOptions({ body: err, type: ResponseType.Error, @@ -138,7 +147,7 @@ export class XHRConnection implements Connection { }); } - setDetectedContentType(req: any /** TODO #9100 */, _xhr: XMLHttpRequest) { + setDetectedContentType(req: any /** TODO Request */, _xhr: any /** XMLHttpRequest */) { // Skip if a custom Content-Type header is provided if (req.headers != null && req.headers.get('Content-Type') != null) { return; diff --git a/modules/@angular/http/test/backends/xhr_backend_spec.ts b/modules/@angular/http/test/backends/xhr_backend_spec.ts index 0199ca1abc..988b74a477 100644 --- a/modules/@angular/http/test/backends/xhr_backend_spec.ts +++ b/modules/@angular/http/test/backends/xhr_backend_spec.ts @@ -9,7 +9,6 @@ import {Injectable} from '@angular/core'; import {AsyncTestCompleter, SpyObject, afterEach, beforeEach, beforeEachProviders, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {__platform_browser_private__} from '@angular/platform-browser'; - import {BrowserXhr} from '../../src/backends/browser_xhr'; import {CookieXSRFStrategy, XHRBackend, XHRConnection} from '../../src/backends/xhr_backend'; import {BaseRequestOptions, RequestOptions} from '../../src/base_request_options'; @@ -486,6 +485,7 @@ export function main() { existingXHRs[0].setStatusCode(statusCode); existingXHRs[0].dispatchEvent('load'); })); + it('should normalize IE\'s 1223 status code into 204', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const statusCode = 1223; @@ -502,6 +502,22 @@ export function main() { existingXHRs[0].dispatchEvent('load'); })); + it('should ignore response body for 204 status code', + inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { + const statusCode = 204; + const connection = new XHRConnection( + sampleRequest, new MockBrowserXHR(), new ResponseOptions({status: statusCode})); + + connection.response.subscribe((res: Response) => { + expect(res.text()).toBe(''); + async.done(); + }); + + existingXHRs[0].setStatusCode(statusCode); + existingXHRs[0].setResponseText('Doge'); + existingXHRs[0].dispatchEvent('load'); + })); + it('should normalize responseText and response', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => { const responseBody = 'Doge';