Libraryless. Click here for Pure Java version (5434L/32K/107K).
1 | !7 |
2 | |
3 | static S vmArgs = "-XX:+PrintGC -Xmx32m"; |
4 | static boolean debug = false; |
5 | |
6 | static int port = 80; |
7 | static S forwardServer = "localhost"; |
8 | |
9 | static Map<S, Integer> forwardMap = litmap( |
10 | "thedevchannel.com", 3000, |
11 | "default", 8081); |
12 | |
13 | static L<S> blockedIPs = litlist("41.193.22.107"); |
14 | |
15 | p { |
16 | // Safety Rebooter - to hold up thedevchannel.com and ai1.lol when admin sleeps! |
17 | |
18 | thread "Rebooter" { |
19 | sleepSeconds(60*60*24); |
20 | nohupJavax(programID(), vmArgs); |
21 | System.exit(0); |
22 | } |
23 | |
24 | // turn off memory leak |
25 | |
26 | noPrintLog(); |
27 | |
28 | // Some regular GC to see accurate mem stats |
29 | |
30 | thread "Auto GC" { |
31 | while true { |
32 | sleepSeconds(60*10); |
33 | System.gc(); |
34 | } |
35 | } |
36 | |
37 | // Now for the actual server... |
38 | |
39 | final ServerSocket serverSocket = new ServerSocket(port); |
40 | |
41 | Thread thread = new Thread("HTTP Proxy Accept Port " + port) { public void run() { |
42 | try { |
43 | while (true) { |
44 | try { |
45 | final Socket s = serverSocket.accept(); |
46 | |
47 | /*thread "Lister" { |
48 | sleepSeconds(5); |
49 | listThreads(); |
50 | }*/ |
51 | |
52 | final S client = dropPrefix("/", s.getInetAddress().toString()); // Hmm... how to get the actual IP properly? |
53 | String threadName = "PROXY: Handling client " + client; |
54 | |
55 | if (blockedIPs.contains(client)) { |
56 | print("Blocked IP!! " + client); |
57 | pcall { s.close(); } |
58 | continue; |
59 | } |
60 | |
61 | Thread t2 = new Thread(threadName) { |
62 | public void run() ctex { |
63 | Socket outSocket = null; |
64 | final new AtomicLong transmitted; |
65 | final new AtomicLong outTransmitted; |
66 | try { |
67 | print("HTTP Proxy Incoming!"); |
68 | |
69 | final OutputStream out = s.getOutputStream(); |
70 | final InputStream in = s.getInputStream(); |
71 | |
72 | // collect headers |
73 | |
74 | new ByteArrayOutputStream headersOut; |
75 | //BufferedReader headerReader = new BufferedReader(new InputStreamReader(in, "US-ASCII"), 1); |
76 | boolean forwardedFor = false; |
77 | S host = null; |
78 | while (true) { |
79 | S line = readHeaderLine(in); |
80 | if (line == null) ret; |
81 | print("Header line read: " + quote(line)); |
82 | |
83 | if (startsWithIgnoreCase(line, "Host:")) |
84 | host = dropPrefixIgnoreCase("Host:", line).trim(); |
85 | else { |
86 | S rewritten; |
87 | if (startsWithIgnoreCase(line, "X-Forwarded-For:")) { |
88 | forwardedFor = true; |
89 | rewritten = line + ", " + client; |
90 | } else |
91 | rewritten = empty(line) ? line : rewriteHeaderLine(line); |
92 | if (!eq(rewritten, line)) |
93 | print("Rewritten as: " + quote(rewritten)); |
94 | if (nempty(rewritten)) { |
95 | byte[] bytes = (rewritten + "\r\n").getBytes("US-ASCII"); |
96 | //print("Sending: " + bytesToHex(bytes)); |
97 | headersOut.write(bytes); |
98 | } |
99 | } |
100 | |
101 | if (l(line) == 0) |
102 | break; |
103 | } |
104 | |
105 | int port = getForwardPort(host); |
106 | |
107 | outSocket = new Socket(forwardServer, port); |
108 | final Socket outS = outSocket; |
109 | print("Connected to " + forwardServer + ":" + port); |
110 | final OutputStream outOut = outSocket.getOutputStream(); |
111 | final InputStream outIn = outSocket.getInputStream(); |
112 | |
113 | outOut.write(headersOut.toByteArray()); |
114 | |
115 | if (!forwardedFor) |
116 | headerSend(outOut, "X-Forwarded-For: " + client); |
117 | |
118 | headerSend(outOut, "Host: " + addPortToHost(host, port)); |
119 | |
120 | headerSend(outOut, ""); // end of headers |
121 | |
122 | outOut.flush(); |
123 | |
124 | // forward content in both directions |
125 | |
126 | //copyStream(outIn, out); |
127 | |
128 | Thread inThread = new Thread("Proxy In") { |
129 | public void run() ctex { |
130 | try { |
131 | byte[] buf = new byte[1]; |
132 | while (true) { |
133 | int n = outIn.read(buf); |
134 | if (n <= 0) return; |
135 | out.write(buf, 0, n); |
136 | long t = transmitted.addAndGet(n); |
137 | if (t % 100000 == 0) print("Transmitted to client: " + t); |
138 | } |
139 | } finally { |
140 | print("Shutting down client output."); |
141 | s.shutdownOutput(); |
142 | } |
143 | } |
144 | }; |
145 | inThread.start(); |
146 | |
147 | Thread outThread = new Thread("Proxy Out") { |
148 | public void run() ctex { |
149 | try { |
150 | print("Proxy Out started."); |
151 | byte[] buf = new byte[1]; |
152 | while (true) { |
153 | int n = in.read(buf); |
154 | if (n <= 0) return; |
155 | outOut.write(buf, 0, n); |
156 | if (outTransmitted.get() == 0) |
157 | print("Proxy Out first byte!"); |
158 | long t = outTransmitted.addAndGet(n); |
159 | if (t % (debug ? 10 : 100000) == 0) print("Transmitted to server: " + t); |
160 | } |
161 | } finally { |
162 | print("Shutting down server output."); |
163 | outS.shutdownOutput(); |
164 | } |
165 | } |
166 | }; |
167 | outThread.start(); |
168 | |
169 | inThread.join(); |
170 | outThread.join(); |
171 | } catch (IOException e) { |
172 | print("[internal] " + e); |
173 | } finally { |
174 | print("Proxy request done, got " + transmitted.get() + ", sent " + outTransmitted.get() + " + headers"); |
175 | pcall { s.close(); } |
176 | pcall { if (outSocket != null) outSocket.close(); } |
177 | } |
178 | } |
179 | }; // Thread t2 |
180 | t2.setDaemon(true); |
181 | t2.start(); |
182 | } catch (SocketTimeoutException e) { |
183 | } |
184 | } |
185 | } catch (IOException e) { |
186 | print("[internal] " + e); |
187 | } |
188 | }}; |
189 | thread.start(); |
190 | |
191 | print("HTTP proxy on port " + port + " started."); |
192 | } |
193 | |
194 | static S rewriteHeaderLine(S line) { |
195 | /*if (startsWithIgnoreCase(line, "Host:")) |
196 | line = "Host: " + forwardServer + ":" + forwardPort;*/ |
197 | ret line; |
198 | } |
199 | |
200 | static int getForwardPort(S host) { |
201 | int i = host.indexOf(':'); |
202 | if (i >= 0) |
203 | host = host.substring(0, i); |
204 | host = host.toLowerCase(); |
205 | for (S key : forwardMap.keySet()) |
206 | if (eq(host, key) || host.endsWith("." + key)) |
207 | ret forwardMap.get(key); |
208 | ret forwardMap.get("default"); |
209 | } |
210 | |
211 | svoid headerSend(OutputStream out, S header) ctex { |
212 | print("Sending header: " + header); |
213 | out.write((header + "\r\n").getBytes("US-ASCII")); |
214 | } |
215 | |
216 | static String readHeaderLine(InputStream inputStream) throws IOException { |
217 | ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); |
218 | int c; |
219 | for (c = inputStream.read(); c != '\n' && c != -1 ; c = inputStream.read()) { |
220 | if (c != '\r') |
221 | byteArrayOutputStream.write(c); |
222 | } |
223 | if (c == -1 && byteArrayOutputStream.size() == 0) { |
224 | return null; |
225 | } |
226 | ret byteArrayOutputStream.toString("US-ASCII"); |
227 | } |
Began life as a copy of #1002783
download show line numbers debug dex old transpilations
Travelled to 15 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, jtubtzbbkimh, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1002822 |
Snippet name: | HTTP Proxy (with partial close, OK) |
Eternal ID of this version: | #1002822/5 |
Text MD5: | e820ec479ced0d5428a4dbebb2c328ef |
Transpilation MD5: | ed270d33b9ca8b3d2cfba60b3fd974b0 |
Author: | stefan |
Category: | eleu |
Type: | JavaX source code |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2021-10-03 17:39:40 |
Source code size: | 7495 bytes / 227 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 751 / 1190 |
Version history: | 4 change(s) |
Referenced in: | [show references] |