2008年6月26日木曜日

サーブレットコンテナ(Tomcat)とリバースプロクシを組み合わせた時の問題解決

Tomcat とリバースプロクシを組み合わせた時に微妙な問題に直面しました。その問題の解決方法をメモしておきます。

状況としては、Tomcat は http://localhost:8080/app/ のようにコンテキストパス /app で動作しています。
/app で動作しているアプリケーションに http://app.example.com/ という URL でリバースプロクシの設定を行います。
Tomcat は他のアプリケーションも動作しているので、ルートコンテキストは用いることができません。
http://app.example.com/ (Apache)

(リバースプロクシ)

http://localhost:8080/app/ (Tomcat)

まずはこんな感じで設定しました。問題は 2つあります。
ProxyPath / http://localhost:8080/app
ProxyPassReverse / http://localhost:8080/

1つ目は Cookie 問題です。Tomcat の発行する Set-Cookie の Path 属性が /app となり、ブラウザが / へのアクセスのときにはリクエストヘッダに Cookie をつけて送信しません。
Set-Cookie: JSESSIONID=XXXXXXXXXXXXX; Path=/app

ここでは、ProxyPassReverseCookiePath ディレクティブを用いて、Set-Cookie の Path属性 /app を / に置換します。
ProxyPath / http://localhost:8080/app/
ProxyPassReverse / http://localhost:8080/
ProxyPassReverseCookiePath /app /

これでブラウザへは次のヘッダが返されます。
Set-Cookie; JSESSIONID=XXXXXXXXXXXXX; Path=/

2つ目はコンテキストパス問題です。Tomcat をルートコンテキストで動かしたり、ルートコンテキストは他で使っていても、ポートを変えてもう 1つ Tomcat を起動すれば問題はすぐ解決するのですが、ここは敢えて別の方法をとってみます。

やりたいことは、次の 2つの URL どちらからアクセスしても同じように動くことです。
http://app.example.com/
http://localhost:8080/app/

今回は以下のような方法を使ってみました。

まず、リバースプロクシの設定に加え X-Context-Path という独自のヘッダを付与します。
RequestHeader set X-Context-Path ""
ProxyPath / http://localhost:8080/app/
ProxyPassReverse / http://localhost:8080/
ProxyPassReverseCookiePath /app /

これで Tomcat は、リバースプロクシからきたリクエストを X-Context-Path というヘッダで判断することができます。
次にサーブレットフィルタを作成し、リクエストオブジェクトをラップして、getContextPath メソッドをオーバーライドします。
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
doFilter((HttpServletRequest) req, (HttpServletResponse) res, chain);
}
public void doFilter(HttpServletRequest req, HttpServletResponse res,
FilterChain chain) throws IOException, ServletException {
// NOTE: X-Context-Path を受け取って、null で無ければその値を
// コンテキストパスとして使用する。
// ※実際は XSS などを埋め込まれないように文字列チェックする必要
// あります。
final String contextPath = req.getHeader("X-Context-Path");
if (contextPath != null) {
req = new HttpServletRequestWrapper(req) {
public String getContextPath() {
return contextPath;
}
};
}
chain.doFilter(req, res);
}

この他に getServerPort や getRequestURI など変更すべき箇所がたくさんありますが省略。。こうしておけば、http://localhost:8080/app/ にアクセスしたときも http://app.example.com/ にアクセスしたときも期待通りに動作します。

1 件のコメント:

匿名 さんのコメント...

After gamers place their bets, a number of} of the chances are struck at random by “lightning”, adding a big multiplier if 온라인카지노 the player’s choice is struck and then the participant wins. Live Casino video games are operated by actual life dealers and broadcast to gamers in actual time - mimicking a land-based on line casino expertise. Stay on top of the action and prepare to play the latest stay on line casino video games – get pleasure from them as quickly as they’re released.