use of io.servicetalk.concurrent.api.Completable in project servicetalk by apple.
the class TimeoutPublisherTest method concurrentTimeoutInvocation.
@Test
void concurrentTimeoutInvocation() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
AtomicReference<Throwable> causeRef = new AtomicReference<>();
// In order to simulate concurrent execution, we introduce an Executor that does not respect the delay for the
// first timer schedule. Internally, we expect the TimeoutPublisher to reschedule the timer. For that we use
// TestExecutor, which will allow us to advance the time and trigger the actual timeout, which will simulate
// concurrent execution.
toSource(publisher.timeout(10, MILLISECONDS, new Executor() {
private final AtomicInteger timerCount = new AtomicInteger();
@Override
public Cancellable schedule(final Runnable task, final long delay, final TimeUnit unit) {
int count = timerCount.incrementAndGet();
if (count <= 2) {
if (count == 1) {
try {
task.run();
} catch (Throwable cause) {
causeRef.compareAndSet(null, cause);
countDownToZero(latch);
}
latch.countDown();
} else {
try {
try {
testExecutor.schedule(task, delay, unit);
testExecutor.advanceTimeBy(delay, unit);
} catch (Throwable cause) {
causeRef.compareAndSet(null, cause);
countDownToZero(latch);
}
latch.countDown();
} catch (Throwable cause) {
causeRef.compareAndSet(null, cause);
countDownToZero(latch);
}
}
}
return IGNORE_CANCEL;
}
@Override
public long currentTime(final TimeUnit unit) {
return testExecutor.currentTime(unit);
}
@Override
public Completable closeAsync() {
throw new UnsupportedOperationException();
}
@Override
public Completable onClose() {
throw new UnsupportedOperationException();
}
@Override
public Cancellable execute(final Runnable task) throws RejectedExecutionException {
throw new UnsupportedOperationException();
}
})).subscribe(subscriber);
latch.await();
assertNull(causeRef.get());
assertThat(subscriber.awaitOnError(), instanceOf(TimeoutException.class));
}
use of io.servicetalk.concurrent.api.Completable in project servicetalk by apple.
the class BackendsStarter method main.
public static void main(String[] args) throws Exception {
// Create an AutoCloseable representing all resources used in this example.
try (CompositeCloseable resources = newCompositeCloseable()) {
// Shared IoExecutor for the application.
IoExecutor ioExecutor = resources.prepend(createIoExecutor());
// This is a single Completable used to await closing of all backends started by this class. It is used to
// provide a way to not let main() exit.
Completable allServicesOnClose = completed();
BackendStarter starter = new BackendStarter(ioExecutor, resources);
final ServerContext recommendationService = starter.start(RECOMMENDATIONS_BACKEND_ADDRESS.port(), RECOMMENDATION_SERVICE_NAME, newRecommendationsService());
allServicesOnClose = allServicesOnClose.merge(recommendationService.onClose());
final ServerContext metadataService = starter.start(METADATA_BACKEND_ADDRESS.port(), METADATA_SERVICE_NAME, newMetadataService());
allServicesOnClose = allServicesOnClose.merge(metadataService.onClose());
final ServerContext userService = starter.start(USER_BACKEND_ADDRESS.port(), USER_SERVICE_NAME, newUserService());
allServicesOnClose = allServicesOnClose.merge(userService.onClose());
final ServerContext ratingService = starter.start(RATINGS_BACKEND_ADDRESS.port(), RATING_SERVICE_NAME, newRatingService());
allServicesOnClose = allServicesOnClose.merge(ratingService.onClose());
// Await termination of all backends started by this class.
allServicesOnClose.toFuture().get();
}
}
use of io.servicetalk.concurrent.api.Completable in project servicetalk by apple.
the class HttpRequestEncoderTest method protocolPayloadEndOutboundShouldNotTriggerOnFailedFlush.
@Test
void protocolPayloadEndOutboundShouldNotTriggerOnFailedFlush() throws Exception {
AtomicReference<CloseHandler> closeHandlerRef = new AtomicReference<>();
try (CompositeCloseable resources = newCompositeCloseable()) {
Processor serverCloseTrigger = newCompletableProcessor();
CountDownLatch serverChannelLatch = new CountDownLatch(1);
AtomicReference<Channel> serverChannelRef = new AtomicReference<>();
ReadOnlyTcpServerConfig sConfig = new TcpServerConfig().asReadOnly();
ServerContext serverContext = resources.prepend(TcpServerBinder.bind(localAddress(0), sConfig, false, SEC, null, (channel, observer) -> DefaultNettyConnection.initChannel(channel, SEC.bufferAllocator(), SEC.executor(), SEC.ioExecutor(), forPipelinedRequestResponse(false, channel.config()), defaultFlushStrategy(), 0L, null, new TcpServerChannelInitializer(sConfig, observer).andThen(channel2 -> {
serverChannelRef.compareAndSet(null, channel2);
serverChannelLatch.countDown();
}), defaultStrategy(), mock(Protocol.class), observer, false, __ -> false), connection -> {
}).toFuture().get());
ReadOnlyHttpClientConfig cConfig = new HttpClientConfig().asReadOnly();
assert cConfig.h1Config() != null;
NettyConnection<Object, Object> conn = resources.prepend(TcpConnector.connect(null, serverHostAndPort(serverContext), cConfig.tcpConfig(), false, CEC, (channel, connectionObserver) -> {
CloseHandler closeHandler = spy(forPipelinedRequestResponse(true, channel.config()));
closeHandlerRef.compareAndSet(null, closeHandler);
return DefaultNettyConnection.initChannel(channel, CEC.bufferAllocator(), CEC.executor(), CEC.ioExecutor(), closeHandler, defaultFlushStrategy(), 0L, cConfig.tcpConfig().sslConfig(), new TcpClientChannelInitializer(cConfig.tcpConfig(), connectionObserver).andThen(new HttpClientChannelInitializer(getByteBufAllocator(CEC.bufferAllocator()), cConfig.h1Config(), closeHandler)).andThen(channel2 -> channel2.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
// Propagate the user event in the pipeline before
// triggering the test condition.
ctx.fireUserEventTriggered(evt);
if (evt instanceof ChannelInputShutdownReadComplete) {
serverCloseTrigger.onComplete();
}
}
})), defaultStrategy(), HTTP_1_1, connectionObserver, true, __ -> false);
}, NoopTransportObserver.INSTANCE).toFuture().get());
// The server needs to wait to close the conneciton until after the client has established the connection.
serverChannelLatch.await();
Channel serverChannel = serverChannelRef.get();
assertNotNull(serverChannel);
assumeFalse(serverChannel instanceof NioSocketChannel, "Windows doesn't emit ChannelInputShutdownReadComplete. Investigation Required.");
((SocketChannel) serverChannel).config().setSoLinger(0);
// Close and send RST concurrently with client write
serverChannel.close();
StreamingHttpRequest request = reqRespFactory.post("/closeme");
fromSource(serverCloseTrigger).toFuture().get();
Completable write = conn.write(from(request, allocator.fromAscii("Bye"), EmptyHttpHeaders.INSTANCE));
assertThrows(ExecutionException.class, () -> write.toFuture().get());
CloseHandler closeHandler = closeHandlerRef.get();
assertNotNull(closeHandler);
verify(closeHandler, never()).protocolPayloadEndOutbound(any(), any());
}
}
use of io.servicetalk.concurrent.api.Completable in project servicetalk by apple.
the class HttpPredicateRouterBuilderTest method testCloseAsyncClosesAllServicesWhenFirstOneIsError.
@Test
void testCloseAsyncClosesAllServicesWhenFirstOneIsError() throws Exception {
when(serviceA.closeAsync()).thenReturn(failCompletable);
when(serviceB.closeAsync()).thenReturn(completableB);
when(serviceC.closeAsync()).thenReturn(completableC);
final StreamingHttpService service = new HttpPredicateRouterBuilder().whenMethod(GET).thenRouteTo(serviceA).whenMethod(POST).thenRouteTo(serviceB).when((ctx, req) -> true).thenRouteTo(serviceC).buildStreaming();
try {
final Completable completable = service.closeAsync();
completable.toFuture().get();
Assertions.fail("Expected an exception from `await`");
} catch (final ExecutionException e) {
assertSame(DELIBERATE_EXCEPTION, e.getCause());
}
verify(serviceA).closeAsync();
verify(serviceB).closeAsync();
verify(serviceC).closeAsync();
failCompletable.verifyNotCancelled();
completableB.verifyNotCancelled();
completableC.verifyNotCancelled();
failCompletable.verifyListenCalled();
completableB.verifyListenCalled();
completableC.verifyListenCalled();
}
use of io.servicetalk.concurrent.api.Completable in project servicetalk by apple.
the class GracefulConnectionClosureHandlingTest method setUp.
void setUp(HttpProtocol protocol, boolean initiateClosureFromClient, boolean useUds, boolean viaProxy) throws Exception {
this.protocol = protocol;
this.initiateClosureFromClient = initiateClosureFromClient;
if (useUds) {
Assumptions.assumeTrue(SERVER_CTX.ioExecutor().isUnixDomainSocketSupported(), "Server's IoExecutor does not support UnixDomainSocket");
Assumptions.assumeTrue(CLIENT_CTX.ioExecutor().isUnixDomainSocketSupported(), "Client's IoExecutor does not support UnixDomainSocket");
assumeFalse(viaProxy, "UDS cannot be used via proxy");
}
assumeFalse(protocol == HTTP_2 && viaProxy, "Proxy is not supported with HTTP/2");
HttpServerBuilder serverBuilder = (useUds ? forAddress(newSocketAddress()) : forAddress(localAddress(0))).protocols(protocol.config).ioExecutor(SERVER_CTX.ioExecutor()).executor(SERVER_CTX.executor()).executionStrategy(defaultStrategy()).enableWireLogging("servicetalk-tests-wire-logger", TRACE, () -> true).appendConnectionAcceptorFilter(original -> new DelegatingConnectionAcceptor(original) {
@Override
public Completable accept(final ConnectionContext context) {
if (!initiateClosureFromClient) {
((NettyConnectionContext) context).onClosing().whenFinally(onClosing::countDown).subscribe();
}
context.onClose().whenFinally(serverConnectionClosed::countDown).subscribe();
connectionAccepted.countDown();
return completed();
}
});
HostAndPort proxyAddress = null;
if (viaProxy) {
// Dummy proxy helps to emulate old intermediate systems that do not support half-closed TCP connections
proxyTunnel = new ProxyTunnel();
proxyAddress = proxyTunnel.startProxy();
serverBuilder.sslConfig(new ServerSslConfigBuilder(DefaultTestCerts::loadServerPem, DefaultTestCerts::loadServerKey).build());
} else {
proxyTunnel = null;
}
serverContext = serverBuilder.listenBlockingStreamingAndAwait((ctx, request, response) -> {
serverReceivedRequest.countDown();
response.addHeader(CONTENT_LENGTH, valueOf(RESPONSE_CONTENT.length()));
serverSendResponse.await();
try (HttpPayloadWriter<String> writer = response.sendMetaData(RAW_STRING_SERIALIZER)) {
// Subscribe to the request payload body before response writer closes
BlockingIterator<Buffer> iterator = request.payloadBody().iterator();
// Consume request payload body asynchronously:
ctx.executionContext().executor().submit(() -> {
int receivedSize = 0;
while (iterator.hasNext()) {
Buffer chunk = iterator.next();
assert chunk != null;
receivedSize += chunk.readableBytes();
}
serverReceivedRequestPayload.add(receivedSize);
}).beforeOnError(cause -> {
LOGGER.error("failure while reading request", cause);
serverReceivedRequestPayload.add(-1);
}).toFuture();
serverSendResponsePayload.await();
writer.write(RESPONSE_CONTENT);
}
});
serverContext.onClose().whenFinally(serverContextClosed::countDown).subscribe();
client = (viaProxy ? forSingleAddress(serverHostAndPort(serverContext)).proxyAddress(proxyAddress).sslConfig(new ClientSslConfigBuilder(DefaultTestCerts::loadServerCAPem).peerHost(serverPemHostname()).build()) : forResolvedAddress(serverContext.listenAddress())).protocols(protocol.config).executor(CLIENT_CTX.executor()).ioExecutor(CLIENT_CTX.ioExecutor()).executionStrategy(defaultStrategy()).enableWireLogging("servicetalk-tests-wire-logger", TRACE, Boolean.TRUE::booleanValue).appendConnectionFactoryFilter(ConnectionFactoryFilter.withStrategy(cf -> initiateClosureFromClient ? new OnClosingConnectionFactoryFilter<>(cf, onClosing) : cf, ExecutionStrategy.offloadNone())).buildStreaming();
connection = client.reserveConnection(client.get("/")).toFuture().get();
connection.onClose().whenFinally(clientConnectionClosed::countDown).subscribe();
// wait until server accepts connection
connectionAccepted.await();
toClose = initiateClosureFromClient ? connection : serverContext;
}
Aggregations