Code to get a Java servlet to act as a proxy?

2020-05-25 04:15发布

问题:

I have two Java web applications that have a single servlet that gets mapped to a specific URL:

red.war/
    WEB-INF/classes
        com.me.myorg.red.RedServlet (maps to http://red.example.com/doStuff)
blue.war/
    WEB-INF/classes
        com.me.myorg.blue.BlueServlet (maps to http://blue.example.com/doStuff)

I want to put these application (I'm calling them my "backend apps") behind a "proxy app" (servlet) that will decide which of these two apps will ultimately service a client-side request.

This proxy web app would take an incoming HTTP request, and determines which of the 2 "backend apps" (red or blue) to forward the request onto. The request would then be forwarded on to either http://red.example.com/doStuff (and then processed by RedServlet#doGet(...)) or http://blue.example.com/doStuff (and then processed by BlueServlet#doGet(...)). The returned response from the backend app (again, either RedServlet#doGet(...) or BlueServlet#doGet(...)) would then be returned to the proxy servlet, and ultimately returned to the client.

In other words, in pseudo-code:

public class ProxyServlet extends HttpServlet {
    @Override
    public doGet(HttpServletRequest request, HttpServletResponse response) {
        String forwardingAddress;
        if(shouldBeRed(request))
            forwardingAddress = "http://red.example.com/doStuff";
        else
            forwardingAddress = "http://blue.example.com/doStuff";

        PrintWriter writer = response.getWriter();

        writer.write(getResponseFromBackend(forwardingAddress, request));
    }

    private String getResponseFromBackend(String addr, HttpServletRequest req) {
        // Somehow forward req to addr and get HTML response...
    }
}

Is this possible? If so, how and what code would I need to write to make it work?

回答1:

You could use a RequestDispatcher to forward your request in the following way:

RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(forwardingAddress);

// here you have the choice whether to use include(..) or forward(..) see below
if(useInclude)
    dispatcher.include(httpRequest, httpResponse);
else
    dispatcher.forward(httpRequest, httpResponse);

... where useInlcude is set to your choice with the following options:

  • include
    This is probably what you want to do: Load the content from the forwardingAdress into your response.
    • This means you could even include multiple targets into a single response.
    • The client will not even realize this procedure nor does he need to be able to see the target document.
  • forward
    Send a forward to the forwardingAddress. This will tell the client to submit a new request to the specified URL.
    • If you do it in a browser with developer tools, you will see a second request.
    • The client must be able to see and load the target URL.
    • You can only forward to a single target.

See, the following links, too:

  • RequestDispatcher javadoc, especially for the notes:
    • forward should be called before the response has been committed to the client (before response body output has been flushed). If the response already has been committed, this method throws an IllegalStateException. Uncommitted output in the response buffer is automatically cleared before the forward.
    • include: The request and response parameters must be either the same objects as were passed to the calling servlet's service method or be subclasses of the ServletRequestWrapper or ServletResponseWrapper classes that wrap them.
  • URLRewriteFilter example
    although this example is implemented using a Filter instead of a Servlet the behavior is the same (Note: this example is part of a framework of mine and hence contains some overhead in the parent classes. Just have a look at the relevant section...)


回答2:

Since there is not yet an approved answer I try to write how I see the solution to this request use apache-http-commons library. In addition I suggest to add a flush on writer.

public class ProxyServlet extends HttpServlet {
@Override
public doGet(HttpServletRequest request, HttpServletResponse response) {
    String forwardingAddress;
    if(shouldBeRed(request))
        forwardingAddress = "http://red.example.com/doStuff";
    else
        forwardingAddress = "http://blue.example.com/doStuff";

    PrintWriter writer = response.getWriter();

    writer.write(getResponseFromBackend(forwardingAddress, request));
    **writer.flush();**
}

private String getResponseFromBackend(String addr, HttpServletRequest req) {
        HttpClient client = new HttpClient();
        HttpMethod method = new GetMethod(url);
        client.executeMethod(method);
        String body=method.getResponseBodyAsString();
        return body;

}

}