use of io.servicetalk.http.api.HttpExecutionStrategy in project servicetalk by apple.
the class ExecutionStrategyInContextTest method testStreaming.
@ParameterizedTest(name = "customStrategy={0}")
@ValueSource(booleans = { false, true })
void testStreaming(boolean customStrategy) throws Exception {
StreamingHttpClient client = initClientAndServer(builder -> builder.listenStreaming((ctx, request, responseFactory) -> {
serviceStrategyRef.set(ctx.executionContext().executionStrategy());
return succeeded(responseFactory.ok());
}), customStrategy).buildStreaming();
clientAsCloseable = client;
if (!customStrategy) {
assert expectedClientStrategy == null;
expectedClientStrategy = defaultStrategy();
assert expectedServerStrategy == null;
expectedServerStrategy = defaultStrategy();
}
HttpExecutionStrategy clientStrat = client.executionContext().executionStrategy();
assertThat("Unexpected client strategy.", clientStrat, equalStrategies(expectedClientStrategy));
client.request(client.get("/")).toFuture().get();
assertThat("Unexpected service strategy", serviceStrategyRef.get(), equalStrategies(expectedServerStrategy));
ReservedStreamingHttpConnection conn = client.reserveConnection(client.get("/")).toFuture().get();
assertThat("Unexpected connection strategy (from execution context).", conn.executionContext().executionStrategy(), equalStrategies(expectedClientStrategy));
assertThat("Unexpected connection strategy (from execution context).", conn.connectionContext().executionContext().executionStrategy(), equalStrategies(expectedClientStrategy));
}
use of io.servicetalk.http.api.HttpExecutionStrategy in project servicetalk by apple.
the class NettyHttpServerConnectionTest method updateFlushStrategy.
@ParameterizedTest(name = "server={0} client={1}")
@MethodSource("executionStrategies")
void updateFlushStrategy(HttpExecutionStrategy serverExecutionStrategy, HttpExecutionStrategy clientExecutionStrategy) throws Exception {
customStrategy = new MockFlushStrategy();
AtomicReference<Cancellable> customCancellableRef = new AtomicReference<>();
AtomicBoolean handledFirstRequest = new AtomicBoolean();
serverContext = HttpServers.forAddress(localAddress(0)).ioExecutor(contextRule.ioExecutor()).appendConnectionAcceptorFilter(original -> original.append(ctx -> {
customCancellableRef.set(((NettyConnectionContext) ctx).updateFlushStrategy((__, ___) -> customStrategy));
return completed();
})).executionStrategy(serverExecutionStrategy).listenStreaming((ctx, request, responseFactory) -> {
if (handledFirstRequest.compareAndSet(false, true)) {
customStrategy.afterFirstWrite(FlushStrategy.FlushSender::flush);
return succeeded(responseFactory.ok().payloadBody(responsePublisher));
}
return succeeded(responseFactory.ok().payloadBody(responsePublisher2));
}).toFuture().get();
client = HttpClients.forSingleAddress(serverHostAndPort(serverContext)).executionStrategy(clientExecutionStrategy).buildStreaming();
StreamingHttpResponse response = client.request(client.newRequest(GET, "/1")).toFuture().get();
FlushStrategy.FlushSender customFlushSender = customStrategy.verifyApplied();
Cancellable customCancellable = customCancellableRef.get();
assertNotNull(customCancellable);
// Verify that the custom strategy is applied and used for flushing.
customStrategy.verifyWriteStarted();
customStrategy.verifyItemWritten(1);
customStrategy.verifyNoMoreInteractions();
String payloadBodyString = "foo";
TestSubscription testSubscription1 = new TestSubscription();
responsePublisher.onSubscribe(testSubscription1);
testSubscription1.awaitRequestN(1);
responsePublisher.onNext(DEFAULT_ALLOCATOR.fromAscii(payloadBodyString));
responsePublisher.onComplete();
customFlushSender.flush();
Buffer responsePayload = response.payloadBody().collect(DEFAULT_ALLOCATOR::newBuffer, (results, current) -> {
results.writeBytes(current);
return results;
}).toFuture().get();
assertEquals(payloadBodyString, responsePayload.toString(US_ASCII));
customStrategy.verifyItemWritten(2);
customStrategy.verifyWriteTerminated();
// Restore the default flush strategy, which should flush on each
customCancellable.cancel();
StreamingHttpResponse response2 = client.request(client.newRequest(GET, "/2")).toFuture().get();
TestSubscription testSubscription2 = new TestSubscription();
responsePublisher2.onSubscribe(testSubscription2);
responsePublisher2.onNext(DEFAULT_ALLOCATOR.fromAscii(payloadBodyString));
responsePublisher2.onComplete();
responsePayload = response2.payloadBody().collect(DEFAULT_ALLOCATOR::newBuffer, (results, current) -> {
results.writeBytes(current);
return results;
}).toFuture().get();
assertEquals(payloadBodyString, responsePayload.toString(US_ASCII));
}
use of io.servicetalk.http.api.HttpExecutionStrategy in project servicetalk by apple.
the class InOrderRouter method handle.
@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext ctx, final StreamingHttpRequest request, final StreamingHttpResponseFactory factory) {
for (final Route pair : routes) {
if (pair.predicate().test(ctx, request)) {
StreamingHttpService service = pair.service();
final HttpExecutionStrategy strategy = pair.routeStrategy();
HttpExecutionContext useContext = ctx.executionContext();
if (null != strategy && useContext.executionStrategy().missing(strategy).hasOffloads()) {
// Additional offloading needed
service = StreamingHttpServiceToOffloadedStreamingHttpService.offloadService(strategy, useContext.executor(), IoThreadFactory.IoThread::currentThreadIsIoThread, service);
}
return service.handle(ctx, request, factory);
}
}
return fallbackService.handle(ctx, request, factory);
}
use of io.servicetalk.http.api.HttpExecutionStrategy in project servicetalk by apple.
the class DefaultMultiAddressUrlHttpClientBuilder method buildStreaming.
@Override
public StreamingHttpClient buildStreaming() {
final CompositeCloseable closeables = newCompositeCloseable();
try {
final HttpClientBuildContext<HostAndPort, InetSocketAddress> buildContext = builderTemplate.copyBuildCtx();
final ClientFactory clientFactory = new ClientFactory(buildContext.builder, singleAddressInitializer);
HttpExecutionContext executionContext = buildContext.builder.executionContextBuilder.build();
final CachingKeyFactory keyFactory = closeables.prepend(new CachingKeyFactory());
FilterableStreamingHttpClient urlClient = closeables.prepend(new StreamingUrlHttpClient(executionContext, clientFactory, keyFactory, defaultReqRespFactory(buildContext.httpConfig().asReadOnly(), executionContext.bufferAllocator())));
// Need to wrap the top level client (group) in order for non-relative redirects to work
urlClient = redirectConfig == null ? urlClient : new RedirectingHttpRequesterFilter(redirectConfig).create(urlClient);
HttpExecutionStrategy computedStrategy = buildContext.builder.computeChainStrategy(executionContext.executionStrategy());
LOGGER.debug("Client created with base strategy {} → computed strategy {}", executionContext.executionStrategy(), computedStrategy);
return new FilterableClientToClient(urlClient, computedStrategy);
} catch (final Throwable t) {
closeables.closeAsync().subscribe();
throw t;
}
}
use of io.servicetalk.http.api.HttpExecutionStrategy in project servicetalk by apple.
the class AbstractStreamingHttpConnection method request.
@Override
public Single<StreamingHttpResponse> request(final StreamingHttpRequest request) {
return defer(() -> {
Publisher<Object> flatRequest;
// See https://tools.ietf.org/html/rfc7230#section-3.3.3
if (canAddRequestContentLength(request)) {
flatRequest = setRequestContentLength(connectionContext().protocol(), request);
} else {
if (emptyMessageBody(request, request.messageBody())) {
flatRequest = flatEmptyMessage(connectionContext().protocol(), request, request.messageBody());
} else {
// Defer subscribe to the messageBody until transport requests it to allow clients retry failed
// requests with non-replayable messageBody
flatRequest = Single.<Object>succeeded(request).concat(request.messageBody(), true);
if (shouldAppendTrailers(connectionContext().protocol(), request)) {
flatRequest = flatRequest.scanWith(HeaderUtils::appendTrailersMapper);
}
}
addRequestTransferEncodingIfNecessary(request);
}
final HttpExecutionStrategy strategy = requestExecutionStrategy(request, executionContext().executionStrategy());
if (strategy.isSendOffloaded()) {
flatRequest = flatRequest.subscribeOn(connectionContext.executionContext().executor(), IoThreadFactory.IoThread::currentThreadIsIoThread);
}
Single<StreamingHttpResponse> resp = invokeClient(flatRequest, determineFlushStrategyForApi(request));
if (strategy.isMetadataReceiveOffloaded()) {
resp = resp.publishOn(connectionContext.executionContext().executor(), IoThreadFactory.IoThread::currentThreadIsIoThread);
}
if (strategy.isDataReceiveOffloaded()) {
resp = resp.map(response -> response.transformMessageBody(payload -> payload.publishOn(connectionContext.executionContext().executor(), IoThreadFactory.IoThread::currentThreadIsIoThread)));
}
return resp.shareContextOnSubscribe();
});
}
Aggregations