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