Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-6544-gziphandler-excludedMimeTypes

This commit is contained in:
Joakim Erdfelt 2021-07-30 07:25:18 -05:00
commit 4d35dd736d
No known key found for this signature in database
GPG Key ID: 2D0E1FB8FE4B68B4
64 changed files with 485 additions and 371 deletions

View File

@ -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

View File

@ -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;

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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
{

View File

@ -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;

View File

@ -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;

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -209,6 +209,7 @@ public class HttpParser
private static final EnumSet<State> __idleStates = EnumSet.of(State.START, State.END, State.CLOSE, State.CLOSED);
private static final EnumSet<State> __completeStates = EnumSet.of(State.END, State.CLOSE, State.CLOSED);
private static final EnumSet<State> __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);
}

View File

@ -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

View File

@ -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<Arguments> 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<Arguments> 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<Arguments> 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));
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
/**

View File

@ -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()

View File

@ -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;
/**

View File

@ -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;

View File

@ -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)
{

View File

@ -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().

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.
* <p>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.
* <p>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.
* <p>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<ReservedThread> _stack;
private final AtomicInteger _size = new AtomicInteger();
private final AtomicInteger _pending = new AtomicInteger();
private final Set<ReservedThread> _threads = ConcurrentHashMap.newKeySet();
private final SynchronousQueue<Runnable> _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<Runnable> _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);
}
}
}
}

View File

@ -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;

View File

@ -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<String> 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<Integer> queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS);
BlockingArrayQueue<Integer> queue = new BlockingArrayQueue<>(1 + THREADS * LOOPS);
final ConcurrentLinkedQueue<Integer> produced = new ConcurrentLinkedQueue<>();
final ConcurrentLinkedQueue<Integer> consumed = new ConcurrentLinkedQueue<>();
Set<Integer> produced = ConcurrentHashMap.newKeySet();
Set<Integer> 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<Integer> prodSet = new HashSet<>(produced);
HashSet<Integer> 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<Boolean> 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);
}
}
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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);
}
}

View File

@ -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/

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;