Search in sources :

Example 1 with SubscribableSingle

use of io.servicetalk.concurrent.api.internal.SubscribableSingle 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)

Example 2 with SubscribableSingle

use of io.servicetalk.concurrent.api.internal.SubscribableSingle in project servicetalk by apple.

the class DefaultJerseyStreamingHttpRouter method handle.

@Override
public Single<StreamingHttpResponse> handle(final HttpServiceContext serviceCtx, final StreamingHttpRequest req, final StreamingHttpResponseFactory factory) {
    return new SubscribableSingle<StreamingHttpResponse>() {

        @Override
        protected void handleSubscribe(final Subscriber<? super StreamingHttpResponse> subscriber) {
            final DelayedCancellable delayedCancellable = new DelayedCancellable();
            DuplicateTerminateDetectorSingle<? super StreamingHttpResponse> dupSub = new DuplicateTerminateDetectorSingle<>(subscriber);
            try {
                dupSub.onSubscribe(delayedCancellable);
            } catch (Throwable cause) {
                handleExceptionFromOnSubscribe(dupSub, cause);
                return;
            }
            try {
                handle0(serviceCtx, req, factory, dupSub, delayedCancellable);
            } catch (final Throwable t) {
                safeOnError(dupSub, t);
            }
        }
    };
}
Also used : Subscriber(io.servicetalk.concurrent.SingleSource.Subscriber) SubscribableSingle(io.servicetalk.concurrent.api.internal.SubscribableSingle) DelayedCancellable(io.servicetalk.concurrent.internal.DelayedCancellable) StreamingHttpResponse(io.servicetalk.http.api.StreamingHttpResponse)

Example 3 with SubscribableSingle

use of io.servicetalk.concurrent.api.internal.SubscribableSingle in project servicetalk by apple.

the class DefaultNettyConnection method initChannel.

/**
 * Given a {@link Channel} this will initialize the {@link ChannelPipeline} and create a
 * {@link DefaultNettyConnection}. The resulting single will complete after the TLS handshake has completed
 * (if applicable) or otherwise after the channel is active and ready to use.
 * @param channel A newly created {@link Channel}.
 * @param allocator The {@link BufferAllocator} to use for the {@link DefaultNettyConnection}.
 * @param executor The {@link Executor} to use for the {@link DefaultNettyConnection}.
 * @param ioExecutor The {@link IoExecutor} to use for the {@link DefaultNettyConnection}.
 * @param closeHandler Manages the half closure of the {@link DefaultNettyConnection}.
 * @param flushStrategy Manages flushing of data for the {@link DefaultNettyConnection}.
 * @param idleTimeoutMs Value for {@link ServiceTalkSocketOptions#IDLE_TIMEOUT IDLE_TIMEOUT} socket option.
 * @param initializer Synchronously initializes the pipeline upon subscribe.
 * @param executionStrategy {@link ExecutionStrategy} to use for this connection.
 * @param protocol {@link Protocol} for the returned {@link DefaultNettyConnection}.
 * @param observer {@link ConnectionObserver} to report network events.
 * @param isClient tells if this {@link Channel} is for the client.
 * @param shouldWait predicate that tells when request payload body should wait for continuation signal.
 * @param <Read> Type of objects read from the {@link NettyConnection}.
 * @param <Write> Type of objects written to the {@link NettyConnection}.
 * @return A {@link Single} that completes with a {@link DefaultNettyConnection} after the channel is activated and
 * ready to use.
 */
public static <Read, Write> Single<DefaultNettyConnection<Read, Write>> initChannel(Channel channel, BufferAllocator allocator, Executor executor, @Nullable IoExecutor ioExecutor, CloseHandler closeHandler, FlushStrategy flushStrategy, @Nullable Long idleTimeoutMs, ChannelInitializer initializer, ExecutionStrategy executionStrategy, Protocol protocol, ConnectionObserver observer, boolean isClient, Predicate<Object> shouldWait) {
    return new SubscribableSingle<DefaultNettyConnection<Read, Write>>() {

        @Override
        protected void handleSubscribe(final SingleSource.Subscriber<? super DefaultNettyConnection<Read, Write>> subscriber) {
            final NettyToStChannelInboundHandler<Read, Write> nettyInboundHandler;
            final DelayedCancellable delayedCancellable;
            try {
                delayedCancellable = new DelayedCancellable();
                boolean supportsIoThread = null != ioExecutor && ioExecutor.isIoThreadSupported();
                DefaultExecutionContext<?> executionContext = new DefaultExecutionContext<>(allocator, fromNettyEventLoop(channel.eventLoop(), supportsIoThread), executor, executionStrategy);
                DefaultNettyConnection<Read, Write> connection = new DefaultNettyConnection<>(channel, executionContext, closeHandler, flushStrategy, idleTimeoutMs, protocol, null, null, NoopDataObserver.INSTANCE, isClient, shouldWait, identity());
                channel.attr(CHANNEL_CLOSEABLE_KEY).set(connection);
                // We need the NettyToStChannelInboundHandler to be last in the pipeline. We accomplish that by
                // calling the ChannelInitializer before we do addLast for the NettyToStChannelInboundHandler.
                // This could mean if there are any synchronous events generated via ChannelInitializer handlers
                // that NettyToStChannelInboundHandler will not see them. This is currently not an issue and would
                // require some pipeline modifications if we wanted to insert NettyToStChannelInboundHandler first,
                // but not allow any other handlers to be after it.
                initializer.init(channel);
                ChannelPipeline pipeline = connection.channel().pipeline();
                nettyInboundHandler = new NettyToStChannelInboundHandler<>(connection, subscriber, delayedCancellable, NettyPipelineSslUtils.isSslEnabled(pipeline), observer);
            } catch (Throwable cause) {
                close(channel, cause);
                deliverErrorFromSource(subscriber, cause);
                return;
            }
            subscriber.onSubscribe(delayedCancellable);
            // We have to add to the pipeline AFTER we call onSubscribe, because adding to the pipeline may invoke
            // callbacks that interact with the subscriber.
            channel.pipeline().addLast(nettyInboundHandler);
        }
    };
}
Also used : DefaultExecutionContext(io.servicetalk.transport.api.DefaultExecutionContext) SubscribableSingle(io.servicetalk.concurrent.api.internal.SubscribableSingle) DelayedCancellable(io.servicetalk.concurrent.internal.DelayedCancellable) ChannelPipeline(io.netty.channel.ChannelPipeline) Subscriber(io.servicetalk.concurrent.CompletableSource.Subscriber)

Aggregations

SubscribableSingle (io.servicetalk.concurrent.api.internal.SubscribableSingle)3 DelayedCancellable (io.servicetalk.concurrent.internal.DelayedCancellable)2 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 ChannelPipeline (io.netty.channel.ChannelPipeline)1 ReferenceCounted (io.netty.util.ReferenceCounted)1 Subscriber (io.servicetalk.concurrent.CompletableSource.Subscriber)1 Subscriber (io.servicetalk.concurrent.SingleSource.Subscriber)1 Single (io.servicetalk.concurrent.api.Single)1 StreamingHttpResponse (io.servicetalk.http.api.StreamingHttpResponse)1 DefaultExecutionContext (io.servicetalk.transport.api.DefaultExecutionContext)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