简体   繁体   中英

Reverse Proxy: Accessing .Net Web Application from Java Web Application

So here is the scenario which we have/want to implement:

  1. We have a java spring based web application deployed on apache tomcat.
  2. In our web application we will provide a 3rd party web application link which is .net based to user. On click of which user will be redirected to .net application but the domain and context root shown in the browser url will remain same that is of java application.
  3. We cannot ask 3rd party team to change anything in their application.
  4. Whatever we have to change should be in our code, also as far as we could we should refrain ourself from any apache tomcat level changes.

Here are the things which we implemented so far:

  1. We tried to implement reverse proxy using following link: https://github.com/mitre/HTTP-Proxy-Servlet
  2. So the basic funda was to call the 3rd party url as a webservice and edit the httpresponse and add our domain and context path to the urls in any file received as response. Though this is not a good solution and consumes lot of time and space but it does the job.

Issue Which we are facing:

  1. Everything is working fine except the ajax request in the .net application.
  2. The Error thrown is: Sys.WebForms.PageRequestManagerParserErrorException: The message received from the server could not be parsed. Common causes for this error are when the response is modified by calls to Response.Write(), response filters, HttpModules, or server trace is enabled.

Here is the code fragment for doing httpresponse modification task:

protected void copyResponseEntity(HttpResponse proxyResponse, HttpServletResponse servletResponse,
                                    HttpRequest proxyRequest, HttpServletRequest servletRequest)throws IOException {
  
    HttpEntity entity = new BufferedHttpEntity(proxyResponse.getEntity());
    if (entity.isChunked()) {
        InputStream is = entity.getContent();
            String proxyBody = EntityUtils.toString(entity);
              proxyBody = proxyBody.replaceAll("/.netContextRoot/", "/ourContextRoot/.netContextRoot/");
    
            InputStream stream = new ByteArrayInputStream(proxyBody.getBytes(StandardCharsets.UTF_8));
    
            OutputStream os = servletResponse.getOutputStream();
            byte[] buffer = new byte[10 * 1024];
            int read;
            while ((read = stream.read(buffer)) != -1) {
              os.write(buffer, 0, read);
              if (doHandleCompression || stream.available() == 0 /* next is.read will block */) {
                os.flush();
              }
            }
            // Entity closing/cleanup is done in the caller (#service)
          } else {
    
            String proxyBody = EntityUtils.toString(entity);
            proxyBody = proxyBody.replaceAll("/.netContextRoot/", "/ourContextRoot/.netContextRoot/");
    
            EntityUtils.updateEntity(proxyResponse, new StringEntity(proxyBody));
            HttpEntity entity2  =  proxyResponse.getEntity();
    
            OutputStream servletOutputStream = servletResponse.getOutputStream();
    
            entity2.writeTo(servletOutputStream);
    
    }
}

Can anybody help us out with this scenario, Also if you have any other solution without making any changes in apache level then pls do mention.

Thanks in Advance.

I finally found some answers to dodge PageRequestManagerParserErrorException.

  1. You can refer this link if you are allowed to make changes in 3rd party application :Do read the comments for better understanding.
  2. You can create a new application for only reverse proxy purpose having contextroot name same as the 3rd party's context root and deploy it in your applications domain, so you will not have to edit the body content of http response for setting up your application path. And then you can call this application from your older one as both will be in same domain.
  3. I did dig up a little bit in the PageRequestManagerParserErrorException and some how found out a flow, The event validation of .net triggers PageRequestManagerParserErrorException when it comes to know that you have edited the httpresponse and the logic they have used is that they just check up the length of httpresponse with the actual length of httpresponse. So if you somehow manage to edit the httpresponse without changing the response's length then boom You have achieved the task.

Note:

a) You can use this link to achieve reverse proxy : As this has only code level change, so its very simple to achieve and also there is no server level config changes required.

b) PageRequestManagerParserErrorException occurs when you edit httpResponse before sending it to client and its triggered by eventValidation of .net.

c) 1st solution is useless if you are not allowed to make any change in the 3rd party application just like mine scenario. For time being I went with the 3rd one as it did the job without much effort.

This is better done as a Filter over a Servlet. The Filter API allows you to plugin some logic about handling requests, but it should be agnostic about how the request is processed. The API defines an ingenious filter chain, which is like a stack of request events that each get to work on the response independently. A filter can be placed at any point in the chain, and it can observe the request and response as it is executed with the doFilter() method. You should be able to detect and wrap the 3rd party request in a filter without knowing its implementation.

Servlet vs Filter

The common approach here is to write Request or Response Wrappers that give access to the underlying stream's read and write methods. I've modified the sample from this example to fit your need:

public class ThirdPartyRoutingResponseWrapper extends HttpServletResponseWrapper {
    private CharArrayWriter writer;
    private boolean _isThirdParty;
      
    public ThirdPartyRoutingResponseWrapper (HttpServletResponse response, boolean isThirdParty) {
        super(response);
        writer = new CharArrayWriter();
        _isThirdParty=isThirdParty;
    }
      
    public PrintWriter getWriter() {
        return new PrintWriter(writer);
    }
      
    public String toString() {
          if(_isThirdParty){
             return writer.toString().replaceAll("/.netContextRoot/", "/ourContextRoot/.netContextRoot/");
          }
          else {
             return writer.toString();
          }
     }
}

This response wrapper will replace the content of the response if the isThirdParty flag is set in the constructor. The filter then sets the flag when it calls doFilter to add the wrapper to the chain:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
            
    //decide if the request is routable:
    boolean needsRoute = ((HttpServletRequest) request).getRequestURI().startsWith("/.netContextRoot/")  ) ;
            
    // execute the request with the wrapper configured for this response
    chain.doFilter(request, new ThirdPartyRoutingResponseWrapper ( response, needsRoute ));
          
}
    
     

So long as your third party request is processed in a lower filter on the chain, this should give access to intercept and modify the response.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM