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.assertFalse;
23  import static org.junit.Assert.assertTrue;
24  
25  import java.io.IOException;
26  import java.io.PrintWriter;
27  
28  import javax.servlet.ServletException;
29  import javax.servlet.http.HttpServlet;
30  import javax.servlet.http.HttpServletRequest;
31  import javax.servlet.http.HttpServletResponse;
32  import javax.servlet.http.HttpSession;
33  import javax.servlet.http.HttpSessionEvent;
34  import javax.servlet.http.HttpSessionListener;
35  
36  import org.eclipse.jetty.client.HttpClient;
37  import org.eclipse.jetty.client.api.ContentResponse;
38  import org.eclipse.jetty.client.api.Request;
39  import org.eclipse.jetty.servlet.ServletContextHandler;
40  import org.eclipse.jetty.servlet.ServletHolder;
41  import org.junit.Test;
42  
43  
44  /**
45   * AbstractLastAccessTimeTest
46   *
47   * This test checks that a session can migrate from node A to node B, kept in use in node B
48   * past the time at which it would have expired due to inactivity on node A but is NOT
49   * scavenged by node A. In other words, it tests that a session that migrates from one node
50   * to another is not timed out on the original node.
51   */
52  public abstract class AbstractLastAccessTimeTest
53  {
54      public abstract AbstractTestServer createServer(int port, int max, int scavenge);
55  
56      @Test
57      public void testLastAccessTime() throws Exception
58      {
59          String contextPath = "";
60          String servletMapping = "/server";
61          int maxInactivePeriod = 8; //session will timeout after 8 seconds
62          int scavengePeriod = 2; //scavenging occurs every 2 seconds
63          AbstractTestServer server1 = createServer(0, maxInactivePeriod, scavengePeriod);
64          TestServlet servlet1 = new TestServlet();
65          ServletHolder holder1 = new ServletHolder(servlet1);
66          ServletContextHandler context = server1.addContext(contextPath);
67          TestSessionListener listener1 = new TestSessionListener();
68          context.addEventListener(listener1);
69          context.addServlet(holder1, servletMapping);
70  
71          try
72          {
73              server1.start();
74              int port1=server1.getPort();
75              AbstractTestServer server2 = createServer(0, maxInactivePeriod, scavengePeriod);
76              server2.addContext(contextPath).addServlet(TestServlet.class, servletMapping);
77  
78              try
79              {
80                  server2.start();
81                  int port2=server2.getPort();
82                  HttpClient client = new HttpClient();
83                  client.start();
84                  try
85                  {
86                      // Perform one request to server1 to create a session
87                      ContentResponse response1 = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init");
88                      assertEquals(HttpServletResponse.SC_OK, response1.getStatus());
89                      assertEquals("test", response1.getContentAsString());
90                      String sessionCookie = response1.getHeaders().get("Set-Cookie");
91                      assertTrue( sessionCookie != null );
92                      // Mangle the cookie, replacing Path with $Path, etc.
93                      sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
94  
95                      // Perform some request to server2 using the session cookie from the previous request
96                      // This should migrate the session from server1 to server2, and leave server1's
97                      // session in a very stale state, while server2 has a very fresh session.
98                      // We want to test that optimizations done to the saving of the shared lastAccessTime
99                      // do not break the correct working
100                     int requestInterval = 500;
101                     for (int i = 0; i < maxInactivePeriod * (1000 / requestInterval); ++i)
102                     {
103                         Request request = client.newRequest("http://localhost:" + port2 + contextPath + servletMapping);
104                         request.header("Cookie", sessionCookie);
105                         ContentResponse response2 = request.send();
106                         assertEquals(HttpServletResponse.SC_OK , response2.getStatus());
107                         assertEquals("test", response2.getContentAsString());
108 
109                         String setCookie = response2.getHeaders().get("Set-Cookie");
110                         if (setCookie!=null)
111                             sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
112 
113                         Thread.sleep(requestInterval);
114                     }
115 
116                     // At this point, session1 should be eligible for expiration.
117                     // Let's wait for the scavenger to run, waiting 2.5 times the scavenger period
118                     Thread.sleep(scavengePeriod * 2500L);
119 
120                     //check that the session was not scavenged over on server1 by ensuring that the SessionListener destroy method wasn't called
121                     assertFalse(listener1.destroyed);
122                 }
123                 finally
124                 {
125                     client.stop();
126                 }
127             }
128             finally
129             {
130                 server2.stop();
131             }
132         }
133         finally
134         {
135             server1.stop();
136         }
137     }
138 
139     public static class TestSessionListener implements HttpSessionListener
140     {
141         public boolean destroyed = false;
142         public boolean created = false;
143 
144         @Override
145         public void sessionDestroyed(HttpSessionEvent se)
146         {
147            destroyed = true;
148         }
149 
150         @Override
151         public void sessionCreated(HttpSessionEvent se)
152         {
153             created = true;
154         }
155     }
156 
157 
158 
159     public static class TestServlet extends HttpServlet
160     {
161 
162 
163         @Override
164         protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
165         {
166             String action = request.getParameter("action");
167             if ("init".equals(action))
168             {
169                 HttpSession session = request.getSession(true);
170                 session.setAttribute("test", "test");
171                 sendResult(session, httpServletResponse.getWriter());
172 
173             }
174             else
175             {
176                 HttpSession session = request.getSession(false);
177 
178                 // if we node hopped we should get the session and test should already be present
179                 sendResult(session, httpServletResponse.getWriter());
180 
181                 if (session!=null)
182                 {
183                     session.setAttribute("test", "test");
184                 }
185             }
186         }
187 
188         private void sendResult(HttpSession session, PrintWriter writer)
189         {
190                 if (session != null)
191                 {
192                         writer.print(session.getAttribute("test"));
193                 }
194                 else
195                 {
196                         writer.print("null");
197                 }
198         }
199     }
200 }