1 /* 2 * Copyright 2015 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package io.netty.handler.ssl; 17 18 import io.netty.channel.Channel; 19 import io.netty.channel.ChannelHandlerContext; 20 import io.netty.channel.ChannelInboundHandlerAdapter; 21 import io.netty.channel.ChannelInitializer; 22 import io.netty.channel.ChannelPipeline; 23 import io.netty.util.internal.ObjectUtil; 24 import io.netty.util.internal.logging.InternalLogger; 25 import io.netty.util.internal.logging.InternalLoggerFactory; 26 27 /** 28 * Configures a {@link ChannelPipeline} depending on the application-level protocol negotiation result of 29 * {@link SslHandler}. For example, you could configure your HTTP pipeline depending on the result of ALPN: 30 * <pre> 31 * public class MyInitializer extends {@link ChannelInitializer}<{@link Channel}> { 32 * private final {@link SslContext} sslCtx; 33 * 34 * public MyInitializer({@link SslContext} sslCtx) { 35 * this.sslCtx = sslCtx; 36 * } 37 * 38 * protected void initChannel({@link Channel} ch) { 39 * {@link ChannelPipeline} p = ch.pipeline(); 40 * p.addLast(sslCtx.newHandler(...)); // Adds {@link SslHandler} 41 * p.addLast(new MyNegotiationHandler()); 42 * } 43 * } 44 * 45 * public class MyNegotiationHandler extends {@link ApplicationProtocolNegotiationHandler} { 46 * public MyNegotiationHandler() { 47 * super({@link ApplicationProtocolNames}.HTTP_1_1); 48 * } 49 * 50 * protected void configurePipeline({@link ChannelHandlerContext} ctx, String protocol) { 51 * if ({@link ApplicationProtocolNames}.HTTP_2.equals(protocol) { 52 * configureHttp2(ctx); 53 * } else if ({@link ApplicationProtocolNames}.HTTP_1_1.equals(protocol)) { 54 * configureHttp1(ctx); 55 * } else { 56 * throw new IllegalStateException("unknown protocol: " + protocol); 57 * } 58 * } 59 * } 60 * </pre> 61 */ 62 public abstract class ApplicationProtocolNegotiationHandler extends ChannelInboundHandlerAdapter { 63 64 private static final InternalLogger logger = 65 InternalLoggerFactory.getInstance(ApplicationProtocolNegotiationHandler.class); 66 67 private final String fallbackProtocol; 68 69 /** 70 * Creates a new instance with the specified fallback protocol name. 71 * 72 * @param fallbackProtocol the name of the protocol to use when 73 * ALPN/NPN negotiation fails or the client does not support ALPN/NPN 74 */ 75 protected ApplicationProtocolNegotiationHandler(String fallbackProtocol) { 76 this.fallbackProtocol = ObjectUtil.checkNotNull(fallbackProtocol, "fallbackProtocol"); 77 } 78 79 @Override 80 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { 81 if (evt instanceof SslHandshakeCompletionEvent) { 82 ctx.pipeline().remove(this); 83 84 SslHandshakeCompletionEvent handshakeEvent = (SslHandshakeCompletionEvent) evt; 85 if (handshakeEvent.isSuccess()) { 86 SslHandler sslHandler = ctx.pipeline().get(SslHandler.class); 87 if (sslHandler == null) { 88 throw new IllegalStateException("cannot find a SslHandler in the pipeline (required for " + 89 "application-level protocol negotiation)"); 90 } 91 String protocol = sslHandler.applicationProtocol(); 92 configurePipeline(ctx, protocol != null? protocol : fallbackProtocol); 93 } else { 94 handshakeFailure(ctx, handshakeEvent.cause()); 95 } 96 } 97 98 ctx.fireUserEventTriggered(evt); 99 } 100 101 /** 102 * Invoked on successful initial SSL/TLS handshake. Implement this method to configure your pipeline 103 * for the negotiated application-level protocol. 104 * 105 * @param protocol the name of the negotiated application-level protocol, or 106 * the fallback protocol name specified in the constructor call if negotiation failed or the client 107 * isn't aware of ALPN/NPN extension 108 */ 109 protected abstract void configurePipeline(ChannelHandlerContext ctx, String protocol) throws Exception; 110 111 /** 112 * Invoked on failed initial SSL/TLS handshake. 113 */ 114 protected void handshakeFailure(ChannelHandlerContext ctx, Throwable cause) throws Exception { 115 logger.warn("{} TLS handshake failed:", ctx.channel(), cause); 116 ctx.close(); 117 } 118 119 @Override 120 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { 121 logger.warn("{} Failed to select the application-level protocol:", ctx.channel(), cause); 122 ctx.close(); 123 } 124 }