Extract delta client
Summary: Extracts the delta client from the bundle downloader. This will allow us to extract an interface, and provide a different implementation for C++ delta bundling (where we will pass deltas directly to native code). Reviewed By: pakoito Differential Revision: D6900904 fbshipit-source-id: 358705615eecc15afa0de3e50478468ad840d250
This commit is contained in:
parent
6e44356c9b
commit
1019bda930
|
@ -0,0 +1,121 @@
|
|||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonToken;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedHashMap;
|
||||
import javax.annotation.Nullable;
|
||||
import okio.BufferedSource;
|
||||
|
||||
public class BundleDeltaClient {
|
||||
|
||||
final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<Number, byte[]>();
|
||||
final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<Number, byte[]>();
|
||||
final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<Number, byte[]>();
|
||||
@Nullable String mDeltaId;
|
||||
|
||||
static boolean isDeltaUrl(String bundleUrl) {
|
||||
return bundleUrl.indexOf(".delta?") != -1;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mDeltaId = null;
|
||||
mDeltaModules.clear();
|
||||
mPreModules.clear();
|
||||
mPostModules.clear();
|
||||
}
|
||||
|
||||
public String toDeltaUrl(String bundleURL) {
|
||||
if (isDeltaUrl(bundleURL) && mDeltaId != null) {
|
||||
return bundleURL + "&deltaBundleId=" + mDeltaId;
|
||||
}
|
||||
return bundleURL;
|
||||
}
|
||||
|
||||
public synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile)
|
||||
throws IOException {
|
||||
|
||||
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
|
||||
|
||||
jsonReader.beginObject();
|
||||
|
||||
int numChangedModules = 0;
|
||||
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("id")) {
|
||||
mDeltaId = jsonReader.nextString();
|
||||
} else if (name.equals("pre")) {
|
||||
numChangedModules += patchDelta(jsonReader, mPreModules);
|
||||
} else if (name.equals("post")) {
|
||||
numChangedModules += patchDelta(jsonReader, mPostModules);
|
||||
} else if (name.equals("delta")) {
|
||||
numChangedModules += patchDelta(jsonReader, mDeltaModules);
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
|
||||
jsonReader.endObject();
|
||||
jsonReader.close();
|
||||
|
||||
if (numChangedModules == 0) {
|
||||
// If we receive an empty delta, we don't need to save the file again (it'll have the
|
||||
// same content).
|
||||
return false;
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
|
||||
|
||||
try {
|
||||
for (byte[] code : mPreModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
|
||||
for (byte[] code : mDeltaModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
|
||||
for (byte[] code : mPostModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
} finally {
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
|
||||
throws IOException {
|
||||
jsonReader.beginArray();
|
||||
|
||||
int numModules = 0;
|
||||
while (jsonReader.hasNext()) {
|
||||
jsonReader.beginArray();
|
||||
|
||||
int moduleId = jsonReader.nextInt();
|
||||
|
||||
if (jsonReader.peek() == JsonToken.NULL) {
|
||||
jsonReader.skipValue();
|
||||
map.remove(moduleId);
|
||||
} else {
|
||||
map.put(moduleId, jsonReader.nextString().getBytes());
|
||||
}
|
||||
|
||||
jsonReader.endArray();
|
||||
numModules++;
|
||||
}
|
||||
|
||||
jsonReader.endArray();
|
||||
|
||||
return numModules;
|
||||
}
|
||||
}
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.util.JsonReader;
|
||||
import android.util.JsonToken;
|
||||
import android.util.Log;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
|
@ -18,10 +16,7 @@ import com.facebook.react.common.DebugServerException;
|
|||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.devsupport.interfaces.DevBundleDownloadListener;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -47,11 +42,8 @@ public class BundleDownloader {
|
|||
|
||||
private final OkHttpClient mClient;
|
||||
|
||||
private final LinkedHashMap<Number, byte[]> mPreModules = new LinkedHashMap<>();
|
||||
private final LinkedHashMap<Number, byte[]> mDeltaModules = new LinkedHashMap<>();
|
||||
private final LinkedHashMap<Number, byte[]> mPostModules = new LinkedHashMap<>();
|
||||
private final BundleDeltaClient mBundleDeltaClient = new BundleDeltaClient();
|
||||
|
||||
private @Nullable String mDeltaId;
|
||||
private @Nullable Call mDownloadBundleFromURLCall;
|
||||
|
||||
public static class BundleInfo {
|
||||
|
@ -110,15 +102,9 @@ public class BundleDownloader {
|
|||
final String bundleURL,
|
||||
final @Nullable BundleInfo bundleInfo) {
|
||||
|
||||
String finalUrl = bundleURL;
|
||||
|
||||
if (isDeltaUrl(bundleURL) && mDeltaId != null) {
|
||||
finalUrl += "&deltaBundleId=" + mDeltaId;
|
||||
}
|
||||
|
||||
final Request request =
|
||||
new Request.Builder()
|
||||
.url(finalUrl)
|
||||
.url(mBundleDeltaClient.toDeltaUrl(bundleURL))
|
||||
// FIXME: there is a bug that makes MultipartStreamReader to never find the end of the
|
||||
// multipart message. This temporarily disables the multipart mode to work around it,
|
||||
// but
|
||||
|
@ -253,11 +239,11 @@ public class BundleDownloader {
|
|||
|
||||
boolean bundleUpdated;
|
||||
|
||||
if (isDeltaUrl(url)) {
|
||||
if (BundleDeltaClient.isDeltaUrl(url)) {
|
||||
// If the bundle URL has the delta extension, we need to use the delta patching logic.
|
||||
bundleUpdated = storeDeltaInFile(body, tmpFile);
|
||||
bundleUpdated = mBundleDeltaClient.storeDeltaInFile(body, tmpFile);
|
||||
} else {
|
||||
resetDeltaCache();
|
||||
mBundleDeltaClient.reset();
|
||||
bundleUpdated = storePlainJSInFile(body, tmpFile);
|
||||
}
|
||||
|
||||
|
@ -286,101 +272,6 @@ public class BundleDownloader {
|
|||
return true;
|
||||
}
|
||||
|
||||
private synchronized boolean storeDeltaInFile(BufferedSource body, File outputFile) throws IOException {
|
||||
|
||||
JsonReader jsonReader = new JsonReader(new InputStreamReader(body.inputStream()));
|
||||
|
||||
jsonReader.beginObject();
|
||||
|
||||
int numChangedModules = 0;
|
||||
|
||||
while (jsonReader.hasNext()) {
|
||||
String name = jsonReader.nextName();
|
||||
if (name.equals("id")) {
|
||||
mDeltaId = jsonReader.nextString();
|
||||
} else if (name.equals("pre")) {
|
||||
numChangedModules += patchDelta(jsonReader, mPreModules);
|
||||
} else if (name.equals("post")) {
|
||||
numChangedModules += patchDelta(jsonReader, mPostModules);
|
||||
} else if (name.equals("delta")) {
|
||||
numChangedModules += patchDelta(jsonReader, mDeltaModules);
|
||||
} else {
|
||||
jsonReader.skipValue();
|
||||
}
|
||||
}
|
||||
|
||||
jsonReader.endObject();
|
||||
jsonReader.close();
|
||||
|
||||
if (numChangedModules == 0) {
|
||||
// If we receive an empty delta, we don't need to save the file again (it'll have the
|
||||
// same content).
|
||||
return false;
|
||||
}
|
||||
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
|
||||
|
||||
try {
|
||||
for (byte[] code : mPreModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
|
||||
for (byte[] code : mDeltaModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
|
||||
for (byte[] code : mPostModules.values()) {
|
||||
fileOutputStream.write(code);
|
||||
fileOutputStream.write('\n');
|
||||
}
|
||||
} finally {
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int patchDelta(JsonReader jsonReader, LinkedHashMap<Number, byte[]> map)
|
||||
throws IOException {
|
||||
jsonReader.beginArray();
|
||||
|
||||
int numModules = 0;
|
||||
while (jsonReader.hasNext()) {
|
||||
jsonReader.beginArray();
|
||||
|
||||
int moduleId = jsonReader.nextInt();
|
||||
|
||||
if (jsonReader.peek() == JsonToken.NULL) {
|
||||
jsonReader.skipValue();
|
||||
map.remove(moduleId);
|
||||
} else {
|
||||
map.put(moduleId, jsonReader.nextString().getBytes());
|
||||
}
|
||||
|
||||
jsonReader.endArray();
|
||||
numModules++;
|
||||
}
|
||||
|
||||
jsonReader.endArray();
|
||||
|
||||
return numModules;
|
||||
}
|
||||
|
||||
private void resetDeltaCache() {
|
||||
mDeltaId = null;
|
||||
|
||||
mDeltaModules.clear();
|
||||
mPreModules.clear();
|
||||
mPostModules.clear();
|
||||
}
|
||||
|
||||
private static boolean isDeltaUrl(String bundleUrl) {
|
||||
return bundleUrl.indexOf(".delta?") != -1;
|
||||
}
|
||||
|
||||
private static void populateBundleInfo(String url, Headers headers, BundleInfo bundleInfo) {
|
||||
bundleInfo.mUrl = url;
|
||||
|
||||
|
|
Loading…
Reference in New Issue