use of io.servicetalk.transport.netty.internal.NettyConnectionContext 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.netty.internal.NettyConnectionContext in project servicetalk by apple.
the class H2PriorKnowledgeFeatureParityTest method bindHttpEchoServer.
private InetSocketAddress bindHttpEchoServer(@Nullable StreamingHttpServiceFilterFactory filterFactory, @Nullable CountDownLatch connectionOnClosingLatch) throws Exception {
HttpServerBuilder serverBuilder = HttpServers.forAddress(localAddress(0)).protocols(h2PriorKnowledge ? h2Default() : h1Default());
if (filterFactory != null) {
serverBuilder.appendServiceFilter(filterFactory);
}
if (connectionOnClosingLatch != null) {
serverBuilder.appendConnectionAcceptorFilter(original -> new DelegatingConnectionAcceptor(original) {
@Override
public Completable accept(final ConnectionContext context) {
((NettyConnectionContext) context).onClosing().whenFinally(connectionOnClosingLatch::countDown).subscribe();
return completed();
}
});
}
h1ServerContext = serverBuilder.listenStreaming((ctx, request, responseFactory) -> {
StreamingHttpResponse resp;
if (request.headers().contains(EXPECT, CONTINUE)) {
if (request.headers().contains(EXPECT_FAIL_HEADER)) {
return succeeded(responseFactory.expectationFailed());
} else {
resp = responseFactory.continueResponse();
}
} else {
resp = responseFactory.ok();
}
resp = resp.transformMessageBody(pub -> pub.ignoreElements().merge(request.messageBody())).transform(new StatelessTrailersTransformer<>());
CharSequence contentType = request.headers().get(CONTENT_TYPE);
if (contentType != null) {
resp.setHeader(CONTENT_TYPE, contentType);
}
CharSequence contentLength = request.headers().get(CONTENT_LENGTH);
if (contentLength != null) {
resp.setHeader(CONTENT_LENGTH, contentLength);
}
CharSequence transferEncoding = request.headers().get(TRANSFER_ENCODING);
if (transferEncoding != null) {
resp.setHeader(TRANSFER_ENCODING, transferEncoding);
}
resp.headers().set(COOKIE, request.headers().valuesIterator(COOKIE));
return succeeded(resp);
}).toFuture().get();
return (InetSocketAddress) h1ServerContext.listenAddress();
}
use of io.servicetalk.transport.netty.internal.NettyConnectionContext 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.transport.netty.internal.NettyConnectionContext in project servicetalk by apple.
the class DeferredServerChannelBinder method bind.
static Single<HttpServerContext> bind(final HttpExecutionContext executionContext, final ReadOnlyHttpServerConfig config, final SocketAddress listenAddress, @Nullable final InfluencerConnectionAcceptor connectionAcceptor, final StreamingHttpService service, final boolean drainRequestPayloadBody, final boolean sniOnly) {
final ReadOnlyTcpServerConfig tcpConfig = config.tcpConfig();
assert tcpConfig.sslContext() != null;
final BiFunction<Channel, ConnectionObserver, Single<NettyConnectionContext>> channelInit = sniOnly ? (channel, connectionObserver) -> sniInitChannel(listenAddress, channel, config, executionContext, service, drainRequestPayloadBody, connectionObserver) : (channel, connectionObserver) -> alpnInitChannel(listenAddress, channel, config, executionContext, service, drainRequestPayloadBody, connectionObserver);
// In case ALPN negotiates h2, h2 connection MUST enable auto read for its Channel.
return TcpServerBinder.bind(listenAddress, tcpConfig, false, executionContext, connectionAcceptor, channelInit, serverConnection -> {
// Start processing requests on http/1.1 connection:
if (serverConnection instanceof NettyHttpServerConnection) {
((NettyHttpServerConnection) serverConnection).process(true);
}
// Nothing to do otherwise as h2 uses auto read on the parent channel
}).map(delegate -> {
LOGGER.debug("Started HTTP server with ALPN for address {}", delegate.listenAddress());
// The ServerContext returned by TcpServerBinder takes care of closing the connectionAcceptor.
return new NettyHttpServer.NettyHttpServerContext(delegate, service, executionContext);
});
}
use of io.servicetalk.transport.netty.internal.NettyConnectionContext in project servicetalk by apple.
the class FlushStrategyOverrideTest method overrideFlush.
@Test
void overrideFlush() throws Throwable {
NettyConnectionContext nctx = (NettyConnectionContext) conn.connectionContext();
MockFlushStrategy clientStrategy = new MockFlushStrategy();
Cancellable c = nctx.updateFlushStrategy((old, isOriginal) -> isOriginal ? clientStrategy : old);
CountDownLatch reqWritten = new CountDownLatch(1);
StreamingHttpRequest req = client.get("/flush").payloadBody(from(1, 2, 3).map(count -> ctx.bufferAllocator().fromAscii("" + count)).afterFinally(reqWritten::countDown));
Future<? extends Collection<Object>> clientResp = conn.request(req).flatMapPublisher(StreamingHttpResponse::messageBody).toFuture();
// Wait for request to be written.
reqWritten.await();
FlushSender clientFlush = clientStrategy.verifyApplied();
clientStrategy.verifyWriteStarted();
clientStrategy.verifyItemWritten(5);
clientStrategy.verifyWriteTerminated();
clientFlush.flush();
MockFlushStrategy serverStrategy = service.getLastUsedStrategy();
FlushSender serverFlush = serverStrategy.verifyApplied();
serverStrategy.verifyWriteStarted();
serverStrategy.verifyItemWritten(5);
serverStrategy.verifyWriteTerminated();
serverFlush.flush();
Collection<Object> chunks = clientResp.get();
assertThat("Unexpected items received.", chunks, hasSize(3));
// revert to flush on each.
c.cancel();
// No more custom strategies.
Collection<Object> secondReqChunks = conn.request(conn.get("")).flatMapPublisher(StreamingHttpResponse::messageBody).toFuture().get();
clientStrategy.verifyNoMoreInteractions();
service.getLastUsedStrategy();
serverStrategy.verifyNoMoreInteractions();
assertThat("Unexpected payload for regular flush.", secondReqChunks, empty());
}
Aggregations