Skip to content

support for variables in 'param-value' from properties file without depending on any 3rd party libs #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This is an HTTP Proxy (aka gateway) in the form of a Java servlet. An HTTP prox
This is hardly the first proxy, so why did I write it and thus why might you use it?

* It's simple -- a single source file implementation
* It's tested -- have confidence it works [![Build Status](https://travis-ci.org/mitre/HTTP-Proxy-Servlet.png)](https://travis-ci.org/mitre/HTTP-Proxy-Servlet)
* It's tested -- have confidence it works [![Build Status](https://travis-ci.org/madhbhavikar/HTTP-Proxy-Servlet.svg?branch=propertiesSupport)](https://travis-ci.org/madhbhavikar/HTTP-Proxy-Servlet)
* It's securable -- via Java EE web.xml or via a servlet filter such as [Spring-Security]([http://static.springsource.org/spring-security/site/)
* It's extendible -- via simple class extension
* It's embeddable -- into your Java web application making testing your app easier
Expand All @@ -15,7 +15,7 @@ I have seen many quick'n'dirty proxies posted in source form on the web such as

This proxy depends on [Apache HttpClient](http://hc.apache.org/httpcomponents-client-ga/), which offers another point of extension for this proxy. At some point I may write an alternative that uses the JDK and thus doesn't have any dependencies, which is desirable. In the mean time, you'll have to add the jar files for this and its dependencies:

+- org.apache.httpcomponents:httpclient:jar:4.2.5:compile
+- org.apache.httpcomponents:httpclient:jar:**4.5.2**:compile
+- org.apache.httpcomponents:httpcore:jar:4.2.4:compile
| +- commons-logging:commons-logging:jar:1.1.1:compile
| \- commons-codec:commons-codec:jar:1.6:compile
Expand All @@ -37,7 +37,7 @@ HTTP POST. Other application parameters can be in your POSTed url-encoded-form s
proxyArgs.

Build & Installation
------------
--------------------

Simply build the jar using "mvn package" at the command line.
The jar is built to "target/smiley-http-proxy-servlet-VERSION.jar".
Expand All @@ -49,7 +49,7 @@ add this to your dependencies in your pom like so:
<dependency>
<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.7</version>
<version>1.9.1</version>
</dependency>

Ivy and other dependency managers can be used as well.
Expand Down Expand Up @@ -136,4 +136,43 @@ proxy:
solr:
servlet_url: /solr/*
target_url: http://solrserver:8983/solr
```

Here is an example of using the proxy with an externalized properties file `http-proxy.properties` and / or `http-proxy-override.properties` which would be plugged into the `web.xml` if it is available in the classpath:

```xml
...
<servlet>
<servlet-name>solr</servlet-name>
<servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class>
<init-param>
<param-name>targetUri</param-name>
<param-value>${some-url}</param-value>
</init-param>
<init-param>
<param-name>log</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
...
```

make sure you put **${**some-url**}** is used if you want to pickup values from the properties file.

The properties file could be:

**http-proxy.properties**:

```properties
some-url=http://www.cisco.com/{x-some-parameter}/someEndpoint
```

Assuming `x-some-parameter` is a custom header parameter.

If this property needs to be overridden for some reason for a different environment for example, then the override properties file could be:

**http-proxy-override.properties**

```properties
some-url=http://www.cisco.com/someContext/someEndpoint
```
16 changes: 14 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

<groupId>org.mitre.dsmiley.httpproxy</groupId>
<artifactId>smiley-http-proxy-servlet</artifactId>
<version>1.9-SNAPSHOT</version>
<version>1.9.1</version>
<packaging>jar</packaging>

<name>Smiley's HTTP Proxy Servlet</name>
Expand All @@ -29,13 +29,25 @@
<!-- I used to work for MITRE for many years but I don't anymore. -->
<!--<organization>MITRE</organization>-->
</developer>
<developer>
<name>Prasad Madhbhavikar</name>
<email>prasad.madhbhavikar@gmail.com</email>
<!-- Currently working with Cisco and the changes are as requested -->
<organization>Cisco</organization>
</developer>
<developer>
<name>Gourav Jangra</name>
<email>jangra.gourav143@gmail.com</email>
<!-- Currently working with Cisco and the changes are as requested -->
<organization>Cisco</organization>
</developer>
</developers>

<scm>
<url>https://github.com/dsmiley/HTTP-Proxy-Servlet</url>
<connection>scm:git:https://dsmiley@github.com/dsmiley/HTTP-Proxy-Servlet.git</connection>
<developerConnection>scm:git:git@github.com:dsmiley/HTTP-Proxy-Servlet.git</developerConnection>
<tag>HEAD</tag>
<tag>smiley-http-proxy-servlet-1.9</tag>
</scm>

<properties>
Expand Down
65 changes: 59 additions & 6 deletions src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,13 @@
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.message.HeaderGroup;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.HttpCookie;
Expand All @@ -54,6 +51,13 @@
import java.util.Enumeration;
import java.util.Formatter;
import java.util.List;
import java.util.Properties;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* An HTTP reverse proxy/gateway servlet. It is designed to be extended for customization
Expand Down Expand Up @@ -112,6 +116,8 @@ public class ProxyServlet extends HttpServlet {

private HttpClient proxyClient;

private Properties configurationProperties = null;

@Override
public String getServletInfo() {
return "A proxy servlet by David Smiley, dsmiley@apache.org";
Expand All @@ -131,7 +137,44 @@ protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
* it can be overridden.
*/
protected String getConfigParam(String key) {
return getServletConfig().getInitParameter(key);
if(configurationProperties == null) {
configurationProperties = getConfigurationProperties();
}
return getValue(configurationProperties, getServletConfig().getInitParameter(key));
}

protected String getValue(Properties configurationProperties, String value){
if(value == null){
return value;
}
if(value.startsWith("${") && value.endsWith("}")){
String key = value.replaceAll("\\$\\{(.*)\\}", "$1");
return (String) configurationProperties.get(key);
}
return value;
}
protected Properties getConfigurationProperties()
{
Properties configurationProperties = new Properties();
try
{
InputStream proxyPropertiesResource = Thread.currentThread().getContextClassLoader().getResourceAsStream("http-proxy.properties");
if (proxyPropertiesResource != null) {
configurationProperties.load(proxyPropertiesResource);
}
}
catch (IOException e) {}
Properties proxyOverrideProperties = new Properties();
try
{
InputStream proxyOverridePropertiesResource = Thread.currentThread().getContextClassLoader().getResourceAsStream("http-proxy-override.properties");
if (proxyOverridePropertiesResource != null) {
proxyOverrideProperties.load(proxyOverridePropertiesResource);
}
}
catch (IOException e) {}
configurationProperties.putAll(proxyOverrideProperties);
return configurationProperties;
}

@Override
Expand Down Expand Up @@ -161,6 +204,12 @@ public void init() throws ServletException {
hcParams.setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.IGNORE_COOKIES);
hcParams.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, false); // See #70
readConfigParam(hcParams, ClientPNames.HANDLE_REDIRECTS, Boolean.class);
readConfigParam(hcParams, ClientPNames.ALLOW_CIRCULAR_REDIRECTS, Boolean.class);
readConfigParam(hcParams, "http.conn-manager.timeout", Integer.class);
readConfigParam(hcParams, ClientPNames.MAX_REDIRECTS, Integer.class);
readConfigParam(hcParams, CoreConnectionPNames.CONNECTION_TIMEOUT, Integer.class);
readConfigParam(hcParams, CoreConnectionPNames.SO_TIMEOUT, Integer.class);
readConfigParam(hcParams, CoreConnectionPNames.STALE_CONNECTION_CHECK, Boolean.class);
proxyClient = createHttpClient(hcParams);
}

Expand Down Expand Up @@ -650,6 +699,10 @@ protected static CharSequence encodeUriQuery(CharSequence in) {
formatter.format("%%%02X",(int)c);//TODO
}
}

if(formatter!= null){
formatter.close();
}
return outBuf != null ? outBuf : in;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,20 @@
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* A proxy servlet in which the target URI is templated from incoming request parameters. The
* format adheres to the <a href="http://tools.ietf.org/html/rfc6570">URI Template RFC</a>, "Level
Expand All @@ -55,7 +57,7 @@ public class URITemplateProxyServlet extends ProxyServlet {
* But that's not how the spec works. So for now we will require a proxy arg to be present
* if defined for this proxy URL.
*/
protected static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{([a-zA-Z0-9_%.]+)\\}");
protected static final Pattern TEMPLATE_PATTERN = Pattern.compile("\\{([a-zA-Z0-9_%-.]+)\\}");
private static final String ATTR_QUERY_STRING =
URITemplateProxyServlet.class.getSimpleName() + ".queryString";

Expand Down Expand Up @@ -100,14 +102,19 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se
params.put(pair.getName(), pair.getValue());
}

LinkedHashMap<String, String> specialHeaders = getVariablesFromRequestHeaders(servletRequest);

//Now rewrite the URL
StringBuffer urlBuf = new StringBuffer();//note: StringBuilder isn't supported by Matcher
Matcher matcher = TEMPLATE_PATTERN.matcher(targetUriTemplate);
while (matcher.find()) {
String arg = matcher.group(1);
String replacement = params.remove(arg);//note we remove
if (replacement == null) {
throw new ServletException("Missing HTTP parameter "+arg+" to fill the template");
replacement = specialHeaders.get(arg);
if (replacement == null) {
throw new ServletException("Missing HTTP parameter " + arg + " to fill the template");
}
}
matcher.appendReplacement(urlBuf, replacement);
}
Expand Down Expand Up @@ -140,4 +147,16 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
return (String) servletRequest.getAttribute(ATTR_QUERY_STRING);
}

private LinkedHashMap<String, String> getVariablesFromRequestHeaders(HttpServletRequest servletRequest) {
LinkedHashMap<String, String> specialHeaders = new LinkedHashMap<>();
Enumeration headerNames = servletRequest.getHeaderNames();

while (headerNames.hasMoreElements()) {
String headerName = (String) headerNames.nextElement();
specialHeaders.put(headerName, servletRequest.getHeader(headerName));
}

return specialHeaders;
}
}