ちょっと懐かしいフレームワークが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だと動かないなんて事がおきました。まぁハマりましたとも・・・