View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2012 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 java.security.SecureRandom;
22  import java.util.Random;
23  
24  import javax.servlet.http.HttpServletRequest;
25  
26  import org.eclipse.jetty.server.SessionIdManager;
27  import org.eclipse.jetty.util.component.AbstractLifeCycle;
28  import org.eclipse.jetty.util.log.Log;
29  import org.eclipse.jetty.util.log.Logger;
30  
31  public abstract class AbstractSessionIdManager extends AbstractLifeCycle implements SessionIdManager
32  {
33      private static final Logger LOG = Log.getLogger(AbstractSessionIdManager.class);
34  
35      private final static String __NEW_SESSION_ID="org.eclipse.jetty.server.newSessionId";
36  
37      protected Random _random;
38      protected boolean _weakRandom;
39      protected String _workerName;
40  
41      /* ------------------------------------------------------------ */
42      public AbstractSessionIdManager()
43      {
44      }
45  
46      /* ------------------------------------------------------------ */
47      public AbstractSessionIdManager(Random random)
48      {
49          _random=random;
50      }
51  
52  
53      /* ------------------------------------------------------------ */
54      /**
55       * Get the workname. If set, the workername is dot appended to the session
56       * ID and can be used to assist session affinity in a load balancer.
57       *
58       * @return String or null
59       */
60      public String getWorkerName()
61      {
62          return _workerName;
63      }
64  
65      /* ------------------------------------------------------------ */
66      /**
67       * Set the workname. If set, the workername is dot appended to the session
68       * ID and can be used to assist session affinity in a load balancer.
69       *
70       * @param workerName
71       */
72      public void setWorkerName(String workerName)
73      {
74          if (workerName.contains("."))
75              throw new IllegalArgumentException("Name cannot contain '.'");
76          _workerName=workerName;
77      }
78  
79      /* ------------------------------------------------------------ */
80      public Random getRandom()
81      {
82          return _random;
83      }
84  
85      /* ------------------------------------------------------------ */
86      public synchronized void setRandom(Random random)
87      {
88          _random=random;
89          _weakRandom=false;
90      }
91  
92      /* ------------------------------------------------------------ */
93      /**
94       * Create a new session id if necessary.
95       *
96       * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
97       */
98      public String newSessionId(HttpServletRequest request, long created)
99      {
100         synchronized (this)
101         {
102             if (request!=null)
103             {
104                 // A requested session ID can only be used if it is in use already.
105                 String requested_id=request.getRequestedSessionId();
106                 if (requested_id!=null)
107                 {
108                     String cluster_id=getClusterId(requested_id);
109                     if (idInUse(cluster_id))
110                         return cluster_id;
111                 }
112 
113                 // Else reuse any new session ID already defined for this request.
114                 String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
115                 if (new_id!=null&&idInUse(new_id))
116                     return new_id;
117             }
118 
119             // pick a new unique ID!
120             String id=null;
121             while (id==null||id.length()==0||idInUse(id))
122             {
123                 long r0=_weakRandom
124                 ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32))
125                 :_random.nextLong();
126                 if (r0<0)
127                     r0=-r0;
128                 long r1=_weakRandom
129                 ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^(((long)request.hashCode())<<32))
130                 :_random.nextLong();
131                 if (r1<0)
132                     r1=-r1;
133                 id=Long.toString(r0,36)+Long.toString(r1,36);
134 
135                 //add in the id of the node to ensure unique id across cluster
136                 //NOTE this is different to the node suffix which denotes which node the request was received on
137                 if (_workerName!=null)
138                     id=_workerName + id;
139             }
140 
141             request.setAttribute(__NEW_SESSION_ID,id);
142             return id;
143         }
144     }
145 
146     /* ------------------------------------------------------------ */
147     @Override
148     protected void doStart() throws Exception
149     {
150        initRandom();
151     }
152 
153     /* ------------------------------------------------------------ */
154     @Override
155     protected void doStop() throws Exception
156     {
157     }
158 
159     /* ------------------------------------------------------------ */
160     /**
161      * Set up a random number generator for the sessionids.
162      *
163      * By preference, use a SecureRandom but allow to be injected.
164      */
165     public void initRandom ()
166     {
167         if (_random==null)
168         {
169             try
170             {
171                 _random=new SecureRandom();
172             }
173             catch (Exception e)
174             {
175                 LOG.warn("Could not generate SecureRandom for session-id randomness",e);
176                 _random=new Random();
177                 _weakRandom=true;
178             }
179         }
180         else
181             _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
182     }
183 
184 
185 }