abstract sclass StandaloneHttpProxy implements AutoCloseable { debug = false; int port = 8443; new Set blockedIPs; abstract ServerSocket makeServerSocket(); ServerSocket serverSocket; void start { serverSocket = new ServerSocket(port); Thread thread = startThread("HTTP Proxy Accept Port " + port, r acceptLoop); print("HTTP proxy on port " + port + " started."); } void acceptLoop { while (true) { try { Socket s = serverSocket.accept(); new Request(s).run(); } catch (SocketTimeoutException e) { } } }}; S rewriteHeaderLine(S line) { /*if (startsWithIgnoreCase(line, "Host:")) line = "Host: " + forwardServer + ":" + forwardPort;*/ ret line; } svoid headerSend(OutputStream out, S header) ctex { print("Sending header: " + header); out.write((header + "\r\n").getBytes("US-ASCII")); } close { dispose serverSocket; } class Request is AutoCloseable { Socket s; // client socket S client; bool blocked; Socket outSocket; new AtomicLong transmitted; new AtomicLong outTransmitted; Thread thread, inThread, outThread; OutputStream outOut; InputStream outIn; *(Socket *s) {} run { client = dropPrefix("/", s.getInetAddress().toString()); // Hmm... how to get the actual IP properly? S threadName = "PROXY: Handling client " + client; if (blocked = blockedIPs.contains(client)) { print("Blocked IP!! " + client); pcall { s.close(); } ret; } thread = startDaemonThread(threadName, r handleIt_impl); } // Request.run void handleIt_impl { try { print("HTTP Proxy Incoming!"); final OutputStream out = s.getOutputStream(); final InputStream in = s.getInputStream(); // collect headers new ByteArrayOutputStream headersOut; //BufferedReader headerReader = new BufferedReader(new InputStreamReader(in, "US-ASCII"), 1); boolean forwardedFor = false; S host = null; while (true) { S line = readHttpHeaderLine(in); if (line == null) ret; print("Header line read: " + quote(line)); if (startsWithIgnoreCase(line, "Host:")) host = dropPrefixIgnoreCase("Host:", line).trim(); else { S rewritten; if (startsWithIgnoreCase(line, "X-Forwarded-For:")) { forwardedFor = true; rewritten = line + ", " + client; } else rewritten = empty(line) ? line : rewriteHeaderLine(line); if (!eq(rewritten, line)) print("Rewritten as: " + quote(rewritten)); if (nempty(rewritten)) { byte[] bytes = (rewritten + "\r\n").getBytes("US-ASCII"); //print("Sending: " + bytesToHex(bytes)); headersOut.write(bytes); } } if (l(line) == 0) break; } HostAndPort hap = forwardServerAndPort(); if (hap == 0 || port == 0) ret with print("Can't forward request"); S forwardServer = hap.host; int port = hap.port; outSocket = new Socket(forwardServer, port); print("Connected to " + forwardServer + ":" + port); outOut = outSocket.getOutputStream(); outIn = outSocket.getInputStream(); outOut.write(headersOut.toByteArray()); if (!forwardedFor) headerSend(outOut, "X-Forwarded-For: " + client); headerSend(outOut, "Host: " + addPortToHost(host, port)); headerSend(outOut, ""); // end of headers outOut.flush(); // forward content in both directions inThread = startThread("Proxy In", r proxyIn_impl); outThread = startThread("Proxy Out", r proxyOut_impl); joinThreads(inThread, outThread); } catch (IOException e) { print("[internal] " + e); } finally { print("Proxy request done, got " + transmitted! + ", sent " + outTransmitted! + " + headers"); close(); } } // end of handleIt_impl void proxyIn_impl ctex { try { byte[] buf = new byte[1]; while (true) { int n = outIn.read(buf); if (n <= 0) return; out.write(buf, 0, n); long t = transmitted.addAndGet(n); if (t % 100000 == 0) print("Transmitted to client: " + t); } } finally { print("Shutting down client output."); s.shutdownOutput(); } } void proxyOut_impl { try { print("Proxy Out started."); byte[] buf = new byte[1]; while (true) { int n = in.read(buf); if (n <= 0) return; outOut.write(buf, 0, n); if (outTransmitted.get() == 0) print("Proxy Out first byte!"); long t = outTransmitted.addAndGet(n); if (t % (debug ? 10 : 100000) == 0) print("Transmitted to server: " + t); } } finally { print("Shutting down server output."); outSocket.shutdownOutput(); } } close { dispose s; dispose outSocket; } } // end of Request }