Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forward HttpServletRequest to a different server

I got a HttpServletRequest request in my Spring Servlet which I would like to forward AS-IS (i.e. GET or POST content) to a different server.

What would be the best way to do it using Spring Framework?

Do I need to grab all the information and build a new HTTPUrlConnection? Or there is an easier way?

like image 637
user1144031 Avatar asked Aug 26 '12 14:08

user1144031


People also ask

How do I forward one servlet to another?

forward() method This method forwards a request from a servlet to another servlet on the same server. It allows one servlet to do the initial processing of a request, obtains the RequestDispatcher object, and forwards the request to another servlet to generate the response.

What is difference between redirect and forward method?

The Forward method forwards a request from one servlet to another resource in a web application and this resource can be another servlet, JSP page, or HTML file. The Redirect method, on the other hand, redirects the request to a different application. You cannot do this with a forward method.

What is the difference between doing a forward versus a sendRedirect in a servlet?

When we use the forward method request is transferred to other resources within the same server for further processing. In case of sendRedirect request is transferred to another resource to a different domain or different server for further processing.


2 Answers

Discussions of whether you should do forwarding this way aside, here's how I did it:

package com.example.servlets;  import java.net.HttpURLConnection; import java.net.URL; import java.util.Enumeration;  import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;  import com.example.servlets.GlobalConstants;  @SuppressWarnings("serial") public class ForwardServlet extends HttpServlet {      @Override     public void doGet(HttpServletRequest req, HttpServletResponse resp) {         forwardRequest("GET", req, resp);     }      @Override     public void doPost(HttpServletRequest req, HttpServletResponse resp) {         forwardRequest("POST", req, resp);     }      private void forwardRequest(String method, HttpServletRequest req, HttpServletResponse resp) {         final boolean hasoutbody = (method.equals("POST"));          try {             final URL url = new URL(GlobalConstants.CLIENT_BACKEND_HTTPS  // no trailing slash                     + req.getRequestURI()                     + (req.getQueryString() != null ? "?" + req.getQueryString() : ""));             HttpURLConnection conn = (HttpURLConnection) url.openConnection();             conn.setRequestMethod(method);              final Enumeration<String> headers = req.getHeaderNames();             while (headers.hasMoreElements()) {                 final String header = headers.nextElement();                 final Enumeration<String> values = req.getHeaders(header);                 while (values.hasMoreElements()) {                     final String value = values.nextElement();                     conn.addRequestProperty(header, value);                 }             }            //conn.setFollowRedirects(false);  // throws AccessDenied exception             conn.setUseCaches(false);             conn.setDoInput(true);             conn.setDoOutput(hasoutbody);             conn.connect();              final byte[] buffer = new byte[16384];             while (hasoutbody) {                 final int read = req.getInputStream().read(buffer);                 if (read <= 0) break;                 conn.getOutputStream().write(buffer, 0, read);             }              resp.setStatus(conn.getResponseCode());             for (int i = 0; ; ++i) {                 final String header = conn.getHeaderFieldKey(i);                 if (header == null) break;                 final String value = conn.getHeaderField(i);                 resp.setHeader(header, value);             }              while (true) {                 final int read = conn.getInputStream().read(buffer);                 if (read <= 0) break;                 resp.getOutputStream().write(buffer, 0, read);             }         } catch (Exception e) {             e.printStackTrace();             // pass         }     } } 

Obviously this could use a bit of work with regard to error handling and the like but it was functional. I stopped using it, however, because it was easier in my case to make calls directly to the CLIENT_BACKEND than to deal with cookies, auth, etc. across two distinct domains.

like image 83
Brian White Avatar answered Sep 20 '22 00:09

Brian White


I also needed to do the same, and after some non optimal with Spring controllers and RestTemplate, I found a better solution: Smiley's HTTP Proxy Servlet. The benefit is, it really does AS-IS proxying, just like Apache's mod_proxy, and it does it in a streaming way, without caching the full request/response in the memory.

Simply, you register a new servlet to the path you want to proxy to another server, and give this servlet the target host as an init parameter. If you are using a traditional web application with a web.xml, you can configure it like following:

<servlet>     <servlet-name>proxy</servlet-name>     <servlet-class>org.mitre.dsmiley.httpproxy.ProxyServlet</servlet-class>     <init-param>       <param-name>targetUri</param-name>       <param-value>http://target.uri/target.path</param-value>     </init-param> </servlet> <servlet-mapping>   <servlet-name>proxy</servlet-name>   <url-pattern>/mapping-path/*</url-pattern> </servlet-mapping> 

or, of course, you can go with the annotation config.

If you are using Spring Boot, it is even easier: You only need to create a bean of type ServletRegistrationBean, with the required configuration:

@Bean public ServletRegistrationBean proxyServletRegistrationBean() {     ServletRegistrationBean bean = new ServletRegistrationBean(             new ProxyServlet(), "/mapping-path/*");     bean.addInitParameter("targetUri", "http://target.uri/target.path");     return bean; } 

This way, you can also use the Spring properties that are available in the environment.

You can even extend the class ProxyServlet and override its methods to customize request/response headers etc, in case you need.

Update: After using Smiley's proxy servlet for some time, we had some timeout issues, it was not working reliably. Switched to Zuul from Netflix, didn't have any problems after that. A tutorial on configuring it with Spring Boot can be found on this link.

like image 21
Utku Özdemir Avatar answered Sep 20 '22 00:09

Utku Özdemir