Skip to content
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

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

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 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
45 changes: 42 additions & 3 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=master)](https://travis-ci.org/madhbhavikar/HTTP-Proxy-Servlet)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it was local change, for my company, will revert. and repush

* 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 Down Expand Up @@ -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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a <role>contributor</role> or something like that which is supported. Is the presence of your name here a big deal here for you? I can think of a contributor or two that contributed substantially more than this PR (which doesn't mean I'm not grateful for all contributions!). Maybe me finally creating a CHANGES.txt would be a good thing to appease the desire to get credit beyond git history. It'd list names.

<name>Prasad Madhbhavikar</name>
<email>[email protected]</email>
<!-- Currently working with Cisco and the changes are as requested -->
<organization>Cisco</organization>
</developer>
<developer>
<name>Gourav Jangra</name>
<email>[email protected]</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://[email protected]/dsmiley/HTTP-Proxy-Servlet.git</connection>
<developerConnection>scm:git:[email protected]:dsmiley/HTTP-Proxy-Servlet.git</developerConnection>
<tag>HEAD</tag>
<tag>smiley-http-proxy-servlet-1.9</tag>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm; I'm not sure what the implication of this change is... but I see it's something to maintain between versions :-/

</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, [email protected]";
Expand All @@ -131,7 +137,44 @@ protected HttpHost getTargetHost(HttpServletRequest servletRequest) {
* it can be overridden.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these javadocs are now missing info about the ${} syntax you added and properties files.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Java docs added to the method

*/
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_%-.]+)\\}");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just looking at the RFC spec. It turns out most characters are supported except a small number. To keep this simple excepting all the characters we should (yet some we shouldn't admittedly), lets simplify this: Pattern.compile("\\{(.+?)\\}")

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pattern has been corrected

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are the headers pulled in advanced to a map here? Instead, why not simply resolve via request.getHeader(headerName)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented


//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;
}
}