Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-6544-gziphandler-excludedMimeTypes
This commit is contained in:
commit
4d35dd736d
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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().
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue