Polish async feature for ServerHttpRequest/Response

ServerHttpAsyncResponseControl wraps a ServetHttpRequest and -Response
pair and allows putting the processing of the request in async mode
so that the response remains open until explicitly closed, either from
the current or from another thread.

ServletServerHttpAsyncResponseControl provides a Serlvet-based
implementation.
This commit is contained in:
Rossen Stoyanchev
2013-08-02 15:12:20 -04:00
parent 0d5901ffb6
commit 9700f09fad
9 changed files with 228 additions and 212 deletions

View File

@@ -22,9 +22,9 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.web.HttpRequestHandler;
@@ -71,13 +71,13 @@ public class SockJsHttpRequestHandler implements HttpRequestHandler {
}
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
public void handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
ServerHttpRequest httpRequest = new AsyncServletServerHttpRequest(request, response);
ServerHttpResponse httpResponse = new ServletServerHttpResponse(response);
ServerHttpRequest serverRequest = new ServletServerHttpRequest(servletRequest);
ServerHttpResponse serverResponse = new ServletServerHttpResponse(servletResponse);
this.sockJsService.handleRequest(httpRequest, httpResponse, this.webSocketHandler);
this.sockJsService.handleRequest(serverRequest, serverResponse, this.webSocketHandler);
}
}

View File

@@ -20,15 +20,16 @@ import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.springframework.http.server.AsyncServerHttpRequest;
import org.springframework.http.server.ServerHttpAsyncResponseControl;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpAsyncRequestControl;
import org.springframework.util.Assert;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.SockJsProcessingException;
import org.springframework.web.socket.support.ExceptionWebSocketHandlerDecorator;
/**
@@ -43,10 +44,12 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
private final BlockingQueue<String> messageCache = new ArrayBlockingQueue<String>(100);
private AsyncServerHttpRequest asyncRequest;
private ServerHttpRequest request;
private ServerHttpResponse response;
private ServerHttpAsyncResponseControl asyncControl;
private String protocol;
@@ -113,8 +116,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
return;
}
this.asyncRequest.setTimeout(-1);
this.asyncRequest.startAsync();
this.asyncControl.start(-1);
scheduleHeartbeat();
tryFlushCache();
@@ -129,16 +131,16 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
Assert.notNull(request, "expected request");
Assert.notNull(response, "expected response");
Assert.notNull(frameFormat, "expected frameFormat");
Assert.isInstanceOf(AsyncServerHttpRequest.class, request, "Expected AsyncServerHttpRequest");
this.asyncRequest = (AsyncServerHttpRequest) request;
this.request = request;
this.response = response;
this.asyncControl = new ServletServerHttpAsyncRequestControl(this.request, this.response);
this.frameFormat = frameFormat;
}
@Override
public synchronized boolean isActive() {
return ((this.asyncRequest != null) && (!this.asyncRequest.isAsyncCompleted()));
return ((this.asyncControl != null) && (!this.asyncControl.isCompleted()));
}
protected BlockingQueue<String> getMessageCache() {
@@ -146,7 +148,7 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
}
protected ServerHttpRequest getRequest() {
return this.asyncRequest;
return this.request;
}
protected ServerHttpResponse getResponse() {
@@ -178,17 +180,18 @@ public abstract class AbstractHttpSockJsSession extends AbstractSockJsSession {
protected synchronized void resetRequest() {
updateLastActiveTime();
if (isActive() && this.asyncRequest.isAsyncStarted()) {
if (isActive() && this.asyncControl.hasStarted()) {
try {
logger.debug("Completing async request");
this.asyncRequest.completeAsync();
this.asyncControl.complete();
}
catch (Throwable ex) {
logger.error("Failed to complete async request: " + ex.getMessage());
}
}
this.asyncRequest = null;
this.request = null;
this.response = null;
this.asyncControl = null;
}
@Override

View File

@@ -17,14 +17,18 @@
package org.springframework.web.socket;
import org.junit.Before;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpAsyncResponseControl;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpAsyncRequestControl;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
/**
* Base class for tests using {@link ServerHttpRequest} and {@link ServerHttpResponse}.
*
* @author Rossen Stoyanchev
*/
public class AbstractHttpRequestTests {
@@ -37,6 +41,8 @@ public class AbstractHttpRequestTests {
protected MockHttpServletResponse servletResponse;
protected ServerHttpAsyncResponseControl asyncControl;
@Before
public void setUp() {
@@ -49,10 +55,15 @@ public class AbstractHttpRequestTests {
}
protected void resetRequestAndResponse() {
resetRequest();
resetResponse();
this.asyncControl = new ServletServerHttpAsyncRequestControl(this.request, this.response);
}
protected void resetRequest() {
this.servletRequest = new MockHttpServletRequest();
this.servletRequest.setAsyncSupported(true);
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse);
this.request = new ServletServerHttpRequest(this.servletRequest);
}
protected void resetResponse() {

View File

@@ -146,7 +146,7 @@ public class AbstractSockJsServiceTests extends AbstractHttpRequestTests {
String body = this.servletResponse.getContentAsString();
assertEquals("{\"entropy\"", body.substring(0, body.indexOf(':')));
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":true,\"websocket\":true}",
assertEquals(",\"origins\":[\"*:*\"],\"cookie_needed\":false,\"websocket\":true}",
body.substring(body.indexOf(',')));
this.service.setDummySessionCookieEnabled(false);

View File

@@ -20,9 +20,9 @@ import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.server.AsyncServletServerHttpRequest;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockHttpServletResponse;
@@ -31,7 +31,6 @@ import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.DefaultFrameFormat;
import org.springframework.web.socket.sockjs.support.frame.SockJsFrame.FrameFormat;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession;
import org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSessionTests.TestAbstractHttpSockJsSession;
import static org.junit.Assert.*;
@@ -67,7 +66,7 @@ public class AbstractHttpSockJsSessionTests extends BaseAbstractSockJsSessionTes
this.servletRequest = new MockHttpServletRequest();
this.servletRequest.setAsyncSupported(true);
this.request = new AsyncServletServerHttpRequest(this.servletRequest, this.servletResponse);
this.request = new ServletServerHttpRequest(this.servletRequest);
}
@Override