// ======================================================================== // $Id: ProxyServlet.java 5263 2009-06-26 09:42:21Z gregw $ // Copyright 2004-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package ch.unifr.pai.twice.widgets.mpproxy.server; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.Socket; import java.net.URL; import java.net.URLConnection; import java.util.Enumeration; import java.util.HashSet; import java.util.zip.GZIPInputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import ch.unifr.pai.twice.widgets.mpproxy.shared.Rewriter; /** * Proxy Servlet. * <p> * Forward requests to another server either as a standard web proxy (as defined * by RFC2616) or as a transparent proxy. * */ public class JettyProxy { protected HashSet<String> _DontProxyHeaders = new HashSet<String>(); { _DontProxyHeaders.add("proxy-connection"); _DontProxyHeaders.add("connection"); _DontProxyHeaders.add("keep-alive"); _DontProxyHeaders.add("transfer-encoding"); _DontProxyHeaders.add("te"); _DontProxyHeaders.add("trailer"); _DontProxyHeaders.add("proxy-authorization"); _DontProxyHeaders.add("proxy-authenticate"); _DontProxyHeaders.add("upgrade"); _DontProxyHeaders.add("content-length"); } public ProcessResult loadFromProxy(HttpServletRequest request, HttpServletResponse response, String uri, String servletPath, String proxyPath) throws ServletException, IOException { System.out.println("LOAD "+uri); if ("CONNECT".equalsIgnoreCase(request.getMethod())) { handleConnect(request, response); } else { URL url = new URL(uri); URLConnection connection = url.openConnection(); connection.setAllowUserInteraction(false); // Set method HttpURLConnection http = null; if (connection instanceof HttpURLConnection) { http = (HttpURLConnection) connection; http.setRequestMethod(request.getMethod()); http.setInstanceFollowRedirects(false); } // check connection header String connectionHdr = request.getHeader("Connection"); if (connectionHdr != null) { connectionHdr = connectionHdr.toLowerCase(); if (connectionHdr.equals("keep-alive") || connectionHdr.equals("close")) connectionHdr = null; } // copy headers boolean xForwardedFor = false; boolean hasContent = false; Enumeration enm = request.getHeaderNames(); while (enm.hasMoreElements()) { // TODO could be better than this! String hdr = (String) enm.nextElement(); String lhdr = hdr.toLowerCase(); if (_DontProxyHeaders.contains(lhdr)) continue; if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0) continue; if ("content-type".equals(lhdr)) hasContent = true; Enumeration vals = request.getHeaders(hdr); while (vals.hasMoreElements()) { String val = (String) vals.nextElement(); if (val != null) { connection.addRequestProperty(hdr, val); xForwardedFor |= "X-Forwarded-For" .equalsIgnoreCase(hdr); } } } // Proxy headers connection.setRequestProperty("Via", "1.1 (jetty)"); if (!xForwardedFor) connection.addRequestProperty("X-Forwarded-For", request.getRemoteAddr()); // a little bit of cache control String cache_control = request.getHeader("Cache-Control"); if (cache_control != null && (cache_control.indexOf("no-cache") >= 0 || cache_control .indexOf("no-store") >= 0)) connection.setUseCaches(false); // customize Connection try { connection.setDoInput(true); // do input thang! InputStream in = request.getInputStream(); if (hasContent) { connection.setDoOutput(true); IOUtils.copy(in, connection.getOutputStream()); } // Connect connection.connect(); } catch (Exception e) { e.printStackTrace(); } InputStream proxy_in = null; // handler status codes etc. int code = 500; if (http != null) { proxy_in = http.getErrorStream(); code = http.getResponseCode(); response.setStatus(code, http.getResponseMessage()); } if (proxy_in == null) { try { proxy_in = connection.getInputStream(); } catch (Exception e) { e.printStackTrace(); proxy_in = http.getErrorStream(); } } // clear response defaults. response.setHeader("Date", null); response.setHeader("Server", null); // set response headers int h = 0; String hdr = connection.getHeaderFieldKey(h); String val = connection.getHeaderField(h); while (hdr != null || val != null) { String lhdr = hdr != null ? hdr.toLowerCase() : null; if (hdr != null && val != null && !_DontProxyHeaders.contains(lhdr)) { if (hdr.equalsIgnoreCase("Location")) { val = Rewriter.translateCleanUrl(val, servletPath, proxyPath); } response.addHeader(hdr, val); } h++; hdr = connection.getHeaderFieldKey(h); val = connection.getHeaderField(h); } boolean isGzipped = connection.getContentEncoding() != null && connection.getContentEncoding().contains("gzip"); response.addHeader("Via", "1.1 (jetty)"); // boolean process = connection.getContentType() == null // || connection.getContentType().isEmpty() // || connection.getContentType().contains("html"); boolean process = connection.getContentType()!=null && connection.getContentType().contains("text"); if (proxy_in != null) { if (!process) { IOUtils.copy(proxy_in, response.getOutputStream()); proxy_in.close(); } else { InputStream in; if (isGzipped && proxy_in!=null && proxy_in.available()>0) { in = new GZIPInputStream(proxy_in); } else{ in = proxy_in; } ByteArrayOutputStream byteArrOS = new ByteArrayOutputStream(); IOUtils.copy(in, byteArrOS); in.close(); if(in!=proxy_in) proxy_in.close(); String charset = response.getCharacterEncoding(); if (charset == null || charset.isEmpty()) { charset = "ISO-8859-1"; } String originalContent = new String( byteArrOS.toByteArray(), charset); byteArrOS.close(); return new ProcessResult(originalContent, connection.getContentType(), charset, isGzipped); } } } return null; } /* ------------------------------------------------------------ */ /** * Resolve requested URL to the Proxied URL * * @param scheme * The scheme of the received request. * @param serverName * The server encoded in the received request(which may be from * an absolute URL in the request line). * @param serverPort * The server port of the received request (which may be from an * absolute URL in the request line). * @param uri * The URI of the received request. * @return The URL to which the request should be proxied. * @throws MalformedURLException */ protected URL proxyHttpURL(String scheme, String serverName, int serverPort, String uri) throws MalformedURLException { return new URL(scheme, serverName, serverPort, uri); } /* ------------------------------------------------------------ */ public void handleConnect(HttpServletRequest request, HttpServletResponse response) throws IOException { String uri = request.getRequestURI(); String port = ""; String host = ""; int c = uri.indexOf(':'); if (c >= 0) { port = uri.substring(c + 1); host = uri.substring(0, c); if (host.indexOf('/') > 0) host = host.substring(host.indexOf('/') + 1); } InetSocketAddress inetAddress = new InetSocketAddress(host, Integer.parseInt(port)); // if // (isForbidden(HttpMessage.__SSL_SCHEME,addrPort.getHost(),addrPort.getPort(),false)) // { // sendForbid(request,response,uri); // } // else { InputStream in = request.getInputStream(); final OutputStream out = response.getOutputStream(); final Socket socket = new Socket(inetAddress.getAddress(), inetAddress.getPort()); response.setStatus(200); response.setHeader("Connection", "close"); response.flushBuffer(); // try { // Thread copy = new Thread(new Runnable() { // public void run() { // try { IOUtils.copy(socket.getInputStream(), out); // } catch (IOException e) { // e.printStackTrace(); // } // } // }); // copy.start(); IOUtils.copy(in, socket.getOutputStream()); // copy.join(); // copy.join(10000); // } // catch (InterruptedException e) { // e.printStackTrace(); // } } } public static class ProcessResult { String content; String contentType; String charset; boolean gzipped; public ProcessResult(String content, String contentType, String charset, boolean gzipped) { this.content = content; this.contentType = contentType; this.charset = charset; this.gzipped = gzipped; } public String getCharset() { return charset; } public String getContent() { return content; } public boolean isGzipped() { return gzipped; } public String getContentType() { return contentType; } } }