1 /*
2 * Copyright 2017 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 * https://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.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufAllocator;
20 import io.netty.channel.ChannelHandler;
21 import io.netty.channel.ChannelHandlerContext;
22 import io.netty.handler.codec.ByteToMessageDecoder;
23 import io.netty.util.ReferenceCountUtil;
24 import io.netty.util.internal.ObjectUtil;
25
26 import javax.net.ssl.SSLContext;
27 import javax.net.ssl.SSLParameters;
28 import java.util.List;
29
30 /**
31 * {@link OptionalSslHandler} is a utility decoder to support both SSL and non-SSL handlers
32 * based on the first message received.
33 */
34 public class OptionalSslHandler extends ByteToMessageDecoder {
35
36 private final SslContext sslContext;
37
38 public OptionalSslHandler(SslContext sslContext) {
39 this.sslContext = ObjectUtil.checkNotNull(sslContext, "sslContext");
40 }
41
42 @Override
43 protected void decode(ChannelHandlerContext context, ByteBuf in, List<Object> out) throws Exception {
44 if (in.readableBytes() < SslUtils.SSL_RECORD_HEADER_LENGTH) {
45 return;
46 }
47 if (SslHandler.isEncrypted(in)) {
48 handleSsl(context);
49 } else {
50 handleNonSsl(context);
51 }
52 }
53
54 private void handleSsl(ChannelHandlerContext context) {
55 SslHandler sslHandler = null;
56 try {
57 sslHandler = newSslHandler(context, sslContext);
58 context.pipeline().replace(this, newSslHandlerName(), sslHandler);
59 sslHandler = null;
60 } finally {
61 // Since the SslHandler was not inserted into the pipeline the ownership of the SSLEngine was not
62 // transferred to the SslHandler.
63 if (sslHandler != null) {
64 ReferenceCountUtil.safeRelease(sslHandler.engine());
65 }
66 }
67 }
68
69 private void handleNonSsl(ChannelHandlerContext context) {
70 ChannelHandler handler = newNonSslHandler(context);
71 if (handler != null) {
72 context.pipeline().replace(this, newNonSslHandlerName(), handler);
73 } else {
74 context.pipeline().remove(this);
75 }
76 }
77
78 /**
79 * Optionally specify the SSL handler name, this method may return {@code null}.
80 * @return the name of the SSL handler.
81 */
82 protected String newSslHandlerName() {
83 return null;
84 }
85
86 /**
87 * Override to configure the SslHandler eg. {@link SSLParameters#setEndpointIdentificationAlgorithm(String)}.
88 * The hostname and port is not known by this method so servers may want to override this method and use the
89 * {@link SslContext#newHandler(ByteBufAllocator, String, int)} variant.
90 *
91 * @param context the {@link ChannelHandlerContext} to use.
92 * @param sslContext the {@link SSLContext} to use.
93 * @return the {@link SslHandler} which will replace the {@link OptionalSslHandler} in the pipeline if the
94 * traffic is SSL.
95 */
96 protected SslHandler newSslHandler(ChannelHandlerContext context, SslContext sslContext) {
97 return sslContext.newHandler(context.alloc());
98 }
99
100 /**
101 * Optionally specify the non-SSL handler name, this method may return {@code null}.
102 * @return the name of the non-SSL handler.
103 */
104 protected String newNonSslHandlerName() {
105 return null;
106 }
107
108 /**
109 * Override to configure the ChannelHandler.
110 * @param context the {@link ChannelHandlerContext} to use.
111 * @return the {@link ChannelHandler} which will replace the {@link OptionalSslHandler} in the pipeline
112 * or {@code null} to simply remove the {@link OptionalSslHandler} if the traffic is non-SSL.
113 */
114 protected ChannelHandler newNonSslHandler(ChannelHandlerContext context) {
115 return null;
116 }
117 }