From 92193480dc120ffafb97bfa599dafefff61b05a7 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Thu, 17 Nov 2016 17:04:50 +0530 Subject: [PATCH 1/8] Support for injecting properties files into web.xml param-value --- README.md | 41 ++++++++++++- pom.xml | 16 ++++- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 58 +++++++++++++++++-- .../httpproxy/URITemplateProxyServlet.java | 9 +-- 4 files changed, 111 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0a7b4f82..0786fabb 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ add this to your dependencies in your pom like so: org.mitre.dsmiley.httpproxy smiley-http-proxy-servlet - 1.7 + 1.9 Ivy and other dependency managers can be used as well. @@ -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 + ... + + solr + org.mitre.dsmiley.httpproxy.ProxyServlet + + targetUri + ${some-url} + + + log + true + + + ... +``` + +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 ``` \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7a6cdf4c..eb0e4c65 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.mitre.dsmiley.httpproxy smiley-http-proxy-servlet - 1.9-SNAPSHOT + 1.9.1 jar Smiley's HTTP Proxy Servlet @@ -29,13 +29,25 @@ + + Prasad Madhbhavikar + prasad.madhbhavikar@gmail.com + + Cisco + + + Gourav Jangra + jangra.gourav143@gmail.com + + Cisco + https://github.com/dsmiley/HTTP-Proxy-Servlet scm:git:https://dsmiley@github.com/dsmiley/HTTP-Proxy-Servlet.git scm:git:git@github.com:dsmiley/HTTP-Proxy-Servlet.git - HEAD + smiley-http-proxy-servlet-1.9 diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index dc927338..44256bb2 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -39,13 +39,9 @@ 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; @@ -54,6 +50,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 @@ -112,6 +115,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"; @@ -131,7 +136,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 @@ -650,6 +692,10 @@ protected static CharSequence encodeUriQuery(CharSequence in) { formatter.format("%%%02X",(int)c);//TODO } } + + if(formatter!= null){ + formatter.close(); + } return outBuf != null ? outBuf : in; } diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java index 95354d66..13e40f41 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java @@ -20,9 +20,6 @@ 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; @@ -32,6 +29,10 @@ 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 URI Template RFC, "Level @@ -55,7 +56,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"; From 198c8d7033f607a5a0a8147b103f0b4c62ab16d6 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Fri, 18 Nov 2016 11:00:55 +0530 Subject: [PATCH 2/8] Changed the version in the doc --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0786fabb..947a9320 100644 --- a/README.md +++ b/README.md @@ -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". @@ -49,7 +49,7 @@ add this to your dependencies in your pom like so: org.mitre.dsmiley.httpproxy smiley-http-proxy-servlet - 1.9 + 1.9.1 Ivy and other dependency managers can be used as well. From d9f627f2e0e28fd00151e5163118aec7f937bdf9 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Mon, 21 Nov 2016 14:20:05 +0530 Subject: [PATCH 3/8] Fix for - Headers were not getting read properly --- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 7 +++++++ .../httpproxy/URITemplateProxyServlet.java | 20 ++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index 44256bb2..c08ca08a 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -36,6 +36,7 @@ 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; @@ -203,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, ClientPNames.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); } diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java index 13e40f41..8715140a 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java @@ -23,6 +23,7 @@ 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; @@ -101,6 +102,8 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se params.put(pair.getName(), pair.getValue()); } + LinkedHashMap specialHeaders = getVariablesFromRequestHeaders(servletRequest); + //Now rewrite the URL StringBuffer urlBuf = new StringBuffer();//note: StringBuilder isn't supported by Matcher Matcher matcher = TEMPLATE_PATTERN.matcher(targetUriTemplate); @@ -108,7 +111,10 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se 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); } @@ -141,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 getVariablesFromRequestHeaders(HttpServletRequest servletRequest) { + LinkedHashMap specialHeaders = new LinkedHashMap<>(); + Enumeration headerNames = servletRequest.getHeaderNames(); + + while (headerNames.hasMoreElements()) { + String headerName = (String) headerNames.nextElement(); + specialHeaders.put(headerName, servletRequest.getHeader(headerName)); + } + + return specialHeaders; + } } From bfba817f2f84ce772f09f923893e1cea2a7a5055 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Mon, 21 Nov 2016 15:57:05 +0530 Subject: [PATCH 4/8] CONN_MANAGER_TIMEOUT issue --- src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index c08ca08a..beeac1ff 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -205,7 +205,7 @@ public void init() throws ServletException { 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, ClientPNames.CONN_MANAGER_TIMEOUT, Integer.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); From 459da71ab2ff1d343a91794155769bbd9b896c20 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Mon, 21 Nov 2016 17:57:24 +0530 Subject: [PATCH 5/8] Changes to the Readme to point to proper status --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 947a9320..0f6b4a20 100644 --- a/README.md +++ b/README.md @@ -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) * 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 From fc16d30509a5bb25e480b562d4ca986e87345e2b Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Tue, 22 Nov 2016 10:24:01 +0530 Subject: [PATCH 6/8] Reverted the change meant for my company --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f6b4a20..947a9320 100644 --- a/README.md +++ b/README.md @@ -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/madhbhavikar/HTTP-Proxy-Servlet.svg?branch=master)](https://travis-ci.org/madhbhavikar/HTTP-Proxy-Servlet) + * 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 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 From f597ee5e64048a383f03d3261693747f476cbb44 Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Tue, 22 Nov 2016 11:00:15 +0530 Subject: [PATCH 7/8] changes as per Discussion with dsmily --- pom.xml | 12 ------- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 34 +++++++++++++++++++ .../httpproxy/URITemplateProxyServlet.java | 18 ++-------- 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/pom.xml b/pom.xml index eb0e4c65..02220dc3 100644 --- a/pom.xml +++ b/pom.xml @@ -29,18 +29,6 @@ - - Prasad Madhbhavikar - prasad.madhbhavikar@gmail.com - - Cisco - - - Gourav Jangra - jangra.gourav143@gmail.com - - Cisco - diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index beeac1ff..bfb2dfb1 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -135,7 +135,41 @@ protected HttpHost getTargetHost(HttpServletRequest servletRequest) { /** * Reads a configuration parameter. By default it reads servlet init parameters but * it can be overridden. + * In case if you are using 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: + * + * ... + * + * solr + * org.mitre.dsmiley.httpproxy.ProxyServlet + * + * targetUri + * ${some-url} + * + * + * log + * true + * + * + * ... + * + * 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: + * + * 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 + * + * some-url=http://www.cisco.com/someContext/someEndpoint */ + protected String getConfigParam(String key) { if(configurationProperties == null) { configurationProperties = getConfigurationProperties(); diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java index 8715140a..90fb226d 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/URITemplateProxyServlet.java @@ -23,7 +23,6 @@ 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; @@ -57,7 +56,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("\\{(.+?)\\}"); private static final String ATTR_QUERY_STRING = URITemplateProxyServlet.class.getSimpleName() + ".queryString"; @@ -102,8 +101,6 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se params.put(pair.getName(), pair.getValue()); } - LinkedHashMap specialHeaders = getVariablesFromRequestHeaders(servletRequest); - //Now rewrite the URL StringBuffer urlBuf = new StringBuffer();//note: StringBuilder isn't supported by Matcher Matcher matcher = TEMPLATE_PATTERN.matcher(targetUriTemplate); @@ -111,7 +108,7 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se String arg = matcher.group(1); String replacement = params.remove(arg);//note we remove if (replacement == null) { - replacement = specialHeaders.get(arg); + replacement = servletRequest.getHeader(arg) ; if (replacement == null) { throw new ServletException("Missing HTTP parameter " + arg + " to fill the template"); } @@ -148,15 +145,4 @@ protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest return (String) servletRequest.getAttribute(ATTR_QUERY_STRING); } - private LinkedHashMap getVariablesFromRequestHeaders(HttpServletRequest servletRequest) { - LinkedHashMap specialHeaders = new LinkedHashMap<>(); - Enumeration headerNames = servletRequest.getHeaderNames(); - - while (headerNames.hasMoreElements()) { - String headerName = (String) headerNames.nextElement(); - specialHeaders.put(headerName, servletRequest.getHeader(headerName)); - } - - return specialHeaders; - } } From ab711f85632e83867b793fde8d242b795283182f Mon Sep 17 00:00:00 2001 From: pmadhbha Date: Tue, 22 Nov 2016 11:11:40 +0530 Subject: [PATCH 8/8] Changes to the doc --- .../mitre/dsmiley/httpproxy/ProxyServlet.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java index bfb2dfb1..e8791a94 100644 --- a/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java +++ b/src/main/java/org/mitre/dsmiley/httpproxy/ProxyServlet.java @@ -139,18 +139,18 @@ protected HttpHost getTargetHost(HttpServletRequest servletRequest) { * http-proxy-override.properties which would be plugged into the web.xml if it is available in the classpath: * * ... - * - * solr - * org.mitre.dsmiley.httpproxy.ProxyServlet - * - * targetUri - * ${some-url} - * - * - * log - * true - * - * + * <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.