re implement ProxyServlet

This commit is contained in:
Sebastian Sdorra
2012-12-21 10:45:56 +01:00
parent b7d646eafb
commit 2994b28f22
6 changed files with 576 additions and 0 deletions

View File

@@ -41,7 +41,9 @@ import java.net.URL;
*
* @author Sebastian Sdorra
* @since 1.8
* @deprecated use {@link ProxyConfigurationProvider} instead.
*/
@Deprecated
public class BasicProxyURLProvider implements ProxyURLProvider
{

View File

@@ -0,0 +1,222 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of SCM-Manager;
* 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 REGENTS 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.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web.proxy;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Objects;
//~--- JDK imports ------------------------------------------------------------
import java.net.URL;
import java.util.Collections;
import java.util.Set;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
/**
*
* @author Sebastian Sdorra
* @since 1.25
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "proxy-configuration")
public class ProxyConfiguration
{
/**
* Constructs ...
*
*/
public ProxyConfiguration() {}
/**
* Constructs ...
*
*
* @param url
* @param requestHeaderExcludes
* @param responseHeaderExcludes
* @param cacheEnabled
*/
public ProxyConfiguration(URL url, Set<String> requestHeaderExcludes,
Set<String> responseHeaderExcludes, boolean cacheEnabled)
{
this.url = url;
this.requestHeaderExcludes = requestHeaderExcludes;
this.responseHeaderExcludes = responseHeaderExcludes;
this.cacheEnabled = cacheEnabled;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param obj
*
* @return
*/
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final ProxyConfiguration other = (ProxyConfiguration) obj;
return Objects.equal(url, other.url)
&& Objects.equal(requestHeaderExcludes, other.requestHeaderExcludes)
&& Objects.equal(responseHeaderExcludes, other.responseHeaderExcludes)
&& Objects.equal(cacheEnabled, other.cacheEnabled);
}
/**
* Method description
*
*
* @return
*/
@Override
public int hashCode()
{
return Objects.hashCode(url, requestHeaderExcludes, responseHeaderExcludes,
cacheEnabled);
}
/**
* Method description
*
*
* @return
*/
@Override
public String toString()
{
//J-
return Objects.toStringHelper(this)
.add("url", url)
.add("requestHeaderExcludes", requestHeaderExcludes)
.add("responseHeaderExcludes", responseHeaderExcludes)
.add("cacheEnabled", cacheEnabled)
.toString();
//J+
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @return
*/
public Set<String> getRequestHeaderExcludes()
{
if (requestHeaderExcludes == null)
{
requestHeaderExcludes = Collections.EMPTY_SET;
}
return requestHeaderExcludes;
}
/**
* Method description
*
*
* @return
*/
public Set<String> getResponseHeaderExcludes()
{
if (responseHeaderExcludes == null)
{
responseHeaderExcludes = Collections.EMPTY_SET;
}
return responseHeaderExcludes;
}
/**
* Method description
*
*
* @return
*/
public URL getUrl()
{
return url;
}
/**
* Method description
*
*
* @return
*/
public boolean isCacheEnabled()
{
return cacheEnabled;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
@XmlElement(name = "cache-enabled")
private boolean cacheEnabled = true;
/** Field description */
@XmlElement(name = "exclude")
@XmlElementWrapper(name = "request-header-excludes")
private Set<String> requestHeaderExcludes;
/** Field description */
@XmlElement(name = "exclude")
@XmlElementWrapper(name = "response-header-excludes")
private Set<String> responseHeaderExcludes;
/** Field description */
private URL url;
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of SCM-Manager;
* 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 REGENTS 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.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web.proxy;
//~--- JDK imports ------------------------------------------------------------
import javax.servlet.http.HttpServletRequest;
/**
*
* @author Sebastian Sdorra
* @since 1.25
*/
public interface ProxyConfigurationProvider
{
/**
* Method description
*
*
* @param request
*
* @return
*/
public ProxyConfiguration getConfiguration(HttpServletRequest request);
}

View File

@@ -65,8 +65,10 @@ import javax.servlet.http.HttpServletResponse;
*
* @author Sebastian Sdorra
* @since 1.8
* @deprecated use {@link ProxyServlet} instead
*/
@Singleton
@Deprecated
public class ProxyServet extends HttpServlet
{

View File

@@ -0,0 +1,293 @@
/**
* Copyright (c) 2010, Sebastian Sdorra All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. 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. 3. Neither the name of SCM-Manager;
* 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 REGENTS 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.
*
* http://bitbucket.org/sdorra/scm-manager
*
*/
package sonia.scm.web.proxy;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Preconditions;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closeables;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
//~--- JDK imports ------------------------------------------------------------
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
*
* @author Sebastian Sdorra
* @since 1.25
*/
@Singleton
public class ProxyServlet extends HttpServlet
{
/**
* the logger for ProxyServlet
*/
private static final Logger logger =
LoggerFactory.getLogger(ProxyServlet.class);
/** Field description */
private static final long serialVersionUID = 5589963595604482849L;
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
* @param configurationProvider
*/
@Inject
public ProxyServlet(ProxyConfigurationProvider configurationProvider)
{
this.configurationProvider = configurationProvider;
}
//~--- methods --------------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param response
*/
@Override
public void service(HttpServletRequest request, HttpServletResponse response)
{
HttpURLConnection con;
try
{
ProxyConfiguration configuration =
configurationProvider.getConfiguration(request);
Preconditions.checkNotNull(configuration);
if (logger.isDebugEnabled())
{
logger.debug("proxy request for {}", configuration);
}
con = createConnection(configuration, request);
copyRequestHeaders(configuration, request, con);
con.connect();
int responseCode = con.getResponseCode();
logger.trace("resonse returned status code {}", responseCode);
response.setStatus(responseCode);
copyResponseHeaders(configuration, con, response);
copyContent(con, response);
con.disconnect();
}
catch (Exception ex)
{
logger.error("could not proxy request", ex);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
/**
* Method description
*
*
* @param con
* @param response
*
* @throws IOException
*/
private void copyContent(HttpURLConnection con, HttpServletResponse response)
throws IOException
{
BufferedInputStream webToProxyBuf = null;
BufferedOutputStream proxyToClientBuf = null;
try
{
webToProxyBuf = new BufferedInputStream(con.getInputStream());
proxyToClientBuf = new BufferedOutputStream(response.getOutputStream());
long bytes = ByteStreams.copy(webToProxyBuf, proxyToClientBuf);
logger.trace("copied {} bytes for proxy", bytes);
}
finally
{
Closeables.closeQuietly(webToProxyBuf);
Closeables.closeQuietly(proxyToClientBuf);
}
}
/**
* Method description
*
*
*
* @param configuration
* @param request
* @param con
*/
private void copyRequestHeaders(ProxyConfiguration configuration,
HttpServletRequest request, HttpURLConnection con)
{
Enumeration<String> names = request.getHeaderNames();
Enumeration<String> values;
String headerName;
String value;
while (names.hasMoreElements())
{
headerName = names.nextElement();
if (!configuration.getRequestHeaderExcludes().contains(headerName))
{
values = request.getHeaders(headerName);
while (values.hasMoreElements())
{
value = values.nextElement();
logger.trace("set request header {}={}", headerName, value);
con.setRequestProperty(headerName, value);
}
}
else if (logger.isTraceEnabled())
{
logger.trace(
"skip request header {}, because it is in the exclude list",
headerName);
}
}
}
/**
* Method description
*
*
*
* @param configuration
* @param con
* @param response
*/
private void copyResponseHeaders(ProxyConfiguration configuration,
HttpURLConnection con, HttpServletResponse response)
{
Map<String, List<String>> headers = con.getHeaderFields();
String key;
for (Entry<String, List<String>> e : headers.entrySet())
{
key = e.getKey();
if (key != null)
{
if (!configuration.getResponseHeaderExcludes().contains(key))
{
for (String v : e.getValue())
{
logger.trace("add response header {}={}", key, v);
response.addHeader(key, v);
}
}
else if (logger.isTraceEnabled())
{
logger.trace(
"skip request header {}, because it is in the exclude list", key);
}
}
else if (logger.isTraceEnabled())
{
logger.trace("response header key is null");
}
}
}
/**
* Method description
*
*
* @param configuration
* @param request
*
* @return
*
* @throws IOException
*/
private HttpURLConnection createConnection(ProxyConfiguration configuration,
HttpServletRequest request)
throws IOException
{
HttpURLConnection con =
(HttpURLConnection) configuration.getUrl().openConnection();
con.setRequestMethod(request.getMethod());
con.setDoOutput(true);
con.setDoInput(true);
con.setUseCaches(configuration.isCacheEnabled());
return con;
}
//~--- fields ---------------------------------------------------------------
/** Field description */
private ProxyConfigurationProvider configurationProvider;
}

View File

@@ -41,7 +41,9 @@ import java.net.URL;
*
* @author Sebastian Sdorra
* @since 1.8
* @deprecated use {@link ProxyConfigurationProvider} instead.
*/
@Deprecated
public interface ProxyURLProvider
{