Add tests for the ordering of the default WebSocketUpgradeFilter.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2020-11-18 15:10:14 +11:00
parent f52e61156d
commit aba2c93eae
4 changed files with 247 additions and 7 deletions

View File

@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.tests;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.List; import java.util.List;
@ -30,7 +31,10 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
@ -38,15 +42,18 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.core.WebSocketConstants;
import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter; import org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -64,6 +71,16 @@ public class JettyWebSocketFilterTest
private WebSocketClient client; private WebSocketClient client;
private ServletContextHandler contextHandler; private ServletContextHandler contextHandler;
@BeforeEach
public void before()
{
server = new Server();
connector = new ServerConnector(server);
server.addConnector(connector);
client = new WebSocketClient();
}
public void start(JettyWebSocketServletContainerInitializer.Configurator configurator) throws Exception public void start(JettyWebSocketServletContainerInitializer.Configurator configurator) throws Exception
{ {
start(configurator, null); start(configurator, null);
@ -76,20 +93,14 @@ public class JettyWebSocketFilterTest
public void start(JettyWebSocketServletContainerInitializer.Configurator configurator, ServletHolder servletHolder) throws Exception public void start(JettyWebSocketServletContainerInitializer.Configurator configurator, ServletHolder servletHolder) throws Exception
{ {
server = new Server();
connector = new ServerConnector(server);
server.addConnector(connector);
contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/"); contextHandler.setContextPath("/");
if (servletHolder != null) if (servletHolder != null)
contextHandler.addServlet(servletHolder, "/"); contextHandler.addServlet(servletHolder, "/");
server.setHandler(contextHandler); server.setHandler(contextHandler);
JettyWebSocketServletContainerInitializer.configure(contextHandler, configurator); JettyWebSocketServletContainerInitializer.configure(contextHandler, configurator);
server.start();
client = new WebSocketClient(); server.start();
client.start(); client.start();
} }
@ -297,6 +308,98 @@ public class JettyWebSocketFilterTest
assertThat(socket.textMessages.poll(), is("hElLo wOrLd")); assertThat(socket.textMessages.poll(), is("hElLo wOrLd"));
} }
@Test
public void testDefaultWebSocketUpgradeFilterOrdering() throws Exception
{
String timeoutFromAltFilter = "5999";
JettyWebSocketWebApp webApp = new JettyWebSocketWebApp("wsuf-ordering1");
Path webXml = MavenTestingUtils.getTestResourcePath("wsuf-ordering1.xml");
webApp.copyWebXml(webXml);
webApp.copyClass(WebSocketEchoServletContextListener.class);
webApp.copyClass(WebSocketEchoServletContextListener.EchoSocket.class);
server.setHandler(webApp);
server.start();
client.start();
// We have both websocket upgrade filters installed.
FilterHolder[] filterHolders = webApp.getServletHandler().getFilters();
assertThat(filterHolders.length, is(2));
assertThat(filterHolders[0].getFilter(), instanceOf(WebSocketUpgradeFilter.class));
assertThat(filterHolders[1].getFilter(), instanceOf(WebSocketUpgradeFilter.class));
// The custom filter defined in web.xml should be first in line so it will do the upgrade.
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + webApp.getContextPath() + "/echo");
EventSocket socket = new EventSocket();
CompletableFuture<Session> connect = client.connect(socket, uri);
try (Session session = connect.get(5, TimeUnit.SECONDS))
{
session.getRemote().sendString("hello world");
session.getRemote().sendString("getIdleTimeout");
}
assertTrue(socket.closeLatch.await(5, TimeUnit.SECONDS));
assertThat(socket.textMessages.poll(), is("hello world"));
assertThat(socket.textMessages.poll(), is(timeoutFromAltFilter));
}
@Test
public void testWebSocketUpgradeFilterOrdering() throws Exception
{
String defaultIdleTimeout = Long.toString(WebSocketConstants.DEFAULT_IDLE_TIMEOUT.toMillis());
JettyWebSocketWebApp webApp = new JettyWebSocketWebApp("wsuf-ordering2");
Path webXml = MavenTestingUtils.getTestResourcePath("wsuf-ordering2.xml");
webApp.copyWebXml(webXml);
webApp.copyClass(WebSocketEchoServletContextListener.class);
webApp.copyClass(WebSocketEchoServletContextListener.EchoSocket.class);
server.setHandler(webApp);
server.start();
client.start();
// We have both websocket upgrade filters installed.
FilterHolder[] filterHolders = webApp.getServletHandler().getFilters();
assertThat(filterHolders.length, is(2));
assertThat(filterHolders[0].getFilter(), instanceOf(WebSocketUpgradeFilter.class));
assertThat(filterHolders[1].getFilter(), instanceOf(WebSocketUpgradeFilter.class));
// The custom filter defined in web.xml should be first in line so it will do the upgrade.
URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + webApp.getContextPath() + "/echo");
EventSocket socket = new EventSocket();
CompletableFuture<Session> connect = client.connect(socket, uri);
try (Session session = connect.get(5, TimeUnit.SECONDS))
{
session.getRemote().sendString("hello world");
session.getRemote().sendString("getIdleTimeout");
}
assertTrue(socket.closeLatch.await(5, TimeUnit.SECONDS));
assertThat(socket.textMessages.poll(), is("hello world"));
assertThat(socket.textMessages.poll(), is(defaultIdleTimeout));
}
@WebListener
public static class WebSocketEchoServletContextListener implements ServletContextListener
{
@Override
public void contextInitialized(ServletContextEvent sce)
{
JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(sce.getServletContext());
container.addMapping("/echo", EchoSocket.class);
}
@WebSocket
public static class EchoSocket
{
@OnWebSocketMessage
public void onMessage(Session session, String message) throws IOException
{
if ("getIdleTimeout".equals(message))
session.getRemote().sendString(Long.toString(session.getIdleTimeout().toMillis()));
else
session.getRemote().sendString(message);
}
}
}
public static class MyUpgradeFilter extends WebSocketUpgradeFilter public static class MyUpgradeFilter extends WebSocketUpgradeFilter
{ {
@Override @Override

View File

@ -0,0 +1,84 @@
package org.eclipse.jetty.websocket.tests;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Path;
import org.eclipse.jetty.toolchain.test.FS;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.resource.PathResource;
import org.eclipse.jetty.webapp.WebAppContext;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.notNullValue;
public class JettyWebSocketWebApp extends WebAppContext
{
private static final Logger LOG = LoggerFactory.getLogger(JettyWebSocketWebApp.class);
private final Path contextDir;
private final Path webInf;
private final Path classesDir;
public JettyWebSocketWebApp(String contextName)
{
// Ensure context directory.
Path testDir = MavenTestingUtils.getTargetTestingPath(JettyWebSocketWebApp.class.getName());
contextDir = testDir.resolve(contextName);
FS.ensureEmpty(contextDir);
// Ensure WEB-INF directories.
webInf = contextDir.resolve("WEB-INF");
FS.ensureDirExists(webInf);
classesDir = webInf.resolve("classes");
FS.ensureDirExists(classesDir);
// Configure the WebAppContext.
setContextPath("/" + contextName);
setBaseResource(new PathResource(contextDir));
addConfiguration(new JettyWebSocketConfiguration());
}
public Path getContextDir()
{
return contextDir;
}
public void createWebXml() throws IOException
{
String emptyWebXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
"<web-app xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://java.sun.com/xml/ns/javaee\" " +
"xsi:schemaLocation=\"http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd\" " +
"metadata-complete=\"false\" version=\"3.0\"></web-app>";
Path webXml = webInf.resolve("web.xml");
try (FileWriter writer = new FileWriter(webXml.toFile()))
{
writer.write(emptyWebXml);
}
}
public void copyWebXml(Path webXml) throws IOException
{
IO.copy(webXml.toFile(), webInf.resolve("web.xml").toFile());
}
public void copyClass(Class<?> clazz) throws Exception
{
ClassLoader cl = Thread.currentThread().getContextClassLoader();
String endpointPath = TypeUtil.toClassReference(clazz);
URL classUrl = cl.getResource(endpointPath);
assertThat("Class URL for: " + clazz, classUrl, notNullValue());
Path destFile = classesDir.resolve(endpointPath);
FS.ensureDirExists(destFile.getParent());
File srcFile = new File(classUrl.toURI());
IO.copy(srcFile, destFile.toFile());
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<filter>
<filter-name>wsuf-alt</filter-name>
<filter-class>org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter</filter-class>
<init-param>
<param-name>idleTimeout</param-name>
<param-value>5999</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wsuf-alt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<!-- Make sure the default filter is first. -->
<filter>
<filter-name>org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter</filter-name>
<filter-class>org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- The custom filter is then tried after. -->
<filter>
<filter-name>wsuf-alt</filter-name>
<filter-class>org.eclipse.jetty.websocket.util.server.WebSocketUpgradeFilter</filter-class>
<init-param>
<param-name>idleTimeout</param-name>
<param-value>5999</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wsuf-alt</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>