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:
Saurabh Aggarwal 2016-06-07 09:32:47 -07:00 committed by Facebook Github Bot 2
parent 49a5fe493e
commit 7e7abff55a
3 changed files with 159 additions and 108 deletions

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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(