Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
commit
7e39d5434a
|
@ -132,6 +132,11 @@
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.kerby</groupId>
|
<groupId>org.apache.kerby</groupId>
|
||||||
<artifactId>kerb-simplekdc</artifactId>
|
<artifactId>kerb-simplekdc</artifactId>
|
||||||
|
|
|
@ -40,7 +40,6 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Exchanger;
|
import java.util.concurrent.Exchanger;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
@ -50,7 +49,6 @@ import java.util.function.LongConsumer;
|
||||||
import jakarta.servlet.AsyncContext;
|
import jakarta.servlet.AsyncContext;
|
||||||
import jakarta.servlet.DispatcherType;
|
import jakarta.servlet.DispatcherType;
|
||||||
import jakarta.servlet.ReadListener;
|
import jakarta.servlet.ReadListener;
|
||||||
import jakarta.servlet.ServletException;
|
|
||||||
import jakarta.servlet.ServletInputStream;
|
import jakarta.servlet.ServletInputStream;
|
||||||
import jakarta.servlet.ServletOutputStream;
|
import jakarta.servlet.ServletOutputStream;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
@ -91,7 +89,6 @@ import org.eclipse.jetty.util.Promise;
|
||||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
import org.eclipse.jetty.util.SocketAddressResolver;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
@ -688,48 +685,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testRequestIdleTimeout(Scenario scenario) throws Exception
|
|
||||||
{
|
|
||||||
long idleTimeout = 1000;
|
|
||||||
start(scenario, new AbstractHandler()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
|
||||||
}
|
|
||||||
catch (InterruptedException x)
|
|
||||||
{
|
|
||||||
throw new ServletException(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
String host = "localhost";
|
|
||||||
int port = connector.getLocalPort();
|
|
||||||
assertThrows(TimeoutException.class, () ->
|
|
||||||
client.newRequest(host, port)
|
|
||||||
.scheme(scenario.getScheme())
|
|
||||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.send());
|
|
||||||
|
|
||||||
// Make another request without specifying the idle timeout, should not fail
|
|
||||||
ContentResponse response = client.newRequest(host, port)
|
|
||||||
.scheme(scenario.getScheme())
|
|
||||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.send();
|
|
||||||
|
|
||||||
assertNotNull(response);
|
|
||||||
assertEquals(200, response.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
public void testSendToIPv6Address(Scenario scenario) throws Exception
|
public void testSendToIPv6Address(Scenario scenario) throws Exception
|
||||||
|
|
|
@ -32,13 +32,12 @@ import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.logging.StacklessLogging;
|
import org.eclipse.jetty.logging.StacklessLogging;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -213,8 +212,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testBadRequestWithSlowRequestRemovesConnection(Scenario scenario) throws Exception
|
public void testBadRequestWithSlowRequestRemovesConnection(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
start(scenario, new EmptyServerHandler());
|
start(scenario, new EmptyServerHandler());
|
||||||
|
@ -423,8 +420,6 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testIdleConnectionIsClosedOnRemoteClose(Scenario scenario) throws Exception
|
public void testIdleConnectionIsClosedOnRemoteClose(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
start(scenario, new EmptyServerHandler());
|
start(scenario, new EmptyServerHandler());
|
||||||
|
@ -448,10 +443,7 @@ public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest
|
||||||
connector.stop();
|
connector.stop();
|
||||||
|
|
||||||
// Give the connection some time to process the remote close
|
// Give the connection some time to process the remote close
|
||||||
TimeUnit.SECONDS.sleep(1);
|
await().atMost(5, TimeUnit.SECONDS).until(() -> idleConnections.size() == 0 && activeConnections.size() == 0);
|
||||||
|
|
||||||
assertEquals(0, idleConnections.size());
|
|
||||||
assertEquals(0, activeConnections.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
|
|
@ -35,8 +35,8 @@ import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@ -91,7 +91,6 @@ public class HttpSenderOverHTTPTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testSendNoRequestContentIncompleteFlush() throws Exception
|
public void testSendNoRequestContentIncompleteFlush() throws Exception
|
||||||
{
|
{
|
||||||
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
|
ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16);
|
||||||
|
@ -105,7 +104,7 @@ public class HttpSenderOverHTTPTest
|
||||||
StringBuilder builder = new StringBuilder(endPoint.takeOutputString());
|
StringBuilder builder = new StringBuilder(endPoint.takeOutputString());
|
||||||
|
|
||||||
// Wait for the write to complete
|
// Wait for the write to complete
|
||||||
TimeUnit.SECONDS.sleep(1);
|
await().atMost(5, TimeUnit.SECONDS).until(() -> endPoint.toEndPointString().contains(",flush=P,"));
|
||||||
|
|
||||||
String chunk = endPoint.takeOutputString();
|
String chunk = endPoint.takeOutputString();
|
||||||
while (chunk.length() > 0)
|
while (chunk.length() > 0)
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.zip.GZIPOutputStream;
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
@ -48,7 +47,6 @@ import org.eclipse.jetty.toolchain.test.Net;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
|
@ -410,47 +408,6 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
assertArrayEquals(data, response.getContent());
|
assertArrayEquals(data, response.getContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testRequestIdleTimeout() throws Exception
|
|
||||||
{
|
|
||||||
final long idleTimeout = 1000;
|
|
||||||
start(new AbstractHandler()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
baseRequest.setHandled(true);
|
|
||||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
|
||||||
}
|
|
||||||
catch (InterruptedException x)
|
|
||||||
{
|
|
||||||
throw new ServletException(x);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final String host = "localhost";
|
|
||||||
final int port = connector.getLocalPort();
|
|
||||||
assertThrows(TimeoutException.class, () ->
|
|
||||||
client.newRequest(host, port)
|
|
||||||
.scheme(scheme)
|
|
||||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.send());
|
|
||||||
|
|
||||||
// Make another request without specifying the idle timeout, should not fail
|
|
||||||
ContentResponse response = client.newRequest(host, port)
|
|
||||||
.scheme(scheme)
|
|
||||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.send();
|
|
||||||
|
|
||||||
assertNotNull(response);
|
|
||||||
assertEquals(200, response.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectionIdleTimeout() throws Exception
|
public void testConnectionIdleTimeout() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.nio.channels.ServerSocketChannel;
|
||||||
import java.nio.channels.SocketChannel;
|
import java.nio.channels.SocketChannel;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
@ -30,7 +29,6 @@ import org.eclipse.jetty.util.thread.TimerScheduler;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -55,23 +53,21 @@ public class SelectorManagerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testConnectTimeoutBeforeSuccessfulConnect() throws Exception
|
public void testConnectTimeoutBeforeSuccessfulConnect() throws Exception
|
||||||
{
|
{
|
||||||
ServerSocketChannel server = ServerSocketChannel.open();
|
ServerSocketChannel server = ServerSocketChannel.open();
|
||||||
server.bind(new InetSocketAddress("localhost", 0));
|
server.bind(new InetSocketAddress("localhost", 0));
|
||||||
SocketAddress address = server.getLocalAddress();
|
SocketAddress address = server.getLocalAddress();
|
||||||
|
|
||||||
final AtomicLong timeoutConnection = new AtomicLong();
|
CountDownLatch connectionFinishedLatch = new CountDownLatch(1);
|
||||||
final long connectTimeout = 1000;
|
CountDownLatch failedConnectionLatch = new CountDownLatch(1);
|
||||||
|
long connectTimeout = 1000;
|
||||||
SelectorManager selectorManager = new SelectorManager(executor, scheduler)
|
SelectorManager selectorManager = new SelectorManager(executor, scheduler)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
|
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
|
||||||
{
|
{
|
||||||
SocketChannelEndPoint endPoint = new SocketChannelEndPoint((SocketChannel)channel, selector, key, getScheduler());
|
return new SocketChannelEndPoint((SocketChannel)channel, selector, key, getScheduler());
|
||||||
endPoint.setIdleTimeout(connectTimeout / 2);
|
|
||||||
return endPoint;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -79,15 +75,17 @@ public class SelectorManagerTest
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
long timeout = timeoutConnection.get();
|
assertTrue(failedConnectionLatch.await(connectTimeout * 2, TimeUnit.MILLISECONDS));
|
||||||
if (timeout > 0)
|
|
||||||
TimeUnit.MILLISECONDS.sleep(timeout);
|
|
||||||
return super.doFinishConnect(channel);
|
return super.doFinishConnect(channel);
|
||||||
}
|
}
|
||||||
catch (InterruptedException e)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
connectionFinishedLatch.countDown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -116,40 +114,36 @@ public class SelectorManagerTest
|
||||||
{
|
{
|
||||||
SocketChannel client1 = SocketChannel.open();
|
SocketChannel client1 = SocketChannel.open();
|
||||||
client1.configureBlocking(false);
|
client1.configureBlocking(false);
|
||||||
client1.connect(address);
|
assertFalse(client1.connect(address));
|
||||||
long timeout = connectTimeout * 2;
|
|
||||||
timeoutConnection.set(timeout);
|
|
||||||
final CountDownLatch latch1 = new CountDownLatch(1);
|
|
||||||
selectorManager.connect(client1, new Callback()
|
selectorManager.connect(client1, new Callback()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void failed(Throwable x)
|
public void failed(Throwable x)
|
||||||
{
|
{
|
||||||
latch1.countDown();
|
failedConnectionLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertTrue(latch1.await(connectTimeout * 3, TimeUnit.MILLISECONDS));
|
assertTrue(failedConnectionLatch.await(connectTimeout * 2, TimeUnit.MILLISECONDS));
|
||||||
assertFalse(client1.isOpen());
|
assertFalse(client1.isOpen());
|
||||||
|
|
||||||
// Wait for the first connect to finish, as the selector thread is waiting in finishConnect().
|
// Wait for the first connect to finish, as the selector thread is waiting in doFinishConnect().
|
||||||
Thread.sleep(timeout);
|
assertTrue(connectionFinishedLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
// Verify that after the failure we can connect successfully.
|
// Verify that after the failure we can connect successfully.
|
||||||
try (SocketChannel client2 = SocketChannel.open())
|
try (SocketChannel client2 = SocketChannel.open())
|
||||||
{
|
{
|
||||||
client2.configureBlocking(false);
|
client2.configureBlocking(false);
|
||||||
client2.connect(address);
|
assertFalse(client2.connect(address));
|
||||||
timeoutConnection.set(0);
|
CountDownLatch successfulConnectionLatch = new CountDownLatch(1);
|
||||||
final CountDownLatch latch2 = new CountDownLatch(1);
|
|
||||||
selectorManager.connect(client2, new Callback()
|
selectorManager.connect(client2, new Callback()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void succeeded()
|
public void succeeded()
|
||||||
{
|
{
|
||||||
latch2.countDown();
|
successfulConnectionLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
assertTrue(latch2.await(connectTimeout * 5, TimeUnit.MILLISECONDS));
|
assertTrue(successfulConnectionLatch.await(connectTimeout * 2, TimeUnit.MILLISECONDS));
|
||||||
assertTrue(client2.isOpen());
|
assertTrue(client2.isOpen());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,9 +34,7 @@ import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.resource.Resource;
|
import org.eclipse.jetty.util.resource.Resource;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
@ -46,8 +44,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
public class ConnectionOpenCloseTest extends AbstractHttpTest
|
public class ConnectionOpenCloseTest extends AbstractHttpTest
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testOpenClose() throws Exception
|
public void testOpenClose() throws Exception
|
||||||
{
|
{
|
||||||
server.setHandler(new AbstractHandler()
|
server.setHandler(new AbstractHandler()
|
||||||
|
@ -97,8 +93,6 @@ public class ConnectionOpenCloseTest extends AbstractHttpTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testOpenRequestClose() throws Exception
|
public void testOpenRequestClose() throws Exception
|
||||||
{
|
{
|
||||||
server.setHandler(new AbstractHandler()
|
server.setHandler(new AbstractHandler()
|
||||||
|
@ -153,15 +147,13 @@ public class ConnectionOpenCloseTest extends AbstractHttpTest
|
||||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
// Wait some time to see if the callbacks are called too many times
|
// Wait some time to see if the callbacks are called too many times
|
||||||
TimeUnit.SECONDS.sleep(1);
|
TimeUnit.MILLISECONDS.sleep(200);
|
||||||
|
|
||||||
assertEquals(2, callbacks.get());
|
assertEquals(2, callbacks.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testSSLOpenRequestClose() throws Exception
|
public void testSSLOpenRequestClose() throws Exception
|
||||||
{
|
{
|
||||||
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
SslContextFactory.Server sslContextFactory = new SslContextFactory.Server();
|
||||||
|
@ -223,7 +215,7 @@ public class ConnectionOpenCloseTest extends AbstractHttpTest
|
||||||
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(closeLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
// Wait some time to see if the callbacks are called too many times
|
// Wait some time to see if the callbacks are called too many times
|
||||||
TimeUnit.SECONDS.sleep(1);
|
TimeUnit.MILLISECONDS.sleep(200);
|
||||||
|
|
||||||
assertEquals(4, callbacks.get());
|
assertEquals(4, callbacks.get());
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,6 @@ import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
|
|
||||||
import static org.eclipse.jetty.http.HttpHeader.CONTENT_LENGTH;
|
import static org.eclipse.jetty.http.HttpHeader.CONTENT_LENGTH;
|
||||||
import static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE;
|
import static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE;
|
||||||
|
@ -277,7 +276,6 @@ public class ResourceHandlerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testSlowBiggest() throws Exception
|
public void testSlowBiggest() throws Exception
|
||||||
{
|
{
|
||||||
_connector.setIdleTimeout(9000);
|
_connector.setIdleTimeout(9000);
|
||||||
|
@ -307,7 +305,7 @@ public class ResourceHandlerTest
|
||||||
ByteBuffer buffer = null;
|
ByteBuffer buffer = null;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Thread.sleep(25);
|
Thread.sleep(10);
|
||||||
int len = in.read(array);
|
int len = in.read(array);
|
||||||
if (len < 0)
|
if (len < 0)
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -54,7 +54,6 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -71,10 +70,9 @@ public class ThreadStarvationTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testDefaultServletSuccess() throws Exception
|
public void testDefaultServletSuccess() throws Exception
|
||||||
{
|
{
|
||||||
int maxThreads = 10;
|
int maxThreads = 6;
|
||||||
QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);
|
QueuedThreadPool threadPool = new QueuedThreadPool(maxThreads, maxThreads);
|
||||||
threadPool.setDetailedDump(true);
|
threadPool.setDetailedDump(true);
|
||||||
_server = new Server(threadPool);
|
_server = new Server(threadPool);
|
||||||
|
@ -86,11 +84,11 @@ public class ThreadStarvationTest
|
||||||
Path resourcePath = Paths.get(directory.getPath(), resourceName);
|
Path resourcePath = Paths.get(directory.getPath(), resourceName);
|
||||||
try (OutputStream output = Files.newOutputStream(resourcePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))
|
try (OutputStream output = Files.newOutputStream(resourcePath, StandardOpenOption.CREATE, StandardOpenOption.WRITE))
|
||||||
{
|
{
|
||||||
byte[] chunk = new byte[1024];
|
byte[] chunk = new byte[256 * 1024];
|
||||||
Arrays.fill(chunk, (byte)'X');
|
Arrays.fill(chunk, (byte)'X');
|
||||||
chunk[chunk.length - 2] = '\r';
|
chunk[chunk.length - 2] = '\r';
|
||||||
chunk[chunk.length - 1] = '\n';
|
chunk[chunk.length - 1] = '\n';
|
||||||
for (int i = 0; i < 256 * 1024; ++i)
|
for (int i = 0; i < 1024; ++i)
|
||||||
{
|
{
|
||||||
output.write(chunk);
|
output.write(chunk);
|
||||||
}
|
}
|
||||||
|
@ -135,10 +133,9 @@ public class ThreadStarvationTest
|
||||||
"\r\n";
|
"\r\n";
|
||||||
output.write(request.getBytes(StandardCharsets.UTF_8));
|
output.write(request.getBytes(StandardCharsets.UTF_8));
|
||||||
output.flush();
|
output.flush();
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for a the servlet to block.
|
// Wait for a thread on the servlet to block.
|
||||||
assertTrue(writePending.await(5, TimeUnit.SECONDS));
|
assertTrue(writePending.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
long expected = Files.size(resourcePath);
|
long expected = Files.size(resourcePath);
|
||||||
|
|
|
@ -63,6 +63,11 @@
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||||
<artifactId>jetty-perf-helper</artifactId>
|
<artifactId>jetty-perf-helper</artifactId>
|
||||||
|
|
|
@ -13,13 +13,10 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util;
|
package org.eclipse.jetty.util;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CyclicBarrier;
|
import java.util.concurrent.CyclicBarrier;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
@ -30,7 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.eclipse.jetty.util.BlockingArrayQueueTest.Await.await;
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
@ -528,35 +525,4 @@ public class BlockingArrayQueueTest
|
||||||
assertThat(queue.size(), Matchers.is(0));
|
assertThat(queue.size(), Matchers.is(0));
|
||||||
assertThat(queue, Matchers.empty());
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
5
pom.xml
5
pom.xml
|
@ -1172,6 +1172,11 @@
|
||||||
<artifactId>hamcrest</artifactId>
|
<artifactId>hamcrest</artifactId>
|
||||||
<version>${hamcrest.version}</version>
|
<version>${hamcrest.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<version>4.1.0</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.testcontainers</groupId>
|
<groupId>org.testcontainers</groupId>
|
||||||
<artifactId>testcontainers-bom</artifactId>
|
<artifactId>testcontainers-bom</artifactId>
|
||||||
|
|
|
@ -46,6 +46,11 @@
|
||||||
<artifactId>slf4j-api</artifactId>
|
<artifactId>slf4j-api</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.eclipse.jetty</groupId>
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
<artifactId>jetty-alpn-java-client</artifactId>
|
<artifactId>jetty-alpn-java-client</artifactId>
|
||||||
|
|
|
@ -60,11 +60,13 @@ import org.eclipse.jetty.http2.HTTP2Session;
|
||||||
import org.eclipse.jetty.http2.api.Session;
|
import org.eclipse.jetty.http2.api.Session;
|
||||||
import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
|
import org.eclipse.jetty.http2.client.http.HttpConnectionOverHTTP2;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EofException;
|
||||||
import org.eclipse.jetty.logging.StacklessLogging;
|
import org.eclipse.jetty.logging.StacklessLogging;
|
||||||
import org.eclipse.jetty.server.Handler;
|
import org.eclipse.jetty.server.Handler;
|
||||||
import org.eclipse.jetty.server.HttpChannel;
|
import org.eclipse.jetty.server.HttpChannel;
|
||||||
import org.eclipse.jetty.server.HttpInput;
|
import org.eclipse.jetty.server.HttpInput;
|
||||||
import org.eclipse.jetty.server.HttpInput.Content;
|
import org.eclipse.jetty.server.HttpInput.Content;
|
||||||
|
import org.eclipse.jetty.server.HttpOutput;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
import org.eclipse.jetty.server.handler.ContextHandler.Context;
|
||||||
|
@ -74,12 +76,11 @@ import org.eclipse.jetty.util.FuturePromise;
|
||||||
import org.eclipse.jetty.util.compression.InflaterPool;
|
import org.eclipse.jetty.util.compression.InflaterPool;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
|
||||||
import static java.nio.ByteBuffer.wrap;
|
import static java.nio.ByteBuffer.wrap;
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
||||||
import static org.eclipse.jetty.http.client.Transport.H2C;
|
import static org.eclipse.jetty.http.client.Transport.H2C;
|
||||||
import static org.eclipse.jetty.http.client.Transport.HTTP;
|
import static org.eclipse.jetty.http.client.Transport.HTTP;
|
||||||
|
@ -398,18 +399,11 @@ public class AsyncIOServletTest extends AbstractTest<AsyncIOServletTest.AsyncTra
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Unstable")
|
|
||||||
@Disabled
|
|
||||||
public void testAsyncWriteClosed(Transport transport) throws Exception
|
public void testAsyncWriteClosed(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
init(transport);
|
init(transport);
|
||||||
|
|
||||||
String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\n";
|
byte[] data = new byte[1024];
|
||||||
for (int i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
text = text + text;
|
|
||||||
}
|
|
||||||
byte[] data = text.getBytes(StandardCharsets.UTF_8);
|
|
||||||
|
|
||||||
CountDownLatch errorLatch = new CountDownLatch(1);
|
CountDownLatch errorLatch = new CountDownLatch(1);
|
||||||
scenario.start(new HttpServlet()
|
scenario.start(new HttpServlet()
|
||||||
|
@ -431,9 +425,26 @@ public class AsyncIOServletTest extends AbstractTest<AsyncIOServletTest.AsyncTra
|
||||||
|
|
||||||
// Wait for the failure to arrive to
|
// Wait for the failure to arrive to
|
||||||
// the server while we are about to write.
|
// the server while we are about to write.
|
||||||
sleep(2000);
|
try
|
||||||
|
{
|
||||||
out.write(data);
|
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (out.isReady())
|
||||||
|
((HttpOutput)out).write(ByteBuffer.wrap(data));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (EofException e)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
package org.eclipse.jetty.http.client;
|
package org.eclipse.jetty.http.client;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -25,9 +26,11 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import jakarta.servlet.ServletException;
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.ServletInputStream;
|
import jakarta.servlet.ServletInputStream;
|
||||||
|
import jakarta.servlet.ServletOutputStream;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import org.eclipse.jetty.client.ContinueProtocolHandler;
|
import org.eclipse.jetty.client.ContinueProtocolHandler;
|
||||||
|
@ -45,11 +48,10 @@ import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.server.Request;
|
import org.eclipse.jetty.server.Request;
|
||||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.junit.jupiter.api.Tag;
|
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
import static org.eclipse.jetty.http.client.Transport.FCGI;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -319,36 +321,40 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testExpect100ContinueWithContentWithResponseFailureBefore100Continue(Transport transport) throws Exception
|
public void testExpect100ContinueWithContentWithResponseFailureBefore100Continue(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
init(transport);
|
init(transport);
|
||||||
long idleTimeout = 1000;
|
AtomicReference<org.eclipse.jetty.client.api.Request> clientRequestRef = new AtomicReference<>();
|
||||||
|
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
scenario.startServer(new AbstractHandler()
|
scenario.startServer(new AbstractHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
clientRequestRef.get().abort(new Exception("abort!"));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
if (!clientLatch.await(5, TimeUnit.SECONDS))
|
||||||
|
throw new ServletException("Server timed out on client latch");
|
||||||
|
serverLatch.countDown();
|
||||||
}
|
}
|
||||||
catch (InterruptedException x)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
throw new ServletException(x);
|
throw new ServletException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
scenario.startClient(httpClient -> httpClient.setIdleTimeout(2 * idleTimeout));
|
scenario.startClient();
|
||||||
|
|
||||||
byte[] content = new byte[1024];
|
byte[] content = new byte[1024];
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
org.eclipse.jetty.client.api.Request clientRequest = scenario.client.newRequest(scenario.newURI());
|
||||||
scenario.client.newRequest(scenario.newURI())
|
clientRequestRef.set(clientRequest);
|
||||||
|
clientRequest
|
||||||
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
||||||
.body(new BytesRequestContent(content))
|
.body(new BytesRequestContent(content))
|
||||||
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
|
||||||
.send(new BufferingResponseListener()
|
.send(new BufferingResponseListener()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -357,21 +363,22 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
assertTrue(result.isFailed());
|
assertTrue(result.isFailed());
|
||||||
assertNotNull(result.getRequestFailure());
|
assertNotNull(result.getRequestFailure());
|
||||||
assertNotNull(result.getResponseFailure());
|
assertNotNull(result.getResponseFailure());
|
||||||
latch.countDown();
|
clientLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
|
assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testExpect100ContinueWithContentWithResponseFailureAfter100Continue(Transport transport) throws Exception
|
public void testExpect100ContinueWithContentWithResponseFailureAfter100Continue(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
init(transport);
|
init(transport);
|
||||||
long idleTimeout = 1000;
|
AtomicReference<org.eclipse.jetty.client.api.Request> clientRequestRef = new AtomicReference<>();
|
||||||
|
CountDownLatch clientLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||||
scenario.startServer(new AbstractHandler()
|
scenario.startServer(new AbstractHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
|
@ -380,9 +387,12 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
// Send 100-Continue and consume the content
|
// Send 100-Continue and consume the content
|
||||||
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
IO.copy(request.getInputStream(), new ByteArrayOutputStream());
|
||||||
|
clientRequestRef.get().abort(new Exception("abort!"));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
|
if (!clientLatch.await(5, TimeUnit.SECONDS))
|
||||||
|
throw new ServletException("Server timed out on client latch");
|
||||||
|
serverLatch.countDown();
|
||||||
}
|
}
|
||||||
catch (InterruptedException x)
|
catch (InterruptedException x)
|
||||||
{
|
{
|
||||||
|
@ -390,11 +400,12 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
scenario.startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
|
scenario.startClient();
|
||||||
|
|
||||||
byte[] content = new byte[1024];
|
byte[] content = new byte[1024];
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
org.eclipse.jetty.client.api.Request clientRequest = scenario.client.newRequest(scenario.newURI());
|
||||||
scenario.client.newRequest(scenario.newURI())
|
clientRequestRef.set(clientRequest);
|
||||||
|
clientRequest
|
||||||
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
||||||
.body(new BytesRequestContent(content))
|
.body(new BytesRequestContent(content))
|
||||||
.send(new BufferingResponseListener()
|
.send(new BufferingResponseListener()
|
||||||
|
@ -405,11 +416,12 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
assertTrue(result.isFailed());
|
assertTrue(result.isFailed());
|
||||||
assertNull(result.getRequestFailure());
|
assertNull(result.getRequestFailure());
|
||||||
assertNotNull(result.getResponseFailure());
|
assertNotNull(result.getResponseFailure());
|
||||||
latch.countDown();
|
clientLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS));
|
assertTrue(clientLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -474,10 +486,16 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testExpect100ContinueWithDeferredContentRespond100Continue(Transport transport) throws Exception
|
public void testExpect100ContinueWithDeferredContentRespond100Continue(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
|
byte[] chunk1 = new byte[]{0, 1, 2, 3};
|
||||||
|
byte[] chunk2 = new byte[]{4, 5, 6, 7};
|
||||||
|
byte[] data = new byte[chunk1.length + chunk2.length];
|
||||||
|
System.arraycopy(chunk1, 0, data, 0, chunk1.length);
|
||||||
|
System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
|
||||||
|
|
||||||
|
CountDownLatch serverLatch = new CountDownLatch(1);
|
||||||
|
AtomicReference<Thread> handlerThread = new AtomicReference<>();
|
||||||
init(transport);
|
init(transport);
|
||||||
scenario.start(new AbstractHandler()
|
scenario.start(new AbstractHandler()
|
||||||
{
|
{
|
||||||
|
@ -485,18 +503,22 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
handlerThread.set(Thread.currentThread());
|
||||||
// Send 100-Continue and echo the content
|
// Send 100-Continue and echo the content
|
||||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
|
||||||
|
ServletOutputStream outputStream = response.getOutputStream();
|
||||||
|
DataInputStream inputStream = new DataInputStream(request.getInputStream());
|
||||||
|
// Block until the 1st chunk is fully received.
|
||||||
|
byte[] buf1 = new byte[chunk1.length];
|
||||||
|
inputStream.readFully(buf1);
|
||||||
|
outputStream.write(buf1);
|
||||||
|
|
||||||
|
serverLatch.countDown();
|
||||||
|
IO.copy(inputStream, outputStream);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
byte[] chunk1 = new byte[]{0, 1, 2, 3};
|
CountDownLatch requestLatch = new CountDownLatch(1);
|
||||||
byte[] chunk2 = new byte[]{4, 5, 6, 7};
|
|
||||||
byte[] data = new byte[chunk1.length + chunk2.length];
|
|
||||||
System.arraycopy(chunk1, 0, data, 0, chunk1.length);
|
|
||||||
System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length);
|
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
AsyncRequestContent content = new AsyncRequestContent();
|
AsyncRequestContent content = new AsyncRequestContent();
|
||||||
scenario.client.newRequest(scenario.newURI())
|
scenario.client.newRequest(scenario.newURI())
|
||||||
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
.headers(headers -> headers.put(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE))
|
||||||
|
@ -507,28 +529,38 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
public void onComplete(Result result)
|
public void onComplete(Result result)
|
||||||
{
|
{
|
||||||
assertArrayEquals(data, getContent());
|
assertArrayEquals(data, getContent());
|
||||||
latch.countDown();
|
requestLatch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread.sleep(1000);
|
// Wait for the handler thread to be blocked in the 1st IO.
|
||||||
|
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||||
|
{
|
||||||
|
Thread thread = handlerThread.get();
|
||||||
|
return thread != null && thread.getState() == Thread.State.WAITING;
|
||||||
|
});
|
||||||
|
|
||||||
content.offer(ByteBuffer.wrap(chunk1));
|
content.offer(ByteBuffer.wrap(chunk1));
|
||||||
|
|
||||||
Thread.sleep(1000);
|
// Wait for the handler thread to be blocked in the 2nd IO.
|
||||||
|
assertTrue(serverLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||||
|
{
|
||||||
|
Thread thread = handlerThread.get();
|
||||||
|
return thread != null && thread.getState() == Thread.State.WAITING;
|
||||||
|
});
|
||||||
|
|
||||||
content.offer(ByteBuffer.wrap(chunk2));
|
content.offer(ByteBuffer.wrap(chunk2));
|
||||||
content.close();
|
content.close();
|
||||||
|
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(requestLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Slow")
|
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testExpect100ContinueWithInitialAndDeferredContentRespond100Continue(Transport transport) throws Exception
|
public void testExpect100ContinueWithInitialAndDeferredContentRespond100Continue(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
|
AtomicReference<Thread> handlerThread = new AtomicReference<>();
|
||||||
init(transport);
|
init(transport);
|
||||||
scenario.start(new AbstractHandler()
|
scenario.start(new AbstractHandler()
|
||||||
{
|
{
|
||||||
|
@ -536,6 +568,7 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
{
|
{
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
|
handlerThread.set(Thread.currentThread());
|
||||||
// Send 100-Continue and echo the content
|
// Send 100-Continue and echo the content
|
||||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||||
}
|
}
|
||||||
|
@ -562,7 +595,12 @@ public class HttpClientContinueTest extends AbstractTest<TransportScenario>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Thread.sleep(1000);
|
// Wait for the handler thread to be blocked in IO.
|
||||||
|
await().atMost(5, TimeUnit.SECONDS).until(() ->
|
||||||
|
{
|
||||||
|
Thread thread = handlerThread.get();
|
||||||
|
return thread != null && thread.getState() == Thread.State.WAITING;
|
||||||
|
});
|
||||||
|
|
||||||
content.offer(ByteBuffer.wrap(chunk2));
|
content.offer(ByteBuffer.wrap(chunk2));
|
||||||
content.close();
|
content.close();
|
||||||
|
|
|
@ -58,7 +58,6 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
|
||||||
|
@ -666,7 +665,6 @@ public class HttpClientStreamTest extends AbstractTest<TransportScenario>
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@DisabledIfSystemProperty(named = "env", matches = "ci") // TODO: SLOW, needs review
|
|
||||||
public void testUploadWithDeferredContentProviderFromInputStream(Transport transport) throws Exception
|
public void testUploadWithDeferredContentProviderFromInputStream(Transport transport) throws Exception
|
||||||
{
|
{
|
||||||
init(transport);
|
init(transport);
|
||||||
|
@ -680,20 +678,22 @@ public class HttpClientStreamTest extends AbstractTest<TransportScenario>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch requestSentLatch = new CountDownLatch(1);
|
||||||
|
CountDownLatch responseLatch = new CountDownLatch(1);
|
||||||
try (AsyncRequestContent content = new AsyncRequestContent())
|
try (AsyncRequestContent content = new AsyncRequestContent())
|
||||||
{
|
{
|
||||||
scenario.client.newRequest(scenario.newURI())
|
scenario.client.newRequest(scenario.newURI())
|
||||||
.scheme(scenario.getScheme())
|
.scheme(scenario.getScheme())
|
||||||
.body(content)
|
.body(content)
|
||||||
|
.onRequestCommit((request) -> requestSentLatch.countDown())
|
||||||
.send(result ->
|
.send(result ->
|
||||||
{
|
{
|
||||||
if (result.isSucceeded() && result.getResponse().getStatus() == 200)
|
if (result.isSucceeded() && result.getResponse().getStatus() == 200)
|
||||||
latch.countDown();
|
responseLatch.countDown();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make sure we provide the content *after* the request has been "sent".
|
// Make sure we provide the content *after* the request has been "sent".
|
||||||
Thread.sleep(1000);
|
assertTrue(requestSentLatch.await(5, TimeUnit.SECONDS));
|
||||||
|
|
||||||
try (ByteArrayInputStream input = new ByteArrayInputStream(new byte[1024]))
|
try (ByteArrayInputStream input = new ByteArrayInputStream(new byte[1024]))
|
||||||
{
|
{
|
||||||
|
@ -705,7 +705,7 @@ public class HttpClientStreamTest extends AbstractTest<TransportScenario>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
|
|
@ -22,10 +22,12 @@ import java.util.Random;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.stream.IntStream;
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import jakarta.servlet.ServletException;
|
||||||
import jakarta.servlet.ServletInputStream;
|
import jakarta.servlet.ServletInputStream;
|
||||||
import jakarta.servlet.ServletOutputStream;
|
import jakarta.servlet.ServletOutputStream;
|
||||||
import jakarta.servlet.http.HttpServlet;
|
import jakarta.servlet.http.HttpServlet;
|
||||||
|
@ -781,6 +783,58 @@ public class HttpClientTest extends AbstractTest<TransportScenario>
|
||||||
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
assertTrue(resultLatch.await(5, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ArgumentsSource(TransportProvider.class)
|
||||||
|
public void testRequestIdleTimeout(Transport transport) throws Exception
|
||||||
|
{
|
||||||
|
init(transport);
|
||||||
|
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
long idleTimeout = 500;
|
||||||
|
scenario.start(new AbstractHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
baseRequest.setHandled(true);
|
||||||
|
if (target.equals("/1"))
|
||||||
|
assertTrue(latch.await(5, TimeUnit.SECONDS));
|
||||||
|
else if (target.equals("/2"))
|
||||||
|
Thread.sleep(2 * idleTimeout);
|
||||||
|
else
|
||||||
|
fail("Unknown path: " + target);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new ServletException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
String host = "localhost";
|
||||||
|
int port = scenario.getNetworkConnectorLocalPortInt().get();
|
||||||
|
assertThrows(TimeoutException.class, () ->
|
||||||
|
scenario.client.newRequest(host, port)
|
||||||
|
.scheme(scenario.getScheme())
|
||||||
|
.path("/1")
|
||||||
|
.idleTimeout(idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.timeout(2 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.send());
|
||||||
|
latch.countDown();
|
||||||
|
|
||||||
|
// Make another request without specifying the idle timeout, should not fail
|
||||||
|
ContentResponse response = scenario.client.newRequest(host, port)
|
||||||
|
.scheme(scenario.getScheme())
|
||||||
|
.path("/2")
|
||||||
|
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
assertNotNull(response);
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
private void sleep(long time) throws IOException
|
private void sleep(long time) throws IOException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
Loading…
Reference in New Issue