1 16 package org.mortbay.http.handler; 17 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.net.HttpURLConnection ; 21 import java.net.InetAddress ; 22 import java.net.MalformedURLException ; 23 import java.net.Socket ; 24 import java.net.URL ; 25 import java.net.URLConnection ; 26 import java.util.Enumeration ; 27 import java.util.HashSet ; 28 import java.util.Set ; 29 30 import org.apache.commons.logging.Log; 31 import org.mortbay.log.LogFactory; 32 import org.mortbay.http.HttpConnection; 33 import org.mortbay.http.HttpException; 34 import org.mortbay.http.HttpFields; 35 import org.mortbay.http.HttpMessage; 36 import org.mortbay.http.HttpRequest; 37 import org.mortbay.http.HttpResponse; 38 import org.mortbay.http.HttpTunnel; 39 import org.mortbay.util.IO; 40 import org.mortbay.util.InetAddrPort; 41 import org.mortbay.util.LineInput; 42 import org.mortbay.util.LogSupport; 43 import org.mortbay.util.StringMap; 44 import org.mortbay.util.URI; 45 46 47 57 public class ProxyHandler extends AbstractHttpHandler 58 { 59 private static Log log = LogFactory.getLog(ProxyHandler.class); 60 61 protected Set _proxyHostsWhiteList; 62 protected Set _proxyHostsBlackList; 63 protected int _tunnelTimeoutMs = 250; 64 private boolean _anonymous=false; 65 private transient boolean _chained=false; 66 67 68 69 73 protected StringMap _DontProxyHeaders = new StringMap(); 74 { 75 Object o = new Object (); 76 _DontProxyHeaders.setIgnoreCase(true); 77 _DontProxyHeaders.put(HttpFields.__ProxyConnection, o); 78 _DontProxyHeaders.put(HttpFields.__Connection, o); 79 _DontProxyHeaders.put(HttpFields.__KeepAlive, o); 80 _DontProxyHeaders.put(HttpFields.__TransferEncoding, o); 81 _DontProxyHeaders.put(HttpFields.__TE, o); 82 _DontProxyHeaders.put(HttpFields.__Trailer, o); 83 _DontProxyHeaders.put(HttpFields.__Upgrade, o); 84 } 85 86 87 91 protected StringMap _ProxyAuthHeaders = new StringMap(); 92 { 93 Object o = new Object (); 94 _ProxyAuthHeaders.put(HttpFields.__ProxyAuthorization, o); 95 _ProxyAuthHeaders.put(HttpFields.__ProxyAuthenticate, o); 96 } 97 98 99 103 protected StringMap _ProxySchemes = new StringMap(); 104 { 105 Object o = new Object (); 106 _ProxySchemes.setIgnoreCase(true); 107 _ProxySchemes.put(HttpMessage.__SCHEME, o); 108 _ProxySchemes.put(HttpMessage.__SSL_SCHEME, o); 109 _ProxySchemes.put("ftp", o); 110 } 111 112 113 116 protected HashSet _allowedConnectPorts = new HashSet (); 117 { 118 _allowedConnectPorts.add(new Integer (80)); 119 _allowedConnectPorts.add(new Integer (8000)); 120 _allowedConnectPorts.add(new Integer (8080)); 121 _allowedConnectPorts.add(new Integer (8888)); 122 _allowedConnectPorts.add(new Integer (443)); 123 _allowedConnectPorts.add(new Integer (8443)); 124 } 125 126 127 128 129 131 public void start() throws Exception 132 { 133 _chained=System.getProperty("http.proxyHost")!=null; 134 super.start(); 135 } 136 137 138 144 public String [] getProxyHostsWhiteList() 145 { 146 if (_proxyHostsWhiteList == null || _proxyHostsWhiteList.size() == 0) 147 return new String [0]; 148 149 String [] hosts = new String [_proxyHostsWhiteList.size()]; 150 hosts = (String []) _proxyHostsWhiteList.toArray(hosts); 151 return hosts; 152 } 153 154 155 160 public void setProxyHostsWhiteList(String [] hosts) 161 { 162 if (hosts == null || hosts.length == 0) 163 _proxyHostsWhiteList = null; 164 else 165 { 166 _proxyHostsWhiteList = new HashSet (); 167 for (int i = 0; i < hosts.length; i++) 168 if (hosts[i] != null && hosts[i].trim().length() > 0) 169 _proxyHostsWhiteList.add(hosts[i]); 170 } 171 } 172 173 174 179 public String [] getProxyHostsBlackList() 180 { 181 if (_proxyHostsBlackList == null || _proxyHostsBlackList.size() == 0) 182 return new String [0]; 183 184 String [] hosts = new String [_proxyHostsBlackList.size()]; 185 hosts = (String []) _proxyHostsBlackList.toArray(hosts); 186 return hosts; 187 } 188 189 190 195 public void setProxyHostsBlackList(String [] hosts) 196 { 197 if (hosts == null || hosts.length == 0) 198 _proxyHostsBlackList = null; 199 else 200 { 201 _proxyHostsBlackList = new HashSet (); 202 for (int i = 0; i < hosts.length; i++) 203 if (hosts[i] != null && hosts[i].trim().length() > 0) 204 _proxyHostsBlackList.add(hosts[i]); 205 } 206 } 207 208 209 public int getTunnelTimeoutMs() 210 { 211 return _tunnelTimeoutMs; 212 } 213 214 215 220 public void setTunnelTimeoutMs(int ms) 221 { 222 _tunnelTimeoutMs = ms; 223 } 224 225 226 public void handle(String pathInContext, String pathParams, HttpRequest request, HttpResponse response) throws HttpException, IOException 227 { 228 URI uri = request.getURI(); 229 230 if (HttpRequest.__CONNECT.equalsIgnoreCase(request.getMethod())) 232 { 233 response.setField(HttpFields.__Connection, "close"); handleConnect(pathInContext, pathParams, request, response); 235 return; 236 } 237 238 try 239 { 240 URL url = isProxied(uri); 242 if (url == null) 243 { 244 if (isForbidden(uri)) 245 sendForbid(request, response, uri); 246 return; 247 } 248 249 if (log.isDebugEnabled()) 250 log.debug("PROXY URL=" + url); 251 252 URLConnection connection = url.openConnection(); 253 connection.setAllowUserInteraction(false); 254 255 HttpURLConnection http = null; 257 if (connection instanceof HttpURLConnection ) 258 { 259 http = (HttpURLConnection ) connection; 260 http.setRequestMethod(request.getMethod()); 261 http.setInstanceFollowRedirects(false); 262 } 263 264 String connectionHdr = request.getField(HttpFields.__Connection); 266 if (connectionHdr != null && (connectionHdr.equalsIgnoreCase(HttpFields.__KeepAlive) || connectionHdr.equalsIgnoreCase(HttpFields.__Close))) 267 connectionHdr = null; 268 269 boolean xForwardedFor = false; 271 boolean hasContent = false; 272 Enumeration enm = request.getFieldNames(); 273 while (enm.hasMoreElements()) 274 { 275 String hdr = (String ) enm.nextElement(); 277 278 if (_DontProxyHeaders.containsKey(hdr) || !_chained && _ProxyAuthHeaders.containsKey(hdr)) 279 continue; 280 if (connectionHdr != null && connectionHdr.indexOf(hdr) >= 0) 281 continue; 282 283 if (HttpFields.__ContentType.equals(hdr)) 284 hasContent = true; 285 286 Enumeration vals = request.getFieldValues(hdr); 287 while (vals.hasMoreElements()) 288 { 289 String val = (String ) vals.nextElement(); 290 if (val != null) 291 { 292 connection.addRequestProperty(hdr, val); 293 xForwardedFor |= HttpFields.__XForwardedFor.equalsIgnoreCase(hdr); 294 } 295 } 296 } 297 298 if (!_anonymous) 300 connection.setRequestProperty("Via", "1.1 (jetty)"); 301 if (!xForwardedFor) 302 connection.addRequestProperty(HttpFields.__XForwardedFor, request.getRemoteAddr()); 303 304 String cache_control = request.getField(HttpFields.__CacheControl); 306 if (cache_control != null && (cache_control.indexOf("no-cache") >= 0 || cache_control.indexOf("no-store") >= 0)) 307 connection.setUseCaches(false); 308 309 customizeConnection(pathInContext, pathParams, request, connection); 311 312 try 313 { 314 connection.setDoInput(true); 315 316 InputStream in = request.getInputStream(); 318 if (hasContent) 319 { 320 connection.setDoOutput(true); 321 IO.copy(in, connection.getOutputStream()); 322 } 323 324 connection.connect(); 326 } 327 catch (Exception e) 328 { 329 LogSupport.ignore(log, e); 330 } 331 332 InputStream proxy_in = null; 333 334 int code = HttpResponse.__500_Internal_Server_Error; 336 if (http != null) 337 { 338 proxy_in = http.getErrorStream(); 339 340 code = http.getResponseCode(); 341 response.setStatus(code); 342 response.setReason(http.getResponseMessage()); 343 } 344 345 if (proxy_in == null) 346 { 347 try 348 { 349 proxy_in = connection.getInputStream(); 350 } 351 catch (Exception e) 352 { 353 LogSupport.ignore(log, e); 354 proxy_in = http.getErrorStream(); 355 } 356 } 357 358 response.removeField(HttpFields.__Date); 360 response.removeField(HttpFields.__Server); 361 362 int h = 0; 364 String hdr = connection.getHeaderFieldKey(h); 365 String val = connection.getHeaderField(h); 366 while (hdr != null || val != null) 367 { 368 if (hdr != null && val != null && !_DontProxyHeaders.containsKey(hdr) && (_chained || !_ProxyAuthHeaders.containsKey(hdr))) 369 response.addField(hdr, val); 370 h++; 371 hdr = connection.getHeaderFieldKey(h); 372 val = connection.getHeaderField(h); 373 } 374 if (!_anonymous) 375 response.setField("Via", "1.1 (jetty)"); 376 377 request.setHandled(true); 379 if (proxy_in != null) 380 IO.copy(proxy_in, response.getOutputStream()); 381 382 } 383 catch (Exception e) 384 { 385 log.warn(e.toString()); 386 LogSupport.ignore(log, e); 387 if (!response.isCommitted()) 388 response.sendError(HttpResponse.__400_Bad_Request); 389 } 390 } 391 392 393 public void handleConnect(String pathInContext, String pathParams, HttpRequest request, HttpResponse response) throws HttpException, IOException 394 { 395 URI uri = request.getURI(); 396 397 try 398 { 399 if (log.isDebugEnabled()) 400 log.debug("CONNECT: " + uri); 401 InetAddrPort addrPort = new InetAddrPort(uri.toString()); 402 403 if (isForbidden(HttpMessage.__SSL_SCHEME, addrPort.getHost(), addrPort.getPort(), false)) 404 { 405 sendForbid(request, response, uri); 406 } 407 else 408 { 409 HttpConnection http_connection=request.getHttpConnection(); 410 http_connection.forceClose(); 411 412 int timeoutMs = 30000; 414 Object maybesocket = http_connection.getConnection(); 415 if (maybesocket instanceof Socket ) 416 { 417 Socket s = (Socket ) maybesocket; 418 timeoutMs = s.getSoTimeout(); 419 } 420 421 422 HttpTunnel tunnel = newHttpTunnel(request,response,addrPort.getInetAddress(), addrPort.getPort(),timeoutMs); 424 425 426 if (tunnel!=null) 427 { 428 if (_tunnelTimeoutMs > 0) 430 { 431 tunnel.getSocket().setSoTimeout(_tunnelTimeoutMs); 432 if (maybesocket instanceof Socket ) 433 { 434 Socket s = (Socket ) maybesocket; 435 s.setSoTimeout(_tunnelTimeoutMs); 436 } 437 } 438 tunnel.setTimeoutMs(timeoutMs); 439 440 customizeConnection(pathInContext, pathParams, request, tunnel.getSocket()); 441 request.getHttpConnection().setHttpTunnel(tunnel); 442 response.setStatus(HttpResponse.__200_OK); 443 response.setContentLength(0); 444 } 445 request.setHandled(true); 446 } 447 } 448 catch (Exception e) 449 { 450 LogSupport.ignore(log, e); 451 response.sendError(HttpResponse.__500_Internal_Server_Error); 452 } 453 } 454 455 456 protected HttpTunnel newHttpTunnel(HttpRequest request, HttpResponse response, InetAddress iaddr, int port, int timeoutMS) throws IOException 457 { 458 try 459 { 460 Socket socket=null; 461 InputStream in=null; 462 463 String chained_proxy_host=System.getProperty("http.proxyHost"); 464 if (chained_proxy_host==null) 465 { 466 socket= new Socket (iaddr, port); 467 socket.setSoTimeout(timeoutMS); 468 socket.setTcpNoDelay(true); 469 } 470 else 471 { 472 int chained_proxy_port = Integer.getInteger("http.proxyPort", 8888).intValue(); 473 474 Socket chain_socket= new Socket (chained_proxy_host, chained_proxy_port); 475 chain_socket.setSoTimeout(timeoutMS); 476 chain_socket.setTcpNoDelay(true); 477 if (log.isDebugEnabled()) log.debug("chain proxy socket="+chain_socket); 478 479 LineInput line_in = new LineInput(chain_socket.getInputStream()); 480 byte[] connect= request.toString().getBytes(org.mortbay.util.StringUtil.__ISO_8859_1); 481 chain_socket.getOutputStream().write(connect); 482 483 String chain_response_line = line_in.readLine(); 484 HttpFields chain_response = new HttpFields(); 485 chain_response.read(line_in); 486 487 int space0 = chain_response_line.indexOf(' '); 489 if (space0>0 && space0+1<chain_response_line.length()) 490 { 491 int space1 = chain_response_line.indexOf(' ',space0+1); 492 493 if (space1>space0) 494 { 495 int code=Integer.parseInt(chain_response_line.substring(space0+1,space1)); 496 497 if (code>=200 && code<300) 498 { 499 socket=chain_socket; 500 in=line_in; 501 } 502 else 503 { 504 Enumeration iter = chain_response.getFieldNames(); 505 while (iter.hasMoreElements()) 506 { 507 String name=(String )iter.nextElement(); 508 if (!_DontProxyHeaders.containsKey(name)) 509 { 510 Enumeration values = chain_response.getValues(name); 511 while(values.hasMoreElements()) 512 { 513 String value=(String )values.nextElement(); 514 response.setField(name, value); 515 } 516 } 517 } 518 response.sendError(code); 519 if (!chain_socket.isClosed()) 520 chain_socket.close(); 521 } 522 } 523 } 524 } 525 526 if (socket==null) 527 return null; 528 HttpTunnel tunnel=new HttpTunnel(socket,in,null); 529 return tunnel; 530 } 531 catch(IOException e) 532 { 533 log.debug(e); 534 response.sendError(HttpResponse.__400_Bad_Request); 535 return null; 536 } 537 } 538 539 540 541 546 protected void customizeConnection(String pathInContext, String pathParams, HttpRequest request, Socket socket) throws IOException 547 { 548 } 549 550 551 554 protected void customizeConnection(String pathInContext, String pathParams, HttpRequest request, URLConnection connection) throws IOException 555 { 556 } 557 558 559 567 protected URL isProxied(URI uri) throws MalformedURLException 568 { 569 if (isForbidden(uri)) 571 return null; 572 573 return new URL (uri.toString()); 575 } 576 577 578 583 protected boolean isForbidden(URI uri) 584 { 585 String scheme = uri.getScheme(); 586 String host = uri.getHost(); 587 int port = uri.getPort(); 588 return isForbidden(scheme, host, port, true); 589 } 590 591 592 601 protected boolean isForbidden(String scheme, String host, int port, boolean openNonPrivPorts) 602 { 603 Integer p = new Integer (port); 605 if (port > 0 && !_allowedConnectPorts.contains(p)) 606 { 607 if (!openNonPrivPorts || port <= 1024) 608 return true; 609 } 610 611 if (scheme == null || !_ProxySchemes.containsKey(scheme)) 613 return true; 614 615 if (_proxyHostsWhiteList != null && !_proxyHostsWhiteList.contains(host)) 617 return true; 618 619 if (_proxyHostsBlackList != null && _proxyHostsBlackList.contains(host)) 621 return true; 622 623 return false; 624 } 625 626 627 631 protected void sendForbid(HttpRequest request, HttpResponse response, URI uri) throws IOException 632 { 633 response.sendError(HttpResponse.__403_Forbidden, "Forbidden for Proxy"); 634 } 635 636 637 640 public boolean isAnonymous() 641 { 642 return _anonymous; 643 } 644 645 646 649 public void setAnonymous(boolean anonymous) 650 { 651 _anonymous = anonymous; 652 } 653 } 654 | Popular Tags |