use of io.servicetalk.concurrent.api.Publisher in project servicetalk by apple.
the class AbstractHttpConnectionTest method requestShouldWriteFlatStreamToConnectionAndReadFlatStreamSplicedIntoResponseAndPayload.
@SuppressWarnings("unchecked")
@Test
void requestShouldWriteFlatStreamToConnectionAndReadFlatStreamSplicedIntoResponseAndPayload() throws Exception {
Buffer chunk1 = allocator.fromAscii("test");
Buffer chunk2 = allocator.fromAscii("payload");
Buffer chunk3 = allocator.fromAscii("payload");
HttpHeaders trailers = headersFactory.newEmptyTrailers();
HttpHeaders headers = headersFactory.newHeaders();
headers.add(TRANSFER_ENCODING, CHUNKED);
StreamingHttpRequest req = newTransportRequest(GET, "/foo", HTTP_1_1, headers, allocator, from(chunk1, chunk2, chunk3, trailers), false, headersFactory);
HttpResponseMetaData respMeta = newResponseMetaData(HTTP_1_1, OK, INSTANCE.newHeaders().add(CONTENT_TYPE, TEXT_PLAIN));
Publisher<Object> respFlat = from(respMeta, chunk1, chunk2, chunk3, trailers);
ArgumentCaptor<Publisher<Object>> reqFlatCaptor = ArgumentCaptor.forClass(Publisher.class);
when(reqResp.apply(reqFlatCaptor.capture())).thenReturn(respFlat);
Single<StreamingHttpResponse> responseSingle = http.request(req);
StreamingHttpResponse resp = awaitIndefinitelyNonNull(responseSingle);
assertThat(reqFlatCaptor.getValue().toFuture().get(), contains(req, chunk1, chunk2, chunk3, trailers));
assertThat(resp.status(), equalTo(OK));
assertThat(resp.version(), equalTo(HTTP_1_1));
assertThat(resp.headers().get(CONTENT_TYPE), equalTo(TEXT_PLAIN));
assertThat(resp.payloadBody().toFuture().get(), contains(chunk1, chunk2, chunk3));
}
use of io.servicetalk.concurrent.api.Publisher in project servicetalk by apple.
the class DefaultPartitionedHttpClientBuilder method buildStreaming.
@Override
public StreamingHttpClient buildStreaming() {
final HttpExecutionContext executionContext = executionContextBuilder.build();
BiIntFunction<Throwable, ? extends Completable> sdRetryStrategy = serviceDiscovererRetryStrategy;
if (sdRetryStrategy == null) {
sdRetryStrategy = retryWithConstantBackoffDeltaJitter(__ -> true, SD_RETRY_STRATEGY_INIT_DURATION, SD_RETRY_STRATEGY_JITTER, executionContext.executor());
}
final ServiceDiscoverer<U, R, PartitionedServiceDiscovererEvent<R>> psd = new DefaultSingleAddressHttpClientBuilder.RetryingServiceDiscoverer<>(serviceDiscoverer, sdRetryStrategy);
final PartitionedClientFactory<U, R, FilterableStreamingHttpClient> clientFactory = (pa, sd) -> {
// build new context, user may have changed anything on the builder from the filter
final SingleAddressHttpClientBuilder<U, R> builder = requireNonNull(builderFactory.get());
builder.serviceDiscoverer(sd);
setExecutionContext(builder, executionContext);
if (clientInitializer != null) {
clientInitializer.initialize(pa, builder);
}
return builder.buildStreaming();
};
final Publisher<PartitionedServiceDiscovererEvent<R>> psdEvents = psd.discover(address).flatMapConcatIterable(identity());
final HttpHeadersFactory headersFactory = this.headersFactory;
final DefaultPartitionedStreamingHttpClientFilter<U, R> partitionedClient = new DefaultPartitionedStreamingHttpClientFilter<>(psdEvents, serviceDiscoveryMaxQueueSize, clientFactory, partitionAttributesBuilderFactory, new DefaultStreamingHttpRequestResponseFactory(executionContext.bufferAllocator(), headersFactory != null ? headersFactory : DefaultHttpHeadersFactory.INSTANCE, HTTP_1_1), executionContext, partitionMapFactory);
LOGGER.debug("Partitioned client created with base strategy {}", executionContext.executionStrategy());
return new FilterableClientToClient(partitionedClient, executionContext.executionStrategy());
}
use of io.servicetalk.concurrent.api.Publisher in project servicetalk by apple.
the class HttpReporter method initReporter.
private PublisherSource.Processor<Span, Span> initReporter(final Builder builder, final HttpClient client) {
final PublisherSource.Processor<Span, Span> buffer;
SpanBytesEncoder spanEncoder = builder.codec.spanBytesEncoder();
final BufferAllocator allocator = client.executionContext().bufferAllocator();
final Publisher<Buffer> spans;
if (!builder.batchingEnabled) {
buffer = newPublisherProcessorDropHeadOnOverflow(builder.maxConcurrentReports);
spans = fromSource(buffer).map(span -> allocator.wrap(spanEncoder.encodeList(Collections.singletonList(span))));
} else {
// As we send maxConcurrentReports number of parallel requests, each with roughly batchSizeHint number of
// spans, we hold a maximum of that many Spans in-memory that we can send in parallel to the collector.
buffer = newPublisherProcessorDropHeadOnOverflow(builder.batchSizeHint * builder.maxConcurrentReports);
spans = fromSource(buffer).buffer(forCountOrTime(builder.batchSizeHint, builder.maxBatchDuration, () -> new ListAccumulator(builder.batchSizeHint), client.executionContext().executor())).filter(accumulate -> !accumulate.isEmpty()).map(bufferedSpans -> allocator.wrap(spanEncoder.encodeList(bufferedSpans)));
}
final CompletableSource.Processor spansTerminated = newCompletableProcessor();
toSource(spans.flatMapCompletable(encodedSpansReporter(client, builder.codec), builder.maxConcurrentReports)).subscribe(spansTerminated);
closeable.prepend(toAsyncCloseable(graceful -> {
closeInitiated = true;
try {
buffer.onComplete();
} catch (Throwable t) {
LOGGER.error("Failed to dispose request buffer. Ignoring.", t);
}
return graceful ? fromSource(spansTerminated) : completed();
}));
return buffer;
}
use of io.servicetalk.concurrent.api.Publisher in project servicetalk by apple.
the class H2PriorKnowledgeFeatureParityTest method clientRespectsSettingsFrame.
@ParameterizedTest(name = "{displayName} [{index}] client={0}, h2PriorKnowledge={1}")
@MethodSource("clientExecutors")
void clientRespectsSettingsFrame(HttpTestExecutionStrategy strategy, boolean h2PriorKnowledge) throws Exception {
setUp(strategy, h2PriorKnowledge);
assumeTrue(h2PriorKnowledge, "Only HTTP/2 supports SETTINGS frames");
int expectedMaxConcurrent = 1;
BlockingQueue<FilterableStreamingHttpConnection> connectionQueue = new LinkedBlockingQueue<>();
BlockingQueue<Publisher<? extends ConsumableEvent<Integer>>> maxConcurrentPubQueue = new LinkedBlockingQueue<>();
AtomicReference<Channel> serverParentChannelRef = new AtomicReference<>();
CountDownLatch serverChannelLatch = new CountDownLatch(1);
CountDownLatch serverSettingsAckLatch = new CountDownLatch(2);
serverAcceptorChannel = bindH2Server(serverEventLoopGroup, new ChannelInitializer<Channel>() {
@Override
protected void initChannel(final Channel ch) {
ch.pipeline().addLast(new EchoHttp2Handler());
}
}, parentPipeline -> parentPipeline.addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (serverParentChannelRef.compareAndSet(null, ctx.channel())) {
serverChannelLatch.countDown();
}
super.channelActive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof Http2SettingsAckFrame) {
serverSettingsAckLatch.countDown();
}
super.channelRead(ctx, msg);
}
}), identity());
InetSocketAddress serverAddress = (InetSocketAddress) serverAcceptorChannel.localAddress();
try (StreamingHttpClient client = forSingleAddress(HostAndPort.of(serverAddress)).protocols(h2PriorKnowledge ? h2Default() : h1Default()).executionStrategy(clientExecutionStrategy).appendConnectionFilter(conn -> new TestConnectionFilter(conn, connectionQueue, maxConcurrentPubQueue)).buildStreaming()) {
Processor<Buffer, Buffer> requestPayload = newProcessor();
client.request(client.post("/0").payloadBody(fromSource(requestPayload))).toFuture().get();
serverChannelLatch.await();
Channel serverParentChannel = serverParentChannelRef.get();
serverParentChannel.writeAndFlush(new DefaultHttp2SettingsFrame(new Http2Settings().maxConcurrentStreams(expectedMaxConcurrent))).sync();
Iterator<? extends ConsumableEvent<Integer>> maxItr = maxConcurrentPubQueue.take().toIterable().iterator();
// Verify that the initial maxConcurrency value is the default number
assertThat("No initial maxConcurrency value", maxItr.hasNext(), is(true));
ConsumableEvent<Integer> next = maxItr.next();
assertThat(next, is(notNullValue()));
assertThat("First event is not the default", next.event(), is(SMALLEST_MAX_CONCURRENT_STREAMS));
// We previously made a request, and intentionally didn't complete the request body. We want to verify
// that we have received the SETTINGS frame reducing the total number of streams to 1.
assertThat("No maxConcurrency value received", maxItr.hasNext(), is(true));
next = maxItr.next();
assertThat(next, is(notNullValue()));
assertThat("maxConcurrency did not change to the expected value", next.event(), is(expectedMaxConcurrent));
// Wait for a server to receive a settings ack
serverSettingsAckLatch.await();
// After this point we want to issue a new request and verify that client selects a new connection.
Processor<Buffer, Buffer> requestPayload2 = newProcessor();
client.request(client.post("/1").payloadBody(fromSource(requestPayload2))).toFuture().get();
// We expect 2 connections to be created.
assertNotSame(connectionQueue.take(), connectionQueue.take());
requestPayload.onComplete();
requestPayload2.onComplete();
}
}
use of io.servicetalk.concurrent.api.Publisher 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