Skip to content

Commit 30c0dbb

Browse files
committed
[UNDERTOW-1869] At InMemorySessionManager.createSession, replace the synchronized block with a putIfAbsent(sessionID, PLACE_HOLDER) to make sure we don't race when creating the session
1 parent 5a8e3a8 commit 30c0dbb

File tree

1 file changed

+46
-46
lines changed

1 file changed

+46
-46
lines changed

core/src/main/java/io/undertow/server/session/InMemorySessionManager.java

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
*/
5353
public class InMemorySessionManager implements SessionManager, SessionManagerStatistics {
5454

55+
// place holder for sessions during id creation, it prevents collision of session ids in race condition scenarios where two sessions are being created with the same id
56+
private static final SessionImpl PLACE_HOLDER_SESSION = new SessionImpl(null, null, null, null, null, null, 0);
57+
5558
private final AttachmentKey<SessionImpl> NEW_SESSION = AttachmentKey.create(SessionImpl.class);
5659

5760
private final SessionIdGenerator sessionIdGenerator;
@@ -166,47 +169,27 @@ public Session createSession(final HttpServerExchange serverExchange, final Sess
166169
}
167170

168171
String sessionID = config.findSessionId(serverExchange);
169-
boolean idRequested = false;
170-
if (sessionID == null) {
171-
sessionID = sessionIdGenerator.createSessionId();
172+
if (sessionID != null) {
173+
if (!saveSessionID(sessionID))
174+
throw UndertowMessages.MESSAGES.sessionWithIdAlreadyExists(sessionID);
175+
// else: succeeded to use requested session id
172176
} else {
173-
idRequested = true;
174-
}
175-
SessionImpl session;
176-
synchronized(this.sessions) {
177-
if(this.sessions.containsKey(sessionID)) {
178-
if(idRequested) {
179-
throw UndertowMessages.MESSAGES.sessionWithIdAlreadyExists(sessionID);
180-
} else {
181-
int count = 0;
182-
sessionID = null;
183-
while (sessionID == null) {
184-
sessionID = sessionIdGenerator.createSessionId();
185-
if (sessions.containsKey(sessionID)) {
186-
sessionID = null;
187-
}
188-
if (count++ == 100) {
189-
//this should 'never' happen
190-
//but we guard against pathalogical session id generators to prevent an infinite loop
191-
throw UndertowMessages.MESSAGES.couldNotGenerateUniqueSessionId();
192-
}
193-
}
194-
}
195-
}
196-
Object evictionToken;
197-
if (evictionQueue != null) {
198-
evictionToken = evictionQueue.offerLastAndReturnToken(sessionID);
199-
} else {
200-
evictionToken = null;
201-
}
202-
session = new SessionImpl(this, sessionID, config, serverExchange.getIoThread(), serverExchange.getConnection().getWorker(), evictionToken, defaultSessionTimeout);
203-
sessions.put(sessionID, session);
204-
config.setSessionId(serverExchange, session.getId());
205-
serverExchange.putAttachment(NEW_SESSION, session);
177+
sessionID = createAndSaveNewID();
206178
}
207-
session.bumpTimeout();
179+
Object evictionToken;
180+
if (evictionQueue != null) {
181+
evictionToken = evictionQueue.offerLastAndReturnToken(sessionID);
182+
} else {
183+
evictionToken = null;
184+
}
185+
final SessionImpl session = new SessionImpl(this, sessionID, config, serverExchange.getIoThread(), serverExchange.getConnection().getWorker(), evictionToken, defaultSessionTimeout);
186+
208187
UndertowLogger.SESSION_LOGGER.debugf("Created session with id %s for exchange %s", sessionID, serverExchange);
188+
saveSession(sessionID, session);
189+
config.setSessionId(serverExchange, session.getId());
190+
session.bumpTimeout();
209191
sessionListeners.sessionCreated(session, serverExchange);
192+
serverExchange.putAttachment(NEW_SESSION, session);
210193

211194
if(statisticsEnabled) {
212195
createdSessionCount.incrementAndGet();
@@ -223,6 +206,26 @@ public Session createSession(final HttpServerExchange serverExchange, final Sess
223206
return session;
224207
}
225208

209+
private boolean saveSessionID(String sessionID) {
210+
return this.sessions.putIfAbsent(sessionID, PLACE_HOLDER_SESSION) == null;
211+
}
212+
213+
private String createAndSaveNewID() {
214+
for (int i = 0; i < 100; i++) {
215+
final String sessionID = sessionIdGenerator.createSessionId();
216+
if (saveSessionID(sessionID))
217+
return sessionID;
218+
}
219+
//this should 'never' happen
220+
//but we guard against pathalogical session id generators to prevent an infinite loop
221+
throw UndertowMessages.MESSAGES.couldNotGenerateUniqueSessionId();
222+
}
223+
224+
private void saveSession(String savedID, SessionImpl session) {
225+
final SessionImpl placeHolder = sessions.put(savedID, session);
226+
assert(placeHolder == PLACE_HOLDER_SESSION);
227+
}
228+
226229
@Override
227230
public Session getSession(final HttpServerExchange serverExchange, final SessionConfig config) {
228231
if (serverExchange != null) {
@@ -633,16 +636,13 @@ public SessionManager getSessionManager() {
633636
@Override
634637
public String changeSessionId(final HttpServerExchange exchange, final SessionConfig config) {
635638
final String oldId = sessionId;
636-
String newId = null;
637-
synchronized(sessionManager.sessions) {
638-
newId = sessionManager.sessionIdGenerator.createSessionId();
639-
this.sessionId = newId;
640-
if(!invalid) {
641-
sessionManager.sessions.put(newId, this);
642-
config.setSessionId(exchange, this.getId());
643-
}
644-
sessionManager.sessions.remove(oldId);
639+
String newId = sessionManager.createAndSaveNewID();
640+
this.sessionId = newId;
641+
if(!invalid) {
642+
sessionManager.saveSession(newId, this);
643+
config.setSessionId(exchange, this.getId());
645644
}
645+
sessionManager.sessions.remove(oldId);
646646
sessionManager.sessionListeners.sessionIdChanged(this, oldId);
647647
UndertowLogger.SESSION_LOGGER.debugf("Changing session id %s to %s", oldId, newId);
648648

0 commit comments

Comments
 (0)