package org.infinispan.server.router.router.impl.hotrod.handlers;
import java.lang.invoke.MethodHandles;
import java.util.List;
import java.util.Optional;
import org.infinispan.commons.logging.LogFactory;
import org.infinispan.server.router.RoutingTable;
import org.infinispan.server.router.logging.RouterLogger;
import org.infinispan.server.router.routes.Route;
import org.infinispan.server.router.routes.SniRouteSource;
import org.infinispan.server.router.routes.hotrod.NettyHandlerRouteDestination;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.handler.ssl.SniHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.util.DomainNameMapping;
/**
* Handler responsible for routing requests to proper backend based on SNI Host Name.
*
* @author Sebastian Ćaskawiec
*/
public class SniRouteHandler extends SniHandler {
private static final RouterLogger logger = LogFactory.getLog(MethodHandles.lookup().lookupClass(), RouterLogger.class);
private final RoutingTable routingTable;
/**
* Creates new {@link SniRouteHandler} based on SNI Domain mapping and the {@link RoutingTable}.
*
* @param mapping SNI Host Name mapping.
* @param routingTable The {@link RoutingTable} for supplying the {@link Route}s.
*/
public SniRouteHandler(DomainNameMapping<? extends SslContext> mapping, RoutingTable routingTable) {
super(mapping);
this.routingTable = routingTable;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
super.decode(ctx, in, out);
if (isHandShaked()) {
// At this point Netty has replaced SNIHandler (formally this) with SSLHandler in the pipeline.
// Now we need to add other handlers at the tail of the queue
logger.debugf("Handshaked with hostname %s", hostname());
Optional<Route<SniRouteSource, NettyHandlerRouteDestination>> route = routingTable.streamRoutes(SniRouteSource.class, NettyHandlerRouteDestination.class)
.filter(r -> r.getRouteSource().getSniHostName().equals(this.hostname()))
.findAny();
NettyHandlerRouteDestination routeDestination = route.orElseThrow(() -> logger.noRouteFound()).getRouteDesitnation();
ChannelInitializer<Channel> channelInitializer = routeDestination.getChannelInitializer();
ctx.pipeline().addLast(channelInitializer);
logger.debug("Replaced with route destination's handlers");
}
}
/**
* Return <code>true</code> if handshake was successful.
*/
public boolean isHandShaked() {
return this.hostname() != null;
}
}