Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Delta Between Two Patch Sets: src/org/adblockplus/brazil/RequestHandler.java

Issue 8484110: ABP/Android proxy service (Closed)
Left Patch Set: ABP/Android proxy service Created Nov. 1, 2012, 9:46 a.m.
Right Patch Set: ABP/Android proxy service Created Nov. 12, 2012, 8:53 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
1 package org.adblockplus.brazil; 1 package org.adblockplus.brazil;
2 2
3 import java.io.EOFException; 3 import java.io.EOFException;
4 import java.io.FilterInputStream; 4 import java.io.FilterInputStream;
5 import java.io.FilterOutputStream; 5 import java.io.FilterOutputStream;
6 import java.io.IOException; 6 import java.io.IOException;
7 import java.io.InterruptedIOException; 7 import java.io.InterruptedIOException;
8 import java.io.OutputStream; 8 import java.io.OutputStream;
9 import java.net.ConnectException; 9 import java.net.ConnectException;
10 import java.net.MalformedURLException; 10 import java.net.MalformedURLException;
11 import java.net.URL; 11 import java.net.URL;
12 import java.net.UnknownHostException; 12 import java.net.UnknownHostException;
13 import java.util.List; 13 import java.util.List;
14 import java.util.Properties; 14 import java.util.regex.Pattern;
15 import java.util.zip.GZIPInputStream; 15 import java.util.zip.GZIPInputStream;
16 import java.util.zip.InflaterInputStream; 16 import java.util.zip.InflaterInputStream;
17 17
18 import org.adblockplus.ChunkedOutputStream; 18 import org.adblockplus.ChunkedOutputStream;
19 import org.adblockplus.android.AdblockPlus; 19 import org.adblockplus.android.AdblockPlus;
20 import org.literateprograms.BoyerMoore; 20 import org.literateprograms.BoyerMoore;
21 21
22 import sunlabs.brazil.server.Handler;
23 import sunlabs.brazil.server.Request; 22 import sunlabs.brazil.server.Request;
24 import sunlabs.brazil.server.Server; 23 import sunlabs.brazil.server.Server;
25 import sunlabs.brazil.util.MatchString; 24 import sunlabs.brazil.util.MatchString;
26 import sunlabs.brazil.util.http.HttpInputStream; 25 import sunlabs.brazil.util.http.HttpInputStream;
27 import sunlabs.brazil.util.http.HttpRequest; 26 import sunlabs.brazil.util.http.HttpRequest;
28 import sunlabs.brazil.util.http.MimeHeaders; 27 import sunlabs.brazil.util.http.MimeHeaders;
29 import android.util.Log; 28 import android.util.Log;
30 29
31 /** 30 /**
32 * The <code>RequestHandler</code> implements a proxy service optionally 31 * The <code>RequestHandler</code> implements a proxy service optionally
(...skipping 22 matching lines...) Expand all
55 * 54 *
56 * <pre> 55 * <pre>
57 * handler=adblock 56 * handler=adblock
58 * adblock.class=org.adblockplus.brazil.RequestHandler 57 * adblock.class=org.adblockplus.brazil.RequestHandler
59 * </pre> 58 * </pre>
60 * 59 *
61 * See the description under {@link sunlabs.brazil.server.Handler#respond 60 * See the description under {@link sunlabs.brazil.server.Handler#respond
62 * respond} for a more detailed explanation. 61 * respond} for a more detailed explanation.
63 */ 62 */
64 63
65 public class RequestHandler implements Handler 64 public class RequestHandler extends BaseRequestHandler
66 { 65 {
67 public static final String PROXY_HOST = "proxyHost";
68 public static final String PROXY_PORT = "proxyPort";
69 public static final String AUTH = "auth";
70
71 private AdblockPlus application; 66 private AdblockPlus application;
72 private String prefix;
73
74 private String via; 67 private String via;
75 68 private static Pattern RE_HTTP = Pattern.compile("^https?:");
76 private String proxyHost; 69
77 private int proxyPort = 80; 70 private boolean shouldLogHeaders;
78 private String auth;
79
80 private boolean shouldLog; // if true, log all headers
Felix Dahlke 2012/11/09 06:04:32 Maybe call it shouldLogHeaders then? That'd make t
Andrey Novikov 2012/11/09 09:23:47 Done.
81 71
82 @Override 72 @Override
83 public boolean init(Server server, String prefix) 73 public boolean init(Server server, String prefix)
84 { 74 {
85 this.prefix = prefix; 75 super.init(server, prefix);
76
86 application = AdblockPlus.getApplication(); 77 application = AdblockPlus.getApplication();
87 78 shouldLogHeaders = (server.props.getProperty(prefix + "proxylog") != null);
88 Properties props = server.props;
89
90 proxyHost = props.getProperty(prefix + PROXY_HOST);
91
92 String s = props.getProperty(prefix + PROXY_PORT);
93 try
94 {
95 proxyPort = Integer.decode(s).intValue();
96 }
97 catch (Exception e)
98 {
Felix Dahlke 2012/11/09 06:04:32 It seems this case occurs when the port is not set
Andrey Novikov 2012/11/09 09:23:47 Done.
99 }
100
101 auth = props.getProperty(prefix + AUTH);
102
103 shouldLog = (props.getProperty(prefix + "proxylog") != null);
104
105 via = " " + server.hostName + ":" + server.listen.getLocalPort() + " (" + se rver.name + ")"; 79 via = " " + server.hostName + ":" + server.listen.getLocalPort() + " (" + se rver.name + ")";
106 80
107 return true; 81 return true;
108 } 82 }
109 83
110 @Override 84 @Override
111 public boolean respond(Request request) throws IOException 85 public boolean respond(Request request) throws IOException
112 { 86 {
113 boolean block = false; 87 boolean block = false;
114 String reqHost = null; 88 String reqHost = null;
115 String refHost = null; 89 String refHost = null;
116 90
91 String referrer = request.getRequestHeader("referer");
92
117 try 93 try
118 { 94 {
119 reqHost = (new URL(request.url)).getHost(); 95 reqHost = (new URL(request.url)).getHost();
120 refHost = (new URL(request.getRequestHeader("referer"))).getHost(); 96 if (referrer != null)
97 refHost = (new URL(referrer)).getHost();
121 } 98 }
122 catch (MalformedURLException e) 99 catch (MalformedURLException e)
123 { 100 {
Felix Dahlke 2012/11/09 06:04:32 Can we really safely go on in this case? At least
Andrey Novikov 2012/11/09 09:23:47 We are transparent, it's not our deal if it's malf
Felix Dahlke 2012/11/09 14:40:46 Hm, but should we just go on with a null reqHost a
Andrey Novikov 2012/11/12 08:53:12 In fact this exception is for referrer, request ca
101 // We are transparent, it's not our deal if it's malformed.
124 } 102 }
125 103
126 try 104 try
127 { 105 {
128 block = application.matches(request.url, request.query, reqHost, refHost, request.getRequestHeader("accept")); 106 if (referrer != null)
107 block = application.matches(request.url, request.query, reqHost, refHost , request.getRequestHeader("accept"));
129 } 108 }
130 catch (Exception e) 109 catch (Exception e)
131 { 110 {
132 Log.e(prefix, "Filter error", e); 111 Log.e(prefix, "Filter error", e);
133 } 112 }
134 113
135 request.log(Server.LOG_LOG, prefix, block + ": " + request.url); 114 request.log(Server.LOG_LOG, prefix, block + ": " + request.url + " ("+ refHo st +")");
115
116 int count = request.server.requestCount;
117 if (shouldLogHeaders)
118 {
119 System.err.println(dumpHeaders(count, request, request.headers, true));
120 }
136 121
137 if (block) 122 if (block)
138 { 123 {
139 request.sendError(502, "Blocked by Adblock Plus"); 124 request.sendHeaders(204, null, 0);
140 return true; 125 return true;
141 } 126 }
142 127
143 // Do not further process non-http requests 128 // Do not further process non-http requests
144 if (request.url.startsWith("http:") == false && request.url.startsWith("http s:") == false) 129 if (!RE_HTTP.matcher(request.url).find())
Felix Dahlke 2012/11/09 06:04:32 How about if (!request.url.matches("^https?:")) ?
Andrey Novikov 2012/11/09 09:23:47 I thought it would be faster...
Felix Dahlke 2012/11/09 14:40:46 Premature optimisation? :) I think you should go w
Andrey Novikov 2012/11/12 08:53:12 Done.
145 { 130 {
146 return false; 131 return false;
147 } 132 }
148 133
149 String url = request.url; 134 String url = request.url;
150 135
151 if ((request.query != null) && (request.query.length() > 0)) 136 if ((request.query != null) && (request.query.length() > 0))
152 { 137 {
153 url += "?" + request.query; 138 url += "?" + request.query;
154 }
155
156 int count = request.server.requestCount;
157 if (shouldLog)
158 {
159 System.err.println(dumpHeaders(count, request, request.headers, true));
160 } 139 }
161 140
162 /* 141 /*
163 * "Proxy-Connection" may be used (instead of just "Connection") 142 * "Proxy-Connection" may be used (instead of just "Connection")
164 * to keep alive a connection between a client and this proxy. 143 * to keep alive a connection between a client and this proxy.
165 */ 144 */
166 String pc = request.headers.get("Proxy-Connection"); 145 String pc = request.headers.get("Proxy-Connection");
167 if (pc != null) 146 if (pc != null)
168 { 147 {
169 request.connectionHeader = "Proxy-Connection"; 148 request.connectionHeader = "Proxy-Connection";
(...skipping 19 matching lines...) Expand all
189 168
190 if (request.postData != null) 169 if (request.postData != null)
191 { 170 {
192 OutputStream out = target.getOutputStream(); 171 OutputStream out = target.getOutputStream();
193 out.write(request.postData); 172 out.write(request.postData);
194 out.close(); 173 out.close();
195 } 174 }
196 175
197 target.connect(); 176 target.connect();
198 177
199 if (shouldLog) 178 if (shouldLogHeaders)
200 { 179 {
201 System.err.println(" " + target.status + "\n" + dumpHeaders(count, request, target.responseHeaders, false)); 180 System.err.println(" " + target.status + "\n" + dumpHeaders(count, request, target.responseHeaders, false));
202 } 181 }
203 HttpRequest.removePointToPointHeaders(target.responseHeaders, true); 182 HttpRequest.removePointToPointHeaders(target.responseHeaders, true);
204 183
205 request.setStatus(target.getResponseCode()); 184 request.setStatus(target.getResponseCode());
206 target.responseHeaders.copyTo(request.responseHeaders); 185 target.responseHeaders.copyTo(request.responseHeaders);
207 try 186 try
208 { 187 {
209 request.responseHeaders.add("Via", target.status.substring(0, 8) + via); 188 request.responseHeaders.add("Via", target.status.substring(0, 8) + via);
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 request.responseHeaders.remove("Content-Length"); 259 request.responseHeaders.remove("Content-Length");
281 request.responseHeaders.remove("Content-Encoding"); 260 request.responseHeaders.remove("Content-Encoding");
282 out = new ChunkedOutputStream(request.out); 261 out = new ChunkedOutputStream(request.out);
283 request.responseHeaders.add("Transfer-Encoding", "chunked"); 262 request.responseHeaders.add("Transfer-Encoding", "chunked");
284 size = Integer.MAX_VALUE; 263 size = Integer.MAX_VALUE;
285 } 264 }
286 265
287 request.sendHeaders(-1, null, -1); 266 request.sendHeaders(-1, null, -1);
288 267
289 byte[] buf = new byte[Math.min(4096, size)]; 268 byte[] buf = new byte[Math.min(4096, size)];
290
291 Log.e(prefix, request.url);
Felix Dahlke 2012/11/09 06:04:32 Log.d()?
Andrey Novikov 2012/11/09 09:23:47 Done.
292 269
293 boolean sent = selectors == null; 270 boolean sent = selectors == null;
294 // TODO Do we need to set encoding here? 271 // TODO Do we need to set encoding here?
295 BoyerMoore matcher = new BoyerMoore("<html".getBytes()); 272 BoyerMoore matcher = new BoyerMoore("<html".getBytes());
296 273
297 while (size > 0) 274 while (size > 0)
298 { 275 {
299 out.flush(); 276 out.flush();
300 277
301 count = in.read(buf, 0, Math.min(buf.length, size)); 278 count = in.read(buf, 0, Math.min(buf.length, size));
(...skipping 24 matching lines...) Expand all
326 out.write(buf, 0, count); 303 out.write(buf, 0, count);
327 } 304 }
328 catch (IOException e) 305 catch (IOException e)
329 { 306 {
330 break; 307 break;
331 } 308 }
332 } 309 }
333 // The correct way would be to close ChunkedOutputStream 310 // The correct way would be to close ChunkedOutputStream
334 // but we can not do it because underlying output stream is 311 // but we can not do it because underlying output stream is
335 // used later in caller code. So we use this ugly hack: 312 // used later in caller code. So we use this ugly hack:
336 try 313 if (out instanceof ChunkedOutputStream)
337 {
338 ((ChunkedOutputStream) out).writeFinalChunk(); 314 ((ChunkedOutputStream) out).writeFinalChunk();
Felix Dahlke 2012/11/09 06:04:32 Why use instanceof? That would avoid class cast ex
Andrey Novikov 2012/11/09 09:23:47 Done.
339 }
340 catch (ClassCastException e)
341 {
342 // ignore
343 }
344 } 315 }
345 } 316 }
346 catch (InterruptedIOException e) 317 catch (InterruptedIOException e)
347 { 318 {
348 /* 319 /*
349 * Read timeout while reading from the remote side. We use a 320 * Read timeout while reading from the remote side. We use a
350 * read timeout in case the target never responds. 321 * read timeout in case the target never responds.
351 */ 322 */
352 request.sendError(408, "Timeout / No response"); 323 request.sendError(408, "Timeout / No response");
353 } 324 }
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
407 } 378 }
408 379
409 for (int i = 0; i < headers.size(); i++) 380 for (int i = 0; i < headers.size(); i++)
410 { 381 {
411 sb.append(prompt).append(headers.getKey(i)); 382 sb.append(prompt).append(headers.getKey(i));
412 sb.append(": ").append(headers.get(i)).append("\n"); 383 sb.append(": ").append(headers.get(i)).append("\n");
413 } 384 }
414 return (sb.toString()); 385 return (sb.toString());
415 } 386 }
416 } 387 }
LEFTRIGHT

Powered by Google App Engine
This is Rietveld