From b2648f84addf424256221369892548f1fb9e45d2 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 7 Mar 2016 08:32:24 -0500 Subject: [PATCH] DefaultWebSessionManager supports multiple sessions Issue: #64 --- .../session/CookieWebSessionIdResolver.java | 2 +- .../session/DefaultWebSessionManager.java | 6 +-- .../server/session/WebSessionIdResolver.java | 2 +- .../DefaultWebSessionManagerTests.java | 50 +++++++++++-------- .../session/WebSessionIntegrationTests.java | 14 +++--- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java index 8530117ccc..1d1fe75bad 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/session/CookieWebSessionIdResolver.java @@ -75,7 +75,7 @@ public class CookieWebSessionIdResolver implements WebSessionIdResolver { @Override - public List resolveSessionId(ServerWebExchange exchange) { + public List resolveSessionIds(ServerWebExchange exchange) { MultiValueMap cookieMap = exchange.getRequest().getCookies(); List cookies = cookieMap.get(getCookieName()); if (cookies == null) { diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/session/DefaultWebSessionManager.java b/spring-web-reactive/src/main/java/org/springframework/web/server/session/DefaultWebSessionManager.java index 5bcb233b95..1eb0e84871 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/session/DefaultWebSessionManager.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/session/DefaultWebSessionManager.java @@ -99,9 +99,9 @@ public class DefaultWebSessionManager implements WebSessionManager { @Override public Mono getSession(ServerWebExchange exchange) { - return Flux.fromIterable(getSessionIdResolver().resolveSessionId(exchange)) + return Flux.fromIterable(getSessionIdResolver().resolveSessionIds(exchange)) + .concatMap(this.sessionStore::retrieveSession) .next() - .then(this.sessionStore::retrieveSession) .then(session -> validateSession(exchange, session)) .otherwiseIfEmpty(createSession(exchange)) .map(session -> extendSession(exchange, session)); @@ -147,7 +147,7 @@ public class DefaultWebSessionManager implements WebSessionManager { // Force explicit start session.start(); - List requestedIds = getSessionIdResolver().resolveSessionId(exchange); + List requestedIds = getSessionIdResolver().resolveSessionIds(exchange); if (requestedIds.isEmpty() || !session.getId().equals(requestedIds.get(0))) { this.sessionIdResolver.setSessionId(exchange, session.getId()); } diff --git a/spring-web-reactive/src/main/java/org/springframework/web/server/session/WebSessionIdResolver.java b/spring-web-reactive/src/main/java/org/springframework/web/server/session/WebSessionIdResolver.java index a57a1cfa7e..77884e7370 100644 --- a/spring-web-reactive/src/main/java/org/springframework/web/server/session/WebSessionIdResolver.java +++ b/spring-web-reactive/src/main/java/org/springframework/web/server/session/WebSessionIdResolver.java @@ -35,7 +35,7 @@ public interface WebSessionIdResolver { * @param exchange the current exchange * @return the session id's or an empty list */ - List resolveSessionId(ServerWebExchange exchange); + List resolveSessionIds(ServerWebExchange exchange); /** * Send the given session id to the client or if the session id is "null" diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/session/DefaultWebSessionManagerTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/session/DefaultWebSessionManagerTests.java index c7c17b8a6a..13efe415b6 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/server/session/DefaultWebSessionManagerTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/server/session/DefaultWebSessionManagerTests.java @@ -20,6 +20,7 @@ import java.time.Clock; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -45,17 +46,15 @@ import static org.junit.Assert.assertSame; */ public class DefaultWebSessionManagerTests { - private DefaultWebSessionManager manager; + private final DefaultWebSessionManager manager = new DefaultWebSessionManager(); - private TestWebSessionIdResolver idResolver; + private final TestWebSessionIdResolver idResolver = new TestWebSessionIdResolver(); private DefaultServerWebExchange exchange; @Before public void setUp() throws Exception { - this.idResolver = new TestWebSessionIdResolver(); - this.manager = new DefaultWebSessionManager(); this.manager.setSessionIdResolver(this.idResolver); MockServerHttpRequest request = new MockServerHttpRequest(HttpMethod.GET, new URI("/path")); @@ -65,66 +64,73 @@ public class DefaultWebSessionManagerTests { @Test - public void getSessionPassive() throws Exception { + public void getSessionWithoutStarting() throws Exception { this.idResolver.setIdsToResolve(Collections.emptyList()); WebSession session = this.manager.getSession(this.exchange).get(); - - assertNotNull(session); - assertFalse(session.isStarted()); - assertFalse(session.isExpired()); - session.save(); - assertNull(this.idResolver.getId()); + assertFalse(session.isStarted()); + assertFalse(session.isExpired()); + assertNull(this.idResolver.getSavedId()); assertNull(this.manager.getSessionStore().retrieveSession(session.getId()).get()); } @Test - public void getSessionForceCreate() throws Exception { + public void startSessionExplicitly() throws Exception { this.idResolver.setIdsToResolve(Collections.emptyList()); WebSession session = this.manager.getSession(this.exchange).get(); session.start(); session.save(); String id = session.getId(); - assertNotNull(this.idResolver.getId()); - assertEquals(id, this.idResolver.getId()); + assertNotNull(this.idResolver.getSavedId()); + assertEquals(id, this.idResolver.getSavedId()); assertSame(session, this.manager.getSessionStore().retrieveSession(id).get()); } @Test - public void getSessionAddAttribute() throws Exception { + public void startSessionImplicitly() throws Exception { this.idResolver.setIdsToResolve(Collections.emptyList()); WebSession session = this.manager.getSession(this.exchange).get(); session.getAttributes().put("foo", "bar"); session.save(); - assertNotNull(this.idResolver.getId()); + assertNotNull(this.idResolver.getSavedId()); } @Test - public void getSessionExisting() throws Exception { + public void existingSession() throws Exception { DefaultWebSession existing = new DefaultWebSession("1", Clock.systemDefaultZone()); this.manager.getSessionStore().storeSession(existing); - this.idResolver.setIdsToResolve(Collections.singletonList("1")); + WebSession actual = this.manager.getSession(this.exchange).get(); assertSame(existing, actual); } @Test - public void getSessionExistingExpired() throws Exception { + public void existingSessionIsExpired() throws Exception { Clock clock = Clock.systemDefaultZone(); DefaultWebSession existing = new DefaultWebSession("1", clock); existing.start(); existing.setLastAccessTime(Instant.now(clock).minus(Duration.ofMinutes(31))); this.manager.getSessionStore().storeSession(existing); - this.idResolver.setIdsToResolve(Collections.singletonList("1")); + WebSession actual = this.manager.getSession(this.exchange).get(); assertNotSame(existing, actual); } + @Test + public void multipleSessions() throws Exception { + DefaultWebSession existing = new DefaultWebSession("3", Clock.systemDefaultZone()); + this.manager.getSessionStore().storeSession(existing); + this.idResolver.setIdsToResolve(Arrays.asList("1", "2", "3")); + + WebSession actual = this.manager.getSession(this.exchange).get(); + assertSame(existing, actual); + } + private static class TestWebSessionIdResolver implements WebSessionIdResolver { @@ -137,12 +143,12 @@ public class DefaultWebSessionManagerTests { this.idsToResolve = idsToResolve; } - public String getId() { + public String getSavedId() { return this.id; } @Override - public List resolveSessionId(ServerWebExchange exchange) { + public List resolveSessionIds(ServerWebExchange exchange) { return this.idsToResolve; } diff --git a/spring-web-reactive/src/test/java/org/springframework/web/server/session/WebSessionIntegrationTests.java b/spring-web-reactive/src/test/java/org/springframework/web/server/session/WebSessionIntegrationTests.java index cc5cfdf437..45cde4ff18 100644 --- a/spring-web-reactive/src/test/java/org/springframework/web/server/session/WebSessionIntegrationTests.java +++ b/spring-web-reactive/src/test/java/org/springframework/web/server/session/WebSessionIntegrationTests.java @@ -43,8 +43,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; - /** + * Integration tests for with a server-side session. + * * @author Rossen Stoyanchev */ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTests { @@ -95,6 +96,8 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe @Test public void expiredSession() throws Exception { + + // First request: no session yet, new session created RequestEntity request = RequestEntity.get(createUri("/")).build(); ResponseEntity response = this.restTemplate.exchange(request, Void.class); @@ -103,11 +106,11 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe assertNotNull(id); assertEquals(1, this.handler.getCount()); - // Set clock back 31 minutes + // Set (server-side) clock back 31 minutes Clock clock = this.sessionManager.getClock(); this.sessionManager.setClock(Clock.offset(clock, Duration.ofMinutes(-31))); - // Access again to update lastAccessTime + // Second request: lastAccessTime updated (with offset clock) request = RequestEntity.get(createUri("/")).header("Cookie", "SESSION=" + id).build(); response = this.restTemplate.exchange(request, Void.class); @@ -115,7 +118,7 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe assertNull(response.getHeaders().get("Set-Cookie")); assertEquals(2, this.handler.getCount()); - // Now it should be expired + // Third request: new session replaces expired session request = RequestEntity.get(createUri("/")).header("Cookie", "SESSION=" + id).build(); response = this.restTemplate.exchange(request, Void.class); @@ -125,9 +128,6 @@ public class WebSessionIntegrationTests extends AbstractHttpHandlerIntegrationTe assertEquals("Expected new session attribute", 1, this.handler.getCount()); } - - // No client side HttpCookie support yet - private String extractSessionId(HttpHeaders headers) { List headerValues = headers.get("Set-Cookie"); assertNotNull(headerValues);