ちょっと懐かしいフレームワークがServletコンテナの微妙な変更で影響を受けていた話
Java

ちょっと懐かしいフレームワークがServletコンテナの微妙な変更で影響を受けていた話

このエントリーをはてなブックマークに追加

技術推進室 浅井です。

どういうわけか最近Seasar2をやっています。
で、SAStrutsでリダイレクトをするときにこんなふうに書くんですが

return "/index?redirect=true";

(今見ても「何だこれ」的な仕様ですが慣れると普通です)
最終的にJetty(サーブレットコンテナ)がコンテキストパスやらスキーマやらを評価しつつURIを組み立てて302を返します。

org.eclipse.jetty.server.Response#sendRedirect(String location) https://github.com/eclipse/jetty.project/blob/jetty-9.2.1.v20140609/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java#L665-L743

と、Jettyって書きましたが社内ではTomcatも使っていて、Tomcatはこんな感じ。

org.apache.catalina.connector.Response#sendRedirect(String location) https://github.com/apache/tomcat/blob/TOMCAT_8_0_0/java/org/apache/catalina/connector/Response.java#L1221-L1273

ここで呼んでるtoAbsoluteがこっち(ちょっと長いです)

org.apache.catalina.connector.Response#toAbsolute(String location) https://github.com/apache/tomcat/blob/TOMCAT_8_0_0/java/org/apache/catalina/connector/Response.java#L1522-L1621

どっちもlocationの値が//から始まってると、以降をホスト名から与えられてるものとして動きます。 それはそれで間違ってないと思うんですが、いつからこの挙動になったかというと、これ↓

Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=51972 Add support for handling scheme/protocol relative URLs for redirects https://github.com/apache/tomcat/commit/e70797ee1fb79ae1e16687c99df4fe75fafc5206

Issueにも書いてあるとおり、そういう変更です。

そもそも2012年の1月ごろにServletのspecに追加されてて、それに伴った変更のようです。Tomcatは7.0.23から対応。 もちろんJettyも対応してて9.1.0くらいから同様の振る舞い。

そして問題はStruts1でして、そんなこと考慮しているわけもなくRequestProcessor#processForwardConfigで、こういうことしてます。(Struts1の実装ですが、SAStrutsも同じ)

// only prepend context path for relative uri
if (uri.startsWith("/")) {
    uri = request.getContextPath() + uri;
}

(http://svn.apache.org/viewvc/struts/struts1/tags/STRUTS_1_3_9/core/src/main/java/org/apache/struts/action/RequestProcessor.java?revision=542274&view=markup から抜粋、SAStruts 1.0.4-sp9 + Struts 1.2.9でも同様)

はい。ここでコンテキストパスが/だとお察し・・・な状況になります。
/indexにリダイレクトさせたつもりが、//indexに書き換えられて、さらにはhttp://indexに・・・)

ところがJettyは設定されたコンテキストパスが1文字だと、リクエスト時に無かったことにしてくれます。良いのか悪いのか。

org.eclipse.jetty.server.handler.ContextHandler https://github.com/eclipse/jetty.project/blob/jetty-9.2.1.v20140609/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java#L1030-L1033

というわけで、コンテキストパスを/に設定していたせいでJettyでは問題なく動いてTomcatだと動かないなんて事がおきました。まぁハマりましたとも・・・


名無しのエンジニア
Google Analyticsで時系列のセグメントを作成する
開発合宿に行ってきたのでレポートするよ! in 房総半島