XHR: support typed arrays for request payloads
Summary: Support `xhr.send(data)` for typed arrays. **Test plan:** run UIExplorer example on iOS and Android. Closes https://github.com/facebook/react-native/pull/11904 Differential Revision: D4425551 fbshipit-source-id: 065ab5873407a406ca4a831068ab138606c3361b
This commit is contained in:
parent
116916b98d
commit
16bb6e87ba
|
@ -61,6 +61,7 @@
|
||||||
"SyntheticEvent": false,
|
"SyntheticEvent": false,
|
||||||
"$Either": false,
|
"$Either": false,
|
||||||
"$All": false,
|
"$All": false,
|
||||||
|
"$ArrayBufferView": false,
|
||||||
"$Tuple": false,
|
"$Tuple": false,
|
||||||
"$Supertype": false,
|
"$Supertype": false,
|
||||||
"$Subtype": false,
|
"$Subtype": false,
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
var React = require('react');
|
var React = require('react');
|
||||||
|
|
||||||
var XHRExampleDownload = require('./XHRExampleDownload');
|
var XHRExampleDownload = require('./XHRExampleDownload');
|
||||||
|
var XHRExampleBinaryUpload = require('./XHRExampleBinaryUpload');
|
||||||
var XHRExampleFormData = require('./XHRExampleFormData');
|
var XHRExampleFormData = require('./XHRExampleFormData');
|
||||||
var XHRExampleHeaders = require('./XHRExampleHeaders');
|
var XHRExampleHeaders = require('./XHRExampleHeaders');
|
||||||
var XHRExampleFetch = require('./XHRExampleFetch');
|
var XHRExampleFetch = require('./XHRExampleFetch');
|
||||||
|
@ -40,6 +41,11 @@ exports.examples = [{
|
||||||
render() {
|
render() {
|
||||||
return <XHRExampleDownload/>;
|
return <XHRExampleDownload/>;
|
||||||
}
|
}
|
||||||
|
}, {
|
||||||
|
title: 'multipart/form-data Upload',
|
||||||
|
render() {
|
||||||
|
return <XHRExampleBinaryUpload/>;
|
||||||
|
}
|
||||||
}, {
|
}, {
|
||||||
title: 'multipart/form-data Upload',
|
title: 'multipart/form-data Upload',
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2013-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* The examples provided by Facebook are for non-commercial testing and
|
||||||
|
* evaluation purposes only.
|
||||||
|
*
|
||||||
|
* Facebook reserves all rights not expressly granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||||
|
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const React = require('react');
|
||||||
|
const ReactNative = require('react-native');
|
||||||
|
const {
|
||||||
|
Alert,
|
||||||
|
Linking,
|
||||||
|
Picker,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableHighlight,
|
||||||
|
View,
|
||||||
|
} = ReactNative;
|
||||||
|
|
||||||
|
const BINARY_TYPES = {
|
||||||
|
String,
|
||||||
|
ArrayBuffer,
|
||||||
|
Int8Array,
|
||||||
|
Uint8Array,
|
||||||
|
Uint8ClampedArray,
|
||||||
|
Int16Array,
|
||||||
|
Uint16Array,
|
||||||
|
Int32Array,
|
||||||
|
Uint32Array,
|
||||||
|
Float32Array,
|
||||||
|
Float64Array,
|
||||||
|
DataView,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SAMPLE_TEXT = `
|
||||||
|
I am the spirit that negates.
|
||||||
|
And rightly so, for all that comes to be
|
||||||
|
Deserves to perish wretchedly;
|
||||||
|
'Twere better nothing would begin.
|
||||||
|
Thus everything that that your terms, sin,
|
||||||
|
Destruction, evil represent—
|
||||||
|
That is my proper element.
|
||||||
|
|
||||||
|
--Faust, JW Goethe
|
||||||
|
`;
|
||||||
|
|
||||||
|
|
||||||
|
class XHRExampleBinaryUpload extends React.Component {
|
||||||
|
|
||||||
|
static handlePostTestServerUpload(xhr: XMLHttpRequest) {
|
||||||
|
if (xhr.status !== 200) {
|
||||||
|
Alert.alert(
|
||||||
|
'Upload failed',
|
||||||
|
'Expected HTTP 200 OK response, got ' + xhr.status
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!xhr.responseText) {
|
||||||
|
Alert.alert(
|
||||||
|
'Upload failed',
|
||||||
|
'No response payload.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var index = xhr.responseText.indexOf('http://www.posttestserver.com/');
|
||||||
|
if (index === -1) {
|
||||||
|
Alert.alert(
|
||||||
|
'Upload failed',
|
||||||
|
'Invalid response payload.'
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var url = xhr.responseText.slice(index).split('\n')[0];
|
||||||
|
console.log('Upload successful: ' + url);
|
||||||
|
Linking.openURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = {
|
||||||
|
type: 'Uint8Array',
|
||||||
|
};
|
||||||
|
|
||||||
|
_upload = () => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', 'http://posttestserver.com/post.php');
|
||||||
|
xhr.onload = () => XHRExampleBinaryUpload.handlePostTestServerUpload(xhr);
|
||||||
|
xhr.setRequestHeader('Content-Type', 'text/plain');
|
||||||
|
|
||||||
|
if (this.state.type === 'String') {
|
||||||
|
xhr.send(SAMPLE_TEXT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const arrayBuffer = new ArrayBuffer(256);
|
||||||
|
const asBytes = new Uint8Array(arrayBuffer);
|
||||||
|
for (let i = 0; i < SAMPLE_TEXT.length; i++) {
|
||||||
|
asBytes[i] = SAMPLE_TEXT.charCodeAt(i);
|
||||||
|
}
|
||||||
|
if (this.state.type === 'ArrayBuffer') {
|
||||||
|
xhr.send(arrayBuffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.state.type === 'Uint8Array') {
|
||||||
|
xhr.send(asBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TypedArrayClass = BINARY_TYPES[this.state.type];
|
||||||
|
xhr.send(new TypedArrayClass(arrayBuffer));
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<Text>Upload 255 bytes as...</Text>
|
||||||
|
<Picker
|
||||||
|
selectedValue={this.state.type}
|
||||||
|
onValueChange={(type) => this.setState({type})}>
|
||||||
|
{Object.keys(BINARY_TYPES).map((type) => (
|
||||||
|
<Picker.Item key={type} label={type} value={type} />
|
||||||
|
))}
|
||||||
|
</Picker>
|
||||||
|
<View style={styles.uploadButton}>
|
||||||
|
<TouchableHighlight onPress={this._upload}>
|
||||||
|
<View style={styles.uploadButtonBox}>
|
||||||
|
<Text style={styles.uploadButtonLabel}>Upload</Text>
|
||||||
|
</View>
|
||||||
|
</TouchableHighlight>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
uploadButton: {
|
||||||
|
marginTop: 16,
|
||||||
|
},
|
||||||
|
uploadButtonBox: {
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 12,
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
borderRadius: 4,
|
||||||
|
},
|
||||||
|
uploadButtonLabel: {
|
||||||
|
color: 'white',
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: '500',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = XHRExampleBinaryUpload;
|
|
@ -38,6 +38,8 @@ const {
|
||||||
View,
|
View,
|
||||||
} = ReactNative;
|
} = ReactNative;
|
||||||
|
|
||||||
|
const XHRExampleBinaryUpload = require('./XHRExampleBinaryUpload');
|
||||||
|
|
||||||
const PAGE_SIZE = 20;
|
const PAGE_SIZE = 20;
|
||||||
|
|
||||||
class XHRExampleFormData extends React.Component {
|
class XHRExampleFormData extends React.Component {
|
||||||
|
@ -109,31 +111,7 @@ class XHRExampleFormData extends React.Component {
|
||||||
xhr.open('POST', 'http://posttestserver.com/post.php');
|
xhr.open('POST', 'http://posttestserver.com/post.php');
|
||||||
xhr.onload = () => {
|
xhr.onload = () => {
|
||||||
this.setState({isUploading: false});
|
this.setState({isUploading: false});
|
||||||
if (xhr.status !== 200) {
|
XHRExampleBinaryUpload.handlePostTestServerUpload(xhr);
|
||||||
Alert.alert(
|
|
||||||
'Upload failed',
|
|
||||||
'Expected HTTP 200 OK response, got ' + xhr.status
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!xhr.responseText) {
|
|
||||||
Alert.alert(
|
|
||||||
'Upload failed',
|
|
||||||
'No response payload.'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var index = xhr.responseText.indexOf('http://www.posttestserver.com/');
|
|
||||||
if (index === -1) {
|
|
||||||
Alert.alert(
|
|
||||||
'Upload failed',
|
|
||||||
'Invalid response payload.'
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var url = xhr.responseText.slice(index).split('\n')[0];
|
|
||||||
console.log('Upload successful: ' + url);
|
|
||||||
Linking.openURL(url);
|
|
||||||
};
|
};
|
||||||
var formdata = new FormData();
|
var formdata = new FormData();
|
||||||
if (this.state.randomPhoto) {
|
if (this.state.randomPhoto) {
|
||||||
|
|
|
@ -16,9 +16,14 @@
|
||||||
const FormData = require('FormData');
|
const FormData = require('FormData');
|
||||||
const NativeEventEmitter = require('NativeEventEmitter');
|
const NativeEventEmitter = require('NativeEventEmitter');
|
||||||
const RCTNetworkingNative = require('NativeModules').Networking;
|
const RCTNetworkingNative = require('NativeModules').Networking;
|
||||||
|
const convertRequestBody = require('convertRequestBody');
|
||||||
|
|
||||||
|
import type {RequestBody} from 'convertRequestBody';
|
||||||
|
|
||||||
type Header = [string, string];
|
type Header = [string, string];
|
||||||
|
|
||||||
|
// Convert FormData headers to arrays, which are easier to consume in
|
||||||
|
// native on Android.
|
||||||
function convertHeadersMapToArray(headers: Object): Array<Header> {
|
function convertHeadersMapToArray(headers: Object): Array<Header> {
|
||||||
const headerArray = [];
|
const headerArray = [];
|
||||||
for (const name in headers) {
|
for (const name in headers) {
|
||||||
|
@ -47,16 +52,19 @@ class RCTNetworking extends NativeEventEmitter {
|
||||||
trackingName: string,
|
trackingName: string,
|
||||||
url: string,
|
url: string,
|
||||||
headers: Object,
|
headers: Object,
|
||||||
data: string | FormData | {uri: string},
|
data: RequestBody,
|
||||||
responseType: 'text' | 'base64',
|
responseType: 'text' | 'base64',
|
||||||
incrementalUpdates: boolean,
|
incrementalUpdates: boolean,
|
||||||
timeout: number,
|
timeout: number,
|
||||||
callback: (requestId: number) => any
|
callback: (requestId: number) => any
|
||||||
) {
|
) {
|
||||||
const body =
|
const body = convertRequestBody(data);
|
||||||
typeof data === 'string' ? {string: data} :
|
if (body && body.formData) {
|
||||||
data instanceof FormData ? {formData: getParts(data)} :
|
body.formData = body.formData.map((part) => ({
|
||||||
data;
|
...part,
|
||||||
|
headers: convertHeadersMapToArray(part.headers),
|
||||||
|
}));
|
||||||
|
}
|
||||||
const requestId = generateRequestId();
|
const requestId = generateRequestId();
|
||||||
RCTNetworkingNative.sendRequest(
|
RCTNetworkingNative.sendRequest(
|
||||||
method,
|
method,
|
||||||
|
@ -80,11 +88,4 @@ class RCTNetworking extends NativeEventEmitter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParts(data) {
|
|
||||||
return data.getParts().map((part) => {
|
|
||||||
part.headers = convertHeadersMapToArray(part.headers);
|
|
||||||
return part;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = new RCTNetworking();
|
module.exports = new RCTNetworking();
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
const FormData = require('FormData');
|
const FormData = require('FormData');
|
||||||
const NativeEventEmitter = require('NativeEventEmitter');
|
const NativeEventEmitter = require('NativeEventEmitter');
|
||||||
const RCTNetworkingNative = require('NativeModules').Networking;
|
const RCTNetworkingNative = require('NativeModules').Networking;
|
||||||
|
const convertRequestBody = require('convertRequestBody');
|
||||||
|
|
||||||
|
import type {RequestBody} from 'convertRequestBody';
|
||||||
|
|
||||||
class RCTNetworking extends NativeEventEmitter {
|
class RCTNetworking extends NativeEventEmitter {
|
||||||
|
|
||||||
|
@ -26,16 +29,13 @@ class RCTNetworking extends NativeEventEmitter {
|
||||||
trackingName: string,
|
trackingName: string,
|
||||||
url: string,
|
url: string,
|
||||||
headers: Object,
|
headers: Object,
|
||||||
data: string | FormData | {uri: string},
|
data: RequestBody,
|
||||||
responseType: 'text' | 'base64',
|
responseType: 'text' | 'base64',
|
||||||
incrementalUpdates: boolean,
|
incrementalUpdates: boolean,
|
||||||
timeout: number,
|
timeout: number,
|
||||||
callback: (requestId: number) => any
|
callback: (requestId: number) => any
|
||||||
) {
|
) {
|
||||||
const body =
|
const body = convertRequestBody(data);
|
||||||
typeof data === 'string' ? {string: data} :
|
|
||||||
data instanceof FormData ? {formData: data.getParts()} :
|
|
||||||
data;
|
|
||||||
RCTNetworkingNative.sendRequest({
|
RCTNetworkingNative.sendRequest({
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
|
|
|
@ -303,6 +303,11 @@ RCT_EXPORT_MODULE()
|
||||||
if (body) {
|
if (body) {
|
||||||
return callback(nil, @{@"body": body});
|
return callback(nil, @{@"body": body});
|
||||||
}
|
}
|
||||||
|
NSString *base64String = [RCTConvert NSString:query[@"base64"]];
|
||||||
|
if (base64String) {
|
||||||
|
NSData *data = [[NSData alloc] initWithBase64EncodedString:base64String options:0];
|
||||||
|
return callback(nil, @{@"body": data});
|
||||||
|
}
|
||||||
NSURLRequest *request = [RCTConvert NSURLRequest:query[@"uri"]];
|
NSURLRequest *request = [RCTConvert NSURLRequest:query[@"uri"]];
|
||||||
if (request) {
|
if (request) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule convertRequestBody
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const binaryToBase64 = require('binaryToBase64');
|
||||||
|
|
||||||
|
const FormData = require('FormData');
|
||||||
|
|
||||||
|
export type RequestBody =
|
||||||
|
string
|
||||||
|
| FormData
|
||||||
|
| {uri: string}
|
||||||
|
| ArrayBuffer
|
||||||
|
| $ArrayBufferView
|
||||||
|
;
|
||||||
|
|
||||||
|
function convertRequestBody(body: RequestBody): Object {
|
||||||
|
if (typeof body === 'string') {
|
||||||
|
return {string: body};
|
||||||
|
}
|
||||||
|
if (body instanceof FormData) {
|
||||||
|
return {formData: body.getParts()};
|
||||||
|
}
|
||||||
|
if (body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
|
||||||
|
// $FlowFixMe: no way to assert that 'body' is indeed an ArrayBufferView
|
||||||
|
return {base64: binaryToBase64(body)};
|
||||||
|
}
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = convertRequestBody;
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*
|
||||||
|
* @providesModule binaryToBase64
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const base64 = require('base64-js');
|
||||||
|
|
||||||
|
function binaryToBase64(data: ArrayBuffer | $ArrayBufferView) {
|
||||||
|
if (data instanceof ArrayBuffer) {
|
||||||
|
data = new Uint8Array(data);
|
||||||
|
}
|
||||||
|
if (data instanceof Uint8Array) {
|
||||||
|
return base64.fromByteArray(data);
|
||||||
|
}
|
||||||
|
if (!ArrayBuffer.isView(data)) {
|
||||||
|
throw new Error('data must be ArrayBuffer or typed array');
|
||||||
|
}
|
||||||
|
const {buffer, byteOffset, byteLength} = data;
|
||||||
|
return base64.fromByteArray(new Uint8Array(buffer, byteOffset, byteLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = binaryToBase64;
|
|
@ -15,24 +15,13 @@ const NativeEventEmitter = require('NativeEventEmitter');
|
||||||
const Platform = require('Platform');
|
const Platform = require('Platform');
|
||||||
const RCTWebSocketModule = require('NativeModules').WebSocketModule;
|
const RCTWebSocketModule = require('NativeModules').WebSocketModule;
|
||||||
const WebSocketEvent = require('WebSocketEvent');
|
const WebSocketEvent = require('WebSocketEvent');
|
||||||
|
const binaryToBase64 = require('binaryToBase64');
|
||||||
|
|
||||||
const EventTarget = require('event-target-shim');
|
const EventTarget = require('event-target-shim');
|
||||||
const base64 = require('base64-js');
|
const base64 = require('base64-js');
|
||||||
|
|
||||||
import type EventSubscription from 'EventSubscription';
|
import type EventSubscription from 'EventSubscription';
|
||||||
|
|
||||||
type ArrayBufferView =
|
|
||||||
Int8Array |
|
|
||||||
Uint8Array |
|
|
||||||
Uint8ClampedArray |
|
|
||||||
Int16Array |
|
|
||||||
Uint16Array |
|
|
||||||
Int32Array |
|
|
||||||
Uint32Array |
|
|
||||||
Float32Array |
|
|
||||||
Float64Array |
|
|
||||||
DataView;
|
|
||||||
|
|
||||||
const CONNECTING = 0;
|
const CONNECTING = 0;
|
||||||
const OPEN = 1;
|
const OPEN = 1;
|
||||||
const CLOSING = 2;
|
const CLOSING = 2;
|
||||||
|
@ -108,7 +97,7 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
|
||||||
this._close(code, reason);
|
this._close(code, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
send(data: string | ArrayBuffer | ArrayBufferView): void {
|
send(data: string | ArrayBuffer | $ArrayBufferView): void {
|
||||||
if (this.readyState === this.CONNECTING) {
|
if (this.readyState === this.CONNECTING) {
|
||||||
throw new Error('INVALID_STATE_ERR');
|
throw new Error('INVALID_STATE_ERR');
|
||||||
}
|
}
|
||||||
|
@ -118,14 +107,8 @@ class WebSocket extends EventTarget(...WEBSOCKET_EVENTS) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) {
|
||||||
if (ArrayBuffer.isView(data)) {
|
RCTWebSocketModule.sendBinary(binaryToBase64(data), this._socketId);
|
||||||
// $FlowFixMe: no way to assert that 'data' is indeed an ArrayBufferView now
|
|
||||||
data = data.buffer;
|
|
||||||
}
|
|
||||||
if (data instanceof ArrayBuffer) {
|
|
||||||
data = base64.fromByteArray(new Uint8Array(data));
|
|
||||||
RCTWebSocketModule.sendBinary(data, this._socketId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ import okhttp3.Request;
|
||||||
import okhttp3.RequestBody;
|
import okhttp3.RequestBody;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import okhttp3.ResponseBody;
|
import okhttp3.ResponseBody;
|
||||||
|
import okio.ByteString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the XMLHttpRequest JavaScript interface.
|
* Implements the XMLHttpRequest JavaScript interface.
|
||||||
|
@ -58,6 +59,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
||||||
private static final String REQUEST_BODY_KEY_STRING = "string";
|
private static final String REQUEST_BODY_KEY_STRING = "string";
|
||||||
private static final String REQUEST_BODY_KEY_URI = "uri";
|
private static final String REQUEST_BODY_KEY_URI = "uri";
|
||||||
private static final String REQUEST_BODY_KEY_FORMDATA = "formData";
|
private static final String REQUEST_BODY_KEY_FORMDATA = "formData";
|
||||||
|
private static final String REQUEST_BODY_KEY_BASE64 = "base64";
|
||||||
private static final String USER_AGENT_HEADER_NAME = "user-agent";
|
private static final String USER_AGENT_HEADER_NAME = "user-agent";
|
||||||
private static final int CHUNK_TIMEOUT_NS = 100 * 1000000; // 100ms
|
private static final int CHUNK_TIMEOUT_NS = 100 * 1000000; // 100ms
|
||||||
private static final int MAX_CHUNK_SIZE_BETWEEN_FLUSHES = 8 * 1024; // 8K
|
private static final int MAX_CHUNK_SIZE_BETWEEN_FLUSHES = 8 * 1024; // 8K
|
||||||
|
@ -251,6 +253,20 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
||||||
} else {
|
} else {
|
||||||
requestBuilder.method(method, RequestBody.create(contentMediaType, body));
|
requestBuilder.method(method, RequestBody.create(contentMediaType, body));
|
||||||
}
|
}
|
||||||
|
} else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) {
|
||||||
|
if (contentType == null) {
|
||||||
|
ResponseUtil.onRequestError(
|
||||||
|
eventEmitter,
|
||||||
|
requestId,
|
||||||
|
"Payload is set but no content-type header specified",
|
||||||
|
null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String base64String = data.getString(REQUEST_BODY_KEY_BASE64);
|
||||||
|
MediaType contentMediaType = MediaType.parse(contentType);
|
||||||
|
requestBuilder.method(
|
||||||
|
method,
|
||||||
|
RequestBody.create(contentMediaType, ByteString.decodeBase64(base64String)));
|
||||||
} else if (data.hasKey(REQUEST_BODY_KEY_URI)) {
|
} else if (data.hasKey(REQUEST_BODY_KEY_URI)) {
|
||||||
if (contentType == null) {
|
if (contentType == null) {
|
||||||
ResponseUtil.onRequestError(
|
ResponseUtil.onRequestError(
|
||||||
|
|
Loading…
Reference in New Issue