Search in sources :

Example 1 with ChannelSet

use of io.servicetalk.transport.netty.internal.ChannelSet 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());
                }
            });
        }
    };
}
Also used : ChannelFuture(io.netty.channel.ChannelFuture) ChannelSet(io.servicetalk.transport.netty.internal.ChannelSet) Channel(io.netty.channel.Channel) EventLoopAwareNettyIoExecutor(io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor) EventLoopAwareNettyIoExecutors.toEventLoopAwareNettyIoExecutor(io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutors.toEventLoopAwareNettyIoExecutor) SubscribableSingle(io.servicetalk.concurrent.api.internal.SubscribableSingle) ChannelHandlerContext(io.netty.channel.ChannelHandlerContext) ServerBootstrap(io.netty.bootstrap.ServerBootstrap) ServerContext(io.servicetalk.transport.api.ServerContext) NettyServerContext(io.servicetalk.transport.netty.internal.NettyServerContext) SubscribableSingle(io.servicetalk.concurrent.api.internal.SubscribableSingle) Single(io.servicetalk.concurrent.api.Single) ChannelInboundHandlerAdapter(io.netty.channel.ChannelInboundHandlerAdapter) ReferenceCounted(io.netty.util.ReferenceCounted)

Aggregations

ServerBootstrap (io.netty.bootstrap.ServerBootstrap)1 Channel (io.netty.channel.Channel)1 ChannelFuture (io.netty.channel.ChannelFuture)1 ChannelHandlerContext (io.netty.channel.ChannelHandlerContext)1 ChannelInboundHandlerAdapter (io.netty.channel.ChannelInboundHandlerAdapter)1 ReferenceCounted (io.netty.util.ReferenceCounted)1 Single (io.servicetalk.concurrent.api.Single)1 SubscribableSingle (io.servicetalk.concurrent.api.internal.SubscribableSingle)1 ServerContext (io.servicetalk.transport.api.ServerContext)1 ChannelSet (io.servicetalk.transport.netty.internal.ChannelSet)1 EventLoopAwareNettyIoExecutor (io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutor)1 EventLoopAwareNettyIoExecutors.toEventLoopAwareNettyIoExecutor (io.servicetalk.transport.netty.internal.EventLoopAwareNettyIoExecutors.toEventLoopAwareNettyIoExecutor)1 NettyServerContext (io.servicetalk.transport.netty.internal.NettyServerContext)1