/******************************************************************************* * Copyright (c) 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.orion.internal.server.hosting; import java.io.IOException; import java.io.InputStream; import java.net.*; import java.util.Enumeration; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.servlets.ProxyServlet; import org.eclipse.jetty.util.IO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Override the Jetty ProxyServlet implementation. Do this to tolerate some unusual * HTTP practices that Ajax libraries are using (see NOTE: below.) */ public class RemoteURLProxyServlet extends ProxyServlet { private final Logger logger = LoggerFactory.getLogger(HostingActivator.PI_SERVER_HOSTING); { // Bug 346139 _DontProxyHeaders.add("host"); } private HttpURI url; private final boolean failEarlyOn404; public RemoteURLProxyServlet(URL url, boolean failEarlyOn404) { try { this.url = new HttpURI(url.toURI()); this.failEarlyOn404 = failEarlyOn404; } catch (URISyntaxException e) { //should be well formed throw new RuntimeException(e); } } @Override protected HttpURI proxyHttpURI(final String scheme, final String serverName, int serverPort, final String uri) throws MalformedURLException { return url; } /* (non-Javadoc) * @see javax.servlet.Servlet#service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) */ public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if ("CONNECT".equalsIgnoreCase(request.getMethod())) { handleConnect(request, response); } else { String uri = request.getRequestURI(); if (request.getQueryString() != null) uri += "?" + request.getQueryString(); HttpURI url = proxyHttpURI(request.getScheme(), request.getServerName(), request.getServerPort(), uri); URL rawURL = new URL(url.toString()); URLConnection connection = rawURL.openConnection(); connection.setAllowUserInteraction(false); // Set method HttpURLConnection http = null; if (connection instanceof HttpURLConnection) { http = (HttpURLConnection) connection; http.setRequestMethod(request.getMethod()); http.setInstanceFollowRedirects(false); // NOTE } // 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; Enumeration<String> enm = request.getHeaderNames(); while (enm.hasMoreElements()) { // TODO could be better than this! String hdr = (String) enm.nextElement(); String lhdr = hdr.toLowerCase(); if ("host".equals(lhdr)) { // Bug 346139: set Host based on the destination URL being proxied int port = url.getPort(); String realHost; if (port == -1 || port == rawURL.getDefaultPort()) realHost = url.getHost(); else realHost = url.getHost() + ":" + port; connection.addRequestProperty("Host", realHost); } if (_DontProxyHeaders.contains(lhdr)) continue; if (connectionHdr != null && connectionHdr.indexOf(lhdr) >= 0) continue; Enumeration<String> 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()); // Bug 346139: prevent an infinite proxy loop by decrementing the Max-Forwards header Enumeration<String> maxForwardsHeaders = request.getHeaders("Max-Forwards"); String maxForwardsHeader = null; while (maxForwardsHeaders.hasMoreElements()) { maxForwardsHeader = (String) maxForwardsHeaders.nextElement(); } int maxForwards = 5; try { maxForwards = Math.max(0, Integer.parseInt(maxForwardsHeader)); } catch (NumberFormatException e) { // Use default } if (maxForwards-- < 1) { response.sendError(HttpURLConnection.HTTP_BAD_GATEWAY, "Max-Forwards exceeded"); return; } connection.addRequestProperty("Max-Forwards", "" + maxForwards); // 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 (isOutputSupported(request)) { connection.setDoOutput(true); IO.copy(in, connection.getOutputStream()); } // Connect connection.connect(); } catch (Exception e) { if (!(e instanceof UnknownHostException)) logger.error("Error connecting to " + url, e); } InputStream proxy_in = null; // handler status codes etc. int code = 500; if (http != null) { proxy_in = http.getErrorStream(); code = http.getResponseCode(); if (failEarlyOn404 && code == 404) { // make sure this is thrown only in the "fail early on 404" case throw new NotFoundException(); } response.setStatus(code); } if (proxy_in == null) { try { proxy_in = connection.getInputStream(); } catch (Exception e) { if (!(e instanceof IOException)) logger.error("Error reading input from " + url, e); if (http != null) 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)) response.addHeader(hdr, val); h++; hdr = connection.getHeaderFieldKey(h); val = connection.getHeaderField(h); } response.addHeader("Via", "1.1 (jetty)"); // Handle if (proxy_in != null) IO.copy(proxy_in, response.getOutputStream()); } } private static boolean isOutputSupported(HttpServletRequest req) { String method = req.getMethod(); return "POST".equals(method) || "PUT".equals(method); } }