SockJs client: add an XhrTransport for Undertow

This change adds a new XhrTransport for the SockJs client
implementation. This transport is based on `UndertowClient`,
Undertow's HTTP client.

Note that this transport can be customized with an OptionMap that is
used by the Xnio worker backing the I/O communications (see
http://xnio.jboss.org).

Implementation tested with undertow 1.0.36, 1.1.0.RC3, 1.2.0.Beta1.

Issue: SPR-12008
This commit is contained in:
Brian Clozel
2014-09-19 18:55:59 +02:00
parent 2a39081819
commit 4cf19df462
4 changed files with 551 additions and 5 deletions

View File

@@ -16,6 +16,8 @@
package org.springframework.web.socket;
import java.io.IOException;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.Servlet;
@@ -34,6 +36,9 @@ import org.springframework.util.Assert;
import org.springframework.util.SocketUtils;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.xnio.ByteBufferSlicePool;
import org.xnio.OptionMap;
import org.xnio.Xnio;
import static io.undertow.servlet.Servlets.*;
@@ -65,15 +70,26 @@ public class UndertowTestServer implements WebSocketTestServer {
public void deployConfig(WebApplicationContext cxt, Filter... filters) {
Assert.state(this.port != -1, "setup() was never called");
DispatcherServletInstanceFactory servletFactory = new DispatcherServletInstanceFactory(cxt);
// manually building WebSocketDeploymentInfo in order to avoid class cast exceptions
// with tomcat's implementation when using undertow 1.1.0+
WebSocketDeploymentInfo info = new WebSocketDeploymentInfo();
try {
info.setWorker(Xnio.getInstance().createWorker(OptionMap.EMPTY));
info.setBuffers(new ByteBufferSlicePool(1024,1024));
}
catch (IOException ex) {
throw new IllegalStateException(ex);
}
DeploymentInfo servletBuilder = deployment()
.setClassLoader(UndertowTestServer.class.getClassLoader())
.setDeploymentName("undertow-websocket-test")
.setContextPath("/")
.addServlet(servlet("DispatcherServlet", DispatcherServlet.class, servletFactory).addMapping("/"))
.addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME, new WebSocketDeploymentInfo());
.addServlet(servlet("DispatcherServlet", DispatcherServlet.class, servletFactory).addMapping("/").setAsyncSupported(true))
.addServletContextAttribute(WebSocketDeploymentInfo.ATTRIBUTE_NAME, info);
for (final Filter filter : filters) {
String filterName = filter.getClass().getName();
servletBuilder.addFilter(new FilterInfo(filterName, filter.getClass(), new FilterInstanceFactory(filter)));
servletBuilder.addFilter(new FilterInfo(filterName, filter.getClass(), new FilterInstanceFactory(filter)).setAsyncSupported(true));
for (DispatcherType type : DispatcherType.values()) {
servletBuilder.addFilterUrlMapping(filterName, "/*", type);
}

View File

@@ -140,7 +140,6 @@ public abstract class AbstractSockJsIntegrationTests {
// Temporarily @Ignore failures caused by suspected Jetty bug
@Ignore
@Test
public void echoWebSocket() throws Exception {
testEcho(100, createWebSocketTransport());
@@ -158,7 +157,6 @@ public abstract class AbstractSockJsIntegrationTests {
testEcho(100, xhrTransport);
}
@Ignore
@Test
public void receiveOneMessageWebSocket() throws Exception {
testReceiveOneMessage(createWebSocketTransport());

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.socket.sockjs.client;
import java.io.IOException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.UndertowTestServer;
import org.springframework.web.socket.WebSocketTestServer;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy;
/**
* @author Brian Clozel
*/
public class UndertowSockJsIntegrationTests extends AbstractSockJsIntegrationTests {
@Override
protected Class<?> upgradeStrategyConfigClass() {
return UndertowTestConfig.class;
}
@Override
protected WebSocketTestServer createWebSocketTestServer() {
return new UndertowTestServer();
}
@Override
protected Transport createWebSocketTransport() {
return new WebSocketTransport(new StandardWebSocketClient());
}
@Override
protected AbstractXhrTransport createXhrTransport() {
try {
return new UndertowXhrTransport();
}
catch (IOException ex) {
throw new IllegalStateException("Could not create UndertowXhrTransport");
}
}
@Configuration
static class UndertowTestConfig {
@Bean
public RequestUpgradeStrategy upgradeStrategy() {
return new UndertowRequestUpgradeStrategy();
}
}
}