4.5 / N [RNFeed] Isolate reusable util methods out of NetworkingModule
Reviewed By: lexs Differential Revision: D3394808 fbshipit-source-id: 19c916be693377651f2c8e21eb3dd490ec68f74c
This commit is contained in:
parent
49a5fe493e
commit
7e7abff55a
|
@ -14,7 +14,6 @@ import javax.annotation.Nullable;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
@ -28,10 +27,9 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.network.OkHttpCallUtil;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
|
@ -178,9 +176,10 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
.build();
|
||||
}
|
||||
|
||||
final RCTDeviceEventEmitter eventEmitter = getEventEmitter(executorToken);
|
||||
Headers requestHeaders = extractHeaders(headers, data);
|
||||
if (requestHeaders == null) {
|
||||
onRequestError(executorToken, requestId, "Unrecognized headers format", null);
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, "Unrecognized headers format", null);
|
||||
return;
|
||||
}
|
||||
String contentType = requestHeaders.get(CONTENT_TYPE_HEADER_NAME);
|
||||
|
@ -191,11 +190,11 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
requestBuilder.method(method, RequestBodyUtil.getEmptyBody(method));
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_STRING)) {
|
||||
if (contentType == null) {
|
||||
onRequestError(
|
||||
executorToken,
|
||||
requestId,
|
||||
"Payload is set but no content-type header specified",
|
||||
null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Payload is set but no content-type header specified",
|
||||
null);
|
||||
return;
|
||||
}
|
||||
String body = data.getString(REQUEST_BODY_KEY_STRING);
|
||||
|
@ -203,7 +202,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
if (RequestBodyUtil.isGzipEncoding(contentEncoding)) {
|
||||
RequestBody requestBody = RequestBodyUtil.createGzip(contentMediaType, body);
|
||||
if (requestBody == null) {
|
||||
onRequestError(executorToken, requestId, "Failed to gzip request body", null);
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, "Failed to gzip request body", null);
|
||||
return;
|
||||
}
|
||||
requestBuilder.method(method, requestBody);
|
||||
|
@ -212,18 +211,22 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
}
|
||||
} else if (data.hasKey(REQUEST_BODY_KEY_URI)) {
|
||||
if (contentType == null) {
|
||||
onRequestError(
|
||||
executorToken,
|
||||
requestId,
|
||||
"Payload is set but no content-type header specified",
|
||||
null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Payload is set but no content-type header specified",
|
||||
null);
|
||||
return;
|
||||
}
|
||||
String uri = data.getString(REQUEST_BODY_KEY_URI);
|
||||
InputStream fileInputStream =
|
||||
RequestBodyUtil.getFileInputStream(getReactApplicationContext(), uri);
|
||||
if (fileInputStream == null) {
|
||||
onRequestError(executorToken, requestId, "Could not retrieve file for uri " + uri, null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Could not retrieve file for uri " + uri,
|
||||
null);
|
||||
return;
|
||||
}
|
||||
requestBuilder.method(
|
||||
|
@ -240,14 +243,18 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
return;
|
||||
}
|
||||
|
||||
requestBuilder.method(method, RequestBodyUtil.createProgressRequest(multipartBuilder.build(), new ProgressRequestListener() {
|
||||
requestBuilder.method(
|
||||
method,
|
||||
RequestBodyUtil.createProgressRequest(
|
||||
multipartBuilder.build(),
|
||||
new ProgressRequestListener() {
|
||||
long last = System.nanoTime();
|
||||
|
||||
@Override
|
||||
public void onRequestProgress(long bytesWritten, long contentLength, boolean done) {
|
||||
long now = System.nanoTime();
|
||||
if (done || shouldDispatch(now, last)) {
|
||||
onDataSend(executorToken, requestId, bytesWritten,contentLength);
|
||||
ResponseUtil.onDataSend(eventEmitter, requestId, bytesWritten, contentLength);
|
||||
last = now;
|
||||
}
|
||||
}
|
||||
|
@ -266,7 +273,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
return;
|
||||
}
|
||||
removeRequest(requestId);
|
||||
onRequestError(executorToken, requestId, e.getMessage(), e);
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, e.getMessage(), e);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -276,26 +283,31 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
}
|
||||
removeRequest(requestId);
|
||||
// Before we touch the body send headers to JS
|
||||
onResponseReceived(executorToken, requestId, response);
|
||||
ResponseUtil.onResponseReceived(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
response.code(),
|
||||
translateHeaders(response.headers()),
|
||||
response.request().url().toString());
|
||||
|
||||
ResponseBody responseBody = response.body();
|
||||
try {
|
||||
if (useIncrementalUpdates) {
|
||||
readWithProgress(executorToken, requestId, responseBody);
|
||||
onRequestSuccess(executorToken, requestId);
|
||||
readWithProgress(eventEmitter, requestId, responseBody);
|
||||
ResponseUtil.onRequestSuccess(eventEmitter, requestId);
|
||||
} else {
|
||||
onDataReceived(executorToken, requestId, responseBody.string());
|
||||
onRequestSuccess(executorToken, requestId);
|
||||
ResponseUtil.onDataReceived(eventEmitter, requestId, responseBody.string());
|
||||
ResponseUtil.onRequestSuccess(eventEmitter, requestId);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
onRequestError(executorToken, requestId, e.getMessage(), e);
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void readWithProgress(
|
||||
ExecutorToken executorToken,
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
ResponseBody responseBody) throws IOException {
|
||||
Reader reader = responseBody.charStream();
|
||||
|
@ -303,7 +315,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
char[] buffer = new char[MAX_CHUNK_SIZE_BETWEEN_FLUSHES];
|
||||
int read;
|
||||
while ((read = reader.read(buffer)) != -1) {
|
||||
onDataReceived(executorToken, requestId, new String(buffer, 0, read));
|
||||
ResponseUtil.onDataReceived(eventEmitter, requestId, new String(buffer, 0, read));
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
|
@ -314,57 +326,6 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
return last + CHUNK_TIMEOUT_NS < now;
|
||||
}
|
||||
|
||||
private void onDataSend(ExecutorToken ExecutorToken, int requestId, long progress, long total) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushInt((int) progress);
|
||||
args.pushInt((int) total);
|
||||
getEventEmitter(ExecutorToken).emit("didSendNetworkData", args);
|
||||
}
|
||||
|
||||
private void onDataReceived(ExecutorToken ExecutorToken, int requestId, String data) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushString(data);
|
||||
|
||||
getEventEmitter(ExecutorToken).emit("didReceiveNetworkData", args);
|
||||
}
|
||||
|
||||
private void onRequestError(ExecutorToken ExecutorToken, int requestId, String error, IOException e) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushString(error);
|
||||
|
||||
if ((e != null) && (e.getClass() == SocketTimeoutException.class)) {
|
||||
args.pushBoolean(true); // last argument is a time out boolean
|
||||
}
|
||||
|
||||
getEventEmitter(ExecutorToken).emit("didCompleteNetworkResponse", args);
|
||||
}
|
||||
|
||||
private void onRequestSuccess(ExecutorToken ExecutorToken, int requestId) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushNull();
|
||||
|
||||
getEventEmitter(ExecutorToken).emit("didCompleteNetworkResponse", args);
|
||||
}
|
||||
|
||||
private void onResponseReceived(
|
||||
ExecutorToken ExecutorToken,
|
||||
int requestId,
|
||||
Response response) {
|
||||
WritableMap headers = translateHeaders(response.headers());
|
||||
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushInt(response.code());
|
||||
args.pushMap(headers);
|
||||
args.pushString(response.request().url().toString());
|
||||
|
||||
getEventEmitter(ExecutorToken).emit("didReceiveNetworkResponse", args);
|
||||
}
|
||||
|
||||
private synchronized void addRequest(int requestId) {
|
||||
mRequestIds.add(requestId);
|
||||
}
|
||||
|
@ -430,6 +391,7 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
ReadableArray body,
|
||||
String contentType,
|
||||
int requestId) {
|
||||
RCTDeviceEventEmitter eventEmitter = getEventEmitter(ExecutorToken);
|
||||
MultipartBody.Builder multipartBuilder = new MultipartBody.Builder();
|
||||
multipartBuilder.setType(MediaType.parse(contentType));
|
||||
|
||||
|
@ -440,11 +402,11 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
ReadableArray headersArray = bodyPart.getArray("headers");
|
||||
Headers headers = extractHeaders(headersArray, null);
|
||||
if (headers == null) {
|
||||
onRequestError(
|
||||
ExecutorToken,
|
||||
requestId,
|
||||
"Missing or invalid header format for FormData part.",
|
||||
null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Missing or invalid header format for FormData part.",
|
||||
null);
|
||||
return null;
|
||||
}
|
||||
MediaType partContentType = null;
|
||||
|
@ -461,27 +423,27 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
multipartBuilder.addPart(headers, RequestBody.create(partContentType, bodyValue));
|
||||
} else if (bodyPart.hasKey(REQUEST_BODY_KEY_URI)) {
|
||||
if (partContentType == null) {
|
||||
onRequestError(
|
||||
ExecutorToken,
|
||||
requestId,
|
||||
"Binary FormData part needs a content-type header.",
|
||||
null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Binary FormData part needs a content-type header.",
|
||||
null);
|
||||
return null;
|
||||
}
|
||||
String fileContentUriStr = bodyPart.getString(REQUEST_BODY_KEY_URI);
|
||||
InputStream fileInputStream =
|
||||
RequestBodyUtil.getFileInputStream(getReactApplicationContext(), fileContentUriStr);
|
||||
if (fileInputStream == null) {
|
||||
onRequestError(
|
||||
ExecutorToken,
|
||||
requestId,
|
||||
"Could not retrieve file for uri " + fileContentUriStr,
|
||||
null);
|
||||
ResponseUtil.onRequestError(
|
||||
eventEmitter,
|
||||
requestId,
|
||||
"Could not retrieve file for uri " + fileContentUriStr,
|
||||
null);
|
||||
return null;
|
||||
}
|
||||
multipartBuilder.addPart(headers, RequestBodyUtil.create(partContentType, fileInputStream));
|
||||
} else {
|
||||
onRequestError(ExecutorToken, requestId, "Unrecognized FormData part.", null);
|
||||
ResponseUtil.onRequestError(eventEmitter, requestId, "Unrecognized FormData part.", null);
|
||||
}
|
||||
}
|
||||
return multipartBuilder;
|
||||
|
@ -519,8 +481,8 @@ public final class NetworkingModule extends ReactContextBaseJavaModule {
|
|||
return headersBuilder.build();
|
||||
}
|
||||
|
||||
private DeviceEventManagerModule.RCTDeviceEventEmitter getEventEmitter(ExecutorToken ExecutorToken) {
|
||||
private RCTDeviceEventEmitter getEventEmitter(ExecutorToken ExecutorToken) {
|
||||
return getReactApplicationContext()
|
||||
.getJSModule(ExecutorToken, DeviceEventManagerModule.RCTDeviceEventEmitter.class);
|
||||
.getJSModule(ExecutorToken, RCTDeviceEventEmitter.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.facebook.react.modules.network;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
|
||||
|
||||
/**
|
||||
* Util methods to send network responses to JS.
|
||||
*/
|
||||
public class ResponseUtil {
|
||||
public static void onDataSend(
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
long progress,
|
||||
long total) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushInt((int) progress);
|
||||
args.pushInt((int) total);
|
||||
eventEmitter.emit("didSendNetworkData", args);
|
||||
}
|
||||
|
||||
public static void onDataReceived(
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
String data) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushString(data);
|
||||
|
||||
eventEmitter.emit("didReceiveNetworkData", args);
|
||||
}
|
||||
|
||||
public static void onRequestError(
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
String error,
|
||||
IOException e) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushString(error);
|
||||
|
||||
if ((e != null) && (e.getClass() == SocketTimeoutException.class)) {
|
||||
args.pushBoolean(true); // last argument is a time out boolean
|
||||
}
|
||||
|
||||
eventEmitter.emit("didCompleteNetworkResponse", args);
|
||||
}
|
||||
|
||||
public static void onRequestSuccess(RCTDeviceEventEmitter eventEmitter, int requestId) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushNull();
|
||||
|
||||
eventEmitter.emit("didCompleteNetworkResponse", args);
|
||||
}
|
||||
|
||||
public static void onResponseReceived(
|
||||
RCTDeviceEventEmitter eventEmitter,
|
||||
int requestId,
|
||||
int statusCode,
|
||||
WritableMap headers,
|
||||
String url) {
|
||||
WritableArray args = Arguments.createArray();
|
||||
args.pushInt(requestId);
|
||||
args.pushInt(statusCode);
|
||||
args.pushMap(headers);
|
||||
args.pushString(url);
|
||||
|
||||
eventEmitter.emit("didReceiveNetworkResponse", args);
|
||||
}
|
||||
}
|
|
@ -84,8 +84,8 @@ public class NetworkingModuleTest {
|
|||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
|
||||
networkingModule.sendRequest(
|
||||
mock(ExecutorToken.class),
|
||||
|
@ -196,8 +196,8 @@ public class NetworkingModuleTest {
|
|||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
|
||||
JavaOnlyMap body = new JavaOnlyMap();
|
||||
body.putString("string", "This is request body");
|
||||
|
@ -234,7 +234,8 @@ public class NetworkingModuleTest {
|
|||
return callMock;
|
||||
}
|
||||
});
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
|
||||
List<JavaOnlyArray> headers = Arrays.asList(
|
||||
JavaOnlyArray.of("Accept", "text/plain"),
|
||||
|
@ -287,8 +288,8 @@ public class NetworkingModuleTest {
|
|||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
mock(ExecutorToken.class),
|
||||
"POST",
|
||||
|
@ -347,8 +348,8 @@ public class NetworkingModuleTest {
|
|||
return callMock;
|
||||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
mock(ExecutorToken.class),
|
||||
"POST",
|
||||
|
@ -445,7 +446,8 @@ public class NetworkingModuleTest {
|
|||
}
|
||||
});
|
||||
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
networkingModule.sendRequest(
|
||||
mock(ExecutorToken.class),
|
||||
"POST",
|
||||
|
@ -501,7 +503,8 @@ public class NetworkingModuleTest {
|
|||
return calls[(Integer) request.tag() - 1];
|
||||
}
|
||||
});
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
networkingModule.initialize();
|
||||
|
||||
for (int idx = 0; idx < requests; idx++) {
|
||||
|
@ -547,7 +550,8 @@ public class NetworkingModuleTest {
|
|||
return calls[(Integer) request.tag() - 1];
|
||||
}
|
||||
});
|
||||
NetworkingModule networkingModule = new NetworkingModule(null, "", httpClient);
|
||||
NetworkingModule networkingModule =
|
||||
new NetworkingModule(mock(ReactApplicationContext.class), "", httpClient);
|
||||
|
||||
for (int idx = 0; idx < requests; idx++) {
|
||||
networkingModule.sendRequest(
|
||||
|
|
Loading…
Reference in New Issue