diff --git a/src/main/java/io/appium/mitmproxy/MitmproxyJava.java b/src/main/java/io/appium/mitmproxy/MitmproxyJava.java index 2da256a..a264d8f 100644 --- a/src/main/java/io/appium/mitmproxy/MitmproxyJava.java +++ b/src/main/java/io/appium/mitmproxy/MitmproxyJava.java @@ -61,11 +61,33 @@ public MitmproxyJava start() throws IOException, TimeoutException { // python script file is zipped inside our jar. extract it into a temporary file. String pythonScriptPath = extractPythonScriptToFile(); + final List mitmproxyStartParams = buildMitmproxyStartParams(pythonScriptPath); + + mitmproxyProcess = new ProcessExecutor() + .command(mitmproxyStartParams) + .redirectOutput(Slf4jStream.ofCaller().asInfo()) + .destroyOnExit() + .start() + .getFuture(); + + if (isLocalModeOnly()) { + waitForLocalModeToStart(); + log.info("Mitmproxy started in local mode"); + } else { + waitForPortToBeInUse(proxyPort); + log.info("Mitmproxy started on port {}", proxyPort); + } + return this; + } + + List buildMitmproxyStartParams(String pythonScriptPath) { final List mitmproxyStartParams = new ArrayList<>(); mitmproxyStartParams.add(mitmproxyPath); mitmproxyStartParams.add("--anticache"); - mitmproxyStartParams.add("-p"); - mitmproxyStartParams.add(String.valueOf(proxyPort)); + if (!isLocalModeOnly()) { + mitmproxyStartParams.add("-p"); + mitmproxyStartParams.add(String.valueOf(proxyPort)); + } mitmproxyStartParams.add("-s"); mitmproxyStartParams.add(pythonScriptPath); @@ -73,17 +95,39 @@ public MitmproxyJava start() throws IOException, TimeoutException { if (isNotEmpty(this.extraMitmdumpParams)) { mitmproxyStartParams.addAll(this.extraMitmdumpParams); } + return mitmproxyStartParams; + } - mitmproxyProcess = new ProcessExecutor() - .command(mitmproxyStartParams) - .redirectOutput(Slf4jStream.ofCaller().asInfo()) - .destroyOnExit() - .start() - .getFuture(); - - waitForPortToBeInUse(proxyPort); - log.info("Mitmproxy started on port {}", proxyPort); - return this; + boolean isLocalModeOnly() { + if (!isNotEmpty(this.extraMitmdumpParams)) { + return false; + } + boolean hasLocalMode = false; + for (int i = 0; i < this.extraMitmdumpParams.size(); i++) { + String param = this.extraMitmdumpParams.get(i); + if (param == null) { + continue; + } + String modeSpec = null; + if ("--mode".equals(param) || "-m".equals(param)) { + if (i + 1 < this.extraMitmdumpParams.size()) { + modeSpec = this.extraMitmdumpParams.get(++i); + } + } else if (param.startsWith("--mode=")) { + modeSpec = param.substring("--mode=".length()); + } else if (param.startsWith("-m=")) { + modeSpec = param.substring("-m=".length()); + } + if (modeSpec != null) { + String normalized = modeSpec.trim().toLowerCase(); + if (normalized.equals("local") || normalized.startsWith("local:") || normalized.startsWith("!local")) { + hasLocalMode = true; + } else { + return false; + } + } + } + return hasLocalMode; } private String extractPythonScriptToFile() throws IOException { @@ -103,8 +147,34 @@ public void stop() throws InterruptedException { if (mitmproxyProcess != null) { mitmproxyProcess.cancel(true); } - server.stop(1000); - waitForPortToBeFree(proxyPort); + if (server != null) { + server.stop(1000); + } + if (!isLocalModeOnly()) { + waitForPortToBeFree(proxyPort); + } + } + + @SneakyThrows + private void waitForLocalModeToStart() { + final LocalDateTime timeoutTime = LocalDateTime.now().plusMinutes(TIMEOUT_FOR_SOCKET_CHECKING_MINS); + + while (true) { + if (timeoutTime.isBefore(LocalDateTime.now())) { + throw new TimeoutException("Timed out waiting for mitmproxy to start in local mode"); + } + if (mitmproxyProcess != null && mitmproxyProcess.isDone()) { + throw new TimeoutException("Mitmproxy process exited prematurely during startup"); + } + if (server != null && !server.getConnections().isEmpty()) { + return; + } + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } @SneakyThrows @@ -138,6 +208,10 @@ private void waitForPortToBeInUse(int port) { throw new TimeoutException("Timed out waiting for mitmproxy to start"); } + if (mitmproxyProcess != null && mitmproxyProcess.isDone()) { + throw new TimeoutException("Mitmproxy process exited prematurely during startup"); + } + try (Socket s = new Socket(LOCALHOST_IP, port)) { return; } catch (IOException ioe) { diff --git a/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java b/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java index 85ea9c3..16bfcc0 100644 --- a/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java +++ b/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java @@ -9,6 +9,7 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeoutException; @@ -131,4 +132,54 @@ public void shouldAddParametersToMitmdumpStart() throws IOException, TimeoutExce verify(spiedParams).toArray(); } + + @Test + public void shouldOmitPortArgumentInLocalModeOnly() { + MitmproxyJava localProxy = new MitmproxyJava( + MITMDUMP_PATH, + (InterceptedMessage m) -> m, + 8089, + Arrays.asList("--mode", "local", "--showhost") + ); + + assertThat(localProxy.isLocalModeOnly()).isTrue(); + assertThat(localProxy.buildMitmproxyStartParams("/path/to/proxy.py")).containsExactly( + MITMDUMP_PATH, + "--anticache", + "-s", + "/path/to/proxy.py", + "--mode", + "local", + "--showhost" + ); + } + + @Test + public void shouldIncludePortArgumentInRegularOrMixedMode() { + MitmproxyJava defaultProxy = new MitmproxyJava( + MITMDUMP_PATH, + (InterceptedMessage m) -> m, + 8089, + Arrays.asList("--showhost") + ); + MitmproxyJava mixedModeProxy = new MitmproxyJava( + MITMDUMP_PATH, + (InterceptedMessage m) -> m, + 8089, + Arrays.asList("--mode", "local:curl", "--mode", "regular") + ); + + assertThat(defaultProxy.isLocalModeOnly()).isFalse(); + assertThat(defaultProxy.buildMitmproxyStartParams("/path/to/proxy.py")).containsExactly( + MITMDUMP_PATH, + "--anticache", + "-p", + "8089", + "-s", + "/path/to/proxy.py", + "--showhost" + ); + assertThat(mixedModeProxy.isLocalModeOnly()).isFalse(); + assertThat(mixedModeProxy.buildMitmproxyStartParams("/path/to/proxy.py")).contains("-p", "8089"); + } } \ No newline at end of file