react-native/Libraries/Network/XMLHttpRequest.js

599 lines
16 KiB
JavaScript
Raw Normal View History

/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
const EventTarget = require('event-target-shim');
const RCTNetworking = require('RCTNetworking');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
const base64 = require('base64-js');
const invariant = require('fbjs/lib/invariant');
/* $FlowFixMe(>=0.54.0 site=react_native_oss) This comment suppresses an error
* found when Flow v0.54 was deployed. To see the error delete this comment and
* run Flow. */
const warning = require('fbjs/lib/warning');
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
const BlobManager = require('BlobManager');
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
export type NativeResponseType = 'base64' | 'blob' | 'text';
export type ResponseType =
| ''
| 'arraybuffer'
| 'blob'
| 'document'
| 'json'
| 'text';
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
export type Response = ?Object | string;
type XHRInterceptor = {
requestSent(id: number, url: string, method: string, headers: Object): void,
responseReceived(
id: number,
url: string,
status: number,
headers: Object,
): void,
dataReceived(id: number, data: string): void,
loadingFinished(id: number, encodedDataLength: number): void,
loadingFailed(id: number, error: string): void,
};
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
// The native blob module is optional so inject it here if available.
if (BlobManager.isAvailable) {
BlobManager.addNetworkingHandler();
}
const UNSENT = 0;
const OPENED = 1;
const HEADERS_RECEIVED = 2;
const LOADING = 3;
const DONE = 4;
const SUPPORTED_RESPONSE_TYPES = {
arraybuffer: typeof global.ArrayBuffer === 'function',
blob: typeof global.Blob === 'function',
document: false,
json: true,
text: true,
'': true,
};
const REQUEST_EVENTS = [
'abort',
'error',
'load',
'loadstart',
'progress',
'timeout',
'loadend',
];
const XHR_EVENTS = REQUEST_EVENTS.concat('readystatechange');
class XMLHttpRequestEventTarget extends EventTarget(...REQUEST_EVENTS) {
onload: ?Function;
onloadstart: ?Function;
onprogress: ?Function;
ontimeout: ?Function;
onerror: ?Function;
onabort: ?Function;
onloadend: ?Function;
}
/**
* Shared base for platform-specific XMLHttpRequest implementations.
*/
class XMLHttpRequest extends EventTarget(...XHR_EVENTS) {
static UNSENT: number = UNSENT;
static OPENED: number = OPENED;
static HEADERS_RECEIVED: number = HEADERS_RECEIVED;
static LOADING: number = LOADING;
static DONE: number = DONE;
static _interceptor: ?XHRInterceptor = null;
UNSENT: number = UNSENT;
OPENED: number = OPENED;
HEADERS_RECEIVED: number = HEADERS_RECEIVED;
LOADING: number = LOADING;
DONE: number = DONE;
// EventTarget automatically initializes these to `null`.
onload: ?Function;
onloadstart: ?Function;
onprogress: ?Function;
ontimeout: ?Function;
onerror: ?Function;
onabort: ?Function;
onloadend: ?Function;
onreadystatechange: ?Function;
readyState: number = UNSENT;
responseHeaders: ?Object;
status: number = 0;
timeout: number = 0;
responseURL: ?string;
withCredentials: boolean = true;
upload: XMLHttpRequestEventTarget = new XMLHttpRequestEventTarget();
_requestId: ?number;
_subscriptions: Array<*>;
_aborted: boolean = false;
_cachedResponse: Response;
_hasError: boolean = false;
_headers: Object;
_lowerCaseResponseHeaders: Object;
_method: ?string = null;
_response: string | ?Object;
_responseType: ResponseType;
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
_response: string = '';
_sent: boolean;
_url: ?string = null;
_timedOut: boolean = false;
_trackingName: string = 'unknown';
_incrementalEvents: boolean = false;
static setInterceptor(interceptor: ?XHRInterceptor) {
XMLHttpRequest._interceptor = interceptor;
}
constructor() {
super();
this._reset();
}
_reset(): void {
this.readyState = this.UNSENT;
this.responseHeaders = undefined;
this.status = 0;
delete this.responseURL;
this._requestId = null;
this._cachedResponse = undefined;
this._hasError = false;
this._headers = {};
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this._response = '';
this._responseType = '';
this._sent = false;
this._lowerCaseResponseHeaders = {};
this._clearSubscriptions();
this._timedOut = false;
}
get responseType(): ResponseType {
return this._responseType;
}
set responseType(responseType: ResponseType): void {
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
if (this._sent) {
throw new Error(
"Failed to set the 'responseType' property on 'XMLHttpRequest': The " +
'response type cannot be set after the request has been sent.',
);
}
if (!SUPPORTED_RESPONSE_TYPES.hasOwnProperty(responseType)) {
warning(
false,
`The provided value '${responseType}' is not a valid 'responseType'.`,
);
return;
}
// redboxes early, e.g. for 'arraybuffer' on ios 7
invariant(
SUPPORTED_RESPONSE_TYPES[responseType] || responseType === 'document',
`The provided value '${responseType}' is unsupported in this environment.`,
);
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
if (responseType === 'blob') {
invariant(
BlobManager.isAvailable,
'Native module BlobModule is required for blob support',
);
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
}
this._responseType = responseType;
}
get responseText(): string {
if (this._responseType !== '' && this._responseType !== 'text') {
throw new Error(
"The 'responseText' property is only available if 'responseType' " +
`is set to '' or 'text', but it is '${this._responseType}'.`,
);
}
if (this.readyState < LOADING) {
return '';
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
return this._response;
}
get response(): Response {
const {responseType} = this;
if (responseType === '' || responseType === 'text') {
return this.readyState < LOADING || this._hasError ? '' : this._response;
}
if (this.readyState !== DONE) {
return null;
}
if (this._cachedResponse !== undefined) {
return this._cachedResponse;
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
switch (responseType) {
case 'document':
this._cachedResponse = null;
break;
case 'arraybuffer':
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this._cachedResponse = base64.toByteArray(this._response).buffer;
break;
case 'blob':
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
if (typeof this._response === 'object' && this._response) {
this._cachedResponse = BlobManager.createFromOptions(this._response);
} else {
throw new Error(`Invalid response for blob: ${this._response}`);
}
break;
case 'json':
try {
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this._cachedResponse = JSON.parse(this._response);
} catch (_) {
this._cachedResponse = null;
}
break;
default:
this._cachedResponse = null;
}
return this._cachedResponse;
}
// exposed for testing
__didCreateRequest(requestId: number): void {
this._requestId = requestId;
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.requestSent(
requestId,
this._url || '',
this._method || 'GET',
this._headers,
);
}
// exposed for testing
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
__didUploadProgress(
requestId: number,
progress: number,
total: number,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
): void {
if (requestId === this._requestId) {
this.upload.dispatchEvent({
type: 'progress',
lengthComputable: true,
loaded: progress,
total,
});
}
}
__didReceiveResponse(
requestId: number,
status: number,
responseHeaders: ?Object,
responseURL: ?string,
): void {
if (requestId === this._requestId) {
this.status = status;
this.setResponseHeaders(responseHeaders);
this.setReadyState(this.HEADERS_RECEIVED);
if (responseURL || responseURL === '') {
this.responseURL = responseURL;
} else {
delete this.responseURL;
}
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.responseReceived(
requestId,
responseURL || this._url || '',
status,
responseHeaders || {},
);
}
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
__didReceiveData(requestId: number, response: string): void {
if (requestId !== this._requestId) {
return;
}
this._response = response;
this._cachedResponse = undefined; // force lazy recomputation
this.setReadyState(this.LOADING);
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.dataReceived(requestId, response);
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
}
__didReceiveIncrementalData(
requestId: number,
responseText: string,
progress: number,
total: number,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
) {
if (requestId !== this._requestId) {
return;
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
if (!this._response) {
this._response = responseText;
} else {
this._response += responseText;
}
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.dataReceived(requestId, responseText);
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this.setReadyState(this.LOADING);
this.__didReceiveDataProgress(requestId, progress, total);
}
__didReceiveDataProgress(
requestId: number,
loaded: number,
total: number,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
): void {
if (requestId !== this._requestId) {
return;
}
this.dispatchEvent({
type: 'progress',
lengthComputable: total >= 0,
loaded,
total,
});
}
// exposed for testing
__didCompleteResponse(
requestId: number,
error: string,
timeOutError: boolean,
): void {
if (requestId === this._requestId) {
if (error) {
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
if (this._responseType === '' || this._responseType === 'text') {
this._response = error;
}
this._hasError = true;
if (timeOutError) {
this._timedOut = true;
}
}
this._clearSubscriptions();
this._requestId = null;
this.setReadyState(this.DONE);
if (error) {
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.loadingFailed(requestId, error);
} else {
XMLHttpRequest._interceptor &&
XMLHttpRequest._interceptor.loadingFinished(
requestId,
this._response.length,
);
}
}
}
_clearSubscriptions(): void {
(this._subscriptions || []).forEach(sub => {
if (sub) {
sub.remove();
}
});
this._subscriptions = [];
}
getAllResponseHeaders(): ?string {
if (!this.responseHeaders) {
// according to the spec, return null if no response has been received
return null;
}
const headers = this.responseHeaders || {};
return Object.keys(headers)
.map(headerName => {
return headerName + ': ' + headers[headerName];
})
.join('\r\n');
}
getResponseHeader(header: string): ?string {
const value = this._lowerCaseResponseHeaders[header.toLowerCase()];
return value !== undefined ? value : null;
}
setRequestHeader(header: string, value: any): void {
if (this.readyState !== this.OPENED) {
throw new Error('Request has not been opened');
}
this._headers[header.toLowerCase()] = String(value);
}
/**
* Custom extension for tracking origins of request.
*/
setTrackingName(trackingName: string): XMLHttpRequest {
this._trackingName = trackingName;
return this;
}
open(method: string, url: string, async: ?boolean): void {
/* Other optional arguments are not supported yet */
if (this.readyState !== this.UNSENT) {
throw new Error('Cannot open, already sending');
}
if (async !== undefined && !async) {
// async is default
throw new Error('Synchronous http requests are not supported');
}
if (!url) {
throw new Error('Cannot load an empty url');
}
this._method = method.toUpperCase();
this._url = url;
this._aborted = false;
this.setReadyState(this.OPENED);
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
send(data: any): void {
if (this.readyState !== this.OPENED) {
throw new Error('Request has not been opened');
}
if (this._sent) {
throw new Error('Request has already been sent');
}
this._sent = true;
const incrementalEvents =
this._incrementalEvents || !!this.onreadystatechange || !!this.onprogress;
this._subscriptions.push(
RCTNetworking.addListener('didSendNetworkData', args =>
this.__didUploadProgress(...args),
),
);
this._subscriptions.push(
RCTNetworking.addListener('didReceiveNetworkResponse', args =>
this.__didReceiveResponse(...args),
),
);
this._subscriptions.push(
RCTNetworking.addListener('didReceiveNetworkData', args =>
this.__didReceiveData(...args),
),
);
this._subscriptions.push(
RCTNetworking.addListener('didReceiveNetworkIncrementalData', args =>
this.__didReceiveIncrementalData(...args),
),
);
this._subscriptions.push(
RCTNetworking.addListener('didReceiveNetworkDataProgress', args =>
this.__didReceiveDataProgress(...args),
),
);
this._subscriptions.push(
RCTNetworking.addListener('didCompleteNetworkResponse', args =>
this.__didCompleteResponse(...args),
),
);
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
let nativeResponseType: NativeResponseType = 'text';
if (this._responseType === 'arraybuffer') {
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
nativeResponseType = 'base64';
}
Implement Blob support for XMLHttpRequest Summary: This PR is a followup to https://github.com/facebook/react-native/pull/11417 and should be merged after that one is merged. 1. Add support for creating blobs from strings, not just other blobs 1. Add the `File` constructor which is a superset of `Blob` 1. Add the `FileReader` API which can be used to read blobs as strings or data url (base64) 1. Add support for uploading and downloading blobs via `XMLHttpRequest` and `fetch` 1. Add ability to download local files on Android so you can do `fetch(uri).then(res => res.blob())` to get a blob for a local file (iOS already supported this) 1. Clone the repo https://github.com/expo/react-native-blob-test 1. Change the `package.json` and update `react-native` dependency to point to this branch, then run `npm install` 1. Run the `server.js` file with `node server.js` 1. Open the `index.common.js` file and replace `localhost` with your computer's IP address 1. Start the packager with `yarn start` and run the app on your device If everything went well, all tests should pass, and you should see a screen like this: ![screen shot 2017-06-08 at 7 53 08 pm](https://user-images.githubusercontent.com/1174278/26936407-435bbce2-4c8c-11e7-9ae3-eb104e46961e.png)! Pull to rerun all tests or tap on specific test to re-run it [GENERAL] [FEATURE] [Blob] - Implement blob support for XMLHttpRequest Closes https://github.com/facebook/react-native/pull/11573 Reviewed By: shergin Differential Revision: D6082054 Pulled By: hramos fbshipit-source-id: cc9c174fdefdfaf6e5d9fd7b300120a01a50e8c1
2018-01-26 17:06:14 +00:00
if (this._responseType === 'blob') {
nativeResponseType = 'blob';
}
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
invariant(this._method, 'Request method needs to be defined.');
invariant(this._url, 'Request URL needs to be defined.');
RCTNetworking.sendRequest(
this._method,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this._trackingName,
this._url,
this._headers,
data,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
nativeResponseType,
incrementalEvents,
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
this.timeout,
this.__didCreateRequest.bind(this),
this.withCredentials,
);
}
abort(): void {
this._aborted = true;
if (this._requestId) {
RCTNetworking.abortRequest(this._requestId);
}
// only call onreadystatechange if there is something to abort,
// below logic is per spec
if (
!(
this.readyState === this.UNSENT ||
(this.readyState === this.OPENED && !this._sent) ||
this.readyState === this.DONE
)
) {
this._reset();
this.setReadyState(this.DONE);
}
// Reset again after, in case modified in handler
this._reset();
}
setResponseHeaders(responseHeaders: ?Object): void {
this.responseHeaders = responseHeaders || null;
const headers = responseHeaders || {};
this._lowerCaseResponseHeaders = Object.keys(headers).reduce(
(lcaseHeaders, headerName) => {
lcaseHeaders[headerName.toLowerCase()] = headers[headerName];
return lcaseHeaders;
},
{},
);
}
setReadyState(newState: number): void {
this.readyState = newState;
this.dispatchEvent({type: 'readystatechange'});
if (newState === this.DONE) {
if (this._aborted) {
this.dispatchEvent({type: 'abort'});
} else if (this._hasError) {
if (this._timedOut) {
this.dispatchEvent({type: 'timeout'});
} else {
this.dispatchEvent({type: 'error'});
}
} else {
this.dispatchEvent({type: 'load'});
}
this.dispatchEvent({type: 'loadend'});
}
}
/* global EventListener */
addEventListener(type: string, listener: EventListener): void {
// If we dont' have a 'readystatechange' event handler, we don't
// have to send repeated LOADING events with incremental updates
// to responseText, which will avoid a bunch of native -> JS
// bridge traffic.
Add responseType as a concept to RCTNetworking, send binary data as base64 Summary: In preparation for Blob support (wherein binary XHR and WebSocket responses can be retained as native data blobs on the native side and JS receives a web-like opaque Blob object), this change makes RCTNetworking aware of the responseType that JS requests. A `xhr.responseType` of `''` or `'text'` translates to a native response type of `'text'`. A `xhr.responseType` of `arraybuffer` translates to a native response type of `base64`, as we currently lack an API to transmit TypedArrays directly to JS. This is analogous to how the WebSocket module already works, and it's a lot more versatile and much less brittle than converting a JS *string* back to a TypedArray, which is what's currently going on. Now that we don't always send text down to JS, JS consumers might still want to get progress updates about a binary download. This is what the `'progress'` event is designed for, so this change also implements that. This change also follows the XHR spec with regards to `xhr.response` and `xhr.responseText`: - if the response type is `'text'`, `xhr.responseText` can be peeked at by the JS consumer. It will be updated periodically as the download progresses, so long as there's either an `onreadystatechange` or `onprogress` handler on the XHR. - if the response type is not `'text'`, `xhr.responseText` can't be accessed and `xhr.response` remains `null` until the response is fully received. `'progress'` events containing response details (total bytes, downloaded so far) are dispatched if there's an `onprogress` handler. Once Blobs are landed, `xhr.responseType` of `'blob'` will correspond to the same native response type, which will cause RCTNetworking to only send a blob ID down to JS, which can then create a `Blob` object from that for consumers. Closes https://github.com/facebook/react-native/pull/8324 Reviewed By: javache Differential Revision: D3508822 Pulled By: davidaurelio fbshipit-source-id: 441b2d4d40265b6036559c3ccb9fa962999fa5df
2016-07-13 11:53:54 +00:00
if (type === 'readystatechange' || type === 'progress') {
this._incrementalEvents = true;
}
super.addEventListener(type, listener);
}
}
module.exports = XMLHttpRequest;