diff --git a/VERSION.txt b/VERSION.txt index 405b0ed2783..372baea126a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -9,7 +9,7 @@ jetty-10.0.6 - 29 June 2021 + 6410 Ensure Jetty IO uses SocketAddress instead of InetSocketAddress + 6418 Bad and/or missing Require-Capability for osgi.serviceloader + 6425 Update to asm 9.1 - + 6447 Deprecate support for UTF16 encoding in URIs + + 6447 Deprecate support for UTF16 encoding in URIs (Resolves CVE-2021-34429) + 6451 Request#getServletPath() returns null for ROOT mapping + 6464 Wrong files/lib definitions in certain *-capture.mod files? + 6473 Improve alias checking in PathResource diff --git a/demos/embedded/src/main/java/org/eclipse/jetty/demos/OneWebAppWithJsp.java b/demos/embedded/src/main/java/org/eclipse/jetty/demos/OneWebAppWithJsp.java index f0379081223..bb948ff1502 100644 --- a/demos/embedded/src/main/java/org/eclipse/jetty/demos/OneWebAppWithJsp.java +++ b/demos/embedded/src/main/java/org/eclipse/jetty/demos/OneWebAppWithJsp.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.demos; import java.io.FileNotFoundException; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Path; import org.eclipse.jetty.annotations.AnnotationConfiguration; diff --git a/demos/embedded/src/test/java/org/eclipse/jetty/demos/LikeJettyXmlTest.java b/demos/embedded/src/test/java/org/eclipse/jetty/demos/LikeJettyXmlTest.java index ddba3f74e23..dda8acd21cb 100644 --- a/demos/embedded/src/test/java/org/eclipse/jetty/demos/LikeJettyXmlTest.java +++ b/demos/embedded/src/test/java/org/eclipse/jetty/demos/LikeJettyXmlTest.java @@ -28,7 +28,6 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class LikeJettyXmlTest extends AbstractEmbeddedTest { diff --git a/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppTest.java b/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppTest.java index 17f26d599e0..7e25f7b1ea8 100644 --- a/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppTest.java +++ b/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppTest.java @@ -28,7 +28,6 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class OneWebAppTest extends AbstractEmbeddedTest { diff --git a/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppWithJspTest.java b/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppWithJspTest.java index cb7d21e8589..e61db09919e 100644 --- a/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppWithJspTest.java +++ b/demos/embedded/src/test/java/org/eclipse/jetty/demos/OneWebAppWithJspTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class OneWebAppWithJspTest extends AbstractEmbeddedTest { diff --git a/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithAnnotationsTest.java b/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithAnnotationsTest.java index 3a2ce059d20..beff08049d2 100644 --- a/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithAnnotationsTest.java +++ b/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithAnnotationsTest.java @@ -27,7 +27,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ServerWithAnnotationsTest extends AbstractEmbeddedTest { diff --git a/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithJNDITest.java b/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithJNDITest.java index 50a55ac0cfc..61dc1a76fd4 100644 --- a/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithJNDITest.java +++ b/demos/embedded/src/test/java/org/eclipse/jetty/demos/ServerWithJNDITest.java @@ -28,7 +28,6 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; -import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ServerWithJNDITest extends AbstractEmbeddedTest { diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java index 8f398a8be25..266baf8b566 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationConfiguration.java @@ -18,7 +18,6 @@ import java.net.MalformedURLException; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java index becf8c70b79..22419e35db6 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationConfiguration.java @@ -16,18 +16,10 @@ package org.eclipse.jetty.annotations; import java.io.File; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.Set; import javax.servlet.ServletContainerInitializer; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import org.eclipse.jetty.annotations.AnnotationConfiguration.ClassInheritanceMap; -import org.eclipse.jetty.annotations.AnnotationConfiguration.DiscoveredServletContainerInitializerHolder; -import org.eclipse.jetty.server.Server; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.JAR; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index 541b49abaa0..2a33f714883 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -46,7 +46,11 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.LongConsumer; +import javax.servlet.AsyncContext; +import javax.servlet.DispatcherType; +import javax.servlet.ReadListener; import javax.servlet.ServletException; +import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -1874,6 +1878,82 @@ public class HttpClientTest extends AbstractHttpClientServerTest } } + @ParameterizedTest + @ArgumentsSource(ScenarioProvider.class) + public void testHttpParserCloseWithAsyncReads(Scenario scenario) throws Exception + { + CountDownLatch serverOnErrorLatch = new CountDownLatch(1); + + start(scenario, new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request jettyRequest, HttpServletRequest request, HttpServletResponse response) throws IOException + { + jettyRequest.setHandled(true); + if (request.getDispatcherType() != DispatcherType.REQUEST) + return; + + AsyncContext asyncContext = request.startAsync(); + asyncContext.setTimeout(2000); // allow async to timeout + ServletInputStream input = request.getInputStream(); + input.setReadListener(new ReadListener() + { + @Override + public void onDataAvailable() throws IOException + { + while (input.isReady()) + { + int read = input.read(); + if (read < 0) + break; + } + } + + @Override + public void onAllDataRead() throws IOException + { + } + + @Override + public void onError(Throwable t) + { + asyncContext.complete(); + serverOnErrorLatch.countDown(); + } + }); + // Close the parser to cause the issue. + org.eclipse.jetty.server.HttpConnection.getCurrentConnection().getParser().close(); + } + }); + server.start(); + + int length = 16; + ByteBuffer chunk1 = ByteBuffer.allocate(length / 2); + AsyncRequestContent content = new AsyncRequestContent(chunk1) + { + @Override + public long getLength() + { + return length; + } + }; + CountDownLatch clientResultLatch = new CountDownLatch(1); + client.newRequest("localhost", connector.getLocalPort()) + .scheme(scenario.getScheme()) + .method(HttpMethod.POST) + .body(content) + .send(result -> clientResultLatch.countDown()); + + Thread.sleep(1000); + + ByteBuffer chunk2 = ByteBuffer.allocate(length / 2); + content.offer(chunk2); + content.close(); + + assertTrue(clientResultLatch.await(5, TimeUnit.SECONDS), "clientResultLatch didn't finish"); + assertTrue(serverOnErrorLatch.await(5, TimeUnit.SECONDS), "serverOnErrorLatch didn't finish"); + } + private void assertCopyRequest(Request original) { Request copy = client.copyRequest((HttpRequest)original, original.getURI()); diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java index 618bc1b20b9..56dc8f7a338 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/util/SPNEGOAuthenticationTest.java @@ -49,7 +49,6 @@ import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.security.Constraint; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.condition.JRE; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ArgumentsSource; import org.slf4j.Logger; diff --git a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java index 9d2cef3dae2..a7f1e5d03ec 100644 --- a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java +++ b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStore.java @@ -24,7 +24,6 @@ import com.hazelcast.config.IndexConfig; import com.hazelcast.config.IndexType; import com.hazelcast.map.IMap; import com.hazelcast.query.Predicate; -import com.hazelcast.query.PredicateBuilder; import com.hazelcast.query.PredicateBuilder.EntryObject; import com.hazelcast.query.Predicates; import org.eclipse.jetty.server.session.AbstractSessionDataStore; diff --git a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java index db3eb238599..fb353e2319f 100644 --- a/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java +++ b/jetty-hazelcast/src/main/java/org/eclipse/jetty/hazelcast/session/HazelcastSessionDataStoreFactory.java @@ -21,7 +21,6 @@ import com.hazelcast.client.config.ClientConfig; import com.hazelcast.client.config.XmlClientConfigBuilder; import com.hazelcast.config.Config; import com.hazelcast.config.MapConfig; -import com.hazelcast.config.SerializationConfig; import com.hazelcast.config.SerializerConfig; import com.hazelcast.config.XmlConfigBuilder; import com.hazelcast.core.Hazelcast; diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index dad562a72e4..07ae88d33d8 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -209,6 +209,7 @@ public class HttpParser private static final EnumSet __idleStates = EnumSet.of(State.START, State.END, State.CLOSE, State.CLOSED); private static final EnumSet __completeStates = EnumSet.of(State.END, State.CLOSE, State.CLOSED); + private static final EnumSet __terminatedStates = EnumSet.of(State.CLOSE, State.CLOSED); private final boolean debugEnabled = LOG.isDebugEnabled(); // Cache debug to help branch prediction private final HttpHandler _handler; @@ -424,6 +425,11 @@ public class HttpParser return __completeStates.contains(_state); } + public boolean isTerminated() + { + return __terminatedStates.contains(_state); + } + public boolean isState(State state) { return _state == state; @@ -1555,7 +1561,7 @@ public class HttpParser if (debugEnabled && whiteSpace > 0) LOG.debug("Discarded {} CR or LF characters", whiteSpace); } - else if (isClose() || isClosed()) + else if (isTerminated()) { BufferUtil.clear(buffer); } diff --git a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties index fe10cfdba3d..fe22dea39f1 100644 --- a/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties +++ b/jetty-http/src/main/resources/org/eclipse/jetty/http/mime.properties @@ -3,11 +3,13 @@ aif=audio/x-aiff aifc=audio/x-aiff aiff=audio/x-aiff apk=application/vnd.android.package-archive +apng=image/apng asc=text/plain asf=video/x.ms.asf asx=video/x.ms.asx au=audio/basic avi=video/x-msvideo +avif=image/avif bcpio=application/x-bcpio bin=application/octet-stream bmp=image/bmp @@ -170,6 +172,7 @@ vxml=application/voicexml+xml wasm=application/wasm wav=audio/x-wav wbmp=image/vnd.wap.wbmp +webp=image/webp wml=text/vnd.wap.wml wmlc=application/vnd.wap.wmlc wmls=text/vnd.wap.wmlscript diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java index b21d3c39da2..0dbff4e0cdd 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/MimeTypesTest.java @@ -13,47 +13,48 @@ package org.eclipse.jetty.http; +import java.util.stream.Stream; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; public class MimeTypesTest { - @Test - public void testGetMimeByExtensionGzip() + public static Stream mimeTypesByExtensionCases() { - assertMimeTypeByExtension("application/gzip", "test.gz"); + return Stream.of( + Arguments.of("test.gz", "application/gzip"), + Arguments.of("foo.webp", "image/webp"), + Arguments.of("zed.avif", "image/avif"), + // make sure that filename case isn't an issue + Arguments.of("test.png", "image/png"), + Arguments.of("TEST.PNG", "image/png"), + Arguments.of("Test.Png", "image/png"), + Arguments.of("test.txt", "text/plain"), + Arguments.of("TEST.TXT", "text/plain"), + // Make sure that multiple dots don't interfere + Arguments.of("org.eclipse.jetty.Logo.png", "image/png"), + // Make sure that a deep path doesn't interfere + Arguments.of("org/eclipse/jetty/Logo.png", "image/png"), + // Make sure that path that looks like a filename doesn't interfere + Arguments.of("org/eclipse.jpg/jetty/Logo.png", "image/png") + ); } - @Test - public void testGetMimeByExtensionPng() + @ParameterizedTest + @MethodSource("mimeTypesByExtensionCases") + public void testMimeTypesByExtension(String filename, String expectedMimeType) { - assertMimeTypeByExtension("image/png", "test.png"); - assertMimeTypeByExtension("image/png", "TEST.PNG"); - assertMimeTypeByExtension("image/png", "Test.Png"); - } - - @Test - public void testGetMimeByExtensionPngMultiDot() - { - assertMimeTypeByExtension("image/png", "org.eclipse.jetty.Logo.png"); - } - - @Test - public void testGetMimeByExtensionPngDeepPath() - { - assertMimeTypeByExtension("image/png", "/org/eclipse/jetty/Logo.png"); - } - - @Test - public void testGetMimeByExtensionText() - { - assertMimeTypeByExtension("text/plain", "test.txt"); - assertMimeTypeByExtension("text/plain", "TEST.TXT"); + MimeTypes mimetypes = new MimeTypes(); + String contentType = mimetypes.getMimeByExtension(filename); + assertThat("MimeTypes.getMimeByExtension(\"" + filename + "\")", + contentType, is(expectedMimeType)); } @Test @@ -64,60 +65,63 @@ public class MimeTypesTest assertNull(contentType); } - private void assertMimeTypeByExtension(String expectedMimeType, String filename) + public static Stream charsetFromContentTypeCases() { - MimeTypes mimetypes = new MimeTypes(); - String contentType = mimetypes.getMimeByExtension(filename); - String prefix = "MimeTypes.getMimeByExtension(" + filename + ")"; - assertNotNull(contentType, prefix); - assertEquals(expectedMimeType, contentType, prefix); + return Stream.of( + Arguments.of("foo/bar;charset=abc;some=else", "abc"), + Arguments.of("foo/bar;charset=abc", "abc"), + Arguments.of("foo/bar ; charset = abc", "abc"), + Arguments.of("foo/bar ; charset = abc ; some=else", "abc"), + Arguments.of("foo/bar;other=param;charset=abc;some=else", "abc"), + Arguments.of("foo/bar;other=param;charset=abc", "abc"), + Arguments.of("foo/bar other = param ; charset = abc", "abc"), + Arguments.of("foo/bar other = param ; charset = abc ; some=else", "abc"), + Arguments.of("foo/bar other = param ; charset = abc", "abc"), + Arguments.of("foo/bar other = param ; charset = \"abc\" ; some=else", "abc"), + Arguments.of("foo/bar", null), + Arguments.of("foo/bar;charset=uTf8", "utf-8"), + Arguments.of("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8"), + Arguments.of("application/pdf; charset=UTF-8", "utf-8"), + Arguments.of("application/pdf;; charset=UTF-8", "utf-8"), + Arguments.of("application/pdf;;; charset=UTF-8", "utf-8"), + Arguments.of("application/pdf;;;; charset=UTF-8", "utf-8"), + Arguments.of("text/html;charset=utf-8", "utf-8") + ); } - private void assertCharsetFromContentType(String contentType, String expectedCharset) + @ParameterizedTest + @MethodSource("charsetFromContentTypeCases") + public void testCharsetFromContentType(String contentType, String expectedCharset) { assertThat("getCharsetFromContentType(\"" + contentType + "\")", MimeTypes.getCharsetFromContentType(contentType), is(expectedCharset)); } - @Test - public void testCharsetFromContentType() + public static Stream contentTypeWithoutCharsetCases() { - assertCharsetFromContentType("foo/bar;charset=abc;some=else", "abc"); - assertCharsetFromContentType("foo/bar;charset=abc", "abc"); - assertCharsetFromContentType("foo/bar ; charset = abc", "abc"); - assertCharsetFromContentType("foo/bar ; charset = abc ; some=else", "abc"); - assertCharsetFromContentType("foo/bar;other=param;charset=abc;some=else", "abc"); - assertCharsetFromContentType("foo/bar;other=param;charset=abc", "abc"); - assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc"); - assertCharsetFromContentType("foo/bar other = param ; charset = abc ; some=else", "abc"); - assertCharsetFromContentType("foo/bar other = param ; charset = abc", "abc"); - assertCharsetFromContentType("foo/bar other = param ; charset = \"abc\" ; some=else", "abc"); - assertCharsetFromContentType("foo/bar", null); - assertCharsetFromContentType("foo/bar;charset=uTf8", "utf-8"); - assertCharsetFromContentType("foo/bar;other=\"charset=abc\";charset=uTf8", "utf-8"); - assertCharsetFromContentType("application/pdf; charset=UTF-8", "utf-8"); - assertCharsetFromContentType("application/pdf;; charset=UTF-8", "utf-8"); - assertCharsetFromContentType("application/pdf;;; charset=UTF-8", "utf-8"); - assertCharsetFromContentType("application/pdf;;;; charset=UTF-8", "utf-8"); - assertCharsetFromContentType("text/html;charset=utf-8", "utf-8"); + return Stream.of( + Arguments.of("foo/bar;charset=abc;some=else", "foo/bar;some=else"), + Arguments.of("foo/bar;charset=abc", "foo/bar"), + Arguments.of("foo/bar ; charset = abc", "foo/bar"), + Arguments.of("foo/bar ; charset = abc ; some=else", "foo/bar;some=else"), + Arguments.of("foo/bar;other=param;charset=abc;some=else", "foo/bar;other=param;some=else"), + Arguments.of("foo/bar;other=param;charset=abc", "foo/bar;other=param"), + Arguments.of("foo/bar ; other = param ; charset = abc", "foo/bar ; other = param"), + Arguments.of("foo/bar ; other = param ; charset = abc ; some=else", "foo/bar ; other = param;some=else"), + Arguments.of("foo/bar ; other = param ; charset = abc", "foo/bar ; other = param"), + Arguments.of("foo/bar ; other = param ; charset = \"abc\" ; some=else", "foo/bar ; other = param;some=else"), + Arguments.of("foo/bar", "foo/bar"), + Arguments.of("foo/bar;charset=uTf8", "foo/bar"), + Arguments.of("foo/bar;other=\"charset=abc\";charset=uTf8", "foo/bar;other=\"charset=abc\""), + Arguments.of("text/html;charset=utf-8", "text/html") + ); } - @Test - public void testContentTypeWithoutCharset() + @ParameterizedTest + @MethodSource("contentTypeWithoutCharsetCases") + public void testContentTypeWithoutCharset(String contentTypeWithCharset, String expectedContentType) { - assertEquals("foo/bar;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=abc;some=else")); - assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=abc")); - assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar ; charset = abc")); - assertEquals("foo/bar;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; charset = abc ; some=else")); - assertEquals("foo/bar;other=param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=param;charset=abc;some=else")); - assertEquals("foo/bar;other=param", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=param;charset=abc")); - assertEquals("foo/bar ; other = param", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc")); - assertEquals("foo/bar ; other = param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc ; some=else")); - assertEquals("foo/bar ; other = param", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = abc")); - assertEquals("foo/bar ; other = param;some=else", MimeTypes.getContentTypeWithoutCharset("foo/bar ; other = param ; charset = \"abc\" ; some=else")); - assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar")); - assertEquals("foo/bar", MimeTypes.getContentTypeWithoutCharset("foo/bar;charset=uTf8")); - assertEquals("foo/bar;other=\"charset=abc\"", MimeTypes.getContentTypeWithoutCharset("foo/bar;other=\"charset=abc\";charset=uTf8")); - assertEquals("text/html", MimeTypes.getContentTypeWithoutCharset("text/html;charset=utf-8")); + assertThat("MimeTypes.getContentTypeWithoutCharset(\"" + contentTypeWithCharset + "\")", + MimeTypes.getContentTypeWithoutCharset(contentTypeWithCharset), is(expectedContentType)); } } diff --git a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java index 30688868355..fa4ddeeed80 100644 --- a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStore.java @@ -17,8 +17,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.server.session.AbstractSessionDataStore; import org.eclipse.jetty.server.session.SessionData; diff --git a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java index c0c2bcf646a..f16c257d10e 100644 --- a/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java +++ b/jetty-infinispan/infinispan-common/src/main/java/org/eclipse/jetty/session/infinispan/InfinispanSessionDataStoreFactory.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.session.infinispan; import org.eclipse.jetty.server.session.AbstractSessionDataStoreFactory; -import org.eclipse.jetty.server.session.SessionData; import org.eclipse.jetty.server.session.SessionDataStore; import org.eclipse.jetty.server.session.SessionHandler; import org.infinispan.commons.api.BasicCache; diff --git a/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java index 51d7f6b0b29..3bf702aebeb 100644 --- a/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java +++ b/jetty-infinispan/infinispan-embedded-query/src/main/java/org/eclipse/jetty/session/infinispan/EmbeddedQueryManagerFactory.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.session.infinispan; -import org.eclipse.jetty.server.session.SessionData; import org.infinispan.Cache; import org.infinispan.commons.api.BasicCache; diff --git a/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java index 96de60771ae..f32292aca91 100644 --- a/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java +++ b/jetty-infinispan/infinispan-remote-query/src/main/java/org/eclipse/jetty/session/infinispan/RemoteQueryManagerFactory.java @@ -13,7 +13,6 @@ package org.eclipse.jetty.session.infinispan; -import org.eclipse.jetty.server.session.SessionData; import org.infinispan.client.hotrod.RemoteCache; import org.infinispan.commons.api.BasicCache; diff --git a/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java b/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java index d92aa2a7e11..bb8a3127cad 100644 --- a/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java +++ b/jetty-infinispan/infinispan-remote-query/src/test/java/org/eclipse/jetty/server/session/infinispan/RemoteQueryManagerTest.java @@ -48,8 +48,6 @@ import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy; import org.testcontainers.junit.jupiter.Testcontainers; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @Testcontainers(disabledWithoutDocker = true) diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java index 3c64b7de5c3..d573e1600da 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/AbstractLoginModule.java @@ -14,8 +14,6 @@ package org.eclipse.jetty.jaas.spi; import java.io.IOException; -import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -32,7 +30,6 @@ import javax.security.auth.spi.LoginModule; import org.eclipse.jetty.jaas.JAASRole; import org.eclipse.jetty.jaas.callback.ObjectCallback; import org.eclipse.jetty.security.UserPrincipal; -import org.eclipse.jetty.util.thread.AutoLock; /** * AbstractLoginModule diff --git a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java index bc27ca7abd0..4881449eaf0 100644 --- a/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java +++ b/jetty-jaas/src/main/java/org/eclipse/jetty/jaas/spi/PropertyFileLoginModule.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.jaas.spi; import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.stream.Collectors; import javax.security.auth.Subject; import javax.security.auth.callback.CallbackHandler; @@ -26,8 +25,6 @@ import org.eclipse.jetty.jaas.PropertyUserStoreManager; import org.eclipse.jetty.security.PropertyUserStore; import org.eclipse.jetty.security.RolePrincipal; import org.eclipse.jetty.security.UserPrincipal; -import org.eclipse.jetty.server.UserIdentity; -import org.eclipse.jetty.util.security.Credential; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractUnassembledWebAppMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractUnassembledWebAppMojo.java index d86e2174013..1d674405a75 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractUnassembledWebAppMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/AbstractUnassembledWebAppMojo.java @@ -17,7 +17,6 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; diff --git a/jetty-osgi/test-jetty-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java b/jetty-osgi/test-jetty-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java index dc2200efa0d..c371e3d2841 100644 --- a/jetty-osgi/test-jetty-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java +++ b/jetty-osgi/test-jetty-osgi-webapp-resources/src/main/java/com/acme/HelloWorld.java @@ -16,7 +16,6 @@ package com.acme; import java.io.IOException; import java.net.URL; import java.util.Enumeration; -import java.util.Set; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiAnnotationParser.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiAnnotationParser.java index 91b8e6a536f..93e11c9aac6 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiAnnotationParser.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiAnnotationParser.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.osgi.test; import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiClasspathResources.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiClasspathResources.java index 2a66d966e17..9ab59dc4f24 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiClasspathResources.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiClasspathResources.java @@ -23,7 +23,6 @@ import aQute.bnd.osgi.Constants; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.ops4j.pax.exam.Configuration; @@ -38,7 +37,6 @@ import org.osgi.framework.BundleContext; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; -import static org.ops4j.pax.exam.CoreOptions.systemProperty; /** * TestJettyOSGiClasspathResources diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java index 088ee4e308e..16d647447b6 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestOSGiUtil.java @@ -25,7 +25,6 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.options.WrappedUrlProvisionOption.OverwriteMode; import org.ops4j.pax.tinybundles.core.TinyBundle; import org.ops4j.pax.tinybundles.core.TinyBundles; import org.osgi.framework.Bundle; @@ -38,7 +37,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.ops4j.pax.exam.CoreOptions.mavenBundle; import static org.ops4j.pax.exam.CoreOptions.systemProperty; -import static org.ops4j.pax.exam.CoreOptions.wrappedBundle; /** * Helper methods for pax-exam tests diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java index cf06898f55a..ed7d556cbff 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/webapp/PlusDescriptorProcessor.java @@ -18,7 +18,6 @@ import java.util.Objects; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NameNotFoundException; -import javax.naming.NamingException; import org.eclipse.jetty.jndi.NamingUtil; import org.eclipse.jetty.plus.annotation.Injection; diff --git a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java index efd7d6ec7b5..728a6b123ff 100644 --- a/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java +++ b/jetty-plus/src/test/java/org/eclipse/jetty/plus/annotation/LifeCycleCallbackCollectionTest.java @@ -16,7 +16,6 @@ package org.eclipse.jetty.plus.annotation; import java.lang.reflect.Method; import javax.servlet.http.HttpServlet; -import org.eclipse.jetty.plus.webapp.PlusDecorator; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java index 250c938d0bb..2e9f07fa0bb 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java @@ -47,7 +47,6 @@ import org.eclipse.jetty.server.HttpChannel; import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; -import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.HostPort; import org.eclipse.jetty.util.Promise; diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java index b5aec2052d4..d7b20c80cac 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java @@ -29,7 +29,6 @@ import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.AsyncRequestContent; import org.eclipse.jetty.client.util.InputStreamRequestContent; -import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Callback; /** diff --git a/jetty-runner/src/it/test-jar-manifest/postbuild.groovy b/jetty-runner/src/it/test-jar-manifest/postbuild.groovy index af66cf7472d..c21ad606fb0 100644 --- a/jetty-runner/src/it/test-jar-manifest/postbuild.groovy +++ b/jetty-runner/src/it/test-jar-manifest/postbuild.groovy @@ -1,4 +1,5 @@ -import java.util.jar.* +import java.util.jar.Attributes +import java.util.jar.JarFile File artifact = new File( basedir, "target/jetty-runner.jar" ) assert artifact.exists() diff --git a/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java b/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java index b808681d4de..c5e4596f1b9 100644 --- a/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java +++ b/jetty-security/src/test/java/org/eclipse/jetty/security/TestLoginService.java @@ -13,11 +13,8 @@ package org.eclipse.jetty.security; -import java.util.ArrayList; import java.util.List; -import java.util.Set; -import org.eclipse.jetty.server.UserIdentity; import org.eclipse.jetty.util.security.Credential; /** diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java index 4523f44f041..61b2a285f59 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/AsyncContextEvent.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.server; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; -import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java index 689ffaadfbe..b675a725c8d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java @@ -90,7 +90,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque LOG.debug("needContent has content immediately available: {}", _content); return true; } - _httpConnection.parseAndFillForContent(); + parseAndFillForContent(); if (_content != null) { if (LOG.isDebugEnabled()) @@ -111,7 +111,7 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque { if (LOG.isDebugEnabled()) LOG.debug("produceContent has no content, parsing and filling"); - _httpConnection.parseAndFillForContent(); + parseAndFillForContent(); } HttpInput.Content result = _content; if (result != null && !result.isSpecial()) @@ -121,6 +121,18 @@ public class HttpChannelOverHttp extends HttpChannel implements HttpParser.Reque return result; } + private void parseAndFillForContent() + { + try + { + _httpConnection.parseAndFillForContent(); + } + catch (Throwable x) + { + _content = new HttpInput.ErrorContent(x); + } + } + @Override public boolean failAllContent(Throwable failure) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index b0fb50dc661..274de01ba1e 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -329,6 +329,11 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http */ void parseAndFillForContent() { + // Defensive check to avoid an infinite select/wakeup/fillAndParseForContent/wait loop + // in case the parser was mistakenly closed and the connection was not aborted. + if (_parser.isTerminated()) + throw new IllegalStateException("Parser is terminated: " + _parser); + // When fillRequestBuffer() is called, it must always be followed by a parseRequestBuffer() call otherwise this method // doesn't trigger EOF/earlyEOF which breaks AsyncRequestReadTest.testPartialReadThenShutdown(). diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java index 7eb95ff1869..ac4d9234edf 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java @@ -28,7 +28,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.nio.file.StandardOpenOption; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -39,7 +38,6 @@ import javax.servlet.http.Part; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.ByteArrayOutputStream2; -import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.MultiMap; import org.eclipse.jetty.util.QuotedStringTokenizer; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index dc0a456a497..33115fc6e57 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -67,7 +67,6 @@ import javax.servlet.http.WebConnection; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.ComplianceViolation; import org.eclipse.jetty.http.HostPortHttpField; -import org.eclipse.jetty.http.HttpCompliance; import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpCookie.SetCookieHttpField; import org.eclipse.jetty.http.HttpField; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingTest.java index 7dbffab2241..67f7d0e0c7d 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/BlockingTest.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.server; import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; @@ -23,13 +22,10 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Predicate; -import java.util.zip.GZIPOutputStream; import javax.servlet.AsyncContext; import javax.servlet.DispatcherType; import javax.servlet.ServletException; @@ -41,14 +37,10 @@ import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.DefaultHandler; -import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.gzip.GzipHandler; -import org.hamcrest.Matchers; -import org.hamcrest.core.Is; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java index c3dfccacf87..e57c71694bc 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpServerTestBase.java @@ -47,7 +47,6 @@ import org.eclipse.jetty.util.IO; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.DisabledIfSystemProperty; -import org.junit.jupiter.api.condition.JRE; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HouseKeeperTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HouseKeeperTest.java index d2d19f6122d..48f3a5b8006 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/HouseKeeperTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/HouseKeeperTest.java @@ -20,7 +20,6 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java index f031361e5b6..dbab277e9ea 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/session/SessionHandlerTest.java @@ -17,7 +17,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import javax.servlet.SessionTrackingMode; -import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionEvent; import javax.servlet.http.HttpSessionListener; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLReadEOFAfterResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLReadEOFAfterResponseTest.java index 28d6de30c8d..021fd90e794 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLReadEOFAfterResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ssl/SSLReadEOFAfterResponseTest.java @@ -34,7 +34,6 @@ import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java index a57ea91366e..0a1b98e5e98 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ListenerHolder.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.servlet; import java.util.EventListener; import javax.servlet.ServletContext; -import javax.servlet.ServletException; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.thread.AutoLock; diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java index f4c80461620..002a79fc0b2 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ServletRequestLogTest.java @@ -42,7 +42,6 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java index 79be0fca894..81d60a57395 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/ConcatServletTest.java @@ -23,7 +23,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.stream.Stream; import javax.servlet.RequestDispatcher; -import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java index 6ed65508033..38c17642b23 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/URLResource.java @@ -22,7 +22,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.channels.ReadableByteChannel; -import java.util.Objects; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.thread.AutoLock; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java index c1510eaff40..81d41d0c7a2 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ReservedThreadExecutor.java @@ -13,34 +13,45 @@ package org.eclipse.jetty.util.thread; -import java.util.concurrent.ConcurrentLinkedDeque; +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import org.eclipse.jetty.util.AtomicBiInteger; import org.eclipse.jetty.util.ProcessorUtils; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.Dumpable; +import org.eclipse.jetty.util.component.DumpableCollection; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static org.eclipse.jetty.util.AtomicBiInteger.getHi; +import static org.eclipse.jetty.util.AtomicBiInteger.getLo; + + /** - * An Executor using preallocated/reserved Threads from a wrapped Executor. + * An Executor using pre-allocated/reserved Threads from a wrapped Executor. *

Calls to {@link #execute(Runnable)} on a {@link ReservedThreadExecutor} will either succeed * with a Thread immediately being assigned the Runnable task, or fail if no Thread is * available. - *

Threads are reserved lazily, with a new reserved thread being allocated from a - * wrapped {@link Executor} when an execution fails. If the {@link #setIdleTimeout(long, TimeUnit)} - * is set to non zero (default 1 minute), then the reserved thread pool will shrink by 1 thread - * whenever it has been idle for that period. + *

Threads are reserved lazily, with a new reserved threads being allocated from the + * {@link Executor} passed to the constructor. Whenever 1 or more reserved threads have been + * idle for more than {@link #getIdleTimeoutMs()} then one reserved thread will return to + * the executor. */ @ManagedObject("A pool for reserved threads") -public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExecutor +public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExecutor, Dumpable { private static final Logger LOG = LoggerFactory.getLogger(ReservedThreadExecutor.class); + private static final long DEFAULT_IDLE_TIMEOUT = TimeUnit.MINUTES.toNanos(1); private static final Runnable STOP = new Runnable() { @Override @@ -57,13 +68,13 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec private final Executor _executor; private final int _capacity; - private final ConcurrentLinkedDeque _stack; - private final AtomicInteger _size = new AtomicInteger(); - private final AtomicInteger _pending = new AtomicInteger(); + private final Set _threads = ConcurrentHashMap.newKeySet(); + private final SynchronousQueue _queue = new SynchronousQueue<>(false); + private final AtomicBiInteger _count = new AtomicBiInteger(); // hi=pending; lo=size; + private final AtomicLong _lastEmptyTime = new AtomicLong(System.nanoTime()); private ThreadPoolBudget.Lease _lease; - private long _idleTime = 1L; - private TimeUnit _idleTimeUnit = TimeUnit.MINUTES; + private long _idleTimeNanos = DEFAULT_IDLE_TIMEOUT; /** * @param executor The executor to use to obtain threads @@ -75,7 +86,6 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { _executor = executor; _capacity = reservedThreads(executor, capacity); - _stack = new ConcurrentLinkedDeque<>(); if (LOG.isDebugEnabled()) LOG.debug("{}", this); } @@ -121,42 +131,39 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec @ManagedAttribute(value = "available reserved threads", readonly = true) public int getAvailable() { - return _stack.size(); + return _count.getLo(); } @ManagedAttribute(value = "pending reserved threads", readonly = true) public int getPending() { - return _pending.get(); + return _count.getHi(); } - @ManagedAttribute(value = "idletimeout in MS", readonly = true) + @ManagedAttribute(value = "idle timeout in ms", readonly = true) public long getIdleTimeoutMs() { - if (_idleTimeUnit == null) - return 0; - return _idleTimeUnit.toMillis(_idleTime); + return NANOSECONDS.toMillis(_idleTimeNanos); } /** * Set the idle timeout for shrinking the reserved thread pool * - * @param idleTime Time to wait before shrinking, or 0 for no timeout. + * @param idleTime Time to wait before shrinking, or 0 for default timeout. * @param idleTimeUnit Time units for idle timeout */ public void setIdleTimeout(long idleTime, TimeUnit idleTimeUnit) { if (isRunning()) throw new IllegalStateException(); - _idleTime = idleTime; - _idleTimeUnit = idleTimeUnit; + _idleTimeNanos = (idleTime <= 0 || idleTimeUnit == null) ? DEFAULT_IDLE_TIMEOUT : idleTimeUnit.toNanos(idleTime); } @Override public void doStart() throws Exception { _lease = ThreadPoolBudget.leaseFrom(getExecutor(), this, _capacity); - _size.set(0); + _count.set(0, 0); super.doStart(); } @@ -168,26 +175,22 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec super.doStop(); - while (true) + // Offer STOP task to all waiting reserved threads. + for (int i = _count.getAndSetLo(-1); i-- > 0;) { - int size = _size.get(); - // If no reserved threads left try setting size to -1 to - // atomically prevent other threads adding themselves to stack. - if (size == 0 && _size.compareAndSet(size, -1)) - break; - - ReservedThread thread = _stack.pollFirst(); - if (thread == null) - { - // Reserved thread must have incremented size but not yet added itself to queue. - // We will spin until it is added. - Thread.onSpinWait(); - continue; - } - - _size.decrementAndGet(); - thread.stop(); + // yield to wait for any reserved threads that have incremented the size but not yet polled + Thread.yield(); + _queue.offer(STOP); } + // Interrupt any reserved thread missed the offer so it doesn't wait too long. + for (ReservedThread reserved : _threads) + { + Thread thread = reserved._thread; + if (thread != null) + thread.interrupt(); + } + _threads.clear(); + _count.getAndSetHi(0); } @Override @@ -207,52 +210,61 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec { if (LOG.isDebugEnabled()) LOG.debug("{} tryExecute {}", this, task); - if (task == null) return false; - ReservedThread thread = _stack.pollFirst(); - if (thread == null) - { - if (task != STOP) - startReservedThread(); - return false; - } + // Offer will only succeed if there is a reserved thread waiting + boolean offered = _queue.offer(task); - int size = _size.decrementAndGet(); - if (!thread.offer(task)) - return false; + // If the offer succeeded we need to reduce the size, unless it is set to -1 in the meantime + int size = _count.getLo(); + while (offered && size > 0 && !_count.compareAndSetLo(size, --size)) + size = _count.getLo(); + // If size is 0 and we are not stopping, start a new reserved thread if (size == 0 && task != STOP) startReservedThread(); - return true; + return offered; } private void startReservedThread() { - try + while (true) { - while (true) + long count = _count.get(); + int pending = getHi(count); + int size = getLo(count); + if (size < 0 || pending + size >= _capacity) + return; + if (size == 0) + _lastEmptyTime.set(System.nanoTime()); + if (!_count.compareAndSet(count, pending + 1, size)) + continue; + + if (LOG.isDebugEnabled()) + LOG.debug("{} startReservedThread p={}", this, pending + 1); + try { - // Not atomic, but there is a re-check in ReservedThread.run(). - int pending = _pending.get(); - int size = _size.get(); - if (pending + size >= _capacity) - return; - if (_pending.compareAndSet(pending, pending + 1)) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} startReservedThread p={}", this, pending + 1); - _executor.execute(new ReservedThread()); - return; - } + ReservedThread thread = new ReservedThread(); + _threads.add(thread); + _executor.execute(thread); } + catch (Throwable e) + { + _count.add(-1, 0); + if (LOG.isDebugEnabled()) + LOG.debug("ignored", e); + } + return; } - catch (RejectedExecutionException e) - { - LOG.trace("IGNORED", e); - } + } + + @Override + public void dump(Appendable out, String indent) throws IOException + { + Dumpable.dumpObjects(out, indent, this, + new DumpableCollection("reserved", _threads)); } @Override @@ -261,136 +273,149 @@ public class ReservedThreadExecutor extends AbstractLifeCycle implements TryExec return String.format("%s@%x{s=%d/%d,p=%d}", getClass().getSimpleName(), hashCode(), - _size.get(), + _count.getLo(), _capacity, - _pending.get()); + _count.getHi()); + } + + private enum State + { + PENDING, + RESERVED, + RUNNING, + IDLE, + STOPPED } private class ReservedThread implements Runnable { - private final SynchronousQueue _task = new SynchronousQueue<>(); - private boolean _starting = true; - - public boolean offer(Runnable task) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} offer {}", this, task); - - try - { - _task.put(task); - return true; - } - catch (Throwable e) - { - LOG.trace("IGNORED", e); - _size.getAndIncrement(); - _stack.offerFirst(this); - return false; - } - } - - public void stop() - { - offer(STOP); - } + // The state and thread are kept only for dumping + private volatile State _state = State.PENDING; + private volatile Thread _thread; private Runnable reservedWait() { if (LOG.isDebugEnabled()) - LOG.debug("{} waiting", this); + LOG.debug("{} waiting {}", this, ReservedThreadExecutor.this); - while (true) + // Keep waiting until stopped, tasked or idle + while (_count.getLo() >= 0) { try { - Runnable task = _idleTime <= 0 ? _task.take() : _task.poll(_idleTime, _idleTimeUnit); + // Always poll at some period as safety to ensure we don't poll forever. + Runnable task = _queue.poll(_idleTimeNanos, NANOSECONDS); if (LOG.isDebugEnabled()) - LOG.debug("{} task={}", this, task); + LOG.debug("{} task={} {}", this, task, ReservedThreadExecutor.this); if (task != null) return task; - if (_stack.remove(this)) + // we have idled out + int size = _count.getLo(); + // decrement size if we have not also been stopped. + while (size > 0) { - if (LOG.isDebugEnabled()) - LOG.debug("{} IDLE", this); - _size.decrementAndGet(); - return STOP; + if (_count.compareAndSetLo(size, --size)) + break; + size = _count.getLo(); } + _state = size >= 0 ? State.IDLE : State.STOPPED; + return STOP; + } catch (InterruptedException e) { - LOG.trace("IGNORED", e); + if (LOG.isDebugEnabled()) + LOG.debug("ignored", e); } } + _state = State.STOPPED; + return STOP; } @Override public void run() { - while (isRunning()) + _thread = Thread.currentThread(); + try { - // test and increment size BEFORE decrementing pending, - // so that we don't have a race starting new pending. - int size = _size.get(); - - // Are we stopped? - if (size < 0) - return; - - // Are we surplus to capacity? - if (size >= _capacity) + while (true) { + long count = _count.get(); + + // reduce pending if this thread was pending + int pending = getHi(count) - (_state == State.PENDING ? 1 : 0); + int size = getLo(count); + + State next; + if (size < 0 || size >= _capacity) + { + // The executor has stopped or this thread is excess to capacity + next = State.STOPPED; + } + else + { + long now = System.nanoTime(); + long lastEmpty = _lastEmptyTime.get(); + if (size > 0 && _idleTimeNanos < (now - lastEmpty) && _lastEmptyTime.compareAndSet(lastEmpty, now)) + { + // it has been too long since we hit zero reserved threads, so are "busy" idle + next = State.IDLE; + } + else + { + // We will become a reserved thread if we can update the count below. + next = State.RESERVED; + size++; + } + } + + // Update count for pending and size + if (!_count.compareAndSet(count, pending, size)) + continue; + if (LOG.isDebugEnabled()) - LOG.debug("{} size {} > capacity {}", this, size, _capacity); - if (_starting) - _pending.decrementAndGet(); - return; - } + LOG.debug("{} was={} next={} size={}+{} capacity={}", this, _state, next, pending, size, _capacity); + _state = next; + if (next != State.RESERVED) + break; - // If we cannot update size then recalculate - if (!_size.compareAndSet(size, size + 1)) - continue; + // We are reserved whilst we are waiting for an offered _task. + Runnable task = reservedWait(); - if (_starting) - { - if (LOG.isDebugEnabled()) - LOG.debug("{} started", this); - _pending.decrementAndGet(); - _starting = false; - } + // Is the task the STOP poison pill? + if (task == STOP) + break; - // Insert ourselves in the stack. Size is already incremented, but - // that only effects the decision to keep other threads reserved. - _stack.offerFirst(this); - - // Once added to the stack, we must always wait for a job on the _task Queue - // and never return early, else we may leave a thread blocked offering a _task. - Runnable task = reservedWait(); - - if (task == STOP) - // return on STOP poison pill - break; - - // Run the task - try - { - task.run(); - } - catch (Throwable e) - { - LOG.warn("Unable to run task", e); + // Run the task + try + { + _state = State.RUNNING; + task.run(); + } + catch (Throwable e) + { + LOG.warn("Unable to run task", e); + } } } - - if (LOG.isDebugEnabled()) - LOG.debug("{} Exited", this); + finally + { + if (LOG.isDebugEnabled()) + LOG.debug("{} exited {}", this, ReservedThreadExecutor.this); + _threads.remove(this); + _thread = null; + } } @Override public String toString() { - return String.format("%s@%x", ReservedThreadExecutor.this, hashCode()); + return String.format("%s@%x{%s,thread=%s}", + getClass().getSimpleName(), + hashCode(), + _state, + _thread); } } -} +} \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java index 1d16dac3f13..8f302dedd75 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/thread/ShutdownThread.java @@ -13,8 +13,6 @@ package org.eclipse.jetty.util.thread; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Arrays; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java index c3bc28a204b..eac29003219 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/BlockingArrayQueueTest.java @@ -13,23 +13,28 @@ package org.eclipse.jetty.util; +import java.time.Duration; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.ListIterator; -import java.util.Random; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; +import static org.eclipse.jetty.util.BlockingArrayQueueTest.Await.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -161,12 +166,12 @@ public class BlockingArrayQueueTest } @Test - @DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review public void testTake() throws Exception { final String[] data = new String[4]; final BlockingArrayQueue queue = new BlockingArrayQueue<>(); + CyclicBarrier barrier = new CyclicBarrier(2); Thread thread = new Thread() { @@ -177,7 +182,7 @@ public class BlockingArrayQueueTest { data[0] = queue.take(); data[1] = queue.take(); - Thread.sleep(1000); + barrier.await(5, TimeUnit.SECONDS); // Wait until the main thread already called offer(). data[2] = queue.take(); data[3] = queue.poll(100, TimeUnit.MILLISECONDS); } @@ -191,35 +196,36 @@ public class BlockingArrayQueueTest thread.start(); - Thread.sleep(1000); + // Wait until the spawned thread is blocked in queue.take(). + await().atMost(5, TimeUnit.SECONDS).until(() -> thread.getState() == Thread.State.WAITING); queue.offer("zero"); queue.offer("one"); queue.offer("two"); + barrier.await(5, TimeUnit.SECONDS); // Notify the spawned thread that offer() was called. thread.join(); assertEquals("zero", data[0]); assertEquals("one", data[1]); assertEquals("two", data[2]); - assertEquals(null, data[3]); + assertNull(data[3]); } @Test - @DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review public void testConcurrentAccess() throws Exception { - final int THREADS = 50; + final int THREADS = 32; final int LOOPS = 1000; - final BlockingArrayQueue queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS); + BlockingArrayQueue queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS); - final ConcurrentLinkedQueue produced = new ConcurrentLinkedQueue<>(); - final ConcurrentLinkedQueue consumed = new ConcurrentLinkedQueue<>(); + Set produced = ConcurrentHashMap.newKeySet(); + Set consumed = ConcurrentHashMap.newKeySet(); - final AtomicBoolean running = new AtomicBoolean(true); + AtomicBoolean consumersRunning = new AtomicBoolean(true); // start consumers - final CyclicBarrier barrier0 = new CyclicBarrier(THREADS + 1); + CyclicBarrier consumersBarrier = new CyclicBarrier(THREADS + 1); for (int i = 0; i < THREADS; i++) { new Thread() @@ -227,20 +233,18 @@ public class BlockingArrayQueueTest @Override public void run() { - final Random random = new Random(); - setPriority(getPriority() - 1); try { - while (running.get()) + while (consumersRunning.get()) { - int r = 1 + random.nextInt(10); + int r = 1 + ThreadLocalRandom.current().nextInt(10); if (r % 2 == 0) { Integer msg = queue.poll(); if (msg == null) { - Thread.sleep(1 + random.nextInt(10)); + Thread.sleep(ThreadLocalRandom.current().nextInt(2)); continue; } consumed.add(msg); @@ -261,7 +265,7 @@ public class BlockingArrayQueueTest { try { - barrier0.await(); + consumersBarrier.await(); } catch (Exception e) { @@ -273,7 +277,7 @@ public class BlockingArrayQueueTest } // start producers - final CyclicBarrier barrier1 = new CyclicBarrier(THREADS + 1); + CyclicBarrier producersBarrier = new CyclicBarrier(THREADS + 1); for (int i = 0; i < THREADS; i++) { final int id = i; @@ -282,16 +286,15 @@ public class BlockingArrayQueueTest @Override public void run() { - final Random random = new Random(); try { for (int j = 0; j < LOOPS; j++) { - Integer msg = random.nextInt(); + Integer msg = ThreadLocalRandom.current().nextInt(); produced.add(msg); if (!queue.offer(msg)) throw new Exception(id + " FULL! " + queue.size()); - Thread.sleep(1 + random.nextInt(10)); + Thread.sleep(ThreadLocalRandom.current().nextInt(2)); } } catch (Exception e) @@ -302,7 +305,7 @@ public class BlockingArrayQueueTest { try { - barrier1.await(); + producersBarrier.await(); } catch (Exception e) { @@ -313,22 +316,22 @@ public class BlockingArrayQueueTest }.start(); } - barrier1.await(); - int size = queue.size(); - int last = size - 1; - while (size > 0 && size != last) + producersBarrier.await(); + + AtomicInteger last = new AtomicInteger(queue.size() - 1); + await().atMost(5, TimeUnit.SECONDS).until(() -> { - last = size; - Thread.sleep(500); - size = queue.size(); - } - running.set(false); - barrier0.await(); + int size = queue.size(); + if (size == 0 && last.get() == size) + return true; + last.set(size); + return false; + }); - HashSet prodSet = new HashSet<>(produced); - HashSet consSet = new HashSet<>(consumed); + consumersRunning.set(false); + consumersBarrier.await(); - assertEquals(prodSet, consSet); + assertEquals(produced, consumed); } @Test @@ -525,4 +528,35 @@ public class BlockingArrayQueueTest assertThat(queue.size(), Matchers.is(0)); assertThat(queue, Matchers.empty()); } + + static class Await + { + private Duration duration; + + public static Await await() + { + return new Await(); + } + + public Await atMost(long time, TimeUnit unit) + { + duration = Duration.ofMillis(unit.toMillis(time)); + return this; + } + + public void until(Callable condition) throws Exception + { + Objects.requireNonNull(duration); + long start = System.nanoTime(); + + while (true) + { + if (condition.call()) + return; + if (duration.minus(Duration.ofNanos(System.nanoTime() - start)).isNegative()) + throw new AssertionError("Duration expired"); + Thread.sleep(10); + } + } + } } diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiReleaseJarFileTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiReleaseJarFileTest.java index 8da1f62033a..87b9f721807 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/MultiReleaseJarFileTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/MultiReleaseJarFileTest.java @@ -22,7 +22,6 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.MultiReleaseJarFile.VersionedJarEntry; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java index 140cb29bcb2..65cacdcd07c 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java @@ -32,7 +32,6 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.PathWatcher.PathWatchEvent; import org.eclipse.jetty.util.PathWatcher.PathWatchEventType; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java index 1d6851747a7..b37a0f67fa9 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/TypeUtilTest.java @@ -19,7 +19,6 @@ import java.nio.file.Paths; import org.eclipse.jetty.util.resource.Resource; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JrtResourceTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JrtResourceTest.java index 600e75e319c..42e728ab273 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JrtResourceTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/resource/JrtResourceTest.java @@ -13,18 +13,10 @@ package org.eclipse.jetty.util.resource; -import java.net.URI; - -import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.TypeUtil; -import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.startsWith; public class JrtResourceTest { diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ReservedThreadExecutorTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ReservedThreadExecutorTest.java index 5f86fa2b1be..e3a08b876bf 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ReservedThreadExecutorTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/thread/ReservedThreadExecutorTest.java @@ -22,10 +22,10 @@ import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -180,6 +180,35 @@ public class ReservedThreadExecutorTest assertThat(_reservedExecutor.getAvailable(), is(0)); } + @Test + public void testBusyShrink() throws Exception + { + final long IDLE = 1000; + + _reservedExecutor.stop(); + _reservedExecutor.setIdleTimeout(IDLE, TimeUnit.MILLISECONDS); + _reservedExecutor.start(); + assertThat(_reservedExecutor.getAvailable(), is(0)); + + assertThat(_reservedExecutor.tryExecute(NOOP), is(false)); + assertThat(_reservedExecutor.tryExecute(NOOP), is(false)); + + _executor.startThread(); + _executor.startThread(); + + waitForAvailable(2); + + int available = _reservedExecutor.getAvailable(); + assertThat(available, is(2)); + + for (int i = 10; i-- > 0;) + { + assertThat(_reservedExecutor.tryExecute(NOOP), is(true)); + Thread.sleep(200); + } + assertThat(_reservedExecutor.getAvailable(), is(1)); + } + @Test public void testReservedIdleTimeoutWithOneReservedThread() throws Exception { @@ -261,7 +290,6 @@ public class ReservedThreadExecutorTest } } - @Disabled @Test public void stressTest() throws Exception { @@ -271,9 +299,9 @@ public class ReservedThreadExecutorTest reserved.setIdleTimeout(0, null); reserved.start(); - final int LOOPS = 1000000; + final int LOOPS = 200000; final AtomicInteger executions = new AtomicInteger(LOOPS); - final CountDownLatch executed = new CountDownLatch(executions.get()); + final CountDownLatch executed = new CountDownLatch(LOOPS); final AtomicInteger usedReserved = new AtomicInteger(0); final AtomicInteger usedPool = new AtomicInteger(0); @@ -322,10 +350,15 @@ public class ReservedThreadExecutorTest assertTrue(executed.await(60, TimeUnit.SECONDS)); + // ensure tryExecute is still working + while (!reserved.tryExecute(() -> {})) + Thread.yield(); + reserved.stop(); pool.stop(); + assertThat(usedReserved.get(), greaterThan(0)); assertThat(usedReserved.get() + usedPool.get(), is(LOOPS)); - System.err.printf("reserved=%d pool=%d total=%d%n", usedReserved.get(), usedPool.get(), LOOPS); + // System.err.printf("reserved=%d pool=%d total=%d%n", usedReserved.get(), usedPool.get(), LOOPS); } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java index f84117fffbb..3fe7d932b60 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClassMatcher.java @@ -39,8 +39,6 @@ import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A matcher for classes based on package and/or location and/or module/ diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClassMatcherTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClassMatcherTest.java index 5beabdf2cd5..20f1d163189 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClassMatcherTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/ClassMatcherTest.java @@ -25,8 +25,6 @@ import org.eclipse.jetty.webapp.ClassMatcher.Entry; import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertFalse; diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java index 653dd3eb38f..d1c573059be 100644 --- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java +++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/AsyncIOServletTest.java @@ -71,7 +71,6 @@ import org.eclipse.jetty.server.handler.ContextHandler.Context; import org.eclipse.jetty.server.handler.gzip.GzipHttpInputInterceptor; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.FuturePromise; -import org.eclipse.jetty.util.compression.CompressionPool; import org.eclipse.jetty.util.compression.InflaterPool; import org.hamcrest.Matchers; import org.junit.jupiter.api.Assumptions; diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java index f841c023ecc..ebd4be03b5b 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/jsp/JspAndDefaultWithoutAliasesTest.java @@ -24,10 +24,8 @@ import java.util.stream.Stream; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.security.HashLoginService; -import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.NetworkConnector; -import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; diff --git a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClusteredOrphanedSessionTest.java b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClusteredOrphanedSessionTest.java index 4c84f4456f0..9442cc8b3a1 100644 --- a/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClusteredOrphanedSessionTest.java +++ b/tests/test-sessions/test-file-sessions/src/test/java/org/eclipse/jetty/server/session/ClusteredOrphanedSessionTest.java @@ -15,7 +15,6 @@ package org.eclipse.jetty.server.session; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/ConcurrencyTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/ConcurrencyTest.java index 93798a411d8..a21686758c3 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/ConcurrencyTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/ConcurrencyTest.java @@ -30,7 +30,6 @@ import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.DisabledIfSystemProperty; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java index 52dbc183b84..11a2028bbc8 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -14,7 +14,6 @@ package org.eclipse.jetty.server.session; import java.io.IOException; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; diff --git a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionInvalidationTest.java b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionInvalidationTest.java index 525b81d2067..254c83ec238 100644 --- a/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionInvalidationTest.java +++ b/tests/test-sessions/test-sessions-common/src/test/java/org/eclipse/jetty/server/session/SessionInvalidationTest.java @@ -25,7 +25,6 @@ import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.StringUtil; import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat;