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

Environmental Variable for Proxy URL #15

Open
gaoagong opened this issue Aug 1, 2013 · 15 comments
Open

Environmental Variable for Proxy URL #15

gaoagong opened this issue Aug 1, 2013 · 15 comments

Comments

@gaoagong
Copy link

gaoagong commented Aug 1, 2013

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.

@dsmiley
Copy link
Collaborator

dsmiley commented Aug 1, 2013

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.

@gaoagong
Copy link
Author

gaoagong commented Aug 1, 2013

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.

http://forum.springsource.org/showthread.php?105931-ServletWrappingController-should-override-request-getPathInfo()-amp-getServletPath()-no

@gaoagong
Copy link
Author

gaoagong commented Aug 2, 2013

I ended up copying the class and updating the getPathInfo() method call with servletRequest.getRequestURI(). Everything seems to be working.

@dsmiley
Copy link
Collaborator

dsmiley commented Aug 2, 2013

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.

@krishna81m
Copy link

Can you guys post the working code to use in a Spring servlet container

@krishna81m
Copy link

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 :(

@dsmiley
Copy link
Collaborator

dsmiley commented Jun 11, 2014

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.

@krishna81m
Copy link

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);

@krishna81m
Copy link

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

@dsmiley
Copy link
Collaborator

dsmiley commented Jun 15, 2014

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.

@krishna81m
Copy link

Sure thing, we were able to incorporate pretty well in our Spring project.

@Siggen
Copy link

Siggen commented Nov 14, 2014

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 !

@dsmiley
Copy link
Collaborator

dsmiley commented Nov 18, 2014

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.

@krishna81m
Copy link

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();
    }

dev-mansonthomas referenced this issue Feb 9, 2017
Since August 2016, a 1.8 version is available in Maven Central. The README.md is updated to show this fact.
@vidhya03
Copy link

Hi,
Which solution is to follow Siggen commented on Nov 14, 2014 or krishna81m commented on Nov 19, 2014
I'm confused.

@edmit edmit mentioned this issue Aug 17, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants