Environmental Variable for Proxy URL
gaoagong opened this issue · comments
I'm loving your ProxyServlet except for one thing. Since the URL that the servlet is proxying to is set in the web.xml file, I am not able to modify it and set it more dynamically. My specific case is that I want to be able to hit a different URL given the environment (Dev, Testing, Prod). I have yet to find an elegant way to dynamically load the URL on startup using a property file that is environment specific. Is there an easy way to do this that I have overlooked?
I looked into servlet filters, maven like tokens in the web.xml, and even extending your servlet class to use spring to inject the URL value in from a property file. The best solution I can think of is to copy your code and manipulate it to not use the initParams, but instead use a spring injected property. My case does not need to change the URL once it is loaded, but I want to be able to have an environment specific value without packaging a separate build for the Testing and Prod environments.
Hi,
Can't you use this?: http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/web/servlet/mvc/ServletWrappingController.html I've used it with this servlet before, setting params from a property file.
Thanks for the quick response.
This will work to set the params, but it introduces a new problem. ServletWrappingController does not override the getPathInfo() method of the request. This means that the local uri for all the follow up GETs (css, javascript, etc) fail. The page displays, but it has no css or javascript, which makes it more or less useless.
I am looking for available options to see if I can fix this issue. I might even extend ServletWrappingController as the link below suggests.
I ended up copying the class and updating the getPathInfo() method call with servletRequest.getRequestURI(). Everything seems to be working.
Interesting. Presumably you had to chop off some of the beginning of getRequestURI(): the context path and the servlet path? The latter part seems to be impossible to determine in a generic sense by my proxy.
Would it be preferable for the proxy to have a method like
protected String computePathInfoEncoded(HttpServletRequest req) {
return encodeUriQuery(req.getPathInfo());
}
That would make an easy extension point for you to return getRequestURI (which is already encoded) and lob of the beginning via some configuration.
Can you guys post the working code to use in a Spring servlet container
Hello, any luck posting this? I don't see the where and why you can override getPathInfo() to return the servletRequest.getRequestURI(). Don't understand where this needs to be done!!!!
INFO:/our-gateway:action: proxy POST uri: /our-gateway/custom/path -- http://another-url/another-endpoint
It should have been
INFO:/our-gateway:action: proxy POST uri: /custom/path -- http://another-url/another-endpoint
proxyResponse = proxyClient.execute(URIUtils.extractHost(targetUriObj), proxyRequest); does not give any info about which servletPath on the target is used and from where :(
It's been awhile since when I last looked at this... but in essence it's not possible to generically plug into Spring's wrapper because Spring's wrapper doesn't supply the correct value from getPathInfo. It's not clear but it seems this @gaoagong copied Spring's code to do it. Another way is to simply copy the proxy provided by this small project (it's just one source file after all), and hack it to work in your environment. I've done that before.
I copied Spring's code only to find, it is just a pass through to the HttpServletRequest which does not allow you override or even set a value.
Then thought about editing ProxyServlet too, but even here, have no clue how to modify this line of code to pass the right path info, any pointers will really help, been waiting for long time now.
proxyResponse = proxyClient.execute(URIUtils.extractHost(targetUriObj), proxyRequest);
Was looking in the wrong place, looks like we need to change the
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest)
method to use servletRequest.getServletPath(). Again, I have no idea why you guys were recommending using getRequestURI() which has source context path in it instead of ServletPath():
So if I wanted http://localhost:8080/our-gateway/custom/path redirected to
http://localhost:8080/another-url/custom/path
and
servletRequest.getPathInfo() is null
servletRequest.getRequestURI() is our-gateway/custom/path
servletRequest.getServletPath() is custom/path
I would choose the servletPath as it just have to be appended to the targetUri.
How different is this from simply updating requestUrl(), where everything else remains same?
@Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { final HttpServletRequestWrapper wrapped = new HttpServletRequestWrapper(request) { @Override public StringBuffer getRequestURL() { final StringBuffer originalUrl = ((HttpServletRequest) getRequest()).getRequestURL(); return new StringBuffer("http://servername2:7001"); } }; chain.doFilter(wrapped, response); }
Feel free to send a pull-request if you think you can improve ProxyServlet. For example... maybe there should be an option where the servlet path is inserted right after targetUri.
Sure thing, we were able to incorporate pretty well in our Spring project.
Thanks all for your inputs.
For the record, here is the code I used :
public class SolrAdminController extends ServletWrappingController {
private String pathToStrip;
public void setPathToStrip(String pathToStrip) {
this.pathToStrip = pathToStrip;
}
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
final HttpServletRequest wrapper = new HttpServletRequestWrapper(request) {
@Override
public String getPathInfo() {
String path = super.getPathInfo();
if (path.startsWith(pathToStrip)) {
final int length = pathToStrip.length();
path = path.substring(length);
}
return path;
}
};
return super.handleRequestInternal(wrapper, response);
}
}
Configured like this :
<bean name="/admin/solr/**" class="...SolrAdminController">
<property name="pathToStrip" value="/admin/solr/"/>
<property name="servletClass" value="org.mitre.dsmiley.httpproxy.ProxyServlet"/>
<property name="servletName" value="solrAdmin"/>
<property name="initParameters">
<props>
<prop key="targetUri">http.//localhost:8080/solr/</prop>
<prop key="log">true</prop>
</props>
</property>
</bean>
And it's working nicely !
Thanks for sharing @Siggen ! I like it, as far as work-arounds go. It's too bad this is necessary though -- if only Spring did it.
Well, we used spring for its servlet mapping and dependency injection/IOC. The only challenge we had was reading/serializing responses from proxied/called host to do some post processing and flushing this response back again to calling host when response gets huge. Wish there was a faster way to do both these in parallel like flushing and reading/serailzing at same time or is there a way read entire response/content from proxied host instead of inputStream.read(buffer) in a loop twice?
<bean id="urlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/some/path">SomeServlet</prop>
..
..
<prop key="/*">proxyServlet</prop>
</props>
</property>
</bean>
<!--
ServletInstanceWrappingController
defaults servlet name to bean name, no need to pass one
targetUri should be read as a configuration parameter
-->
<bean id="SomeServlet"
class="com.abcd.gateway.controller.ServletInstanceWrappingController">
<property name="servletInstance" >
<bean class="com.abcd.servlet.ProxyServlet" />
</property>
<property name="initParameters">
<props>
<prop key="targetUri">${rest.provider.endpoint}</prop>
</props>
</property>
</bean>
protected String rewriteUrlFromRequest(HttpServletRequest servletRequest) throws ProxyException {
StringBuilder uri = new StringBuilder(500);
uri.append(targetUri);
// Handle the path given to the servlet
if (servletRequest.getPathInfo() != null) {
// ex: /my/path.html
uri.append(encodeUriQuery(servletRequest.getPathInfo()));
} // SPRING HttpServletWrappedController BUG HERE
else if (servletRequest.getServletPath() != null) {
uri.append(encodeUriQuery(servletRequest.getServletPath()));
}
// Handle the query string
String queryString = servletRequest.getQueryString();// ex:(following
// '?'):
// name=value&foo=bar#fragment
if (queryString != null && queryString.length() > 0) {
uri.append('?');
int fragIdx = queryString.indexOf('#');
String queryNoFrag = (fragIdx < 0 ? queryString : queryString
.substring(0, fragIdx));
uri.append(encodeUriQuery(queryNoFrag));
if (doSendUrlFragment && fragIdx >= 0) {
uri.append('#');
uri.append(encodeUriQuery(queryString.substring(fragIdx + 1)));
}
}
return uri.toString();
}
Hi,
Which solution is to follow Siggen commented on Nov 14, 2014 or krishna81m commented on Nov 19, 2014
I'm confused.