use of io.servicetalk.transport.api.ExecutionStrategy 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;
}
use of io.servicetalk.transport.api.ExecutionStrategy in project servicetalk by apple.
the class DefaultMultiAddressUrlHttpClientBuilderTest method internalClientsUseDifferentExecutionContextWhenConfigured.
@Test
void internalClientsUseDifferentExecutionContextWhenConfigured() throws Exception {
// Assert prerequisites first.
// Use different strategies, as ExecutionContextExtension shares the same strategy.
HttpExecutionStrategy internalExecutionStrategy = HttpExecutionStrategies.customStrategyBuilder().offloadNone().build();
HttpExecutionStrategy externalExecutionStrategy = HttpExecutionStrategies.customStrategyBuilder().offloadAll().build();
assertThat(internalExecutionStrategy, not(equalTo(externalExecutionStrategy)));
BufferAllocator internalBufferAllocator = BufferAllocators.PREFER_DIRECT_ALLOCATOR;
BufferAllocator externalBufferAllocator = BufferAllocators.PREFER_HEAP_ALLOCATOR;
assertThat(internalBufferAllocator, not(equalTo(externalBufferAllocator)));
assertThat(CTX.executor(), not(equalTo(INTERNAL_CLIENT_CTX.executor())));
assertThat(CTX.ioExecutor(), not(equalTo(INTERNAL_CLIENT_CTX.ioExecutor())));
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)).executionStrategy(offloadNever()).listenStreamingAndAwait((ctx, request, responseFactory) -> succeeded(responseFactory.ok()))) {
AtomicReference<BufferAllocator> actualInternalBufferAllocator = new AtomicReference<>();
AtomicReference<IoExecutor> actualInternalIoExecutor = new AtomicReference<>();
AtomicReference<Executor> actualInternalExecutor = new AtomicReference<>();
AtomicReference<ExecutionStrategy> actualInternalExecutionStrategy = new AtomicReference<>();
final StreamingHttpClient streamingHttpClient = HttpClients.forMultiAddressUrl().initializer((scheme, address, builder) -> builder.executionStrategy(internalExecutionStrategy).executor(INTERNAL_CLIENT_CTX.executor()).ioExecutor(INTERNAL_CLIENT_CTX.ioExecutor()).bufferAllocator(internalBufferAllocator).executionStrategy(internalExecutionStrategy).appendClientFilter(client -> {
HttpExecutionContext internalContext = client.executionContext();
actualInternalBufferAllocator.set(internalContext.bufferAllocator());
actualInternalExecutor.set(internalContext.executor());
actualInternalIoExecutor.set(internalContext.ioExecutor());
actualInternalExecutionStrategy.set(internalContext.executionStrategy());
return new StreamingHttpClientFilter(client) {
};
})).executor(CTX.executor()).ioExecutor(CTX.ioExecutor()).executionStrategy(externalExecutionStrategy).bufferAllocator(externalBufferAllocator).buildStreaming();
assertNotNull(streamingHttpClient);
// Check external client
final HttpExecutionContext executionContext = streamingHttpClient.executionContext();
assertThat(executionContext.executor(), equalTo(CTX.executor()));
assertThat(executionContext.executionStrategy(), equalTo(externalExecutionStrategy));
assertThat(executionContext.ioExecutor(), equalTo(CTX.ioExecutor()));
assertThat(executionContext.bufferAllocator(), equalTo(CTX.bufferAllocator()));
// Make a request to trigger the filter execution that extracts the execution context.
final HostAndPort address = HostAndPort.of((InetSocketAddress) serverContext.listenAddress());
streamingHttpClient.reserveConnection(streamingHttpClient.get("http://" + address)).toFuture().get();
// Check internal client
assertThat(actualInternalBufferAllocator.get(), equalTo(internalBufferAllocator));
assertThat(actualInternalExecutor.get(), equalTo(INTERNAL_CLIENT_CTX.executor()));
assertThat(actualInternalIoExecutor.get(), equalTo(INTERNAL_CLIENT_CTX.ioExecutor()));
assertThat(actualInternalExecutionStrategy.get(), equalTo(internalExecutionStrategy));
streamingHttpClient.closeAsync().toFuture().get();
}
}
use of io.servicetalk.transport.api.ExecutionStrategy in project servicetalk by apple.
the class DefaultSingleAddressHttpClientBuilder method buildStreaming.
private static <U, R> StreamingHttpClient buildStreaming(final HttpClientBuildContext<U, R> ctx) {
final ReadOnlyHttpClientConfig roConfig = ctx.httpConfig().asReadOnly();
final HttpExecutionContext executionContext = ctx.builder.executionContextBuilder.build();
if (roConfig.h2Config() != null && roConfig.hasProxy()) {
throw new IllegalStateException("Proxying is not yet supported with HTTP/2");
}
// Track resources that potentially need to be closed when an exception is thrown during buildStreaming
final CompositeCloseable closeOnException = newCompositeCloseable();
try {
final Publisher<? extends Collection<? extends ServiceDiscovererEvent<R>>> sdEvents = ctx.serviceDiscoverer(executionContext).discover(ctx.address());
ConnectionFactoryFilter<R, FilterableStreamingHttpConnection> connectionFactoryFilter = ctx.builder.connectionFactoryFilter;
ExecutionStrategy connectionFactoryStrategy = ctx.builder.strategyComputation.buildForConnectionFactory();
final SslContext sslContext = roConfig.tcpConfig().sslContext();
if (roConfig.hasProxy() && sslContext != null) {
assert roConfig.connectAddress() != null;
ConnectionFactoryFilter<R, FilterableStreamingHttpConnection> proxy = new ProxyConnectConnectionFactoryFilter<>(roConfig.connectAddress());
connectionFactoryFilter = proxy.append(connectionFactoryFilter);
connectionFactoryStrategy = connectionFactoryStrategy.merge(proxy.requiredOffloads());
}
final HttpExecutionStrategy executionStrategy = executionContext.executionStrategy();
// closed by the LoadBalancer
final ConnectionFactory<R, LoadBalancedStreamingHttpConnection> connectionFactory;
final StreamingHttpRequestResponseFactory reqRespFactory = defaultReqRespFactory(roConfig, executionContext.bufferAllocator());
if (roConfig.isH2PriorKnowledge()) {
H2ProtocolConfig h2Config = roConfig.h2Config();
assert h2Config != null;
connectionFactory = new H2LBHttpConnectionFactory<>(roConfig, executionContext, ctx.builder.connectionFilterFactory, reqRespFactory, connectionFactoryStrategy, connectionFactoryFilter, ctx.builder.loadBalancerFactory::toLoadBalancedConnection);
} else if (roConfig.tcpConfig().preferredAlpnProtocol() != null) {
H1ProtocolConfig h1Config = roConfig.h1Config();
H2ProtocolConfig h2Config = roConfig.h2Config();
connectionFactory = new AlpnLBHttpConnectionFactory<>(roConfig, executionContext, ctx.builder.connectionFilterFactory, new AlpnReqRespFactoryFunc(executionContext.bufferAllocator(), h1Config == null ? null : h1Config.headersFactory(), h2Config == null ? null : h2Config.headersFactory()), connectionFactoryStrategy, connectionFactoryFilter, ctx.builder.loadBalancerFactory::toLoadBalancedConnection);
} else {
H1ProtocolConfig h1Config = roConfig.h1Config();
assert h1Config != null;
connectionFactory = new PipelinedLBHttpConnectionFactory<>(roConfig, executionContext, ctx.builder.connectionFilterFactory, reqRespFactory, connectionFactoryStrategy, connectionFactoryFilter, ctx.builder.loadBalancerFactory::toLoadBalancedConnection);
}
final LoadBalancer<LoadBalancedStreamingHttpConnection> lb = closeOnException.prepend(ctx.builder.loadBalancerFactory.newLoadBalancer(targetAddress(ctx), sdEvents, connectionFactory));
ContextAwareStreamingHttpClientFilterFactory currClientFilterFactory = ctx.builder.clientFilterFactory;
if (roConfig.hasProxy() && sslContext == null) {
// If we're talking to a proxy over http (not https), rewrite the request-target to absolute-form, as
// specified by the RFC: https://tools.ietf.org/html/rfc7230#section-5.3.2
currClientFilterFactory = appendFilter(currClientFilterFactory, ctx.builder.proxyAbsoluteAddressFilterFactory());
}
if (ctx.builder.addHostHeaderFallbackFilter) {
currClientFilterFactory = appendFilter(currClientFilterFactory, new HostHeaderHttpRequesterFilter(ctx.builder.hostToCharSequenceFunction.apply(ctx.builder.address)));
}
FilterableStreamingHttpClient lbClient = closeOnException.prepend(new LoadBalancedStreamingHttpClient(executionContext, lb, reqRespFactory));
if (ctx.builder.retryingHttpRequesterFilter == null) {
ctx.builder.retryingHttpRequesterFilter = DEFAULT_AUTO_RETRIES;
currClientFilterFactory = appendFilter(currClientFilterFactory, ctx.builder.retryingHttpRequesterFilter);
}
HttpExecutionStrategy computedStrategy = ctx.builder.strategyComputation.buildForClient(executionStrategy);
LOGGER.debug("Client for {} created with base strategy {} → computed strategy {}", targetAddress(ctx), executionStrategy, computedStrategy);
return new FilterableClientToClient(currClientFilterFactory != null ? currClientFilterFactory.create(lbClient, lb.eventStream(), ctx.sdStatus) : lbClient, computedStrategy);
} catch (final Throwable t) {
closeOnException.closeAsync().subscribe();
throw t;
}
}
use of io.servicetalk.transport.api.ExecutionStrategy in project servicetalk by apple.
the class ClientStrategyInfluencerChainBuilder method add.
void add(ConnectionFactoryFilter<?, FilterableStreamingHttpConnection> connectionFactoryFilter) {
ExecutionStrategy filterOffloads = connectionFactoryFilter.requiredOffloads();
if (offloadNever() == filterOffloads) {
LOGGER.warn("{}#requiredOffloads() returns offloadNever(), which is unexpected. " + "offloadNone() should be used instead. " + "Making automatic adjustment, consider updating the filter.", connectionFactoryFilter);
filterOffloads = offloadNone();
}
if (defaultStrategy() == filterOffloads) {
LOGGER.warn("{}#requiredOffloads() returns defaultStrategy(), which is unexpected. " + "offloadAll() (safe default) or more appropriate custom strategy should be used instead." + "Making automatic adjustment, consider updating the filter.", connectionFactoryFilter);
filterOffloads = offloadAll();
}
connFactoryChain = null != connFactoryChain ? connFactoryChain.merge(filterOffloads) : ConnectAndHttpExecutionStrategy.from(filterOffloads);
}
Aggregations