Permalink
Browse files

feat(http): add support for ArrayBuffer

Add the buffer option to store response in ArrayBuffer
Improve the interface to get back response independently of the buffer type
  • Loading branch information...
1 parent 0ccb6e0 commit 1266460386fe34c9e0f022b8cd1be311f3b58b9a @cassand cassand committed with jeffbcross Feb 24, 2016
@@ -28,7 +28,7 @@ export {JSONPBackend, JSONPConnection} from './src/backends/jsonp_backend';
export {CookieXSRFStrategy, XHRBackend, XHRConnection} from './src/backends/xhr_backend';
export {BaseRequestOptions, RequestOptions} from './src/base_request_options';
export {BaseResponseOptions, ResponseOptions} from './src/base_response_options';
-export {ReadyState, RequestMethod, ResponseType} from './src/enums';
+export {ReadyState, RequestMethod, ResponseBuffer, ResponseType} from './src/enums';
export {Headers} from './src/headers';
export {Http, Jsonp} from './src/http';
export {Connection, ConnectionBackend, RequestOptionsArgs, ResponseOptionsArgs, XSRFStrategy} from './src/interfaces';
@@ -12,7 +12,7 @@ import {Observable} from 'rxjs/Observable';
import {Observer} from 'rxjs/Observer';
import {ResponseOptions} from '../base_response_options';
-import {ContentType, ReadyState, RequestMethod, ResponseType} from '../enums';
+import {ContentType, ReadyState, RequestMethod, ResponseType, ResponseBuffer} from '../enums';
import {isPresent, isString} from '../facade/lang';
import {Headers} from '../headers';
import {getResponseURL, isSuccess} from '../http_utils';
@@ -24,6 +24,7 @@ import {BrowserXhr} from './browser_xhr';
const XSSI_PREFIX = /^\)\]\}',?\n/;
+
/**
* Creates connections using `XMLHttpRequest`. Given a fully-qualified
* request, an `XHRConnection` will immediately create an `XMLHttpRequest` object and send the
@@ -51,7 +52,7 @@ export class XHRConnection implements Connection {
_xhr.withCredentials = req.withCredentials;
}
// load event handler
- let onLoad = () => {
+ let onLoad = () => {
// 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)
@@ -108,6 +109,20 @@ export class XHRConnection implements Connection {
req.headers.forEach((values, name) => _xhr.setRequestHeader(name, values.join(',')));
}
+ // Select the correct buffer type to store the response
+ if (isPresent(req.buffer) && isPresent(_xhr.responseType)) switch (req.buffer) {
+ case ResponseBuffer.ArrayBuffer:
+ _xhr.responseType = "arraybuffer";
+ break;
+ case ResponseBuffer.Json:
+ _xhr.responseType = "json";
+ break;
+ default:
+ case ResponseBuffer.Text:
+ _xhr.responseType = "text";
+ break;
+ }
+
_xhr.addEventListener('load', onLoad);
_xhr.addEventListener('error', onError);
@@ -10,7 +10,7 @@ import {Injectable} from '@angular/core';
import {isPresent, isString} from '../src/facade/lang';
-import {RequestMethod} from './enums';
+import {RequestMethod, ResponseBuffer} from './enums';
import {Headers} from './headers';
import {normalizeMethodName} from './http_utils';
import {RequestOptionsArgs} from './interfaces';
@@ -69,7 +69,13 @@ export class RequestOptions {
* Enable use credentials for a {@link Request}.
*/
withCredentials: boolean;
- constructor({method, headers, body, url, search, withCredentials}: RequestOptionsArgs = {}) {
+ /*
+ * Select a buffer to store the response, such as ArrayBuffer, Blob, Json (or Document)
+ */
+ buffer: ResponseBuffer;
+
+ constructor({method, headers, body, url, search, withCredentials, buffer}: RequestOptionsArgs = {}) {
+
this.method = isPresent(method) ? normalizeMethodName(method) : null;
this.headers = isPresent(headers) ? headers : null;
this.body = isPresent(body) ? body : null;
@@ -78,6 +84,7 @@ export class RequestOptions {
(isString(search) ? new URLSearchParams(<string>(search)) : <URLSearchParams>(search)) :
null;
this.withCredentials = isPresent(withCredentials) ? withCredentials : null;
+ this.buffer = isPresent(buffer) ? buffer : null;
}
/**
@@ -117,7 +124,8 @@ export class RequestOptions {
this.search,
withCredentials: isPresent(options) && isPresent(options.withCredentials) ?
options.withCredentials :
- this.withCredentials
+ this.withCredentials,
+ buffer: isPresent(options) && isPresent(options.buffer) ? options.buffer : this.buffer
});
}
}
@@ -44,11 +44,11 @@ import {ResponseOptionsArgs} from './interfaces';
* @experimental
*/
export class ResponseOptions {
- // TODO: ArrayBuffer | FormData | Blob
+ // TODO: FormData | Blob
/**
- * String or Object representing the body of the {@link Response}.
+ * String, Object, ArrayBuffer representing the body of the {@link Response}.
*/
- body: string|Object;
+ body: string | Object | ArrayBuffer;
/**
* Http {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html status code}
* associated with the response.
@@ -61,3 +61,12 @@ export enum ContentType {
BLOB,
ARRAY_BUFFER
}
+
+/*
+ * Define which buffer to use to store the response
+ */
+export enum ResponseBuffer {
+ ArrayBuffer,
+ Json,
+ Text
+}
@@ -35,7 +35,8 @@ function mergeOptions(
search: providedOpts.search,
headers: providedOpts.headers,
body: providedOpts.body,
- withCredentials: providedOpts.withCredentials
+ withCredentials: providedOpts.withCredentials,
+ buffer: providedOpts.buffer
}));
}
if (isPresent(method)) {
@@ -38,4 +38,12 @@ export function getResponseURL(xhr: any): string {
return;
}
+export function stringToArrayBuffer(string: String): ArrayBuffer {
+ let view = new Uint16Array(string.length);
+ for (var i = 0, strLen = string.length; i < strLen; i++) {
+ view[i] = string.charCodeAt(i);
+ }
+ return view.buffer;
+}
+
export {isJsObject} from '../src/facade/lang';
@@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {ReadyState, RequestMethod, ResponseType} from './enums';
+import {ReadyState, RequestMethod, ResponseType, ResponseBuffer} from './enums';
import {Headers} from './headers';
import {Request} from './static_request';
import {URLSearchParams} from './url_search_params';
@@ -52,6 +52,7 @@ export interface RequestOptionsArgs {
headers?: Headers;
body?: any;
withCredentials?: boolean;
+ buffer?: ResponseBuffer;
}
/**
@@ -66,8 +67,8 @@ export interface RequestArgs extends RequestOptionsArgs { url: string; }
* @experimental
*/
export type ResponseOptionsArgs = {
- // TODO: Support Blob, ArrayBuffer, JSON
- body?: string | Object | FormData; status?: number; statusText?: string; headers?: Headers;
+ // TODO: Support Blob, JSON
+ body?: string | Object | FormData | ArrayBuffer; status?: number; statusText?: string; headers?: Headers;
type?: ResponseType;
url?: string;
}
@@ -8,7 +8,7 @@
import {StringWrapper, isPresent} from '../src/facade/lang';
-import {ContentType, RequestMethod} from './enums';
+import {ContentType, RequestMethod, ResponseBuffer} from './enums';
import {Headers} from './headers';
import {normalizeMethodName} from './http_utils';
import {RequestArgs} from './interfaces';
@@ -72,6 +72,8 @@ export class Request {
private contentType: ContentType;
/** Enable use credentials */
withCredentials: boolean;
+ /* Select a buffer to store the response */
+ buffer: ResponseBuffer;
constructor(requestOptions: RequestArgs) {
// TODO: assert that url is present
let url = requestOptions.url;
@@ -95,6 +97,7 @@ export class Request {
// TODO(jeffbcross): implement behavior
this.headers = new Headers(requestOptions.headers);
this.withCredentials = requestOptions.withCredentials;
+ this.buffer = requestOptions.buffer;
}
@@ -12,7 +12,7 @@ import {Json, isString} from '../src/facade/lang';
import {ResponseOptions} from './base_response_options';
import {ResponseType} from './enums';
import {Headers} from './headers';
-import {isJsObject} from './http_utils';
+import {isJsObject, stringToArrayBuffer} from './http_utils';
/**
@@ -83,8 +83,10 @@ export class Response {
* Spec](https://fetch.spec.whatwg.org/#headers-class).
*/
headers: Headers;
- // TODO: Support ArrayBuffer, JSON, FormData, Blob
- private _body: string|Object;
+
+ // TODO: Support FormData, Blob
+ private _body: string | Object | ArrayBuffer;
+
constructor(responseOptions: ResponseOptions) {
this._body = responseOptions.body;
this.status = responseOptions.status;
@@ -96,12 +98,6 @@ export class Response {
}
/**
- * Not yet implemented
- */
- // TODO: Blob return type
- blob(): any { throw new BaseException('"blob()" method not implemented on Response superclass'); }
-
- /**
* Attempts to return body as parsed `JSON` object, or raises an exception.
*/
json(): any {
@@ -110,24 +106,51 @@ export class Response {
jsonResponse = this._body;
} else if (isString(this._body)) {
jsonResponse = Json.parse(<string>this._body);
+ } else if (this._body instanceof ArrayBuffer) {
+ jsonResponse = Json.parse(this.text());
+ } else {
+ jsonResponse = this._body;
}
return jsonResponse;
}
/**
* Returns the body as a string, presuming `toString()` can be called on the response body.
*/
- text(): string { return this._body.toString(); }
+ text(): string {
+ var textResponse: string;
+ if (this._body instanceof ArrayBuffer) {
+ textResponse = String.fromCharCode.apply(null, new Uint16Array(<ArrayBuffer>this._body));
+ } else if (isJsObject(this._body)) {
+ textResponse = Json.stringify(this._body);
+ } else {
+ textResponse = this._body.toString();
+ }
+ return textResponse;
+ }
/**
- * Not yet implemented
+ * Return the body as an ArrayBuffer
*/
- // TODO: ArrayBuffer return type
- arrayBuffer(): any {
- throw new BaseException('"arrayBuffer()" method not implemented on Response superclass');
+ arrayBuffer(): ArrayBuffer {
+ var bufferResponse: ArrayBuffer;
+ if (this._body instanceof ArrayBuffer) {
+ bufferResponse = <ArrayBuffer>this._body;
+ } else {
+ bufferResponse = stringToArrayBuffer(this.text());
+ }
+ return bufferResponse;
}
+
toString(): string {
return `Response with status: ${this.status} ${this.statusText} for URL: ${this.url}`;
}
+
+ /**
+ * Not yet implemented
+ */
+ // TODO: Blob return type
+ blob(): any { throw new BaseException('"blob()" method not implemented on Response superclass'); }
+
}
@@ -35,6 +35,7 @@ class MockBrowserXHR extends BrowserXhr {
send: any;
open: any;
response: any;
+ responseType: string;
responseText: string;
setRequestHeader: any;
callbacks = new Map<string, Function>();
Oops, something went wrong.

0 comments on commit 1266460

Please sign in to comment.