Код, чтобы заставить сервлет Java действовать как прокси-сервер?

У меня есть два веб-приложения Java, которые имеют один сервлет, который сопоставляется с определенным 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)

Я хочу поместить это приложение (я называю их своими "бэкэнд-приложениями" ) за "прокси-приложение" (сервлет), которые решат, какое из этих двух приложений будет в конечном счете обслуживать клиентский запрос.

Это прокси-приложение будет принимать входящий HTTP-запрос и определяет, какое из двух "бэкэнд-приложений" (красного или синего) отправляет запрос. Затем запрос будет перенаправлен на http://red.example.com/doStuff (а затем обрабатывается RedServlet#doGet(...)) или http://blue.example.com/doStuff (а затем обрабатывается BlueServlet#doGet(...)). Возвращенный ответ из бэкэнд-приложения (опять же, RedServlet#doGet(...) или BlueServlet#doGet(...)) затем будет возвращен в сервлет-прокси и в конечном итоге возвращается клиенту.

Другими словами, в псевдокоде:

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...
    }
}

Возможно ли это? Если да, как и какой код мне нужно написать, чтобы заставить его работать?

Ответы

Ответ 1

Вы можете использовать RequestDispatcher для пересылки вашего запроса следующим образом:

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

... где useInlcude устанавливается по вашему выбору со следующими параметрами:

  • включить
    Это, вероятно, то, что вы хотите сделать: Загрузите содержимое из forwardingAdress в свой ответ.
    • Это означает, что вы можете включить несколько целей в один ответ.
    • Клиент даже не поймет, что этот процессуальный агент должен уметь видеть целевой документ.
  • вперед
    Отправьте запрос на forwardingAddress. Это сообщит клиенту, чтобы он отправил новый запрос на указанный URL.
    • Если вы сделаете это в браузере с инструментами разработчика, вы увидите второй запрос.
    • Клиент должен иметь возможность видеть и загружать целевой URL.
    • Вы можете перенаправлять только одну цель.

Смотрите также следующие ссылки:

  • RequestDispatcher javadoc, особенно для заметок:
    • forward должен быть вызван до того, как ответ был передан клиенту (до того, как был удален поток тела ответа). Если ответ уже был зафиксирован, этот метод генерирует исключение IllegalStateException. Незафиксированный вывод в буфере ответа автоматически очищается перед форвардом.
    • include: параметры запроса и ответа должны быть либо теми же объектами, которые были переданы вызывающему сервису сервлетов, либо быть подклассами классов ServletRequestWrapper или ServletResponseWrapper, которые их завершают.
  • Пример URLRewriteFilter, хотя этот пример реализован с использованием Filter вместо Servlet, поведение одинаковое (Примечание: этот пример является частью из моей шахты и, следовательно, содержит некоторые накладные расходы в родительских классах. Просто посмотрите на соответствующий раздел...)