]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/server/session/AbstractSessionIdManager.java
Importing upstream Jetty jetty-9.2.1.v20140609
[gigi.git] / lib / jetty / org / eclipse / jetty / server / session / AbstractSessionIdManager.java
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 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     protected String _workerAttr;
41     protected long _reseed=100000L;
42
43     /* ------------------------------------------------------------ */
44     public AbstractSessionIdManager()
45     {
46     }
47
48     /* ------------------------------------------------------------ */
49     public AbstractSessionIdManager(Random random)
50     {
51         _random=random;
52     }
53
54
55     /* ------------------------------------------------------------ */
56     /**
57      * Get the workname. If set, the workername is dot appended to the session
58      * ID and can be used to assist session affinity in a load balancer.
59      *
60      * @return String or null
61      */
62     @Override
63     public String getWorkerName()
64     {
65         return _workerName;
66     }
67
68     /* ------------------------------------------------------------ */
69     /**
70      * Set the workname. If set, the workername is dot appended to the session
71      * ID and can be used to assist session affinity in a load balancer.
72      * A worker name starting with $ is used as a request attribute name to
73      * lookup the worker name that can be dynamically set by a request
74      * customiser.
75      *
76      * @param workerName
77      */
78     public void setWorkerName(String workerName)
79     {
80         if (isRunning())
81             throw new IllegalStateException(getState());
82         if (workerName.contains("."))
83             throw new IllegalArgumentException("Name cannot contain '.'");
84         _workerName=workerName;
85     }
86
87     /* ------------------------------------------------------------ */
88     public Random getRandom()
89     {
90         return _random;
91     }
92
93     /* ------------------------------------------------------------ */
94     public synchronized void setRandom(Random random)
95     {
96         _random=random;
97         _weakRandom=false;
98     }
99
100     /* ------------------------------------------------------------ */
101     /**
102      * @return the reseed probability
103      */
104     public long getReseed()
105     {
106         return _reseed;
107     }
108
109     /* ------------------------------------------------------------ */
110     /** Set the reseed probability.
111      * @param reseed  If non zero then when a random long modulo the reseed value == 1, the {@link SecureRandom} will be reseeded.
112      */
113     public void setReseed(long reseed)
114     {
115         _reseed = reseed;
116     }
117
118     /* ------------------------------------------------------------ */
119     /**
120      * Create a new session id if necessary.
121      *
122      * @see org.eclipse.jetty.server.SessionIdManager#newSessionId(javax.servlet.http.HttpServletRequest, long)
123      */
124     @Override
125     public String newSessionId(HttpServletRequest request, long created)
126     {
127         synchronized (this)
128         {
129             if (request==null)
130                 return newSessionId(created);
131
132             // A requested session ID can only be used if it is in use already.
133             String requested_id=request.getRequestedSessionId();
134             if (requested_id!=null)
135             {
136                 String cluster_id=getClusterId(requested_id);
137                 if (idInUse(cluster_id))
138                     return cluster_id;
139             }
140
141             // Else reuse any new session ID already defined for this request.
142             String new_id=(String)request.getAttribute(__NEW_SESSION_ID);
143             if (new_id!=null&&idInUse(new_id))
144                 return new_id;
145
146             // pick a new unique ID!
147             String id = newSessionId(request.hashCode());
148
149             request.setAttribute(__NEW_SESSION_ID,id);
150             return id;
151         }
152     }
153
154     /* ------------------------------------------------------------ */
155     public String newSessionId(long seedTerm)
156     {
157         // pick a new unique ID!
158         String id=null;
159         while (id==null||id.length()==0||idInUse(id))
160         {
161             long r0=_weakRandom
162                     ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
163                     :_random.nextLong();
164             if (r0<0)
165                 r0=-r0;
166                     
167             // random chance to reseed
168             if (_reseed>0 && (r0%_reseed)== 1L)
169             {
170                 LOG.debug("Reseeding {}",this);
171                 if (_random instanceof SecureRandom)
172                 {
173                     SecureRandom secure = (SecureRandom)_random;
174                     secure.setSeed(secure.generateSeed(8));
175                 }
176                 else
177                 {
178                     _random.setSeed(_random.nextLong()^System.currentTimeMillis()^seedTerm^Runtime.getRuntime().freeMemory());
179                 }
180             }
181             
182             long r1=_weakRandom
183                 ?(hashCode()^Runtime.getRuntime().freeMemory()^_random.nextInt()^((seedTerm)<<32))
184                 :_random.nextLong();
185             if (r1<0)
186                 r1=-r1;
187             
188             id=Long.toString(r0,36)+Long.toString(r1,36);
189
190             //add in the id of the node to ensure unique id across cluster
191             //NOTE this is different to the node suffix which denotes which node the request was received on
192             if (_workerName!=null)
193                 id=_workerName + id;
194     
195         }
196         return id;
197     }
198
199
200     /* ------------------------------------------------------------ */
201     @Override
202     public abstract void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request);
203
204     
205     /* ------------------------------------------------------------ */
206     @Override
207     protected void doStart() throws Exception
208     {
209        initRandom();
210        _workerAttr=(_workerName!=null && _workerName.startsWith("$"))?_workerName.substring(1):null;
211     }
212
213     /* ------------------------------------------------------------ */
214     @Override
215     protected void doStop() throws Exception
216     {
217     }
218
219     /* ------------------------------------------------------------ */
220     /**
221      * Set up a random number generator for the sessionids.
222      *
223      * By preference, use a SecureRandom but allow to be injected.
224      */
225     public void initRandom ()
226     {
227         if (_random==null)
228         {
229             try
230             {
231                 _random=new SecureRandom();
232             }
233             catch (Exception e)
234             {
235                 LOG.warn("Could not generate SecureRandom for session-id randomness",e);
236                 _random=new Random();
237                 _weakRandom=true;
238             }
239         }
240         else
241             _random.setSeed(_random.nextLong()^System.currentTimeMillis()^hashCode()^Runtime.getRuntime().freeMemory());
242     }
243
244     /** Get the session ID with any worker ID.
245      *
246      * @param clusterId
247      * @param request
248      * @return sessionId plus any worker ID.
249      */
250     @Override
251     public String getNodeId(String clusterId, HttpServletRequest request)
252     {
253         if (_workerName!=null)
254         {
255             if (_workerAttr==null)
256                 return clusterId+'.'+_workerName;
257
258             String worker=(String)request.getAttribute(_workerAttr);
259             if (worker!=null)
260                 return clusterId+'.'+worker;
261         }
262     
263         return clusterId;
264     }
265
266     /** Get the session ID without any worker ID.
267      *
268      * @param nodeId the node id
269      * @return sessionId without any worker ID.
270      */
271     @Override
272     public String getClusterId(String nodeId)
273     {
274         int dot=nodeId.lastIndexOf('.');
275         return (dot>0)?nodeId.substring(0,dot):nodeId;
276     }
277
278
279 }