同じ名前のクッキーを二つ返した際の挙動

同じ名前のクッキーをブラウザに返すと、後にセットしたクッキーが使われるんだよなぁ・・と思い、試してみた。MyFaseを使ったアプリがあったので、PhaseListenerのbeforePhaseとafterPhaseを利用。コードは下記の通り

package test;


import javax.faces.context.ExternalContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;


import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


public class TestPhaseListener implements PhaseListener {

   /**
    * クッキーの名前。今回は同一名で二つ作成するため、この名前を使いまわす
    */
   private final String COOKIE_NAME = "test_Cookie";
   
   /**
    * クッキーの値1
    */
   private final String COOKIE_VALUE_1 = "cookie_Value_No1";
   
   /**
    * クッキーの値2
    */
   private final String COOKIE_VALUE_2 = "cookie_Value_No2";
   
   /**
    * ブラウザの有効期間

    * ブラウザを閉じるまで有効なクッキー
    */
   private final int COOKIE_MAXAGE_SESSION = -1;
   
   /**
    * ブラウザの有効期間

    * 即削除される
    */
   private final int COOKIE_MAXAGE_DELETE = 0;
   
   public void beforePhase(PhaseEvent event) {
      // クッキーをセット。値1を指定
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_1, COOKIE_MAXAGE_SESSION);
   }
   
   public void afterPhase(PhaseEvent event) {
      // クッキーをセット。値2を指定
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_2, COOKIE_MAXAGE_SESSION);
   }
   
   private void createAndSetCookie
      (PhaseEvent event, String cookieName, String cookieValue, int cookieMaxAge)
   {   
      // ExternalContextを取得
      ExternalContext context = event.getFacesContext().getExternalContext();
      
      // リクエストとレスポンスを取得
      HttpServletRequest request = (HttpServletRequest)context.getRequest();
      HttpServletResponse response = (HttpServletResponse)context.getResponse();
      
      // 受け取った名前と値を持つクッキーを作成する
      Cookie cookie = new Cookie(cookieName, cookieValue);
      
      // クッキーの有効期間を設定
      cookie.setMaxAge(cookieMaxAge);
      
      // 有効なパスを設定する。同一ホスト上、全てに対して有効
      cookie.setPath(request.getContextPath());
      
      // レスポンスにクッキーをセット
      response.addCookie(cookie);
   }

   public PhaseId getPhaseId() {
      // TODO Auto-generated method stub
      return PhaseId.RESTORE_VIEW;
   }
}


JSFを知らない人向けの解説。

とあるフェーズの開始前と終了後に同じ名前のクッキーをセットしている。名前の通り「beforePhase」が開始前、「afterPhase」が終了後。開始前に値1を、終了後に値2をセットする。


結果:レスポンス

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: test_Cookie=cookie_Value_No1;
Set-Cookie: test_Cookie=cookie_Value_No2;
  :


結果:上記レスポンスを受け取った後のリクエス

  :
Cookie: JSESSIONID=***; test_Cookie=cookie_Value_No2
  :


値2が残ってるね。まぁ当たり前か。ちなみに逆にしてみたら値1が残ったので、後からセットされたほうが優先らしい。

次、後からセットしたクッキーのMaxAgeが0だった場合。クッキーのMaxAgeが0だとブラウザがクッキーを削除するのだけど、最初にセットされたクッキーまで削除されるのか?

コードを少々修正。

  :
   /**
    * ブラウザの有効期間

    * ブラウザを閉じるまで有効なクッキー
    */
   private final int COOKIE_MAXAGE_SESSION = -1;
   
   /**
    * ブラウザの有効期間

    * 即削除される
    */
   private final int COOKIE_MAXAGE_DELETE = 0;
   
   public void beforePhase(PhaseEvent event) {
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_1, COOKIE_MAXAGE_SESSION);
   }
   
   public void afterPhase(PhaseEvent event) {
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_2, COOKIE_MAXAGE_DELETE);
   }
  :


結果:レスポンス

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: test_Cookie=cookie_Value_No1;
Set-Cookie: test_Cookie=cookie_Value_No2;
  :


結果:上記レスポンスを受け取った後のリクエス

  :
Cookie: JSESSIONID=***;
  :


あら、最初にセットしたもの一緒に消えてるね。ちなみに逆にすると、

  :
   public void beforePhase(PhaseEvent event) {
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_1, COOKIE_MAXAGE_DELETE);
   }
   
   public void afterPhase(PhaseEvent event) {
      createAndSetCookie(event, COOKIE_NAME, COOKIE_VALUE_2, COOKIE_MAXAGE_SESSION);
   }
  :


結果:レスポンス

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Set-Cookie: test_Cookie=cookie_Value_No1;
Set-Cookie: test_Cookie=cookie_Value_No2;
  :


結果:上記レスポンスを受け取った後のリクエス

  :
Cookie: JSESSIONID=***; test_Cookie=cookie_Value_No2
  :


後からセットしたクッキーが残っていることを確認。


結論。


・同じ名前のクッキーは後からセットしたものが有効で、先にセットされたものは破棄される。
・有効なクッキーと削除するクッキーを同じ名前で一緒に送った場合、後からセットしたのが有効ならそれが残る。後からセットしたものが削除するクッキーだった場合は両方削除される。


つまり、レスポンスを見ると同じ名前のクッキーが二つ返されれているけれども、最初にセットしたクッキーは無効ということ。
まぁ当たり前だけど、ちゃんと確認できた。


もしかしたらRFCあたりを解読すれば書いているかもしれない。ブラウザに依存しそうだから、あまりやらない方がいいのかもしれないね。