Send large responses in chunks

Summary:
It's common for responses to be completely missing in the network inspector. This is because they are larger than can be serialized in one go on some devices, so we drop all messages larger than 1MB.

This changes the android client to send large responses in individually serialized batches. This way we avoid running out of memory and can still send arbitrarily large payloads.

Changelog: Android network inspector can now handle responses large than 1MB.

Reviewed By: passy

Differential Revision: D22999905

fbshipit-source-id: ff4eb8fa72a7e42ea90d12ffe0f20c6d1e58b7e5
This commit is contained in:
John Knox
2020-08-10 08:46:06 -07:00
committed by Facebook GitHub Bot
parent 0065ddedd7
commit 9efcbdceaf
7 changed files with 348 additions and 29 deletions

View File

@@ -12,7 +12,9 @@ import com.facebook.flipper.core.ErrorReportingRunnable;
import com.facebook.flipper.core.FlipperArray;
import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.common.BufferingFlipperPlugin;
import java.util.Arrays;
import java.util.List;
import javax.annotation.Nullable;
public class NetworkFlipperPlugin extends BufferingFlipperPlugin implements NetworkReporter {
public static final String ID = "Network";
@@ -64,18 +66,42 @@ public class NetworkFlipperPlugin extends BufferingFlipperPlugin implements Netw
responseInfo.body = null;
}
final FlipperObject response =
new FlipperObject.Builder()
.put("id", responseInfo.requestId)
.put("timestamp", responseInfo.timeStamp)
.put("status", responseInfo.statusCode)
.put("reason", responseInfo.statusReason)
.put("headers", toFlipperObject(responseInfo.headers))
.put("isMock", responseInfo.isMock)
.put("data", toBase64(responseInfo.body))
.build();
int numChunks =
responseInfo.body == null
? 1
: (int) Math.ceil((double) responseInfo.body.length / MAX_BODY_SIZE_IN_BYTES);
send("newResponse", response);
for (int i = 0; i < numChunks; i++) {
byte[] chunk =
responseInfo.body == null
? null
: Arrays.copyOfRange(
responseInfo.body,
i * MAX_BODY_SIZE_IN_BYTES,
Math.min((i + 1) * MAX_BODY_SIZE_IN_BYTES, responseInfo.body.length));
final FlipperObject response =
i == 0
? new FlipperObject.Builder()
.put("id", responseInfo.requestId)
.put("timestamp", responseInfo.timeStamp)
.put("status", responseInfo.statusCode)
.put("reason", responseInfo.statusReason)
.put("headers", toFlipperObject(responseInfo.headers))
.put("isMock", responseInfo.isMock)
.put("data", toBase64(chunk))
.put("totalChunks", numChunks)
.put("index", i)
.build()
: new FlipperObject.Builder()
.put("id", responseInfo.requestId)
.put("timestamp", responseInfo.timeStamp)
.put("totalChunks", numChunks)
.put("index", i)
.put("data", toBase64(chunk))
.build();
send(numChunks == 1 ? "newResponse" : "partialResponse", response);
}
}
};
@@ -99,7 +125,7 @@ public class NetworkFlipperPlugin extends BufferingFlipperPlugin implements Netw
job.run();
}
private String toBase64(byte[] bytes) {
private String toBase64(@Nullable byte[] bytes) {
if (bytes == null) {
return null;
}
@@ -122,10 +148,6 @@ public class NetworkFlipperPlugin extends BufferingFlipperPlugin implements Netw
return false;
}
if (responseInfo.body != null && responseInfo.body.length > MAX_BODY_SIZE_IN_BYTES) {
return true;
}
return contentType.value.contains("image/")
|| contentType.value.contains("video/")
|| contentType.value.contains("application/zip");

View File

@@ -9,6 +9,7 @@ package com.facebook.flipper.plugins.network;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
public interface NetworkReporter {
void reportRequest(RequestInfo requestInfo);
@@ -54,7 +55,7 @@ public interface NetworkReporter {
public int statusCode;
public String statusReason;
public List<Header> headers = new ArrayList<>();
public byte[] body;
public @Nullable byte[] body;
public boolean isMock = false;
public Header getFirstHeader(final String name) {