1 /* 2 * Copyright 2014 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.netty5.handler.ssl; 17 18 import javax.net.ssl.SSLEngine; 19 import java.util.Collections; 20 import java.util.List; 21 22 import static io.netty5.handler.ssl.ApplicationProtocolUtil.toList; 23 import static io.netty5.util.internal.ObjectUtil.checkNonEmpty; 24 import static java.util.Objects.requireNonNull; 25 26 /** 27 * Provides an {@link SSLEngine} agnostic way to configure a {@link ApplicationProtocolNegotiator}. 28 */ 29 public final class ApplicationProtocolConfig { 30 31 /** 32 * The configuration that disables application protocol negotiation. 33 */ 34 public static final ApplicationProtocolConfig DISABLED = new ApplicationProtocolConfig(); 35 36 private final List<String> supportedProtocols; 37 private final Protocol protocol; 38 private final SelectorFailureBehavior selectorBehavior; 39 private final SelectedListenerFailureBehavior selectedBehavior; 40 41 /** 42 * Create a new instance. 43 * @param protocol The application protocol functionality to use. 44 * @param selectorBehavior How the peer selecting the protocol should behave. 45 * @param selectedBehavior How the peer being notified of the selected protocol should behave. 46 * @param supportedProtocols The order of iteration determines the preference of support for protocols. 47 */ 48 public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior, 49 SelectedListenerFailureBehavior selectedBehavior, Iterable<String> supportedProtocols) { 50 this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols)); 51 } 52 53 /** 54 * Create a new instance. 55 * @param protocol The application protocol functionality to use. 56 * @param selectorBehavior How the peer selecting the protocol should behave. 57 * @param selectedBehavior How the peer being notified of the selected protocol should behave. 58 * @param supportedProtocols The order of iteration determines the preference of support for protocols. 59 */ 60 public ApplicationProtocolConfig(Protocol protocol, SelectorFailureBehavior selectorBehavior, 61 SelectedListenerFailureBehavior selectedBehavior, String... supportedProtocols) { 62 this(protocol, selectorBehavior, selectedBehavior, toList(supportedProtocols)); 63 } 64 65 /** 66 * Create a new instance. 67 * @param protocol The application protocol functionality to use. 68 * @param selectorBehavior How the peer selecting the protocol should behave. 69 * @param selectedBehavior How the peer being notified of the selected protocol should behave. 70 * @param supportedProtocols The order of iteration determines the preference of support for protocols. 71 */ 72 private ApplicationProtocolConfig( 73 Protocol protocol, SelectorFailureBehavior selectorBehavior, 74 SelectedListenerFailureBehavior selectedBehavior, List<String> supportedProtocols) { 75 this.supportedProtocols = Collections.unmodifiableList( 76 requireNonNull(supportedProtocols, "supportedProtocols")); 77 this.protocol = requireNonNull(protocol, "protocol"); 78 this.selectorBehavior = requireNonNull(selectorBehavior, "selectorBehavior"); 79 this.selectedBehavior = requireNonNull(selectedBehavior, "selectedBehavior"); 80 81 if (protocol == Protocol.NONE) { 82 throw new IllegalArgumentException("protocol (" + Protocol.NONE + ") must not be " + Protocol.NONE + '.'); 83 } 84 checkNonEmpty(supportedProtocols, "supportedProtocols"); 85 } 86 87 /** 88 * A special constructor that is used to instantiate {@link #DISABLED}. 89 */ 90 private ApplicationProtocolConfig() { 91 supportedProtocols = Collections.emptyList(); 92 protocol = Protocol.NONE; 93 selectorBehavior = SelectorFailureBehavior.CHOOSE_MY_LAST_PROTOCOL; 94 selectedBehavior = SelectedListenerFailureBehavior.ACCEPT; 95 } 96 97 /** 98 * Defines which application level protocol negotiation to use. 99 */ 100 public enum Protocol { 101 NONE, NPN, ALPN, NPN_AND_ALPN 102 } 103 104 /** 105 * Defines the most common behaviors for the peer that selects the application protocol. 106 */ 107 public enum SelectorFailureBehavior { 108 /** 109 * If the peer who selects the application protocol doesn't find a match this will result in the failing the 110 * handshake with a fatal alert. 111 * <p> 112 * For example in the case of ALPN this will result in a 113 * <a herf="https://tools.ietf.org/html/rfc7301#section-3.2">no_application_protocol(120)</a> alert. 114 */ 115 FATAL_ALERT, 116 /** 117 * If the peer who selects the application protocol doesn't find a match it will pretend no to support 118 * the TLS extension by not advertising support for the TLS extension in the handshake. This is used in cases 119 * where a "best effort" is desired to talk even if there is no matching protocol. 120 */ 121 NO_ADVERTISE, 122 /** 123 * If the peer who selects the application protocol doesn't find a match it will just select the last protocol 124 * it advertised support for. This is used in cases where a "best effort" is desired to talk even if there 125 * is no matching protocol, and the assumption is the "most general" fallback protocol is typically listed last. 126 * <p> 127 * This may be <a href="https://tools.ietf.org/html/rfc7301#section-3.2">illegal for some RFCs</a> but was 128 * observed behavior by some SSL implementations, and is supported for flexibility/compatibility. 129 */ 130 CHOOSE_MY_LAST_PROTOCOL 131 } 132 133 /** 134 * Defines the most common behaviors for the peer which is notified of the selected protocol. 135 */ 136 public enum SelectedListenerFailureBehavior { 137 /** 138 * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer 139 * didn't advertise support for the TLS extension then the handshake will continue and the application protocol 140 * is assumed to be accepted. 141 */ 142 ACCEPT, 143 /** 144 * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer 145 * didn't advertise support for the TLS extension then the handshake will be failed with a fatal alert. 146 */ 147 FATAL_ALERT, 148 /** 149 * If the peer who is notified what protocol was selected determines the selection was not matched, or the peer 150 * didn't advertise support for the TLS extension then the handshake will continue assuming the last protocol 151 * supported by this peer is used. This is used in cases where a "best effort" is desired to talk even if there 152 * is no matching protocol, and the assumption is the "most general" fallback protocol is typically listed last. 153 */ 154 CHOOSE_MY_LAST_PROTOCOL 155 } 156 157 /** 158 * The application level protocols supported. 159 */ 160 public List<String> supportedProtocols() { 161 return supportedProtocols; 162 } 163 164 /** 165 * Get which application level protocol negotiation to use. 166 */ 167 public Protocol protocol() { 168 return protocol; 169 } 170 171 /** 172 * Get the desired behavior for the peer who selects the application protocol. 173 */ 174 public SelectorFailureBehavior selectorFailureBehavior() { 175 return selectorBehavior; 176 } 177 178 /** 179 * Get the desired behavior for the peer who is notified of the selected protocol. 180 */ 181 public SelectedListenerFailureBehavior selectedListenerFailureBehavior() { 182 return selectedBehavior; 183 } 184 }