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?
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With