Added progress updates for all XMLHttpRequest upload types / fix crash on closed connection
Reviewed By: mdvacca Differential Revision: D6937754 fbshipit-source-id: 89b963e16adf1bd3200806fd7374d28aa618b62f
This commit is contained in:
parent
fdef3784f0
commit
27b6348f86
|
@ -343,11 +343,11 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
}
|
||||
}
|
||||
|
||||
RequestBody requestBody;
|
||||
if (data == null) {
|
||||
requestBuilder.method(method, RequestBodyUtil.getEmptyBody(method));
|
||||
requestBody = RequestBodyUtil.getEmptyBody(method);
|
||||
} else if (handler != null) {
|
||||
RequestBody requestBody = handler.toRequestBody(data, contentType);
|
||||
requestBuilder.method(method, requestBody);
|
||||
requestBody = handler.toRequestBody(data, contentType);
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_STRING)) {
|
||||
if (contentType == null) {
|
||||
ResponseUtil.onRequestError(
|
||||
|
@ -360,14 +360,13 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
String body = data.getString(REQUEST_BODY_KEY_STRING);
|
||||
MediaType contentMediaType = MediaType.parse(contentType);
|
||||
if (RequestBodyUtil.isGzipEncoding(contentEncoding)) {
|
||||
RequestBody requestBody = RequestBodyUtil.createGzip(contentMediaType, body);
|
||||
requestBody = RequestBodyUtil.createGzip(contentMediaType, body);
|
||||
if (requestBody == null) {
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, "Failed to gzip request body", null);
|
||||
return;
|
||||
}
|
||||
requestBuilder.method(method, requestBody);
|
||||
} else {
|
||||
requestBuilder.method(method, RequestBody.create(contentMediaType, body));
|
||||
requestBody = RequestBody.create(contentMediaType, body);
|
||||
}
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_BASE64)) {
|
||||
if (contentType == null) {
|
||||
|
@ -380,9 +379,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
}
|
||||
String base64String = data.getString(REQUEST_BODY_KEY_BASE64);
|
||||
MediaType contentMediaType = MediaType.parse(contentType);
|
||||
requestBuilder.method(
|
||||
method,
|
||||
RequestBody.create(contentMediaType, ByteString.decodeBase64(base64String)));
|
||||
requestBody = RequestBody.create(contentMediaType, ByteString.decodeBase64(base64String));
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_URI)) {
|
||||
if (contentType == null) {
|
||||
ResponseUtil.onRequestError(
|
||||
|
@ -403,9 +400,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
null);
|
||||
return;
|
||||
}
|
||||
requestBuilder.method(
|
||||
method,
|
||||
RequestBodyUtil.create(MediaType.parse(contentType), fileInputStream));
|
||||
requestBody = RequestBodyUtil.create(MediaType.parse(contentType), fileInputStream);
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_FORMDATA)) {
|
||||
if (contentType == null) {
|
||||
contentType = "multipart/form-data";
|
||||
|
@ -416,28 +411,16 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
if (multipartBuilder == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
requestBuilder.method(
|
||||
method,
|
||||
RequestBodyUtil.createProgressRequest(
|
||||
multipartBuilder.build(),
|
||||
new ProgressListener() {
|
||||
long last = System.nanoTime();
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesWritten, long contentLength, boolean done) {
|
||||
long now = System.nanoTime();
|
||||
if (done || shouldDispatch(now, last)) {
|
||||
ResponseUtil.onDataSend(eventEmitter, requestId, bytesWritten, contentLength);
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
}));
|
||||
requestBody = multipartBuilder.build();
|
||||
} else {
|
||||
// Nothing in data payload, at least nothing we could understand anyway.
|
||||
requestBuilder.method(method, RequestBodyUtil.getEmptyBody(method));
|
||||
requestBody = RequestBodyUtil.getEmptyBody(method);
|
||||
}
|
||||
|
||||
requestBuilder.method(
|
||||
method,
|
||||
wrapRequestBodyWithProgressEmitter(requestBody, eventEmitter, requestId));
|
||||
|
||||
addRequest(requestId);
|
||||
client.newCall(requestBuilder.build()).enqueue(
|
||||
new Callback() {
|
||||
|
@ -515,6 +498,29 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
});
|
||||
}
|
||||
|
||||
private RequestBody wrapRequestBodyWithProgressEmitter(
|
||||
final RequestBody requestBody,
|
||||
final RCTDeviceEventEmitter eventEmitter,
|
||||
final int requestId) {
|
||||
if(requestBody == null) {
|
||||
return null;
|
||||
}
|
||||
return RequestBodyUtil.createProgressRequest(
|
||||
requestBody,
|
||||
new ProgressListener() {
|
||||
long last = System.nanoTime();
|
||||
|
||||
@Override
|
||||
public void onProgress(long bytesWritten, long contentLength, boolean done) {
|
||||
long now = System.nanoTime();
|
||||
if (done || shouldDispatch(now, last)) {
|
||||
ResponseUtil.onDataSend(eventEmitter, requestId, bytesWritten, contentLength);
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void readWithProgress(
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
|
|
|
@ -10,59 +10,72 @@
|
|||
package com.facebook.react.modules.network;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.RequestBody;
|
||||
import okio.BufferedSink;
|
||||
import okio.Buffer;
|
||||
import okio.Sink;
|
||||
import okio.ForwardingSink;
|
||||
import okio.Okio;
|
||||
import okio.Sink;
|
||||
|
||||
public class ProgressRequestBody extends RequestBody {
|
||||
|
||||
private final RequestBody mRequestBody;
|
||||
private final ProgressListener mProgressListener;
|
||||
private BufferedSink mBufferedSink;
|
||||
private long mContentLength = 0L;
|
||||
|
||||
public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
|
||||
mRequestBody = requestBody;
|
||||
mProgressListener = progressListener;
|
||||
mRequestBody = requestBody;
|
||||
mProgressListener = progressListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaType contentType() {
|
||||
return mRequestBody.contentType();
|
||||
return mRequestBody.contentType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() throws IOException {
|
||||
return mRequestBody.contentLength();
|
||||
if (mContentLength == 0) {
|
||||
mContentLength = mRequestBody.contentLength();
|
||||
}
|
||||
return mContentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(BufferedSink sink) throws IOException {
|
||||
if (mBufferedSink == null) {
|
||||
mBufferedSink = Okio.buffer(sink(sink));
|
||||
if (mBufferedSink == null) {
|
||||
mBufferedSink = Okio.buffer(outputStreamSink(sink));
|
||||
}
|
||||
|
||||
// contentLength changes for input streams, since we're using inputStream.available(),
|
||||
// so get the length before writing to the sink
|
||||
contentLength();
|
||||
|
||||
mRequestBody.writeTo(mBufferedSink);
|
||||
mBufferedSink.flush();
|
||||
}
|
||||
|
||||
private Sink outputStreamSink(BufferedSink sink) {
|
||||
return Okio.sink(new CountingOutputStream(sink.outputStream()) {
|
||||
@Override
|
||||
public void write(byte[] data, int offset, int byteCount) throws IOException {
|
||||
super.write(data, offset, byteCount);
|
||||
sendProgressUpdate();
|
||||
}
|
||||
mRequestBody.writeTo(mBufferedSink);
|
||||
mBufferedSink.flush();
|
||||
}
|
||||
|
||||
private Sink sink(Sink sink) {
|
||||
return new ForwardingSink(sink) {
|
||||
long bytesWritten = 0L;
|
||||
long contentLength = 0L;
|
||||
@Override
|
||||
public void write(int data) throws IOException {
|
||||
super.write(data);
|
||||
sendProgressUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Buffer source, long byteCount) throws IOException {
|
||||
super.write(source, byteCount);
|
||||
if (contentLength == 0) {
|
||||
contentLength = contentLength();
|
||||
}
|
||||
bytesWritten += byteCount;
|
||||
mProgressListener.onProgress(
|
||||
bytesWritten, contentLength, bytesWritten == contentLength);
|
||||
}
|
||||
};
|
||||
private void sendProgressUpdate() throws IOException {
|
||||
long bytesWritten = getCount();
|
||||
long contentLength = contentLength();
|
||||
mProgressListener.onProgress(
|
||||
bytesWritten, contentLength, bytesWritten == contentLength);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,6 +200,10 @@ public class NetworkingModuleTest {
|
|||
|
||||
@Test
|
||||
public void testSuccessfulPostRequest() throws Exception {
|
||||
RCTDeviceEventEmitter emitter = mock(RCTDeviceEventEmitter.class);
|
||||
ReactApplicationContext context = mock(ReactApplicationContext.class);
|
||||
when(context.getJSModule(any(Class.class))).thenReturn(emitter);
|
||||
|
||||
OkHttpClient httpClient = mock(OkHttpClient.class);
|
||||
when(httpClient.newCall(any(Request.class))).thenAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
|
@ -211,12 +215,13 @@ public class NetworkingModuleTest {
|
|||
OkHttpClient.Builder clientBuilder = mock(OkHttpClient.Builder.class);
|
||||
when(clientBuilder.build()).thenReturn(httpClient);
|
||||
when(httpClient.newBuilder()).thenReturn(clientBuilder);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
NetworkingModule networkingModule = new NetworkingModule(context, "", httpClient);
|
||||
|
||||
JavaOnlyMap body = new JavaOnlyMap();
|
||||
body.putString("string", "This is request body");
|
||||
|
||||
mockEvents();
|
||||
|
||||
networkingModule.sendRequest(
|
||||
"POST",
|
||||
"http://somedomain/bar",
|
||||
|
@ -630,4 +635,4 @@ public class NetworkingModuleTest {
|
|||
assertThat(requestIdArguments.getAllValues().contains(idx + 1)).isTrue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue