diff --git a/.gitignore b/.gitignore index 3b9e72b..43936ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .gradle -.idea/* +.idea out build __pycache__ diff --git a/src/main/java/io/appium/mitmproxy/InterceptedMessage.java b/src/main/java/io/appium/mitmproxy/InterceptedMessage.java index 47326b2..527c7be 100644 --- a/src/main/java/io/appium/mitmproxy/InterceptedMessage.java +++ b/src/main/java/io/appium/mitmproxy/InterceptedMessage.java @@ -26,9 +26,10 @@ public class InterceptedMessage { public int responseCode; public List responseHeaders; + private final static ObjectMapper objectMapper = new ObjectMapper(); public InterceptedMessage(ByteBuffer buffer) throws IOException { - buffer = buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.order(ByteOrder.LITTLE_ENDIAN); int metadataSize = buffer.getInt(); int request_content_size = buffer.getInt(); @@ -43,8 +44,7 @@ public InterceptedMessage(ByteBuffer buffer) throws IOException { responseBody = new byte[response_content_size]; buffer.get(responseBody); - ObjectMapper mapper = new ObjectMapper(); - JsonNode metadata = mapper.readTree(metadataBytes); + JsonNode metadata = objectMapper.readTree(metadataBytes); requestMethod = metadata.get("request").get("method").asText(); requestURL = new URL(metadata.get("request").get("url").asText()); JsonNode headers = metadata.get("request").get("headers"); @@ -72,13 +72,12 @@ public ByteBuffer serializedResponseToMitmproxy() throws JsonProcessingException // create JSON for metadata. Which is the responseCode and responseHeaders. // while we're at it, set the Content-Length header - ObjectMapper mapper = new ObjectMapper(); - ObjectNode metadataRoot = mapper.createObjectNode(); + ObjectNode metadataRoot = objectMapper.createObjectNode(); metadataRoot.put("status_code", responseCode); - ArrayNode headersNode = mapper.createArrayNode(); + ArrayNode headersNode = objectMapper.createArrayNode(); List headerNodes = responseHeaders.stream().map((h) -> { - ArrayNode headerPair = mapper.createArrayNode(); + ArrayNode headerPair = objectMapper.createArrayNode(); headerPair.add(h[0]); if (h[0].equals("content-length")) { headerPair.add(Integer.toString(contentLength)); @@ -90,11 +89,10 @@ public ByteBuffer serializedResponseToMitmproxy() throws JsonProcessingException headersNode.addAll(headerNodes); metadataRoot.set("headers", headersNode); - String metadataJson = mapper.writeValueAsString(metadataRoot); + String metadataJson = objectMapper.writeValueAsString(metadataRoot); byte[] metadata = metadataJson.getBytes(StandardCharsets.UTF_8); int metadataLength = metadata.length; - ByteBuffer buffer = ByteBuffer.allocate(8 + metadataLength + contentLength); buffer.order(ByteOrder.LITTLE_ENDIAN); buffer.putInt(metadataLength); diff --git a/src/main/java/io/appium/mitmproxy/MitmproxyJava.java b/src/main/java/io/appium/mitmproxy/MitmproxyJava.java index 61a7897..b940a79 100644 --- a/src/main/java/io/appium/mitmproxy/MitmproxyJava.java +++ b/src/main/java/io/appium/mitmproxy/MitmproxyJava.java @@ -1,5 +1,6 @@ package io.appium.mitmproxy; +import org.apache.commons.io.IOUtils; import org.zeroturnaround.exec.ProcessExecutor; import org.zeroturnaround.exec.ProcessResult; import org.zeroturnaround.exec.stream.slf4j.Slf4jStream; @@ -9,8 +10,8 @@ import java.io.IOException; import java.io.InputStream; import java.net.InetSocketAddress; +import java.net.ServerSocket; import java.net.Socket; -import java.net.URISyntaxException; import java.util.concurrent.Future; import java.util.concurrent.TimeoutException; import java.util.function.Function; @@ -18,18 +19,27 @@ public class MitmproxyJava { private String mitmproxyPath; + private Function messageInterceptor; + private int proxyPort; private MitmproxyServer server; private Future mitmproxyProcess; public static final int WEBSOCKET_PORT = 8765; - public MitmproxyJava(String mitmproxyPath, Function messageInterceptor) { + public MitmproxyJava(String mitmproxyPath, Function messageInterceptor, int proxyPort) { this.mitmproxyPath = mitmproxyPath; - server = new MitmproxyServer(new InetSocketAddress("localhost", WEBSOCKET_PORT), messageInterceptor); - server.start(); + this.messageInterceptor = messageInterceptor; + this.proxyPort = proxyPort; + } + + public MitmproxyJava(String mitmproxyPath, Function messageInterceptor) { + this(mitmproxyPath, messageInterceptor, 8080); } - public void start() throws IOException, TimeoutException, URISyntaxException { - System.out.println("starting mitmproxy on port 8080"); + public void start() throws IOException, TimeoutException { + System.out.println("starting mitmproxy on port " + proxyPort); + + server = new MitmproxyServer(new InetSocketAddress("localhost", WEBSOCKET_PORT), messageInterceptor); + server.start(); // python script file is zipped inside our jar. extract it into a temporary file. String pythonScriptPath = extractPythonScriptToFile(); @@ -41,35 +51,25 @@ public void start() throws IOException, TimeoutException, URISyntaxException { .start() .getFuture(); - waitForPortToBeInUse(8080); - System.out.println("mitmproxy started on port 8080"); + waitForPortToBeInUse(proxyPort); + System.out.println("mitmproxy started on port " + proxyPort); } - private String extractPythonScriptToFile() throws URISyntaxException, IOException { + private String extractPythonScriptToFile() throws IOException { File outfile = File.createTempFile("mitmproxy-python-plugin", ".py"); - InputStream instream = getClass().getClassLoader().getResourceAsStream("scripts/proxy.py"); - FileOutputStream outstream = new FileOutputStream(outfile); - - byte[] buffer = new byte[1024]; + try ( + InputStream inputStream = getClass().getClassLoader().getResourceAsStream("scripts/proxy.py"); + FileOutputStream outputStream = new FileOutputStream(outfile)) { - int length; - /*copying the contents from input stream to - * output stream using read and write methods - */ - while ((length = instream.read(buffer)) > 0){ - outstream.write(buffer, 0, length); + IOUtils.copy(inputStream, outputStream); } - //Closing the input/output file streams - instream.close(); - outstream.close(); - return outfile.getCanonicalPath(); } - public void stop() throws IOException, InterruptedException { + public void stop() throws InterruptedException { if (mitmproxyProcess != null) { mitmproxyProcess.cancel(true); } @@ -79,30 +79,17 @@ public void stop() throws IOException, InterruptedException { private void waitForPortToBeInUse(int port) throws TimeoutException { boolean inUse = false; - Socket s = null; int tries = 0; int maxTries = 60 * 1000 / 100; while (!inUse) { - try - { - s = new Socket("localhost", port); - } - catch (IOException e) - { + + try (Socket s = new Socket("localhost", port)) { + break; + } catch (IOException e) { inUse = false; } - finally - { - if(s != null) { - inUse = true; - try { - s.close(); - } catch (Exception e) { - } - break; - } - } + tries++; if (tries == maxTries) { throw new TimeoutException("Timed out waiting for mitmproxy to start"); diff --git a/src/main/java/io/appium/mitmproxy/MitmproxyServer.java b/src/main/java/io/appium/mitmproxy/MitmproxyServer.java index 309af12..d0f97ce 100644 --- a/src/main/java/io/appium/mitmproxy/MitmproxyServer.java +++ b/src/main/java/io/appium/mitmproxy/MitmproxyServer.java @@ -31,40 +31,39 @@ public void onClose(WebSocket conn, int code, String reason, boolean remote) { @Override public void onMessage(WebSocket conn, String message) { - System.out.println("received message from " + conn.getRemoteSocketAddress() + ": " + message); + System.out.println("received message from " + conn.getRemoteSocketAddress() + ": " + message); } @Override - public void onMessage( WebSocket conn, ByteBuffer message ) { + public void onMessage(WebSocket conn, ByteBuffer rawInputMessage) { InterceptedMessage intercepted = null; - InterceptedMessage modifiedMessage = null; try { - intercepted = new InterceptedMessage(message); + intercepted = new InterceptedMessage(rawInputMessage); } catch (IOException e) { - System.out.println("Could not parse message"); + System.out.println("Could not parse rawInputMessage"); e.printStackTrace(); } - modifiedMessage = interceptor.apply(intercepted); + InterceptedMessage modifiedMessage = interceptor.apply(intercepted); // if the supplied interceptor function does not return a message, assume no changes were intended and just // complete the request if (modifiedMessage == null) { - modifiedMessage = intercepted; - } - - try { - conn.send(modifiedMessage.serializedResponseToMitmproxy()); - } catch (JsonProcessingException e) { - System.out.println("Could not encode response to mitmproxy"); - e.printStackTrace(); + conn.send(rawInputMessage); + } else { + try { + conn.send(modifiedMessage.serializedResponseToMitmproxy()); + } catch (JsonProcessingException e) { + System.out.println("Could not encode response to mitmproxy"); + e.printStackTrace(); + } } } @Override public void onError(WebSocket conn, Exception ex) { - System.err.println("an error occured on connection " + conn.getRemoteSocketAddress() + ":" + ex); + System.err.println("an error occured on connection " + conn.getRemoteSocketAddress() + ":" + ex); } @Override diff --git a/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java b/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java index f893cfc..ac3fecb 100644 --- a/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java +++ b/src/test/java/io/appium/mitmproxy/MitmproxyJavaTest.java @@ -11,28 +11,32 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; import static junit.framework.TestCase.assertTrue; public class MitmproxyJavaTest { +// private static final String MITMDUMP_PATH = "/usr/local/bin/mitmdump"; + private static final String MITMDUMP_PATH = "C:\\Python37\\Scripts\\mitmdump"; + + @Test - public void ConstructorTest() throws URISyntaxException, IOException, InterruptedException { - MitmproxyJava proxy = new MitmproxyJava("/usr/local/bin/mitmdump", (InterceptedMessage m) -> { + public void ConstructorTest() throws InterruptedException, IOException, TimeoutException { + MitmproxyJava proxy = new MitmproxyJava(MITMDUMP_PATH, (InterceptedMessage m) -> { System.out.println(m.requestURL.toString()); return m; }); + proxy.start(); System.out.println("advanced in test"); proxy.stop(); } @Test - public void SimpleTest() throws InterruptedException, ExecutionException, TimeoutException, IOException, URISyntaxException, UnirestException { - List messages = new ArrayList(); + public void SimpleTest() throws InterruptedException, TimeoutException, IOException, URISyntaxException, UnirestException { + List messages = new ArrayList<>(); - MitmproxyJava proxy = new MitmproxyJava("/usr/local/bin/mitmdump", (InterceptedMessage m) -> { + MitmproxyJava proxy = new MitmproxyJava(MITMDUMP_PATH, (InterceptedMessage m) -> { messages.add(m); return m; }); @@ -51,10 +55,10 @@ public void SimpleTest() throws InterruptedException, ExecutionException, Timeou } @Test - public void NullInterceptorReturnTest() throws InterruptedException, ExecutionException, TimeoutException, IOException, URISyntaxException, UnirestException { - List messages = new ArrayList(); + public void NullInterceptorReturnTest() throws InterruptedException, TimeoutException, IOException, URISyntaxException, UnirestException { + List messages = new ArrayList<>(); - MitmproxyJava proxy = new MitmproxyJava("/usr/local/bin/mitmdump", (InterceptedMessage m) -> { + MitmproxyJava proxy = new MitmproxyJava(MITMDUMP_PATH, (InterceptedMessage m) -> { messages.add(m); return null; }); @@ -73,10 +77,10 @@ public void NullInterceptorReturnTest() throws InterruptedException, ExecutionEx } @Test - public void ResponseModificationTest() throws InterruptedException, ExecutionException, TimeoutException, IOException, URISyntaxException, UnirestException { - List messages = new ArrayList(); + public void ResponseModificationTest() throws InterruptedException, TimeoutException, IOException, URISyntaxException, UnirestException { + List messages = new ArrayList<>(); - MitmproxyJava proxy = new MitmproxyJava("/usr/local/bin/mitmdump", (InterceptedMessage m) -> { + MitmproxyJava proxy = new MitmproxyJava(MITMDUMP_PATH, (InterceptedMessage m) -> { messages.add(m); m.responseBody = "Hi from Test".getBytes(StandardCharsets.UTF_8); return m;