View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server.session;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertNotNull;
23  import static org.junit.Assert.assertTrue;
24  import static org.junit.Assert.fail;
25  
26  import java.io.IOException;
27  import java.io.Serializable;
28  import java.util.ArrayList;
29  import java.util.List;
30  
31  import javax.servlet.ServletException;
32  import javax.servlet.http.HttpServlet;
33  import javax.servlet.http.HttpServletRequest;
34  import javax.servlet.http.HttpServletResponse;
35  import javax.servlet.http.HttpSession;
36  import javax.servlet.http.HttpSessionBindingEvent;
37  import javax.servlet.http.HttpSessionBindingListener;
38  import javax.servlet.http.HttpSessionEvent;
39  import javax.servlet.http.HttpSessionListener;
40  
41  import org.eclipse.jetty.client.HttpClient;
42  import org.eclipse.jetty.client.api.ContentResponse;
43  import org.eclipse.jetty.client.api.Request;
44  import org.eclipse.jetty.servlet.ServletContextHandler;
45  import org.eclipse.jetty.servlet.ServletHolder;
46  import org.junit.Test;
47  
48  /**
49   * AbstractSessionInvalidateAndCreateTest
50   *
51   * This test verifies that invalidating an existing session and creating
52   * a new session within the scope of a single request will expire the
53   * newly created session correctly (removed from the server and session listeners called).
54   * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=377610
55   */
56  public abstract class AbstractSessionInvalidateAndCreateTest
57  {
58      public class MySessionListener implements HttpSessionListener
59      {
60          List<String> destroys;
61  
62          public void sessionCreated(HttpSessionEvent e)
63          {
64  
65          }
66  
67          public void sessionDestroyed(HttpSessionEvent e)
68          {
69              if (destroys == null)
70                  destroys = new ArrayList<>();
71  
72              destroys.add((String)e.getSession().getAttribute("identity"));
73          }
74      }
75  
76      public abstract AbstractTestServer createServer(int port, int max, int scavenge);
77  
78  
79  
80      public void pause(int scavengePeriod)
81      {
82          try
83          {
84              Thread.sleep(scavengePeriod * 3000L);
85          }
86          catch (InterruptedException e)
87          {
88              e.printStackTrace();
89          }
90      }
91  
92      @Test
93      public void testSessionScavenge() throws Exception
94      {
95          String contextPath = "";
96          String servletMapping = "/server";
97          int inactivePeriod = 4;
98          int scavengePeriod = 1;
99          AbstractTestServer server = createServer(0, inactivePeriod, scavengePeriod);
100         ServletContextHandler context = server.addContext(contextPath);
101         TestServlet servlet = new TestServlet();
102         ServletHolder holder = new ServletHolder(servlet);
103         context.addServlet(holder, servletMapping);
104         MySessionListener listener = new MySessionListener();
105         context.getSessionHandler().addEventListener(listener);
106     
107         try
108         {
109             server.start();
110             int port1 = server.getPort();
111             
112             HttpClient client = new HttpClient();
113             client.start();
114             try
115             {
116                 String url = "http://localhost:" + port1 + contextPath + servletMapping;
117 
118 
119                 // Create the session
120                 ContentResponse response1 = client.GET(url + "?action=init");
121                 assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
122                 String sessionCookie = response1.getHeaders().get("Set-Cookie");
123                 assertTrue(sessionCookie != null);
124                 // Mangle the cookie, replacing Path with $Path, etc.
125                 sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
126 
127                 // Make a request which will invalidate the existing session and create a new one
128                 Request request2 = client.newRequest(url + "?action=test");
129                 request2.header("Cookie", sessionCookie);
130                 ContentResponse response2 = request2.send();
131                 assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
132 
133                 // Wait for the scavenger to run and the session to have expired
134                 pause(inactivePeriod + scavengePeriod);
135 
136                 //test that the session created in the last test is scavenged:
137                 //the HttpSessionListener should have been called when session1 was invalidated and session2 was scavenged
138                 assertTrue(listener.destroys.contains("session1"));
139                 assertTrue(listener.destroys.contains("session2"));
140                 //session2's HttpSessionBindingListener should have been called when it was scavenged
141                 assertTrue(servlet.unbound);
142             }
143             finally
144             {
145                 client.stop();
146             }
147         }
148         finally
149         {
150             server.stop();
151         }
152     }
153 
154     public static class TestServlet extends HttpServlet
155     {
156         private boolean unbound = false;
157         
158         public class MySessionBindingListener implements HttpSessionBindingListener, Serializable
159         {
160 
161             public void valueUnbound(HttpSessionBindingEvent event)
162             {
163                 unbound = true;
164             }
165 
166             public void valueBound(HttpSessionBindingEvent event)
167             {
168 
169             }
170         }
171 
172         @Override
173         protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
174         {
175             String action = request.getParameter("action");
176             if ("init".equals(action))
177             {
178                 HttpSession session = request.getSession(true);
179                 session.setAttribute("identity", "session1");
180             }
181             else if ("test".equals(action))
182             {
183                 HttpSession session = request.getSession(false);
184                 if (session != null)
185                 {
186                     //invalidate existing session
187                     session.invalidate();
188 
189                     //now try to access the invalid session
190                     try
191                     {
192                         session.getAttribute("identity");
193                         fail("Session should be invalid");
194                     }
195                     catch (IllegalStateException e)
196                     {
197                         assertNotNull(e.getMessage());
198                         assertTrue(e.getMessage().contains("id"));
199                     }
200 
201                     //now make a new session
202                     session = request.getSession(true);
203                     session.setAttribute("identity", "session2");
204                     session.setAttribute("listener", new MySessionBindingListener());
205                 }
206                 else
207                     fail("Session already missing");
208             }
209         }
210     }
211 }