();
if (this.servletRequest.getCookies() != null) {
- for (Cookie cookie : this.servletRequest.getCookies()) {
- this.cookies.addCookie(cookie.getName(), cookie.getValue());
+ for (javax.servlet.http.Cookie cookie : this.servletRequest.getCookies()) {
+ this.cookies.put(cookie.getName(), new ServletServerCookie(cookie));
}
}
+ this.cookies = Collections.unmodifiableMap(this.cookies);
}
return this.cookies;
}
diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java
index ccdf58f475..b65d0cdcad 100644
--- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java
+++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java
@@ -20,10 +20,9 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
+
import javax.servlet.http.HttpServletResponse;
-import org.springframework.http.Cookie;
-import org.springframework.http.Cookies;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.util.Assert;
@@ -42,8 +41,6 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
private boolean headersWritten = false;
- private final Cookies cookies = new Cookies();
-
/**
* Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
@@ -72,28 +69,20 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
return (this.headersWritten ? HttpHeaders.readOnlyHttpHeaders(this.headers) : this.headers);
}
- @Override
- public Cookies getCookies() {
- return (this.headersWritten ? Cookies.readOnlyCookies(this.cookies) : this.cookies);
- }
-
@Override
public OutputStream getBody() throws IOException {
- writeCookies();
writeHeaders();
return this.servletResponse.getOutputStream();
}
@Override
public void flush() throws IOException {
- writeCookies();
writeHeaders();
this.servletResponse.flushBuffer();
}
@Override
public void close() {
- writeCookies();
writeHeaders();
}
@@ -116,14 +105,4 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
this.headersWritten = true;
}
}
-
- private void writeCookies() {
- if (!this.headersWritten) {
- for (Cookie source : this.cookies.getCookies()) {
- javax.servlet.http.Cookie target = new javax.servlet.http.Cookie(source.getName(), source.getValue());
- target.setPath("/");
- this.servletResponse.addCookie(target);
- }
- }
- }
}
diff --git a/spring-web/src/test/java/org/springframework/http/MockHttpInputMessage.java b/spring-web/src/test/java/org/springframework/http/MockHttpInputMessage.java
index 0ca8a29aca..f7b4f45b9a 100644
--- a/spring-web/src/test/java/org/springframework/http/MockHttpInputMessage.java
+++ b/spring-web/src/test/java/org/springframework/http/MockHttpInputMessage.java
@@ -31,8 +31,6 @@ public class MockHttpInputMessage implements HttpInputMessage {
private final InputStream body;
- private final Cookies cookies = new Cookies();
-
public MockHttpInputMessage(byte[] contents) {
Assert.notNull(contents, "'contents' must not be null");
@@ -53,9 +51,4 @@ public class MockHttpInputMessage implements HttpInputMessage {
public InputStream getBody() throws IOException {
return body;
}
-
- @Override
- public Cookies getCookies() {
- return this.cookies ;
- }
}
diff --git a/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java b/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java
index 3287a7d93f..dbb39e62b9 100644
--- a/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java
+++ b/spring-web/src/test/java/org/springframework/http/MockHttpOutputMessage.java
@@ -21,7 +21,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
-import static org.mockito.BDDMockito.*;
+import static org.mockito.Mockito.*;
/**
* @author Arjen Poutsma
@@ -32,9 +32,6 @@ public class MockHttpOutputMessage implements HttpOutputMessage {
private final ByteArrayOutputStream body = spy(new ByteArrayOutputStream());
- private final Cookies cookies = new Cookies();
-
-
@Override
public HttpHeaders getHeaders() {
return headers;
@@ -53,9 +50,4 @@ public class MockHttpOutputMessage implements HttpOutputMessage {
byte[] bytes = getBodyAsBytes();
return new String(bytes, charset);
}
-
- @Override
- public Cookies getCookies() {
- return this.cookies;
- }
}
diff --git a/spring-web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java b/spring-web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java
index 0358bf42bf..6b77577b80 100644
--- a/spring-web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java
+++ b/spring-web/src/test/java/org/springframework/http/client/InterceptingClientHttpRequestFactoryTests.java
@@ -28,8 +28,6 @@ import java.util.List;
import org.junit.Before;
import org.junit.Test;
-
-import org.springframework.http.Cookies;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
@@ -254,8 +252,6 @@ public class InterceptingClientHttpRequestFactoryTests {
private boolean executed = false;
- private Cookies cookies = new Cookies();
-
private RequestMock() {
}
@@ -292,11 +288,6 @@ public class InterceptingClientHttpRequestFactoryTests {
executed = true;
return responseMock;
}
-
- @Override
- public Cookies getCookies() {
- return this.cookies ;
- }
}
private static class ResponseMock implements ClientHttpResponse {
@@ -307,8 +298,6 @@ public class InterceptingClientHttpRequestFactoryTests {
private HttpHeaders headers = new HttpHeaders();
- private Cookies cookies = new Cookies();
-
@Override
public HttpStatus getStatusCode() throws IOException {
return statusCode;
@@ -337,10 +326,5 @@ public class InterceptingClientHttpRequestFactoryTests {
@Override
public void close() {
}
-
- @Override
- public Cookies getCookies() {
- return this.cookies ;
- }
}
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java
index f80c92ddbf..dbd351924c 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/support/AbstractSockJsService.java
@@ -78,7 +78,7 @@ public abstract class AbstractSockJsService implements SockJsService {
private int streamBytesLimit = 128 * 1024;
- private boolean jsessionIdCookieRequired = true;
+ private boolean sessionCookieEnabled = false;
private long heartbeatTime = 25 * 1000;
@@ -187,23 +187,22 @@ public abstract class AbstractSockJsService implements SockJsService {
}
/**
- * Some load balancers do sticky sessions, but only if there is a JSESSIONID
+ * Some load balancers do sticky sessions, but only if there is a "JSESSIONID"
* cookie. Even if it is set to a dummy value, it doesn't matter since
* session information is added by the load balancer.
*
- * Set this option to indicate if a JSESSIONID cookie should be created. The
- * default value is "true".
+ *
The default value is "false" since Java servers set the session cookie.
*/
- public void setJsessionIdCookieRequired(boolean jsessionIdCookieRequired) {
- this.jsessionIdCookieRequired = jsessionIdCookieRequired;
+ public void setDummySessionCookieEnabled(boolean sessionCookieEnabled) {
+ this.sessionCookieEnabled = sessionCookieEnabled;
}
/**
* Whether setting JSESSIONID cookie is necessary.
- * @see #setJsessionIdCookieRequired(boolean)
+ * @see #setDummySessionCookieEnabled(boolean)
*/
- public boolean isJsessionIdCookieRequired() {
- return this.jsessionIdCookieRequired;
+ public boolean isDummySessionCookieEnabled() {
+ return this.sessionCookieEnabled;
}
/**
@@ -476,7 +475,7 @@ public abstract class AbstractSockJsService implements SockJsService {
addCorsHeaders(request, response);
addNoCacheHeaders(response);
- String content = String.format(INFO_CONTENT, random.nextInt(), isJsessionIdCookieRequired(), isWebSocketEnabled());
+ String content = String.format(INFO_CONTENT, random.nextInt(), isDummySessionCookieEnabled(), isWebSocketEnabled());
response.getBody().write(content.getBytes());
}
else if (HttpMethod.OPTIONS.equals(request.getMethod())) {
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportType.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportType.java
index f9a93d5c1e..1a17c7f780 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportType.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/TransportType.java
@@ -91,7 +91,7 @@ public enum TransportType {
return this.headerHints.contains("cors");
}
- public boolean setsJsessionId() {
+ public boolean sendsSessionCookie() {
return this.headerHints.contains("jsessionid");
}
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsService.java b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsService.java
index 2248664a90..4c01c0b417 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsService.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsService.java
@@ -118,7 +118,7 @@ public class DefaultSockJsService extends AbstractSockJsService {
* @param transportHandlerOverrides zero or more overrides to the default transport
* handler types.
*/
- public DefaultSockJsService(TaskScheduler taskScheduler, Set transportHandlers,
+ public DefaultSockJsService(TaskScheduler taskScheduler, Collection transportHandlers,
TransportHandler... transportHandlerOverrides) {
super(taskScheduler);
@@ -254,11 +254,10 @@ public class DefaultSockJsService extends AbstractSockJsService {
addNoCacheHeaders(response);
}
- if (transportType.setsJsessionId() && isJsessionIdCookieRequired()) {
- Cookie cookie = request.getCookies().getCookie("JSESSIONID");
- String jsid = (cookie != null) ? cookie.getValue() : "dummy";
- // TODO: bypass use of Cookie object (causes Jetty to set Expires header)
- response.getHeaders().set("Set-Cookie", "JSESSIONID=" + jsid + ";path=/");
+ if (transportType.sendsSessionCookie() && isDummySessionCookieEnabled()) {
+ Cookie cookie = request.getCookies().get("JSESSIONID");
+ String value = (cookie != null) ? cookie.getValue() : "dummy";
+ response.getHeaders().set("Set-Cookie", "JSESSIONID=" + value + ";path=/");
}
if (transportType.supportsCors()) {
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/AbstractSockJsServiceTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/AbstractSockJsServiceTests.java
index deabe9bf00..2e34aa92f7 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/AbstractSockJsServiceTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/support/AbstractSockJsServiceTests.java
@@ -149,7 +149,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":true,\"websocket\":true}",
body.substring(body.indexOf(',')));
- this.service.setJsessionIdCookieRequired(false);
+ this.service.setDummySessionCookieEnabled(false);
this.service.setWebSocketsEnabled(false);
handleRequest("GET", "/a/info", HttpStatus.OK);
@@ -214,6 +214,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
assertEquals(httpStatus.value(), this.servletResponse.getStatus());
}
+
private static class TestSockJsService extends AbstractSockJsService {
private String sessionId;
diff --git a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java
index c91fe926ff..0d64d79e6f 100644
--- a/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java
+++ b/spring-websocket/src/test/java/org/springframework/web/socket/sockjs/transport/handler/DefaultSockJsServiceTests.java
@@ -16,26 +16,18 @@
package org.springframework.web.socket.sockjs.transport.handler;
-import java.util.Collections;
-import java.util.HashSet;
+import java.util.Arrays;
import java.util.Map;
-import java.util.Set;
import org.junit.Before;
import org.junit.Test;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.server.ServerHttpRequest;
-import org.springframework.http.server.ServerHttpResponse;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.web.socket.AbstractHttpRequestTests;
import org.springframework.web.socket.WebSocketHandler;
-import org.springframework.web.socket.WebSocketSession;
-import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.TransportType;
-import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
-import org.springframework.web.socket.sockjs.transport.handler.SockJsSessionFactory;
-import org.springframework.web.socket.sockjs.transport.session.AbstractSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.StubSockJsServiceConfig;
import org.springframework.web.socket.sockjs.transport.session.TestSockJsSession;
@@ -50,10 +42,42 @@ import static org.mockito.Mockito.*;
*/
public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
- @Override
+ private static final String sockJsPrefix = "mysockjs";
+
+ private static final String sessionId = "session1";
+
+ private static final String sessionUrlPrefix = "/mysockjs/server1/" + sessionId + "/";
+
+
+ @Mock private SessionCreatingTransportHandler xhrHandler;
+
+ @Mock private TransportHandler xhrSendHandler;
+
+ @Mock private WebSocketHandler wsHandler;
+
+ @Mock private TaskScheduler taskScheduler;
+
+ private TestSockJsSession session;
+
+ private DefaultSockJsService service;
+
+
@Before
- public void setUp() {
+ public void setup() {
+
super.setUp();
+
+ MockitoAnnotations.initMocks(this);
+
+ this.session = new TestSockJsSession(sessionId, new StubSockJsServiceConfig(), this.wsHandler);
+
+ when(this.xhrHandler.getTransportType()).thenReturn(TransportType.XHR);
+ when(this.xhrHandler.createSession(sessionId, this.wsHandler)).thenReturn(this.session);
+ when(this.xhrSendHandler.getTransportType()).thenReturn(TransportType.XHR_SEND);
+
+ this.service = new DefaultSockJsService(this.taskScheduler,
+ Arrays.asList(this.xhrHandler, this.xhrSendHandler));
+ this.service.setValidSockJsPrefixes(sockJsPrefix);
}
@Test
@@ -76,24 +100,14 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
@Test
public void handleTransportRequestXhr() throws Exception {
- setRequest("POST", "/a/server/session/xhr");
-
- TaskScheduler taskScheduler = mock(TaskScheduler.class);
- StubXhrTransportHandler xhrHandler = new StubXhrTransportHandler();
- Set transportHandlers = Collections.singleton(xhrHandler);
- WebSocketHandler webSocketHandler = mock(WebSocketHandler.class);
-
- DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
- service.handleTransportRequest(this.request, this.response, webSocketHandler, "123", TransportType.XHR.value());
+ setRequest("POST", sessionUrlPrefix + "xhr");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
assertEquals(200, this.servletResponse.getStatus());
- assertNotNull(xhrHandler.session);
- assertSame(webSocketHandler, xhrHandler.webSocketHandler);
-
+ verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
verify(taskScheduler).scheduleAtFixedRate(any(Runnable.class), eq(service.getDisconnectDelay()));
assertEquals("no-store, no-cache, must-revalidate, max-age=0", this.response.getHeaders().getCacheControl());
- assertEquals("JSESSIONID=dummy;path=/", this.response.getHeaders().getFirst("Set-Cookie"));
assertEquals("*", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
assertEquals("true", this.response.getHeaders().getFirst("Access-Control-Allow-Credentials"));
}
@@ -101,14 +115,8 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
@Test
public void handleTransportRequestXhrOptions() throws Exception {
- setRequest("OPTIONS", "/a/server/session/xhr");
-
- TaskScheduler taskScheduler = mock(TaskScheduler.class);
- StubXhrTransportHandler xhrHandler = new StubXhrTransportHandler();
- Set transportHandlers = Collections.singleton(xhrHandler);
-
- DefaultSockJsService service = new DefaultSockJsService(taskScheduler, transportHandlers);
- service.handleTransportRequest(this.request, this.response, null, "123", TransportType.XHR.value());
+ setRequest("OPTIONS", sessionUrlPrefix + "xhr");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
assertEquals(204, this.servletResponse.getStatus());
assertEquals("*", this.response.getHeaders().getFirst("Access-Control-Allow-Origin"));
@@ -116,14 +124,44 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
assertEquals("OPTIONS, POST", this.response.getHeaders().getFirst("Access-Control-Allow-Methods"));
}
+ @Test
+ public void dummySessionCookieEnabled() throws Exception {
+
+ setRequest("POST", sessionUrlPrefix + "xhr");
+ this.service.setDummySessionCookieEnabled(true);
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
+
+ assertEquals(200, this.servletResponse.getStatus());
+ assertEquals("JSESSIONID=dummy;path=/", this.servletResponse.getHeader("Set-Cookie"));
+ }
+
+ @Test
+ public void dummySessionCookieDisabled() throws Exception {
+
+ setRequest("POST", sessionUrlPrefix + "xhr");
+ this.service.setDummySessionCookieEnabled(false);
+ this.service.handleTransportRequest(this.request, this.response, this.wsHandler, sessionId, "xhr");
+
+ assertEquals(200, this.servletResponse.getStatus());
+ assertNull(this.servletResponse.getHeader("Set-Cookie"));
+ }
+
+ @Test
+ public void dummySessionCookieReuseRequestCookieValue() throws Exception {
+
+ setRequest("POST", sessionUrlPrefix + "xhr");
+ this.servletRequest.addHeader("Cookie", "JSESSIONID=123456789");
+ this.service.handleTransportRequest(this.request, this.response, this.wsHandler, sessionId, "xhr");
+
+ assertEquals(200, this.servletResponse.getStatus());
+ assertNull(this.servletResponse.getHeader("Set-Cookie"));
+ }
+
@Test
public void handleTransportRequestNoSuitableHandler() throws Exception {
- setRequest("POST", "/a/server/session/xhr");
-
- Set transportHandlers = new HashSet<>();
- DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
- service.handleTransportRequest(this.request, this.response, null, "123", TransportType.XHR.value());
+ setRequest("POST", sessionUrlPrefix + "eventsource");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
assertEquals(404, this.servletResponse.getStatus());
}
@@ -131,71 +169,28 @@ public class DefaultSockJsServiceTests extends AbstractHttpRequestTests {
@Test
public void handleTransportRequestXhrSend() throws Exception {
- this.servletRequest.setMethod("POST");
+ setRequest("POST", sessionUrlPrefix + "xhr_send");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
- Set transportHandlers = new HashSet<>();
- transportHandlers.add(new StubXhrTransportHandler());
- transportHandlers.add(new StubXhrSendTransportHandler());
- WebSocketHandler wsHandler = mock(WebSocketHandler.class);
- DefaultSockJsService service = new DefaultSockJsService(mock(TaskScheduler.class), transportHandlers);
-
- service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR_SEND.value());
-
- assertEquals(404, this.servletResponse.getStatus()); // dropped (no session)
+ assertEquals(404, this.servletResponse.getStatus()); // no session yet
resetResponse();
- service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR.value());
+ setRequest("POST", sessionUrlPrefix + "xhr");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
- assertEquals(200, this.servletResponse.getStatus());
+ assertEquals(200, this.servletResponse.getStatus()); // session created
+ verify(this.xhrHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
resetResponse();
- service.handleTransportRequest(this.request, this.response, wsHandler, "123", TransportType.XHR_SEND.value());
+ setRequest("POST", sessionUrlPrefix + "xhr_send");
+ this.service.handleRequest(this.request, this.response, this.wsHandler);
- assertEquals(200, this.servletResponse.getStatus());
+ assertEquals(200, this.servletResponse.getStatus()); // session exists
+ verify(this.xhrSendHandler).handleRequest(this.request, this.response, this.wsHandler, this.session);
}
- private static class StubXhrTransportHandler implements TransportHandler, SockJsSessionFactory {
-
- WebSocketHandler webSocketHandler;
-
- WebSocketSession session;
-
- @Override
- public TransportType getTransportType() {
- return TransportType.XHR;
- }
-
- @Override
- public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
- WebSocketHandler handler, WebSocketSession session) throws SockJsProcessingException {
-
- this.webSocketHandler = handler;
- this.session = session;
- }
-
- @Override
- public AbstractSockJsSession createSession(String sessionId, WebSocketHandler webSocketHandler) {
- return new TestSockJsSession(sessionId, new StubSockJsServiceConfig(), webSocketHandler);
- }
-
- }
-
- private static class StubXhrSendTransportHandler implements TransportHandler {
-
- @Override
- public TransportType getTransportType() {
- return TransportType.XHR_SEND;
- }
-
- @Override
- public void handleRequest(ServerHttpRequest request, ServerHttpResponse response,
- WebSocketHandler handler, WebSocketSession session) throws SockJsProcessingException {
-
- if (session == null) {
- response.setStatusCode(HttpStatus.NOT_FOUND);
- }
- }
+ interface SessionCreatingTransportHandler extends TransportHandler, SockJsSessionFactory {
}
}