Merge branch 'jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
olivier lamy 2019-08-02 13:46:45 +10:00
commit 5be9f1a927
4 changed files with 345 additions and 11 deletions

View File

@ -457,7 +457,7 @@
</goals> </goals>
<configuration> <configuration>
<includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds> <includeGroupIds>org.eclipse.jetty,org.eclipse.jetty.websocket</includeGroupIds>
<excludeArtifactIds>infinispan-embedded,infinispan-remote</excludeArtifactIds> <excludeArtifactIds>infinispan-embedded,infinispan-remote,jetty-test-helper,alpn-api,javax.security.auth.message,javax.activation</excludeArtifactIds>
<classifier>config</classifier> <classifier>config</classifier>
<failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact> <failOnMissingClassifierArtifact>false</failOnMissingClassifierArtifact>
<excludes>META-INF/**</excludes> <excludes>META-INF/**</excludes>

View File

@ -25,6 +25,7 @@ import java.io.PrintWriter;
import java.net.URI; import java.net.URI;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel; import java.nio.channels.SeekableByteChannel;
import java.nio.file.FileStore;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
@ -58,6 +59,7 @@ import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.PathResource;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -86,6 +88,17 @@ public class HugeResourceTest
staticBase = MavenTestingUtils.getTargetTestingPath(HugeResourceTest.class.getSimpleName() + "-static-base"); staticBase = MavenTestingUtils.getTargetTestingPath(HugeResourceTest.class.getSimpleName() + "-static-base");
FS.ensureDirExists(staticBase); FS.ensureDirExists(staticBase);
FileStore baseFileStore = Files.getFileStore(staticBase);
// Calculation is (1GB + 4GB + 10GB) == 15GB
// once for static source files
// once again for multipart/form temp files
// for a total of (at least) 30GB needed.
Assumptions.assumeTrue(baseFileStore.getUnallocatedSpace() > 30 * GB,
String.format("FileStore %s of %s needs at least 30GB of free space for this test (only had %,.2fGB)",
baseFileStore, staticBase, (double)(baseFileStore.getUnallocatedSpace() / GB)));
makeStaticFile(staticBase.resolve("test-1g.dat"), 1 * GB); makeStaticFile(staticBase.resolve("test-1g.dat"), 1 * GB);
makeStaticFile(staticBase.resolve("test-4g.dat"), 4 * GB); makeStaticFile(staticBase.resolve("test-4g.dat"), 4 * GB);
makeStaticFile(staticBase.resolve("test-10g.dat"), 10 * GB); makeStaticFile(staticBase.resolve("test-10g.dat"), 10 * GB);
@ -109,10 +122,11 @@ public class HugeResourceTest
} }
@AfterAll @AfterAll
public static void cleanupStaticFiles() public static void cleanupTestFiles()
{ {
FS.ensureDeleted(staticBase); FS.ensureDeleted(staticBase);
FS.ensureDeleted(outputDir); FS.ensureDeleted(outputDir);
FS.ensureDeleted(multipartTempDir);
} }
private static void makeStaticFile(Path staticFile, long size) throws IOException private static void makeStaticFile(Path staticFile, long size) throws IOException
@ -211,19 +225,18 @@ public class HugeResourceTest
Response response = responseListener.get(5, TimeUnit.SECONDS); Response response = responseListener.get(5, TimeUnit.SECONDS);
assertThat("HTTP Response Code", response.getStatus(), is(200)); assertThat("HTTP Response Code", response.getStatus(), is(200));
dumpResponse(response); // dumpResponse(response);
String contentLength = response.getHeaders().get(HttpHeader.CONTENT_LENGTH); String contentLength = response.getHeaders().get(HttpHeader.CONTENT_LENGTH);
long contentLengthLong = Long.parseLong(contentLength); long contentLengthLong = Long.parseLong(contentLength);
assertThat("Http Response Header: \"Content-Length: " + contentLength + "\"", contentLengthLong, is(expectedSize)); assertThat("Http Response Header: \"Content-Length: " + contentLength + "\"", contentLengthLong, is(expectedSize));
Path outputFile = outputDir.resolve(filename); try (ByteCountingOutputStream out = new ByteCountingOutputStream();
try (OutputStream out = Files.newOutputStream(outputFile);
InputStream in = responseListener.getInputStream()) InputStream in = responseListener.getInputStream())
{ {
IO.copy(in, out); IO.copy(in, out);
assertThat("Downloaded Files Size: " + filename, out.getCount(), is(expectedSize));
} }
assertThat("Downloaded Files Size: " + filename, Files.size(outputFile), is(expectedSize));
} }
@ParameterizedTest @ParameterizedTest
@ -237,7 +250,7 @@ public class HugeResourceTest
Request request = client.newRequest(destUri).method(HttpMethod.POST).content(pathContentProvider); Request request = client.newRequest(destUri).method(HttpMethod.POST).content(pathContentProvider);
ContentResponse response = request.send(); ContentResponse response = request.send();
assertThat("HTTP Response Code", response.getStatus(), is(200)); assertThat("HTTP Response Code", response.getStatus(), is(200));
dumpResponse(response); // dumpResponse(response);
String responseBody = response.getContentAsString(); String responseBody = response.getContentAsString();
assertThat("Response", responseBody, containsString("bytes-received=" + expectedSize)); assertThat("Response", responseBody, containsString("bytes-received=" + expectedSize));
@ -256,7 +269,7 @@ public class HugeResourceTest
Request request = client.newRequest(destUri).method(HttpMethod.POST).content(multipart); Request request = client.newRequest(destUri).method(HttpMethod.POST).content(multipart);
ContentResponse response = request.send(); ContentResponse response = request.send();
assertThat("HTTP Response Code", response.getStatus(), is(200)); assertThat("HTTP Response Code", response.getStatus(), is(200));
dumpResponse(response); // dumpResponse(response);
String responseBody = response.getContentAsString(); String responseBody = response.getContentAsString();
String expectedResponse = String.format("part[%s].size=%d", name, expectedSize); String expectedResponse = String.format("part[%s].size=%d", name, expectedSize);

View File

@ -0,0 +1,316 @@
//
// ========================================================================
// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.websocket.tests.server;
import java.net.URI;
import java.nio.ByteBuffer;
import java.time.Duration;
import java.time.temporal.TemporalAmount;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.websocket.api.RemoteEndpoint;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketPartialListener;
import org.eclipse.jetty.websocket.api.util.WSURI;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.util.TextUtil;
import org.eclipse.jetty.websocket.server.internal.JettyServerFrameHandlerFactory;
import org.eclipse.jetty.websocket.servlet.FrameHandlerFactory;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
public class PartialListenerTest
{
private Server server;
private PartialCreator partialCreator;
private WebSocketClient client;
@BeforeEach
public void startServer() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
ServletHolder closeEndpoint = new ServletHolder(new WebSocketServlet()
{
@Override
public void configure(WebSocketServletFactory factory)
{
factory.setIdleTimeout( Duration.ofMillis( 2 ));
partialCreator = new PartialCreator();
factory.setCreator(partialCreator);
}
protected FrameHandlerFactory getFactory()
{
JettyServerFrameHandlerFactory
frameHandlerFactory = JettyServerFrameHandlerFactory.getFactory( getServletContext());
if (frameHandlerFactory == null)
throw new IllegalStateException("JettyServerFrameHandlerFactory not found");
return frameHandlerFactory;
}
});
context.addServlet(closeEndpoint, "/ws");
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
server.start();
}
@AfterEach
public void stopServer() throws Exception
{
server.stop();
}
@BeforeEach
public void startClient() throws Exception
{
client = new WebSocketClient();
client.start();
}
@AfterEach
public void stopClient() throws Exception
{
client.stop();
}
private void close(Session session)
{
if (session != null)
{
session.close();
}
}
@Test
public void testPartialText() throws Exception
{
ClientUpgradeRequest request = new ClientUpgradeRequest();
CloseTrackingEndpoint clientEndpoint = new CloseTrackingEndpoint();
URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/ws"));
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try (StacklessLogging ignore = new StacklessLogging(WebSocketSession.class))
{
session = futSession.get(5, SECONDS);
RemoteEndpoint clientRemote = session.getRemote();
clientRemote.sendPartialString("hello", false);
clientRemote.sendPartialString(" ", false);
clientRemote.sendPartialString("world", true);
PartialEndpoint serverEndpoint = partialCreator.partialEndpoint;
String event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=hello, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload= , fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=world, fin=true]"));
}
finally
{
close(session);
}
}
@Test
public void testPartialBinary() throws Exception
{
ClientUpgradeRequest request = new ClientUpgradeRequest();
CloseTrackingEndpoint clientEndpoint = new CloseTrackingEndpoint();
URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/ws"));
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try (StacklessLogging ignore = new StacklessLogging(WebSocketSession.class))
{
session = futSession.get(5, SECONDS);
RemoteEndpoint clientRemote = session.getRemote();
clientRemote.sendPartialBytes(BufferUtil.toBuffer("hello"), false);
clientRemote.sendPartialBytes(BufferUtil.toBuffer(" "), false);
clientRemote.sendPartialBytes(BufferUtil.toBuffer("world"), true);
PartialEndpoint serverEndpoint = partialCreator.partialEndpoint;
String event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<<hello>>>, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<< >>>, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<<world>>>, fin=true]"));
}
finally
{
close(session);
}
}
/**
* Test to ensure that the internal state tracking the partial messages is reset after each complete message.
*/
@Test
public void testPartial_TextBinaryText() throws Exception
{
ClientUpgradeRequest request = new ClientUpgradeRequest();
CloseTrackingEndpoint clientEndpoint = new CloseTrackingEndpoint();
URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/ws"));
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try (StacklessLogging ignore = new StacklessLogging(WebSocketSession.class))
{
session = futSession.get(5, SECONDS);
RemoteEndpoint clientRemote = session.getRemote();
clientRemote.sendPartialString("hello", false);
clientRemote.sendPartialString(" ", false);
clientRemote.sendPartialString("world", true);
clientRemote.sendPartialBytes(BufferUtil.toBuffer("greetings"), false);
clientRemote.sendPartialBytes(BufferUtil.toBuffer(" "), false);
clientRemote.sendPartialBytes(BufferUtil.toBuffer("mars"), true);
clientRemote.sendPartialString("salutations", false);
clientRemote.sendPartialString(" ", false);
clientRemote.sendPartialString("phobos", true);
PartialEndpoint serverEndpoint = partialCreator.partialEndpoint;
String event;
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=hello, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload= , fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=world, fin=true]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<<greetings>>>, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<< >>>, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("BINARY[payload=<<<mars>>>, fin=true]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=salutations, fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload= , fin=false]"));
event = serverEndpoint.partialEvents.poll(5, SECONDS);
assertThat("Event", event, is("TEXT[payload=phobos, fin=true]"));
}
finally
{
close(session);
}
}
public static class PartialCreator implements WebSocketCreator
{
public PartialEndpoint partialEndpoint;
@Override
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
{
partialEndpoint = new PartialEndpoint();
return partialEndpoint;
}
}
public static class PartialEndpoint implements WebSocketPartialListener
{
public Session session;
public CountDownLatch closeLatch = new CountDownLatch(1);
public LinkedBlockingQueue<String> partialEvents = new LinkedBlockingQueue<>();
@Override
public void onWebSocketClose(int statusCode, String reason)
{
closeLatch.countDown();
}
@Override
public void onWebSocketConnect(Session session)
{
this.session = session;
}
@Override
public void onWebSocketError(Throwable cause)
{
cause.printStackTrace(System.err);
}
@Override
public void onWebSocketPartialBinary(ByteBuffer payload, boolean fin)
{
// our testcases always send bytes limited in the US-ASCII range.
partialEvents.offer(String.format("BINARY[payload=<<<%s>>>, fin=%b]", BufferUtil.toUTF8String(payload), fin));
}
@Override
public void onWebSocketPartialText(String payload, boolean fin)
{
partialEvents.offer(String.format("TEXT[payload=%s, fin=%b]", TextUtil.maxStringLength(30, payload), fin));
}
}
}

11
pom.xml
View File

@ -58,12 +58,14 @@
<!-- testing --> <!-- testing -->
<it.debug>false</it.debug> <it.debug>false</it.debug>
<jetty.test.version>5.3</jetty.test.version> <jetty.test.version>5.3</jetty.test.version>
<!-- springboot is only used for jetty-maven-plugin it test <!-- springboot is only used for jetty-maven-plugin it test
otherwise depending on Spring Boot might be chicken and egg issue :) --> otherwise depending on Spring Boot might be chicken and egg issue :) -->
<springboot.version>2.1.1.RELEASE</springboot.version> <springboot.version>2.1.1.RELEASE</springboot.version>
<annotation-api.version>1.3.4</annotation-api.version> <annotation-api.version>1.3.4</annotation-api.version>
<jackson-databind.version>2.9.7</jackson-databind.version> <jackson-databind.version>2.9.7</jackson-databind.version>
<localRepoPath>${settings.localRepository}</localRepoPath> <localRepoPath>${project.build.directory}/local-repo</localRepoPath>
<settingsPath>src/it/settings.xml</settingsPath>
</properties> </properties>
@ -502,7 +504,7 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId> <artifactId>maven-invoker-plugin</artifactId>
<version>3.2.1-SNAPSHOT</version> <version>3.2.1-20190725.112516-11</version>
<configuration> <configuration>
<writeJunitReport>true</writeJunitReport> <writeJunitReport>true</writeJunitReport>
<junitPackageName>org.eclipse.jetty.maven.its</junitPackageName> <junitPackageName>org.eclipse.jetty.maven.its</junitPackageName>
@ -515,7 +517,7 @@
<timeoutInSeconds>300</timeoutInSeconds> <timeoutInSeconds>300</timeoutInSeconds>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo> <cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<localRepositoryPath>${localRepoPath}</localRepositoryPath> <localRepositoryPath>${localRepoPath}</localRepositoryPath>
<!--settingsFile>src/it/settings.xml</settingsFile--> <settingsFile>${settingsPath}</settingsFile>
<skipInvocation>${skipTests}</skipInvocation> <skipInvocation>${skipTests}</skipInvocation>
<pomIncludes> <pomIncludes>
<pomInclude>*/pom.xml</pomInclude> <pomInclude>*/pom.xml</pomInclude>
@ -1276,6 +1278,9 @@
</profile> </profile>
<profile> <profile>
<id>ci</id> <id>ci</id>
<properties>
<settingsPath>${env.GLOBAL_MVN_SETTINGS}</settingsPath>
</properties>
<modules> <modules>
<module>aggregates/jetty-all</module> <module>aggregates/jetty-all</module>
</modules> </modules>