All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactor.ipc.netty.channel.ContextHandler Maven / Gradle / Ivy

There is a newer version: 0.7.15.RELEASE
Show newest version
/*
 * Copyright (c) 2011-2018 Pivotal Software Inc, All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package reactor.ipc.netty.channel;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.pool.ChannelPool;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future;
import org.reactivestreams.Publisher;
import reactor.core.Disposable;
import reactor.core.publisher.Mono;
import reactor.core.publisher.MonoSink;
import reactor.ipc.netty.NettyContext;
import reactor.ipc.netty.NettyPipeline;
import reactor.ipc.netty.options.ClientOptions;
import reactor.ipc.netty.options.NettyOptions;
import reactor.ipc.netty.options.ServerOptions;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.function.Tuple2;

/**
 * A one time-set channel pipeline callback to emit {@link NettyContext} state for clean
 * disposing. A {@link ContextHandler} is bound to a user-facing {@link MonoSink}
 *
 * @param  the channel type
 *
 * @author Stephane Maldini
 */
public abstract class ContextHandler
		extends ChannelInitializer implements Disposable, Consumer {

	/**
	 * Create a new client context
	 *
	 * @param sink
	 * @param options
	 * @param loggingHandler
	 * @param secure
	 * @param channelOpFactory
	 * @param 
	 *
	 * @return a new {@link ContextHandler} for clients
	 */
	public static  ContextHandler newClientContext(
			MonoSink sink,
			ClientOptions options,
			LoggingHandler loggingHandler,
			boolean secure,
			SocketAddress providedAddress,
			ChannelOperations.OnNew channelOpFactory) {
		return newClientContext(sink,
				options,
				loggingHandler,
				secure,
				providedAddress,
				null,
				channelOpFactory);
	}

	/**
	 * Create a new client context with optional pool support
	 *
	 * @param sink
	 * @param options
	 * @param loggingHandler
	 * @param secure
	 * @param providedAddress
	 * @param channelOpFactory
	 * @param pool
	 * @param 
	 *
	 * @return a new {@link ContextHandler} for clients
	 */
	public static  ContextHandler newClientContext(
			MonoSink sink,
			ClientOptions options,
			LoggingHandler loggingHandler,
			boolean secure,
			SocketAddress providedAddress,
			ChannelPool pool, ChannelOperations.OnNew channelOpFactory) {
		if (pool != null) {
			return new PooledClientContextHandler<>(channelOpFactory,
					options,
					sink,
					loggingHandler,
					secure,
					providedAddress,
					pool);
		}
		return new ClientContextHandler<>(channelOpFactory,
				options,
				sink,
				loggingHandler,
				secure,
				providedAddress);
	}

	/**
	 * Create a new server context
	 *
	 * @param sink
	 * @param options
	 * @param loggingHandler
	 * @param channelOpFactory
	 *
	 * @return a new {@link ContextHandler} for servers
	 */
	public static ContextHandler newServerContext(MonoSink sink,
			ServerOptions options,
			LoggingHandler loggingHandler,
			ChannelOperations.OnNew channelOpFactory) {
		return new ServerContextHandler(channelOpFactory, options, sink, loggingHandler, options.getAddress());
	}

	final MonoSink           sink;
	final NettyOptions               options;
	final LoggingHandler                   loggingHandler;
	final SocketAddress                    providedAddress;
	final ChannelOperations.OnNew channelOpFactory;

	BiConsumer> pipelineConfigurator;
	boolean                                              fired;
	boolean                                              autoCreateOperations;

	/**
	 * @param channelOpFactory
	 * @param options
	 * @param sink
	 * @param loggingHandler
	 * @param providedAddress the {@link InetSocketAddress} targeted by the operation
	 * associated with that handler (useable eg. for SNI), or null if unavailable.
	 */
	@SuppressWarnings("unchecked")
	protected ContextHandler(ChannelOperations.OnNew channelOpFactory,
			NettyOptions options,
			MonoSink sink,
			LoggingHandler loggingHandler,
			SocketAddress providedAddress) {
		this.channelOpFactory =
				Objects.requireNonNull(channelOpFactory, "channelOpFactory");
		this.options = options;
		this.sink = sink;
		this.loggingHandler = loggingHandler;
		this.autoCreateOperations = true;
		this.providedAddress = providedAddress;

	}

	/**
	 * Setup protocol specific handlers such as HTTP codec. Should be called before
	 * binding the context to any channel or bootstrap.
	 *
	 * @param pipelineConfigurator a configurator for extra codecs in the {@link
	 * ChannelPipeline}
	 *
	 * @return this context
	 */
	public final ContextHandler onPipeline(BiConsumer> pipelineConfigurator) {
		this.pipelineConfigurator =
				Objects.requireNonNull(pipelineConfigurator, "pipelineConfigurator");
		return this;
	}

	/**
	 * Allow the {@link ChannelOperations} to be created automatically on pipeline setup
	 *
	 * @param autoCreateOperations should auto create {@link ChannelOperations}
	 *
	 * @return this context
	 */
	public final ContextHandler autoCreateOperations(boolean autoCreateOperations) {
		this.autoCreateOperations = autoCreateOperations;
		return this;
	}

	/**
	 * Return a new {@link ChannelOperations} or null if one of the two
	 * following conditions are not met:
	 * 
    *
  • {@link #autoCreateOperations(boolean)} is true *
  • *
  • The passed message is not null
  • *
* * @param channel the current {@link Channel} * @param msg an optional message inbound, meaning the channel has already been * started before * * @return a new {@link ChannelOperations} */ @SuppressWarnings("unchecked") public final ChannelOperations createOperations(Channel channel, Object msg) { if (autoCreateOperations || msg != null) { ChannelOperations op = channelOpFactory.create((CHANNEL) channel, this, msg); if (op != null) { ChannelOperations old = ChannelOperations.tryGetAndSet(channel, op); if (old != null) { if (log.isDebugEnabled()) { log.debug(channel.toString() + "Mixed pooled connection " + "operations between " + op + " - and a previous one " + old); } return null; } if (this.options.afterNettyContextInit() != null) { try { this.options.afterNettyContextInit().accept(op.context()); } catch (Throwable t) { log.error("Could not apply afterNettyContextInit callback {}", t.toString()); } } channel.pipeline() .get(ChannelOperationsHandler.class).lastContext = this; channel.eventLoop().execute(op::onHandlerStart); } return op; } return null; } /** * Trigger {@link MonoSink#success(Object)} that will signal * {@link reactor.ipc.netty.NettyConnector#newHandler(BiFunction)} returned * {@link Mono} subscriber. * * @param context optional context to succeed the associated {@link MonoSink} */ public abstract void fireContextActive(NettyContext context); /** * Trigger {@link MonoSink#error(Throwable)} that will signal * {@link reactor.ipc.netty.NettyConnector#newHandler(BiFunction)} returned * {@link Mono} subscriber. * * @param t error to fail the associated {@link MonoSink} */ public void fireContextError(Throwable t) { if (!fired) { fired = true; sink.error(t); } else if (AbortedException.isConnectionReset(t)) { if (log.isDebugEnabled()) { log.error("Connection closed remotely", t); } } else { log.error("Error cannot be forwarded to user-facing Mono", t); } } /** * One-time only future setter * * @param future the connect/bind future to associate with and cancel on dispose */ public abstract void setFuture(Future future); /** * @param channel */ protected void doStarted(Channel channel) { //ignore } @Override protected void initChannel(CHANNEL ch) throws Exception { accept(ch); } /** * Initialize pipeline * * @param ch channel to initialize */ protected abstract void doPipeline(Channel ch); @Override @SuppressWarnings("unchecked") public void accept(Channel channel) { doPipeline(channel); if (options.onChannelInit() != null) { if (options.onChannelInit() .test(channel)) { if (log.isDebugEnabled()) { log.debug("DROPPED by onChannelInit predicate {}", channel); } doDropped(channel); return; } } try { if (pipelineConfigurator != null) { pipelineConfigurator.accept(channel.pipeline(), (ContextHandler) this); } channel.pipeline() .addLast(NettyPipeline.ReactiveBridge, new ChannelOperationsHandler(this)); } catch (Exception t) { if (log.isErrorEnabled()) { log.error("Error while binding a channelOperation with: " + channel .toString() + " on " + channel.pipeline(), t); } } finally { if (null != options.afterChannelInit()) { options.afterChannelInit() .accept(channel); } } if (log.isDebugEnabled()) { log.debug("After pipeline {}", channel.pipeline() .toString()); } } /** * @param channel */ protected void doDropped(Channel channel) { //ignore } /** * Cleanly terminate a channel according to the current context handler type. * Server might keep alive and recycle connections, pooled client will release and * classic client will close. * * @param channel the channel to unregister */ protected void terminateChannel(Channel channel) { dispose(); } protected Tuple2 getSNI() { return null; //will ignore SNI } /** * Return a Publisher to signal onComplete on {@link Channel} close or release. * * @param channel the channel to monitor * * @return a Publisher to signal onComplete on {@link Channel} close or release. */ protected abstract Publisher onCloseOrRelease(Channel channel); static final Logger log = Loggers.getLogger(ContextHandler.class); static void addSslAndLogHandlers(NettyOptions options, ContextHandler sink, LoggingHandler loggingHandler, boolean secure, Tuple2 sniInfo, ChannelPipeline pipeline) { SslHandler sslHandler = secure ? options.getSslHandler(pipeline.channel().alloc(), sniInfo) : null; if (sslHandler != null) { if (log.isDebugEnabled() && sniInfo != null) { log.debug("SSL enabled using engine {} and SNI {}", sslHandler.engine() .getClass() .getSimpleName(), sniInfo); } else if (log.isDebugEnabled()) { log.debug("SSL enabled using engine {}", sslHandler.engine() .getClass() .getSimpleName()); } if (log.isTraceEnabled()) { pipeline.addFirst(NettyPipeline.SslLoggingHandler, new LoggingHandler(SslReadHandler.class)); pipeline.addAfter(NettyPipeline.SslLoggingHandler, NettyPipeline.SslHandler, sslHandler); } else { pipeline.addFirst(NettyPipeline.SslHandler, sslHandler); } if (log.isDebugEnabled()) { pipeline.addAfter(NettyPipeline.SslHandler, NettyPipeline.LoggingHandler, loggingHandler); pipeline.addAfter(NettyPipeline.LoggingHandler, NettyPipeline.SslReader, new SslReadHandler(sink)); } else { pipeline.addAfter(NettyPipeline.SslHandler, NettyPipeline.SslReader, new SslReadHandler(sink)); } } else if (log.isDebugEnabled()) { pipeline.addFirst(NettyPipeline.LoggingHandler, loggingHandler); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy