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());
}
});
}
};
}
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);
}
}
};
}
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);
}
};
}
Aggregations