use of io.servicetalk.concurrent.api.Single in project servicetalk by apple.
the class H2PriorKnowledgeFeatureParityTest method clientConnectionFilterAsyncContext.
@ParameterizedTest(name = "{displayName} [{index}] client={0}, h2PriorKnowledge={1}")
@MethodSource("clientExecutors")
void clientConnectionFilterAsyncContext(HttpTestExecutionStrategy strategy, boolean h2PriorKnowledge) throws Exception {
setUp(strategy, h2PriorKnowledge);
InetSocketAddress serverAddress = bindHttpEchoServer();
final Queue<Throwable> errorQueue = new ConcurrentLinkedQueue<>();
try (BlockingHttpClient client = forSingleAddress(HostAndPort.of(serverAddress)).protocols(h2PriorKnowledge ? h2Default() : h1Default()).executionStrategy(clientExecutionStrategy).appendConnectionFilter(connection -> new StreamingHttpConnectionFilter(connection) {
@Override
public Single<StreamingHttpResponse> request(final StreamingHttpRequest request) {
return asyncContextTestRequest(errorQueue, delegate(), request);
}
}).buildBlocking()) {
final String responseBody = "foo";
HttpResponse response = client.request(client.post("/0").payloadBody(responseBody, textSerializerUtf8()));
assertEquals(responseBody, response.payloadBody(textSerializerUtf8()));
assertNoAsyncErrors(errorQueue);
}
}
use of io.servicetalk.concurrent.api.Single in project servicetalk by apple.
the class H2PriorKnowledgeFeatureParityTest method serverSendsInvalidContentLength.
private void serverSendsInvalidContentLength(boolean addTrailers, BiConsumer<HttpHeaders, Integer> headersModifier) throws Exception {
assumeFalse(!h2PriorKnowledge && addTrailers, "HTTP/1.1 does not support Content-Length with trailers");
InetSocketAddress serverAddress = bindHttpEchoServer(service -> new StreamingHttpServiceFilter(service) {
@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext ctx, final StreamingHttpRequest request, final StreamingHttpResponseFactory responseFactory) {
return delegate().handle(ctx, request, responseFactory).flatMap(resp -> resp.transformMessageBody(// honored during "streaming -> aggregated -> streaming" conversion.
pub -> addTrailers ? pub : pub.filter(i -> i instanceof Buffer)).toResponse().map(aggResp -> {
aggResp.headers().remove(TRANSFER_ENCODING);
headersModifier.accept(aggResp.headers(), aggResp.payloadBody().readableBytes());
return aggResp.toStreamingResponse();
}));
}
}, null);
try (BlockingHttpClient client = forSingleAddress(HostAndPort.of(serverAddress)).protocols(h2PriorKnowledge ? h2Default() : h1Default()).executionStrategy(clientExecutionStrategy).buildBlocking()) {
HttpRequest request = client.get("/").payloadBody("a", textSerializerUtf8());
if (addTrailers) {
request.trailers().set("mytrailer", "myvalue");
}
if (h2PriorKnowledge) {
assertThrows(Http2Exception.class, () -> client.request(request));
} else {
try (ReservedBlockingHttpConnection reservedConn = client.reserveConnection(request)) {
assertThrows(IOException.class, () -> {
// Either the current request or the next one should fail
reservedConn.request(request);
reservedConn.request(client.get("/"));
});
}
}
}
}
use of io.servicetalk.concurrent.api.Single in project servicetalk by apple.
the class H2PriorKnowledgeFeatureParityTest method serverGracefulClose.
@ParameterizedTest(name = "{displayName} [{index}] client={0}, h2PriorKnowledge={1}")
@MethodSource("clientExecutors")
void serverGracefulClose(HttpTestExecutionStrategy strategy, boolean h2PriorKnowledge) throws Exception {
setUp(strategy, h2PriorKnowledge);
CountDownLatch serverReceivedRequestLatch = new CountDownLatch(1);
CountDownLatch connectionOnClosingLatch = new CountDownLatch(1);
InetSocketAddress serverAddress = bindHttpEchoServer(service -> new StreamingHttpServiceFilter(service) {
@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext ctx, final StreamingHttpRequest request, final StreamingHttpResponseFactory responseFactory) {
serverReceivedRequestLatch.countDown();
return delegate().handle(ctx, request, responseFactory);
}
}, connectionOnClosingLatch);
StreamingHttpClient client = forSingleAddress(HostAndPort.of(serverAddress)).protocols(h2PriorKnowledge ? h2Default() : h1Default()).executionStrategy(clientExecutionStrategy).buildStreaming();
Processor<Buffer, Buffer> requestBody = newProcessor();
// We want to make a request, and intentionally not complete it. While the request is in process we invoke
// closeAsyncGracefully and verify that we wait until the request has completed before the underlying
// transport is closed.
StreamingHttpRequest request = client.post("/").payloadBody(fromSource(requestBody));
StreamingHttpResponse response = client.request(request).toFuture().get();
// Wait for the server the process the request.
serverReceivedRequestLatch.await();
// Initiate graceful close on the server
assertNotNull(h1ServerContext);
CountDownLatch onServerCloseLatch = new CountDownLatch(1);
h1ServerContext.onClose().subscribe(onServerCloseLatch::countDown);
h1ServerContext.closeAsyncGracefully().subscribe();
assertTrue(connectionOnClosingLatch.await(300, MILLISECONDS));
try (BlockingHttpClient client2 = forSingleAddress(HostAndPort.of(serverAddress)).protocols(h2PriorKnowledge ? h2Default() : h1Default()).executionStrategy(clientExecutionStrategy).buildBlocking()) {
assertThrows(Throwable.class, () -> client2.request(client2.get("/")), "server has initiated graceful close, subsequent connections/requests are expected to fail.");
}
// We expect this to timeout, because we have not completed the outstanding request.
assertFalse(onServerCloseLatch.await(30, MILLISECONDS));
requestBody.onComplete();
HttpResponse fullResponse = response.toResponse().toFuture().get();
assertEquals(0, fullResponse.payloadBody().readableBytes());
onServerCloseLatch.await();
}
use of io.servicetalk.concurrent.api.Single in project servicetalk by apple.
the class RequestResponseContextTest method setUp.
void setUp(Api api) {
this.api = api;
connectionFilterFactory(connection -> new StreamingHttpConnectionFilter(connection) {
@Override
public Single<StreamingHttpResponse> request(StreamingHttpRequest request) {
final List<String> requestCtxValue = request.context().get(CLIENT_REQUEST_KEY);
try {
assertThat(requestCtxValue, is(notNullValue()));
assertThat(requestCtxValue, hasSize(1));
request.addHeader(CONTEXT_HEADER, requestCtxValue.get(0));
} catch (Throwable t) {
asyncError.add(t);
}
return delegate().request(request).map(response -> {
response.context().put(CLIENT_RESPONSE_KEY, valueOf(response.headers().get(CONTEXT_HEADER)));
return response;
});
}
});
serviceFilterFactory(service -> new StreamingHttpServiceFilter(service) {
@Override
public Single<StreamingHttpResponse> handle(HttpServiceContext ctx, StreamingHttpRequest request, StreamingHttpResponseFactory responseFactory) {
CharSequence requestCtxValue = request.headers().get(CONTEXT_HEADER);
try {
assertThat(requestCtxValue, is(notNullValue()));
request.context().put(SERVER_REQUEST_KEY, singletonList(requestCtxValue.toString()));
} catch (Throwable t) {
asyncError.add(t);
}
return delegate().handle(ctx, request, responseFactory).map(response -> {
response.headers().add(CONTEXT_HEADER, valueOf(response.context().get(SERVER_RESPONSE_KEY)));
return response;
});
}
});
setUp(CACHED, CACHED_SERVER);
}
use of io.servicetalk.concurrent.api.Single in project servicetalk by apple.
the class HttpLifecycleObserverTest method testClientCancelsRequestBeforeResponse.
@ParameterizedTest(name = "{displayName} [{index}] protocol={0}")
@EnumSource(HttpProtocol.class)
void testClientCancelsRequestBeforeResponse(HttpProtocol protocol) throws Exception {
CountDownLatch requestReceived = new CountDownLatch(1);
serviceFilterFactory(service -> new StreamingHttpServiceFilter(service) {
@Override
public Single<StreamingHttpResponse> handle(HttpServiceContext ctx, StreamingHttpRequest request, StreamingHttpResponseFactory responseFactory) {
return delegate().handle(ctx, request.transformMessageBody(mb -> mb.afterOnNext(__ -> requestReceived.countDown())), responseFactory);
}
});
setUp(protocol);
StreamingHttpClient client = streamingHttpClient();
Future<StreamingHttpResponse> responseFuture = client.request(client.post(SVC_NEVER).payloadBody(Publisher.from(CONTENT.duplicate()).concat(Publisher.never()))).toFuture();
requestReceived.await();
responseFuture.cancel(true);
bothTerminate.await();
verify(serverExchangeObserver, await()).onExchangeFinally();
clientInOrder.verify(clientLifecycleObserver).onNewExchange();
clientInOrder.verify(clientExchangeObserver).onRequest(any(StreamingHttpRequest.class));
clientInOrder.verify(clientExchangeObserver).onConnectionSelected(any(ConnectionInfo.class));
clientInOrder.verify(clientExchangeObserver).onResponseCancel();
clientRequestInOrder.verify(clientRequestObserver).onRequestDataRequested(anyLong());
clientRequestInOrder.verify(clientRequestObserver).onRequestData(any(Buffer.class));
clientRequestInOrder.verify(clientRequestObserver).onRequestCancel();
clientInOrder.verify(clientExchangeObserver).onExchangeFinally();
verifyNoMoreInteractions(clientLifecycleObserver, clientExchangeObserver, clientRequestObserver);
verifyNoInteractions(clientResponseObserver);
serverInOrder.verify(serverLifecycleObserver).onNewExchange();
serverInOrder.verify(serverExchangeObserver).onConnectionSelected(any(ConnectionInfo.class));
serverInOrder.verify(serverExchangeObserver).onRequest(any(StreamingHttpRequest.class));
serverRequestInOrder.verify(serverRequestObserver, atLeastOnce()).onRequestDataRequested(anyLong());
serverRequestInOrder.verify(serverRequestObserver).onRequestData(any(Buffer.class));
serverRequestInOrder.verify(serverRequestObserver).onRequestError(any(IOException.class));
// because of offloading, cancel from the IO-thread may race with an error propagated through request publisher:
verify(serverExchangeObserver, atMostOnce()).onResponseCancel();
verify(serverExchangeObserver, atMostOnce()).onResponse(any(StreamingHttpResponse.class));
verify(serverResponseObserver, atMostOnce()).onResponseDataRequested(anyLong());
verify(serverResponseObserver, atMostOnce()).onResponseComplete();
serverInOrder.verify(serverExchangeObserver).onExchangeFinally();
verifyNoMoreInteractions(serverLifecycleObserver, serverExchangeObserver, serverRequestObserver, serverResponseObserver);
}
Aggregations