Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 64 additions & 15 deletions README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.PatternSyntaxException;
import org.json.JSONException;
import org.json.JSONObject;

Expand Down Expand Up @@ -87,6 +88,38 @@ public void load() {
);
}

private List<NativeProxyRule> parseProxyRules(JSArray rawRules, String optionName, PluginCall call) {
List<NativeProxyRule> parsed = new ArrayList<>();
if (rawRules == null) {
return parsed;
}

for (int i = 0; i < rawRules.length(); i++) {
try {
JSONObject rule = rawRules.getJSONObject(i);
parsed.add(
new NativeProxyRule(
rule.optString("id", null),
NativeProxyRule.compilePattern(rule.optString("urlRegex", null)),
NativeProxyRule.compilePattern(rule.optString("methodRegex", null)),
NativeProxyRule.compilePattern(rule.optString("headerRegex", null)),
NativeProxyRule.compilePattern(rule.optString("bodyRegex", null)),
NativeProxyRule.compilePattern(rule.optString("statusRegex", null)),
NativeProxyRule.compilePattern(rule.optString("responseHeaderRegex", null)),
NativeProxyRule.compilePattern(rule.optString("responseBodyRegex", null)),
rule.optBoolean("mainFrameOnly", false),
NativeProxyRule.Action.fromString(rule.optString("action", "continue"))
)
);
} catch (JSONException | PatternSyntaxException e) {
call.reject("Invalid " + optionName + " at index " + i + ": " + e.getMessage());
return null;
}
}

return parsed;
}

private void registerWebView(String id, WebViewDialog dialog) {
webViewDialogs.put(id, dialog);
webViewStack.remove(id);
Expand Down Expand Up @@ -576,6 +609,16 @@ public void openWebView(PluginCall call) {
}

options.setProxyRequests(Boolean.TRUE.equals(call.getBoolean("proxyRequests", false)));
List<NativeProxyRule> outboundProxyRules = parseProxyRules(call.getArray("outboundProxyRules"), "outboundProxyRules", call);
if (outboundProxyRules == null) {
return;
}
options.setOutboundProxyRules(outboundProxyRules);
List<NativeProxyRule> inboundProxyRules = parseProxyRules(call.getArray("inboundProxyRules"), "inboundProxyRules", call);
if (inboundProxyRules == null) {
return;
}
options.setInboundProxyRules(inboundProxyRules);

try {
// Try to set buttonNearDone if present, with better error handling
Expand Down Expand Up @@ -725,9 +768,21 @@ public void confirmBtnClicked(String url) {
}

@Override
public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String wvId) {
public void proxyRequestEvent(
String requestId,
String phase,
String url,
String method,
String headersJson,
String body,
Integer status,
String responseHeadersJson,
String responseBody,
String wvId
) {
JSObject data = new JSObject();
data.put("requestId", requestId);
data.put("phase", phase);
data.put("url", url);
data.put("method", method);
try {
Expand All @@ -736,6 +791,17 @@ public void proxyRequestEvent(String requestId, String url, String method, Strin
data.put("headers", new JSObject());
}
data.put("body", body);
if (status != null) {
data.put("status", status);
}
if (responseHeadersJson != null) {
try {
data.put("responseHeaders", new JSObject(responseHeadersJson));
} catch (Exception e) {
data.put("responseHeaders", new JSObject());
}
}
data.put("responseBody", responseBody);
data.put("webviewId", wvId);
notifyListeners("proxyRequest", data);
}
Expand Down Expand Up @@ -1089,8 +1155,11 @@ public void handleProxyRequest(PluginCall call) {
call.reject("Target WebView not found for proxy request");
return;
}
JSObject response = call.getObject("response");
webViewDialog.handleProxyResponse(requestId, response);
JSObject decision = call.getObject("decision");
if (decision == null) {
decision = call.getObject("response");
}
webViewDialog.handleProxyResponse(requestId, decision);
call.resolve();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package ee.forgr.capacitor_inappbrowser;

import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class NativeProxyRule {

public enum Action {
CONTINUE,
CANCEL,
DELEGATE_TO_JS;

public static Action fromString(String value) {
if (value == null) {
return CONTINUE;
}
switch (value) {
case "cancel":
return CANCEL;
case "delegateToJs":
return DELEGATE_TO_JS;
default:
return CONTINUE;
}
}
}

private final String id;
private final Pattern urlPattern;
private final Pattern methodPattern;
private final Pattern headerPattern;
private final Pattern bodyPattern;
private final Pattern statusPattern;
private final Pattern responseHeaderPattern;
private final Pattern responseBodyPattern;
private final boolean mainFrameOnly;
private final Action action;

public NativeProxyRule(
String id,
Pattern urlPattern,
Pattern methodPattern,
Pattern headerPattern,
Pattern bodyPattern,
Pattern statusPattern,
Pattern responseHeaderPattern,
Pattern responseBodyPattern,
boolean mainFrameOnly,
Action action
) {
this.id = id;
this.urlPattern = urlPattern;
this.methodPattern = methodPattern;
this.headerPattern = headerPattern;
this.bodyPattern = bodyPattern;
this.statusPattern = statusPattern;
this.responseHeaderPattern = responseHeaderPattern;
this.responseBodyPattern = responseBodyPattern;
this.mainFrameOnly = mainFrameOnly;
this.action = action;
}

public String getId() {
return id;
}

public boolean isMainFrameOnly() {
return mainFrameOnly;
}

public Action getAction() {
return action;
}

public boolean matches(
String url,
String method,
String serializedHeaders,
String decodedBody,
boolean isMainFrame,
Integer statusCode,
String serializedResponseHeaders,
String decodedResponseBody
) {
if (mainFrameOnly && !isMainFrame) {
return false;
}
if (!matchesPattern(urlPattern, url)) {
return false;
}
if (!matchesPattern(methodPattern, method)) {
return false;
}
if (!matchesPattern(headerPattern, serializedHeaders)) {
return false;
}
if (!matchesPattern(bodyPattern, decodedBody)) {
return false;
}
if (!matchesPattern(statusPattern, statusCode != null ? String.valueOf(statusCode) : null)) {
return false;
}
if (!matchesPattern(responseHeaderPattern, serializedResponseHeaders)) {
return false;
}
if (!matchesPattern(responseBodyPattern, decodedResponseBody)) {
return false;
}
return true;
}

private boolean matchesPattern(Pattern pattern, String value) {
if (pattern == null) {
return true;
}
if (value == null) {
return false;
}
return pattern.matcher(value).find();
}

public static Pattern compilePattern(String raw) throws PatternSyntaxException {
if (raw == null || raw.isEmpty()) {
return null;
}
return Pattern.compile(raw, Pattern.CASE_INSENSITIVE);
}
}
22 changes: 22 additions & 0 deletions android/src/main/java/ee/forgr/capacitor_inappbrowser/Options.java
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ public int getWidth() {
private String preShowScript;
private String toolbarTextColor;
private boolean proxyRequests = false;
private List<NativeProxyRule> outboundProxyRules = new ArrayList<>();
private List<NativeProxyRule> inboundProxyRules = new ArrayList<>();
private boolean materialPicker = false;
private int textZoom = 100; // Default text zoom is 100%
private boolean preventDeeplink = false;
Expand Down Expand Up @@ -268,6 +270,26 @@ public void setProxyRequests(boolean proxyRequests) {
this.proxyRequests = proxyRequests;
}

public List<NativeProxyRule> getOutboundProxyRules() {
return outboundProxyRules;
}

public void setOutboundProxyRules(List<NativeProxyRule> outboundProxyRules) {
this.outboundProxyRules = outboundProxyRules != null ? outboundProxyRules : new ArrayList<>();
}

public List<NativeProxyRule> getInboundProxyRules() {
return inboundProxyRules;
}

public void setInboundProxyRules(List<NativeProxyRule> inboundProxyRules) {
this.inboundProxyRules = inboundProxyRules != null ? inboundProxyRules : new ArrayList<>();
}

public boolean shouldEnableNativeProxy() {
return proxyRequests || !outboundProxyRules.isEmpty() || !inboundProxyRules.isEmpty();
}

public PluginCall getPluginCall() {
return pluginCall;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,16 @@ public interface WebViewCallbacks {

public void confirmBtnClicked(String url);

public void proxyRequestEvent(String requestId, String url, String method, String headersJson, String body, String webviewId);
public void proxyRequestEvent(
String requestId,
String phase,
String url,
String method,
String headersJson,
String body,
Integer status,
String responseHeadersJson,
String responseBody,
String webviewId
);
}
Loading