SpringのCORS機能がRFC 3986なホスト名に対応してなかった・・・

「[java.net.URIで扱えるホスト名はRFC 2396 (RFC 3986じゃない・・)](http://qiita.com/kazuki43zoo/items/dba746f224ea4f7d34d6)」を投稿するきっかけとなったSpring WebのCORS(Cross-Origin Resource Sharing)機能のエラーは、Spring Frameworkのバグでした・・・ :persevere: 
バグ内容は、[SPR-14305](https://jira.spring.io/browse/SPR-14305)を参照してくだい。

なお、このバグは、最新の開発ライン（投稿時点では未リリース）であるSpring Framework 4.3(Spring Boot 1.4)では既に解消されており、Spring Framework 4.2.7.RELEASEへバックポートされる予定です。

# 環境

* Java SE 8
* Spring Framework 4.2.6.RELEASE(Spring Boot 1.3.5.RELEASE)

# 事象

リクエストにOriginヘッダーがあるとCORS(Cross-Origin Resource Sharing)関連の処理が動き、その処理の中でリクエストURLを`java.net.URI`に変換するロジックがあります。`URI`クラスはRFC 3986に対応していないため、例えばリクエストURLが「 `http://spring_app/`」だとホスト名が`null`になり、Spring側の処理の中でNPEが発生しています。

```text:スタックトレースの抜粋
...
2016-05-25 04:46:48.890 ERROR 84097 --- [io-8080-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
    at org.springframework.web.util.WebUtils.isSameOrigin(WebUtils.java:816) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.cors.DefaultCorsProcessor.processRequest(DefaultCorsProcessor.java:76) ~[spring-web-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.handler.AbstractHandlerMapping$CorsInterceptor.preHandle(AbstractHandlerMapping.java:503) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:134) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:956) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:895) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) ~[tomcat-embed-core-8.0.33.jar:8.0.33]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) ~[spring-webmvc-4.2.6.RELEASE.jar:4.2.6.RELEASE]
...
```

この事象を再現するプロジェクトは、https://github.com/spring-projects/spring-framework-issues/blob/master/SPR-14305/ にあります。具体的な発生条件は、 [テストコード ReproTests.java](https://github.com/spring-projects/spring-framework-issues/blob/master/SPR-14305/src/test/java/org/springframework/issues/ReproTests.java)をごらんください。

# 対処方法

基本的には・・・
Spring Framework 4.3.0(2016/6/8リリース予定) or 4.2.7(2016/7/20リリース予定)がリリースされるのを待ちましょう！！ 

どうしてもリリースを待てない場合は、`org.springframework.web.cors.DefaultCorsProcessor`の`processRequest`メソッドをオーバーライドし、拡張したクラスをSpring MVC(`AbstractHandlerMapping`の`corsProcessor`プロパティ)に適用することでエラーを回避することは可能です。

# まとめ

今回の事象はSpringのバグでしたが、Javaの`java.net.URI`クラスがRFC 3986に対応していないことがそもそもの原因です・・・。[RFC 3986サポート用のIssue](https://bugs.openjdk.java.net/browse/JDK-6791060)はあがっているようですが、残念ながら完全に放置状態です :sob: 
今回のように使用しているライブラリにバグや改善の余地がある場合は、オレオレ拡張などで個別（アドホック）に対応するのではなく、ライブラリ側にIssueをあげてライブラリ本家に取り込んでもらえるように働きかけるようにしましょう！！！