use of io.servicetalk.transport.api.ServerContext in project servicetalk by apple.
the class TcpServerBinder method bind.
/**
* Create a {@link ServerContext} that represents a socket which is bound and listening on the
* {@code listenAddress}.
*
* @param listenAddress The address to bind to.
* @param config The {@link ReadOnlyTcpServerConfig} to use for the bind socket and newly accepted sockets.
* @param autoRead if {@code true} auto read will be enabled for new {@link Channel}s.
* @param executionContext The {@link ExecutionContext} to use for the bind socket.
* @param connectionAcceptor The {@link ConnectionAcceptor} used to filter newly accepted sockets.
* @param connectionFunction Used to create a new {@link NettyConnection} from a {@link Channel}.
* @param connectionConsumer Used to consume the result of {@code connectionFunction} after initialization and
* filtering is done. This can be used for protocol specific initialization and to start data flow.
* @param <CC> The type of {@link ConnectionContext} that is created for each accepted socket.
* @return a {@link Single} that completes with a {@link ServerContext} that represents a socket which is bound and
* listening on the {@code listenAddress}.
*/
public static <CC extends ConnectionContext> Single<ServerContext> bind(SocketAddress listenAddress, final ReadOnlyTcpServerConfig config, final boolean autoRead, final ExecutionContext<?> executionContext, @Nullable final InfluencerConnectionAcceptor connectionAcceptor, final BiFunction<Channel, ConnectionObserver, Single<CC>> connectionFunction, final Consumer<CC> connectionConsumer) {
requireNonNull(connectionFunction);
requireNonNull(connectionConsumer);
listenAddress = toNettyAddress(listenAddress);
EventLoopAwareNettyIoExecutor nettyIoExecutor = toEventLoopAwareNettyIoExecutor(executionContext.ioExecutor());
ServerBootstrap bs = new ServerBootstrap();
configure(config, autoRead, bs, nettyIoExecutor.eventLoopGroup(), listenAddress.getClass());
ChannelSet channelSet = new ChannelSet(executionContext.executionStrategy().isCloseOffloaded() ? executionContext.executor() : immediate());
bs.handler(new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) {
// Verify that we do not leak pooled memory in the "accept" pipeline
if (msg instanceof ReferenceCounted) {
try {
throw new IllegalArgumentException("Unexpected ReferenceCounted msg in 'accept' pipeline: " + msg);
} finally {
((ReferenceCounted) msg).release();
}
}
if (msg instanceof Channel) {
final Channel channel = (Channel) msg;
if (!channel.isActive()) {
channel.close();
LOGGER.debug("Channel ({}) is accepted, but was already inactive", msg);
return;
} else if (!channelSet.addIfAbsent(channel)) {
LOGGER.warn("Channel ({}) not added to ChannelSet", msg);
return;
}
}
ctx.fireChannelRead(msg);
}
});
bs.childHandler(new io.netty.channel.ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) {
Single<CC> connectionSingle = connectionFunction.apply(channel, config.transportObserver().onNewConnection(channel.localAddress(), channel.remoteAddress()));
if (connectionAcceptor != null) {
connectionSingle = connectionSingle.flatMap(conn -> defer(() -> connectionAcceptor.accept(conn).concat(succeeded(conn))).subscribeOn(connectionAcceptor.requiredOffloads().isConnectOffloaded() ? executionContext.executor() : immediate()));
}
connectionSingle.beforeOnError(cause -> {
// Getting the remote-address may involve volatile reads and potentially a syscall, so guard it.
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Failed to create a connection for remote address {}", channel.remoteAddress(), cause);
}
close(channel, cause);
}).subscribe(connectionConsumer);
}
});
ChannelFuture future = bs.bind(listenAddress);
return new SubscribableSingle<ServerContext>() {
@Override
protected void handleSubscribe(Subscriber<? super ServerContext> subscriber) {
subscriber.onSubscribe(() -> future.cancel(true));
future.addListener((ChannelFuture f) -> {
Channel channel = f.channel();
Throwable cause = f.cause();
if (cause == null) {
subscriber.onSuccess(NettyServerContext.wrap(channel, channelSet, connectionAcceptor, executionContext));
} else {
close(channel, f.cause());
subscriber.onError(f.cause());
}
});
}
};
}
use of io.servicetalk.transport.api.ServerContext in project servicetalk by apple.
the class HttpProvidersTest method testSingleAddressHttpClientBuilderProviderForResolvedAddress.
@Test
void testSingleAddressHttpClientBuilderProviderForResolvedAddress() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)).listenStreamingAndAwait(new TestServiceStreaming())) {
SocketAddress serverAddress = serverContext.listenAddress();
TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress);
try (BlockingHttpClient client = HttpClients.forResolvedAddress(serverAddress).buildBlocking()) {
assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1));
HttpResponse response = client.request(client.get(SVC_ECHO));
assertThat(response.status(), is(OK));
assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1));
}
}
}
use of io.servicetalk.transport.api.ServerContext in project servicetalk by apple.
the class HttpProvidersTest method testSingleAddressHttpClientBuilderProviderWithHostAndPort.
@Test
void testSingleAddressHttpClientBuilderProviderWithHostAndPort() throws Exception {
try (ServerContext serverContext = HttpServers.forAddress(localAddress(0)).listenStreamingAndAwait(new TestServiceStreaming())) {
HostAndPort serverAddress = serverHostAndPort(serverContext);
TestSingleAddressHttpClientBuilderProvider.MODIFY_FOR_ADDRESS.set(serverAddress);
try (BlockingHttpClient client = HttpClients.forSingleAddress(serverAddress).buildBlocking()) {
assertThat(TestSingleAddressHttpClientBuilderProvider.BUILD_COUNTER.get(), is(1));
HttpResponse response = client.request(client.get(SVC_ECHO));
assertThat(response.status(), is(OK));
assertThat(TestSingleAddressHttpClientBuilderProvider.CONNECTION_COUNTER.get(), is(1));
}
}
}
use of io.servicetalk.transport.api.ServerContext in project servicetalk by apple.
the class HttpRequestEncoderTest method protocolPayloadEndOutboundShouldNotTriggerOnFailedFlush.
@Test
void protocolPayloadEndOutboundShouldNotTriggerOnFailedFlush() throws Exception {
AtomicReference<CloseHandler> closeHandlerRef = new AtomicReference<>();
try (CompositeCloseable resources = newCompositeCloseable()) {
Processor serverCloseTrigger = newCompletableProcessor();
CountDownLatch serverChannelLatch = new CountDownLatch(1);
AtomicReference<Channel> serverChannelRef = new AtomicReference<>();
ReadOnlyTcpServerConfig sConfig = new TcpServerConfig().asReadOnly();
ServerContext serverContext = resources.prepend(TcpServerBinder.bind(localAddress(0), sConfig, false, SEC, null, (channel, observer) -> DefaultNettyConnection.initChannel(channel, SEC.bufferAllocator(), SEC.executor(), SEC.ioExecutor(), forPipelinedRequestResponse(false, channel.config()), defaultFlushStrategy(), 0L, null, new TcpServerChannelInitializer(sConfig, observer).andThen(channel2 -> {
serverChannelRef.compareAndSet(null, channel2);
serverChannelLatch.countDown();
}), defaultStrategy(), mock(Protocol.class), observer, false, __ -> false), connection -> {
}).toFuture().get());
ReadOnlyHttpClientConfig cConfig = new HttpClientConfig().asReadOnly();
assert cConfig.h1Config() != null;
NettyConnection<Object, Object> conn = resources.prepend(TcpConnector.connect(null, serverHostAndPort(serverContext), cConfig.tcpConfig(), false, CEC, (channel, connectionObserver) -> {
CloseHandler closeHandler = spy(forPipelinedRequestResponse(true, channel.config()));
closeHandlerRef.compareAndSet(null, closeHandler);
return DefaultNettyConnection.initChannel(channel, CEC.bufferAllocator(), CEC.executor(), CEC.ioExecutor(), closeHandler, defaultFlushStrategy(), 0L, cConfig.tcpConfig().sslConfig(), new TcpClientChannelInitializer(cConfig.tcpConfig(), connectionObserver).andThen(new HttpClientChannelInitializer(getByteBufAllocator(CEC.bufferAllocator()), cConfig.h1Config(), closeHandler)).andThen(channel2 -> channel2.pipeline().addLast(new ChannelInboundHandlerAdapter() {
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
// Propagate the user event in the pipeline before
// triggering the test condition.
ctx.fireUserEventTriggered(evt);
if (evt instanceof ChannelInputShutdownReadComplete) {
serverCloseTrigger.onComplete();
}
}
})), defaultStrategy(), HTTP_1_1, connectionObserver, true, __ -> false);
}, NoopTransportObserver.INSTANCE).toFuture().get());
// The server needs to wait to close the conneciton until after the client has established the connection.
serverChannelLatch.await();
Channel serverChannel = serverChannelRef.get();
assertNotNull(serverChannel);
assumeFalse(serverChannel instanceof NioSocketChannel, "Windows doesn't emit ChannelInputShutdownReadComplete. Investigation Required.");
((SocketChannel) serverChannel).config().setSoLinger(0);
// Close and send RST concurrently with client write
serverChannel.close();
StreamingHttpRequest request = reqRespFactory.post("/closeme");
fromSource(serverCloseTrigger).toFuture().get();
Completable write = conn.write(from(request, allocator.fromAscii("Bye"), EmptyHttpHeaders.INSTANCE));
assertThrows(ExecutionException.class, () -> write.toFuture().get());
CloseHandler closeHandler = closeHandlerRef.get();
assertNotNull(closeHandler);
verify(closeHandler, never()).protocolPayloadEndOutbound(any(), any());
}
}
use of io.servicetalk.transport.api.ServerContext in project servicetalk by apple.
the class HttpSerializationErrorTest method serializationMapThrowsPropagatesToClient.
@ParameterizedTest
@MethodSource("executors")
void serializationMapThrowsPropagatesToClient(HttpTestExecutionStrategy serverStrategy) throws Exception {
serverExecutionStrategy = serverStrategy.executorSupplier.get();
TypeReference<Map<String, Object>> mapType = new TypeReference<Map<String, Object>>() {
};
HttpSerializerDeserializer<Map<String, Object>> httpSerializer = jsonSerializer(JACKSON.serializerDeserializer(mapType));
HttpStreamingSerializerDeserializer<Map<String, Object>> httpStreamingSerializer = jsonStreamingSerializer(JACKSON.streamingSerializerDeserializer(mapType));
try (ServerContext srv = HttpServers.forAddress(localAddress(0)).executionStrategy(serverExecutionStrategy).listenAndAwait((ctx, request, responseFactory) -> responseFactory.ok().toStreamingResponse().payloadBody(request.toStreamingRequest().payloadBody(httpStreamingSerializer).map(result -> {
throw DELIBERATE_EXCEPTION;
}), httpStreamingSerializer).toResponse());
BlockingHttpClient clt = HttpClients.forSingleAddress(serverHostAndPort(srv)).buildBlocking()) {
HttpResponse resp = clt.request(clt.post("/foo").payloadBody(emptyMap(), httpSerializer));
assertEquals(INTERNAL_SERVER_ERROR, resp.status());
}
}
Aggregations