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

Side by Side Diff: src/org/adblockplus/brazil/RequestHandler.java

Issue 5697499218051072: Usage of new API, cleanups (reduced) (Closed)
Patch Set: Fixed leftover first-batch review issues. Created April 16, 2014, 5:51 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * This file is part of Adblock Plus <http://adblockplus.org/>, 2 * This file is part of Adblock Plus <http://adblockplus.org/>,
3 * Copyright (C) 2006-2014 Eyeo GmbH 3 * Copyright (C) 2006-2014 Eyeo GmbH
4 * 4 *
5 * Adblock Plus is free software: you can redistribute it and/or modify 5 * Adblock Plus is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as 6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation. 7 * published by the Free Software Foundation.
8 * 8 *
9 * Adblock Plus is distributed in the hope that it will be useful, 9 * Adblock Plus is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
(...skipping 30 matching lines...) Expand all
41 41
42 import sunlabs.brazil.server.Request; 42 import sunlabs.brazil.server.Request;
43 import sunlabs.brazil.server.Server; 43 import sunlabs.brazil.server.Server;
44 import sunlabs.brazil.util.MatchString; 44 import sunlabs.brazil.util.MatchString;
45 import sunlabs.brazil.util.http.HttpInputStream; 45 import sunlabs.brazil.util.http.HttpInputStream;
46 import sunlabs.brazil.util.http.HttpRequest; 46 import sunlabs.brazil.util.http.HttpRequest;
47 import android.util.Log; 47 import android.util.Log;
48 48
49 /** 49 /**
50 * The <code>RequestHandler</code> implements a proxy service optionally 50 * The <code>RequestHandler</code> implements a proxy service optionally
51 * modifying output. 51 * modifying output. The
52 * The following configuration parameters are used to initialize this 52 * following configuration parameters are used to initialize this
53 * <code>Handler</code>: 53 * <code>Handler</code>:
54 * <dl class=props> 54 * <dl class=props>
55 * 55 *
56 * <dt>prefix, suffix, glob, match 56 * <dt>prefix, suffix, glob, match
57 * <dd>Specify the URL that triggers this handler. (See {@link MatchString}). 57 * <dd>Specify the URL that triggers this handler. (See {@link MatchString}).
58 * <dt>auth 58 * <dt>auth
59 * <dd>The value of the proxy-authenticate header (if any) sent to the upstream 59 * <dd>The value of the proxy-authenticate header (if any) sent to the upstream
60 * proxy 60 * proxy
61 * <dt>proxyHost 61 * <dt>proxyHost
62 * <dd>If specified, the name of the upstream proxy 62 * <dd>If specified, the name of the upstream proxy
(...skipping 12 matching lines...) Expand all
75 * handler=adblock 75 * handler=adblock
76 * adblock.class=org.adblockplus.brazil.RequestHandler 76 * adblock.class=org.adblockplus.brazil.RequestHandler
77 * </pre> 77 * </pre>
78 * 78 *
79 * See the description under {@link sunlabs.brazil.server.Handler#respond 79 * See the description under {@link sunlabs.brazil.server.Handler#respond
80 * respond} for a more detailed explanation. 80 * respond} for a more detailed explanation.
81 */ 81 */
82 82
83 public class RequestHandler extends BaseRequestHandler 83 public class RequestHandler extends BaseRequestHandler
84 { 84 {
85 private static final Pattern RE_HTTP = Pattern.compile("^https?:");
86
85 private AdblockPlus application; 87 private AdblockPlus application;
86 private String via; 88 private String via;
87 private static Pattern RE_HTTP = Pattern.compile("^https?:");
88 89
89 @Override 90 @Override
90 public boolean init(Server server, String prefix) 91 public boolean init(final Server server, final String prefix)
91 { 92 {
92 super.init(server, prefix); 93 super.init(server, prefix);
93 94
94 application = AdblockPlus.getApplication(); 95 application = AdblockPlus.getApplication();
95 via = " " + server.hostName + ":" + server.listen.getLocalPort() + " (" + se rver.name + ")"; 96 via = " " + server.hostName + ":" + server.listen.getLocalPort() + " (" + se rver.name + ")";
96 97
97 return true; 98 return true;
98 } 99 }
99 100
100 @Override 101 @Override
101 public boolean respond(Request request) throws IOException 102 public boolean respond(final Request request) throws IOException
102 { 103 {
103 boolean block = false; 104 boolean block = false;
104 105
105 try 106 try
106 { 107 {
107 block = application.matches(request.url, request.query, request.getRequest Header("referer"), request.getRequestHeader("accept")); 108 block = application.matches(request.url, request.query, request.getRequest Header("referer"), request.getRequestHeader("accept"));
108 } 109 }
109 catch (Exception e) 110 catch (final Exception e)
110 { 111 {
111 Log.e(prefix, "Filter error", e); 112 Log.e(prefix, "Filter error", e);
112 } 113 }
113 114
114 request.log(Server.LOG_LOG, prefix, block + ": " + request.url); 115 request.log(Server.LOG_LOG, prefix, block + ": " + request.url);
115 116
116 int count = request.server.requestCount; 117 int count = request.server.requestCount;
117 if (shouldLogHeaders) 118 if (shouldLogHeaders)
118 { 119 {
119 System.err.println(dumpHeaders(count, request, request.headers, true)); 120 System.err.println(dumpHeaders(count, request, request.headers, true));
(...skipping 15 matching lines...) Expand all
135 136
136 if ((request.query != null) && (request.query.length() > 0)) 137 if ((request.query != null) && (request.query.length() > 0))
137 { 138 {
138 url += "?" + request.query; 139 url += "?" + request.query;
139 } 140 }
140 141
141 /* 142 /*
142 * "Proxy-Connection" may be used (instead of just "Connection") 143 * "Proxy-Connection" may be used (instead of just "Connection")
143 * to keep alive a connection between a client and this proxy. 144 * to keep alive a connection between a client and this proxy.
144 */ 145 */
145 String pc = request.headers.get("Proxy-Connection"); 146 final String pc = request.headers.get("Proxy-Connection");
146 if (pc != null) 147 if (pc != null)
147 { 148 {
148 request.connectionHeader = "Proxy-Connection"; 149 request.connectionHeader = "Proxy-Connection";
149 request.keepAlive = pc.equalsIgnoreCase("Keep-Alive"); 150 request.keepAlive = pc.equalsIgnoreCase("Keep-Alive");
150 } 151 }
151 152
152 HttpRequest.removePointToPointHeaders(request.headers, false); 153 HttpRequest.removePointToPointHeaders(request.headers, false);
153 154
154 HttpRequest target = new HttpRequest(url); 155 final HttpRequest target = new HttpRequest(url);
155 try 156 try
156 { 157 {
157 target.setMethod(request.method); 158 target.setMethod(request.method);
158 request.headers.copyTo(target.requestHeaders); 159 request.headers.copyTo(target.requestHeaders);
159 160
160 if (proxyHost != null) 161 if (proxyHost != null)
161 { 162 {
162 target.setProxy(proxyHost, proxyPort); 163 target.setProxy(proxyHost, proxyPort);
163 if (auth != null) 164 if (auth != null)
164 { 165 {
165 target.requestHeaders.add("Proxy-Authorization", auth); 166 target.requestHeaders.add("Proxy-Authorization", auth);
166 } 167 }
167 } 168 }
168 169
169 if (request.postData != null) 170 if (request.postData != null)
170 { 171 {
171 OutputStream out = target.getOutputStream(); 172 final OutputStream out = target.getOutputStream();
172 out.write(request.postData); 173 out.write(request.postData);
173 out.close(); 174 out.close();
174 } 175 }
175 else 176 else
176 { 177 {
177 target.setHttpInputStream(request.in); 178 target.setHttpInputStream(request.in);
178 } 179 }
179 target.connect(); 180 target.connect();
180 181
181 if (shouldLogHeaders) 182 if (shouldLogHeaders)
182 { 183 {
183 System.err.println(" " + target.status + "\n" + dumpHeaders(count, request, target.responseHeaders, false)); 184 System.err.println(" " + target.status + "\n" + dumpHeaders(count, request, target.responseHeaders, false));
184 } 185 }
185 HttpRequest.removePointToPointHeaders(target.responseHeaders, true); 186 HttpRequest.removePointToPointHeaders(target.responseHeaders, true);
186 187
187 request.setStatus(target.getResponseCode()); 188 request.setStatus(target.getResponseCode());
188 target.responseHeaders.copyTo(request.responseHeaders); 189 target.responseHeaders.copyTo(request.responseHeaders);
189 try 190 try
190 { 191 {
191 request.responseHeaders.add("Via", target.status.substring(0, 8) + via); 192 request.responseHeaders.add("Via", target.status.substring(0, 8) + via);
192 } 193 }
193 catch (StringIndexOutOfBoundsException e) 194 catch (final StringIndexOutOfBoundsException e)
194 { 195 {
195 request.responseHeaders.add("Via", via); 196 request.responseHeaders.add("Via", via);
196 } 197 }
197 198
198 // Detect if we need to add ElemHide filters 199 // Detect if we need to add ElemHide filters
199 String type = request.responseHeaders.get("Content-Type"); 200 final String type = request.responseHeaders.get("Content-Type");
200 201
201 String[] selectors = null; 202 String[] selectors = null;
202 if (type != null && type.toLowerCase().startsWith("text/html")) 203 if (type != null && type.toLowerCase().startsWith("text/html"))
203 { 204 {
204 String reqHost = ""; 205 String reqHost = "";
205 206
206 try 207 try
207 { 208 {
208 reqHost = (new URL(request.url)).getHost(); 209 reqHost = (new URL(request.url)).getHost();
209 } 210 }
210 catch (MalformedURLException e) 211 catch (final MalformedURLException e)
211 { 212 {
212 // We are transparent, it's not our deal if it's malformed. 213 // We are transparent, it's not our deal if it's malformed.
213 } 214 }
214 215
215 selectors = application.getSelectorsForDomain(reqHost); 216 selectors = application.getSelectorsForDomain(reqHost);
216 } 217 }
217 // If no filters are applicable just pass through the response 218 // If no filters are applicable just pass through the response
218 if (selectors == null || target.getResponseCode() != 200) 219 if (selectors == null || target.getResponseCode() != 200)
219 { 220 {
220 int contentLength = target.getContentLength(); 221 final int contentLength = target.getContentLength();
221 if (contentLength == 0) 222 if (contentLength == 0)
222 { 223 {
223 // we do not use request.sendResponse to avoid arbitrary 224 // we do not use request.sendResponse to avoid arbitrary
224 // 200 -> 204 response code conversion 225 // 200 -> 204 response code conversion
225 request.sendHeaders(-1, null, -1); 226 request.sendHeaders(-1, null, -1);
226 } 227 }
227 else 228 else
228 { 229 {
229 request.sendResponse(target.getInputStream(), contentLength, null, -1) ; 230 request.sendResponse(target.getInputStream(), contentLength, null, -1) ;
230 } 231 }
231 } 232 }
232 // Insert filters otherwise 233 // Insert filters otherwise
233 else 234 else
234 { 235 {
235 HttpInputStream his = target.getInputStream(); 236 final HttpInputStream his = target.getInputStream();
236 int size = target.getContentLength(); 237 int size = target.getContentLength();
237 if (size < 0) 238 if (size < 0)
238 { 239 {
239 size = Integer.MAX_VALUE; 240 size = Integer.MAX_VALUE;
240 } 241 }
241 242
242 FilterInputStream in = null; 243 FilterInputStream in = null;
243 FilterOutputStream out = null; 244 FilterOutputStream out = null;
244 245
245 // Detect if content needs decoding 246 // Detect if content needs decoding
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 { 284 {
284 final Matcher matcher = Pattern.compile("charset=([^;]*)").matcher(con tentType); 285 final Matcher matcher = Pattern.compile("charset=([^;]*)").matcher(con tentType);
285 if (matcher.matches()) 286 if (matcher.matches())
286 { 287 {
287 try 288 try
288 { 289 {
289 final String extractedCharsetName = matcher.group(0); 290 final String extractedCharsetName = matcher.group(0);
290 Charset.forName(extractedCharsetName); 291 Charset.forName(extractedCharsetName);
291 charsetName = extractedCharsetName; 292 charsetName = extractedCharsetName;
292 } 293 }
293 catch (IllegalArgumentException e) 294 catch (final IllegalArgumentException e)
294 { 295 {
295 Log.e(prefix, "Unsupported site charset, falling back to " + chars etName, e); 296 Log.e(prefix, "Unsupported site charset, falling back to " + chars etName, e);
296 } 297 }
297 } 298 }
298 } 299 }
299 300
300 request.sendHeaders(-1, null, -1); 301 request.sendHeaders(-1, null, -1);
301 302
302 byte[] buf = new byte[Math.min(4096, size)]; 303 final byte[] buf = new byte[Math.min(4096, size)];
303 304
304 boolean sent = selectors == null; 305 boolean sent = selectors == null;
305 BoyerMoore matcher = new BoyerMoore("<html".getBytes()); 306 final BoyerMoore matcher = new BoyerMoore("<html".getBytes());
306 307
307 while (size > 0) 308 while (size > 0)
308 { 309 {
309 out.flush(); 310 out.flush();
310 311
311 count = in.read(buf, 0, Math.min(buf.length, size)); 312 count = in.read(buf, 0, Math.min(buf.length, size));
312 if (count < 0) 313 if (count < 0)
313 { 314 {
314 break; 315 break;
315 } 316 }
316 size -= count; 317 size -= count;
317 try 318 try
318 { 319 {
319 // Search for <html> tag 320 // Search for <html> tag
320 if (!sent && count > 0) 321 if (!sent && count > 0)
321 { 322 {
322 List<Integer> matches = matcher.match(buf, 0, count); 323 final List<Integer> matches = matcher.match(buf, 0, count);
323 if (!matches.isEmpty()) 324 if (!matches.isEmpty())
324 { 325 {
325 // Add filters right before match 326 // Add filters right before match
326 int m = matches.get(0); 327 final int m = matches.get(0);
327 out.write(buf, 0, m); 328 out.write(buf, 0, m);
328 out.write("<style type=\"text/css\">\n".getBytes()); 329 out.write("<style type=\"text/css\">\n".getBytes());
329 out.write(StringUtils.join(selectors, ",\r\n").getBytes(charsetN ame)); 330 out.write(StringUtils.join(selectors, ",\r\n").getBytes(charsetN ame));
330 out.write("{ display: none !important }</style>\n".getBytes()); 331 out.write("{ display: none !important }</style>\n".getBytes());
331 out.write(buf, m, count - m); 332 out.write(buf, m, count - m);
332 sent = true; 333 sent = true;
333 continue; 334 continue;
334 } 335 }
335 } 336 }
336 out.write(buf, 0, count); 337 out.write(buf, 0, count);
337 } 338 }
338 catch (IOException e) 339 catch (final IOException e)
339 { 340 {
340 break; 341 break;
341 } 342 }
342 } 343 }
343 // The correct way would be to close ChunkedOutputStream 344 // The correct way would be to close ChunkedOutputStream
344 // but we can not do it because underlying output stream is 345 // but we can not do it because underlying output stream is
345 // used later in caller code. So we use this ugly hack: 346 // used later in caller code. So we use this ugly hack:
346 if (out instanceof ChunkedOutputStream) 347 if (out instanceof ChunkedOutputStream)
347 ((ChunkedOutputStream) out).writeFinalChunk(); 348 ((ChunkedOutputStream) out).writeFinalChunk();
348 } 349 }
349 } 350 }
350 catch (InterruptedIOException e) 351 catch (final InterruptedIOException e)
351 { 352 {
352 /* 353 /*
353 * Read timeout while reading from the remote side. We use a 354 * Read timeout while reading from the remote side. We use a
354 * read timeout in case the target never responds. 355 * read timeout in case the target never responds.
355 */ 356 */
356 request.sendError(408, "Timeout / No response"); 357 request.sendError(408, "Timeout / No response");
357 } 358 }
358 catch (EOFException e) 359 catch (final EOFException e)
359 { 360 {
360 request.sendError(500, "No response"); 361 request.sendError(500, "No response");
361 } 362 }
362 catch (UnknownHostException e) 363 catch (final UnknownHostException e)
363 { 364 {
364 request.sendError(500, "Unknown host"); 365 request.sendError(500, "Unknown host");
365 } 366 }
366 catch (ConnectException e) 367 catch (final ConnectException e)
367 { 368 {
368 request.sendError(500, "Connection refused"); 369 request.sendError(500, "Connection refused");
369 } 370 }
370 catch (IOException e) 371 catch (final IOException e)
371 { 372 {
372 /* 373 /*
373 * An IOException will happen if we can't communicate with the 374 * An IOException will happen if we can't communicate with the
374 * target or the client. Rather than attempting to discriminate, 375 * target or the client. Rather than attempting to discriminate,
375 * just send an error message to the client, and let the send 376 * just send an error message to the client, and let the send
376 * fail if the client was the one that was in error. 377 * fail if the client was the one that was in error.
377 */ 378 */
378 379
379 String msg = "Error from proxy"; 380 String msg = "Error from proxy";
380 if (e.getMessage() != null) 381 if (e.getMessage() != null)
381 { 382 {
382 msg += ": " + e.getMessage(); 383 msg += ": " + e.getMessage();
383 } 384 }
384 request.sendError(500, msg); 385 request.sendError(500, msg);
385 Log.e(prefix, msg, e); 386 Log.e(prefix, msg, e);
386 } 387 }
387 finally 388 finally
388 { 389 {
389 target.close(); 390 target.close();
390 } 391 }
391 return true; 392 return true;
392 } 393 }
393 } 394 }
OLDNEW
« no previous file with comments | « src/org/adblockplus/brazil/BaseRequestHandler.java ('k') | src/org/adblockplus/brazil/SSLConnectionHandler.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld