Skip to content

Commit 9498da5

Browse files
committed
Clarify behavior of WebSession#save()
+ minor update to the InMemoryWebSession to match the defined behavior. Issue: SPR-17051
1 parent 66d7301 commit 9498da5

File tree

3 files changed

+65
-12
lines changed

3 files changed

+65
-12
lines changed

spring-web/src/main/java/org/springframework/web/server/WebSession.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,19 @@ default <T> T getAttributeOrDefault(String name, T defaultValue) {
116116
Mono<Void> invalidate();
117117

118118
/**
119-
* Save the session persisting attributes (e.g. if stored remotely) and also
120-
* sending the session id to the client if the session is new.
121-
* <p>Note that a session must be started explicitly via {@link #start()} or
122-
* implicitly by adding attributes or otherwise this method has no effect.
119+
* Save the session through the {@code WebSessionStore} as follows:
120+
* <ul>
121+
* <li>If the session is new (i.e. created but never persisted), it must have
122+
* been started explicitly via {@link #start()} or implicitly by adding
123+
* attributes, or otherwise this method should have no effect.
124+
* <li>If the session was retrieved through the {@code WebSessionStore},
125+
* the implementation for this method must check whether the session was
126+
* {@link #invalidate() invalidated} and if so return an error.
127+
* </ul>
128+
* <p>Note that this method is not intended for direct use by applications.
129+
* Instead it is automatically invoked just before the response is
130+
* committed is committed.
123131
* @return {@code Mono} to indicate completion with success or error
124-
* <p>Typically this method should be automatically invoked just before the
125-
* response is committed so applications don't have to by default.
126132
*/
127133
Mono<Void> save();
128134

spring-web/src/main/java/org/springframework/web/server/session/InMemoryWebSessionStore.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -240,17 +240,35 @@ public Mono<Void> invalidate() {
240240

241241
@Override
242242
public Mono<Void> save() {
243+
244+
checkMaxSessionsLimit();
245+
246+
// Implicitly started session..
247+
if (!getAttributes().isEmpty()) {
248+
this.state.compareAndSet(State.NEW, State.STARTED);
249+
}
250+
251+
if (isStarted()) {
252+
// Save
253+
InMemoryWebSessionStore.this.sessions.put(this.getId(), this);
254+
255+
// Unless it was invalidated
256+
if (this.state.get().equals(State.EXPIRED)) {
257+
InMemoryWebSessionStore.this.sessions.remove(this.getId());
258+
return Mono.error(new IllegalStateException("Session was invalidated"));
259+
}
260+
}
261+
262+
return Mono.empty();
263+
}
264+
265+
private void checkMaxSessionsLimit() {
243266
if (sessions.size() >= maxSessions) {
244267
expiredSessionChecker.removeExpiredSessions(clock.instant());
245268
if (sessions.size() >= maxSessions) {
246-
return Mono.error(new IllegalStateException("Max sessions limit reached: " + sessions.size()));
269+
throw new IllegalStateException("Max sessions limit reached: " + sessions.size());
247270
}
248271
}
249-
if (!getAttributes().isEmpty()) {
250-
this.state.compareAndSet(State.NEW, State.STARTED);
251-
}
252-
InMemoryWebSessionStore.this.sessions.put(this.getId(), this);
253-
return Mono.empty();
254272
}
255273

256274
@Override

spring-web/src/test/java/org/springframework/web/server/session/InMemoryWebSessionStoreTests.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ public void lastAccessTimeIsUpdatedOnRetrieve() {
8383
assertNotNull(session1);
8484
String id = session1.getId();
8585
Instant time1 = session1.getLastAccessTime();
86+
session1.start();
8687
session1.save().block();
8788

8889
// Fast-forward a few seconds
@@ -95,6 +96,34 @@ public void lastAccessTimeIsUpdatedOnRetrieve() {
9596
assertTrue(time1.isBefore(time2));
9697
}
9798

99+
@Test // SPR-17051
100+
public void sessionInvalidatedBeforeSave() {
101+
// Request 1 creates session
102+
WebSession session1 = this.store.createWebSession().block();
103+
assertNotNull(session1);
104+
String id = session1.getId();
105+
session1.start();
106+
session1.save().block();
107+
108+
// Request 2 retrieves session
109+
WebSession session2 = this.store.retrieveSession(id).block();
110+
assertNotNull(session2);
111+
assertSame(session1, session2);
112+
113+
// Request 3 retrieves and invalidates
114+
WebSession session3 = this.store.retrieveSession(id).block();
115+
assertNotNull(session3);
116+
assertSame(session1, session3);
117+
session3.invalidate().block();
118+
119+
// Request 2 saves session after invalidated
120+
session2.save().block();
121+
122+
// Session should not be present
123+
WebSession session4 = this.store.retrieveSession(id).block();
124+
assertNull(session4);
125+
}
126+
98127
@Test
99128
public void expirationCheckPeriod() {
100129

0 commit comments

Comments
 (0)