From 7860bf7e17bcb1e2de653542f96ef22cdb974793 Mon Sep 17 00:00:00 2001 From: Harald Kuhr Date: Thu, 6 May 2021 00:14:21 +0200 Subject: [PATCH] XXX: Remove old servlet class. (cherry picked from commit 2f9768a1d4ad117ef8892cbf10ed57be65ddb953) --- .../twelvemonkeys/servlet/ProxyServlet.java | 440 ------------------ 1 file changed, 440 deletions(-) delete mode 100755 servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java diff --git a/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java b/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java deleted file mode 100755 index 12d7c28b..00000000 --- a/servlet/src/main/java/com/twelvemonkeys/servlet/ProxyServlet.java +++ /dev/null @@ -1,440 +0,0 @@ -/* - * Copyright (c) 2008, Harald Kuhr - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * * Neither the name of the copyright holder nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package com.twelvemonkeys.servlet; - -import com.twelvemonkeys.io.FileUtil; -import com.twelvemonkeys.lang.StringUtil; - -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.Enumeration; - -/** - * A simple proxy servlet implementation. Supports HTTP and HTTPS. - *

- * Note: The servlet is not a true HTTP proxy as described in - * RFC 2616, - * instead it passes on all incoming HTTP requests to the configured remote - * server. - * Useful for bypassing firewalls or to avoid exposing internal network - * infrastructure to external clients. - *

- *

- * At the moment, no caching of content is implemented. - *

- *

- * If the {@code remoteServer} init parameter is not set, the servlet will - * respond by sending a {@code 500 Internal Server Error} response to the client. - * If the configured remote server is down, or unreachable, the servlet will - * respond by sending a {@code 502 Bad Gateway} response to the client. - * Otherwise, the response from the remote server will be tunneled unmodified - * to the client. - *

- * - * @author Harald Kuhr - * @author last modified by $Author: haku $ - * - * @version $Id: ProxyServlet.java#1 $ - */ -public class ProxyServlet extends GenericServlet { - - /** Remote server host name or IP address */ - protected String remoteServer = null; - /** Remote server port */ - protected int remotePort = 80; - /** Remote server "mount" path */ - protected String remotePath = ""; - - private static final String HTTP_REQUEST_HEADER_HOST = "host"; - private static final String HTTP_RESPONSE_HEADER_SERVER = "server"; - private static final String MESSAGE_REMOTE_SERVER_NOT_CONFIGURED = "Remote server not configured."; - - /** - * Called by {@code init} to set the remote server. Must be a valid host - * name or IP address. No default. - * - * @param pRemoteServer - */ - public void setRemoteServer(String pRemoteServer) { - remoteServer = pRemoteServer; - } - - /** - * Called by {@code init} to set the remote port. Must be a number. - * Default is {@code 80}. - * - * @param pRemotePort - */ - public void setRemotePort(String pRemotePort) { - try { - remotePort = Integer.parseInt(pRemotePort); - } - catch (NumberFormatException e) { - log("RemotePort must be a number!", e); - } - } - - /** - * Called by {@code init} to set the remote path. May be an empty string - * for the root path, or any other valid path on the remote server. - * Default is {@code ""}. - * - * @param pRemotePath - */ - public void setRemotePath(String pRemotePath) { - if (StringUtil.isEmpty(pRemotePath)) { - pRemotePath = ""; - } - else if (pRemotePath.charAt(0) != '/') { - pRemotePath = "/" + pRemotePath; - } - - remotePath = pRemotePath; - } - - /** - * Override {@code service} to use HTTP specifics. - * - * @param pRequest - * @param pResponse - * - * @throws ServletException - * @throws IOException - * - * @see #service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) - */ - public final void service(ServletRequest pRequest, ServletResponse pResponse) throws ServletException, IOException { - service((HttpServletRequest) pRequest, (HttpServletResponse) pResponse); - } - - /** - * Services a single request. - * Supports HTTP and HTTPS. - * - * @param pRequest - * @param pResponse - * - * @throws ServletException - * @throws IOException - * - * @see ProxyServlet Class descrition - */ - protected void service(HttpServletRequest pRequest, HttpServletResponse pResponse) throws ServletException, IOException { - // Sanity check configuration - if (remoteServer == null) { - log(MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); - pResponse.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, - MESSAGE_REMOTE_SERVER_NOT_CONFIGURED); - return; - } - - HttpURLConnection remoteConnection = null; - try { - // Recreate request URI for remote request - String requestURI = createRemoteRequestURI(pRequest); - URL remoteURL = new URL(pRequest.getScheme(), remoteServer, remotePort, requestURI); - - // Get connection, with method from original request - // NOTE: The actual connection is not done before we ask for streams... - // NOTE: The HttpURLConnection is supposed to handle multiple - // requests to the same server internally - String method = pRequest.getMethod(); - remoteConnection = (HttpURLConnection) remoteURL.openConnection(); - remoteConnection.setRequestMethod(method); - - // Copy header fields - copyHeadersFromClient(pRequest, remoteConnection); - - // Do proxy specifc stuff? - // TODO: Read up the specs from RFC 2616 (HTTP) on proxy behaviour - // TODO: RFC 2616 says "[a] proxy server MUST NOT establish an HTTP/1.1 - // persistent connection with an HTTP/1.0 client" - - // Copy message body from client to remote server - copyBodyFromClient(pRequest, remoteConnection); - - // Set response status code from remote server to client - int responseCode = remoteConnection.getResponseCode(); - pResponse.setStatus(responseCode); - //System.out.println("Response is: " + responseCode + " " + remoteConnection.getResponseMessage()); - - // Copy header fields back - copyHeadersToClient(remoteConnection, pResponse); - - // More proxy specific stuff? - - // Copy message body from remote server to client - copyBodyToClient(remoteConnection, pResponse); - } - catch (ConnectException e) { - // In case we could not connecto to the remote server - log("Could not connect to remote server.", e); - pResponse.sendError(HttpServletResponse.SC_BAD_GATEWAY, e.getMessage()); - } - finally { - // Disconnect from server - // TODO: Should we actually do this? - if (remoteConnection != null) { - remoteConnection.disconnect(); - } - } - } - - /** - * Copies the message body from the remote server to the client (outgoing - * {@code HttpServletResponse}). - * - * @param pRemoteConnection - * @param pResponse - * - * @throws IOException - */ - private void copyBodyToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) throws IOException { - InputStream fromRemote = null; - OutputStream toClient = null; - - try { - // Get either input or error stream - try { - fromRemote = pRemoteConnection.getInputStream(); - } - catch (IOException e) { - // If exception, use errorStream instead - fromRemote = pRemoteConnection.getErrorStream(); - } - - // I guess the stream might be null if there is no response other - // than headers (Continue, No Content, etc). - if (fromRemote != null) { - toClient = pResponse.getOutputStream(); - FileUtil.copy(fromRemote, toClient); - } - } - finally { - if (fromRemote != null) { - try { - fromRemote.close(); - } - catch (IOException e) { - log("Stream from remote could not be closed.", e); - } - } - if (toClient != null) { - try { - toClient.close(); - } - catch (IOException e) { - log("Stream to client could not be closed.", e); - } - } - } - } - - /** - * Copies the message body from the client (incomming - * {@code HttpServletRequest}) to the remote server if the request method - * is {@code POST} or PUT. - * Otherwise this method does nothing. - * - * @param pRequest - * @param pRemoteConnection - * - * @throws java.io.IOException - */ - private void copyBodyFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) throws IOException { - // If this is a POST or PUT, copy message body from client remote server - if (!("POST".equals(pRequest.getMethod()) || "PUT".equals(pRequest.getMethod()))) { - return; - } - - // NOTE: Setting doOutput to true, will make it a POST request (why?)... - pRemoteConnection.setDoOutput(true); - - // Get streams and do the copying - InputStream fromClient = null; - OutputStream toRemote = null; - try { - fromClient = pRequest.getInputStream(); - toRemote = pRemoteConnection.getOutputStream(); - FileUtil.copy(fromClient, toRemote); - } - finally { - if (fromClient != null) { - try { - fromClient.close(); - } - catch (IOException e) { - log("Stream from client could not be closed.", e); - } - } - if (toRemote != null) { - try { - toRemote.close(); - } - catch (IOException e) { - log("Stream to remote could not be closed.", e); - } - } - } - } - - /** - * Creates the remote request URI based on the incoming request. - * The URI will include any query strings etc. - * - * @param pRequest - * - * @return a {@code String} representing the remote request URI - */ - private String createRemoteRequestURI(HttpServletRequest pRequest) { - StringBuilder requestURI = new StringBuilder(remotePath); - requestURI.append(pRequest.getPathInfo()); - - if (!StringUtil.isEmpty(pRequest.getQueryString())) { - requestURI.append("?"); - requestURI.append(pRequest.getQueryString()); - } - - return requestURI.toString(); - } - - /** - * Copies headers from the remote connection back to the client - * (the outgoing HttpServletResponse). All headers except the "Server" - * header are copied. - * - * @param pRemoteConnection - * @param pResponse - */ - private void copyHeadersToClient(HttpURLConnection pRemoteConnection, HttpServletResponse pResponse) { - // NOTE: There is no getHeaderFieldCount method or similar... - // Also, the getHeaderFields() method was introduced in J2SE 1.4, and - // we want to be 1.2 compatible. - // So, just try to loop until there are no more headers. - int i = 0; - while (true) { - String key = pRemoteConnection.getHeaderFieldKey(i); - // NOTE: getHeaderField(String) returns only the last value - String value = pRemoteConnection.getHeaderField(i); - - // If the key is not null, life is simple, and Sun is shining - // However, the default implementations includes the HTTP response - // code ("HTTP/1.1 200 Ok" or similar) as a header field with - // key "null" (why..?)... - // In addition, we want to skip the original "Server" header - if (key != null && !HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { - //System.out.println("client <<<-- remote: " + key + ": " + value); - pResponse.setHeader(key, value); - } - else if (value == null) { - // If BOTH key and value is null, there are no more header fields - break; - } - - i++; - } - - /* 1.4+ version below.... - Map headers = pRemoteConnection.getHeaderFields(); - for (Iterator iterator = headers.entrySet().iterator(); iterator.hasNext();) { - Map.Entry header = (Map.Entry) iterator.next(); - - List values = (List) header.getValue(); - - for (Iterator valueIter = values.iterator(); valueIter.hasNext();) { - String value = (String) valueIter.next(); - String key = (String) header.getKey(); - - // Skip the server header - if (HTTP_RESPONSE_HEADER_SERVER.equalsIgnoreCase(key)) { - key = null; - } - - // The default implementations includes the HTTP response code - // ("HTTP/1.1 200 Ok" or similar) as a header field with - // key "null" (why..?)... - if (key != null) { - //System.out.println("client <<<-- remote: " + key + ": " + value); - pResponse.setHeader(key, value); - } - } - } - */ - } - - /** - * Copies headers from the client (the incoming {@code HttpServletRequest}) - * to the outgoing connection. - * All headers except the "Host" header are copied. - * - * @param pRequest - * @param pRemoteConnection - */ - private void copyHeadersFromClient(HttpServletRequest pRequest, HttpURLConnection pRemoteConnection) { - Enumeration headerNames = pRequest.getHeaderNames(); - while (headerNames.hasMoreElements()) { - String headerName = (String) headerNames.nextElement(); - Enumeration headerValues = pRequest.getHeaders(headerName); - - // Skip the "host" header, as we want something else - if (HTTP_REQUEST_HEADER_HOST.equalsIgnoreCase(headerName)) { - // Skip this header - headerName = null; - } - - // Set the the header to the remoteConnection - if (headerName != null) { - // Convert from multiple line to single line, comma separated, as - // there seems to be a shortcoming in the URLConneciton API... - StringBuilder headerValue = new StringBuilder(); - while (headerValues.hasMoreElements()) { - String value = (String) headerValues.nextElement(); - headerValue.append(value); - if (headerValues.hasMoreElements()) { - headerValue.append(", "); - } - } - - //System.out.println("client -->>> remote: " + headerName + ": " + headerValue); - pRemoteConnection.setRequestProperty(headerName, headerValue.toString()); - } - } - } -}