View Javadoc
1   /*
2    * Copyright 2020 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.codec.quic;
17  
18  import io.netty.channel.Channel;
19  import io.netty.channel.ChannelHandler;
20  import io.netty.channel.ChannelOption;
21  import io.netty.util.AttributeKey;
22  import io.netty.util.internal.ObjectUtil;
23  import io.netty.util.internal.logging.InternalLogger;
24  import org.jetbrains.annotations.Nullable;
25  
26  import java.util.HashMap;
27  import java.util.LinkedHashMap;
28  import java.util.Map;
29  
30  public final class Quic {
31      @SuppressWarnings("unchecked")
32      static final Map.Entry<ChannelOption<?>, Object>[] EMPTY_OPTION_ARRAY = new Map.Entry[0];
33      @SuppressWarnings("unchecked")
34      static final Map.Entry<AttributeKey<?>, Object>[] EMPTY_ATTRIBUTE_ARRAY = new Map.Entry[0];
35  
36      static final int MAX_DATAGRAM_SIZE = 1350;
37  
38      static final int RESET_TOKEN_LEN = 16;
39  
40      private static final Throwable UNAVAILABILITY_CAUSE;
41  
42      static {
43          Throwable cause = null;
44  
45          try {
46              String version = Quiche.quiche_version();
47              assert version != null;
48          } catch (Throwable error) {
49              cause = error;
50          }
51  
52          UNAVAILABILITY_CAUSE = cause;
53      }
54  
55      /**
56       * The maximum length of the <a href="https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2">connection id</a>.
57       *
58       */
59      public static final int MAX_CONN_ID_LEN = 20;
60  
61      /**
62       * Return if the given QUIC version is supported.
63       *
64       * @param version   the version.
65       * @return          {@code true} if supported, {@code false} otherwise.
66       */
67      public static boolean isVersionSupported(int version) {
68          return isAvailable() && Quiche.quiche_version_is_supported(version);
69      }
70  
71      /**
72       * Returns {@code true} if and only if the QUIC implementation is usable on the running platform is available.
73       *
74       * @return {@code true} if this QUIC implementation can be used on the current platform, {@code false} otherwise.
75       */
76      public static boolean isAvailable() {
77          return UNAVAILABILITY_CAUSE == null;
78      }
79  
80      /**
81       * Ensure that QUIC implementation is usable on the running platform is available.
82       *
83       * @throws UnsatisfiedLinkError if unavailable
84       */
85      public static void ensureAvailability() {
86          if (UNAVAILABILITY_CAUSE != null) {
87              throw (Error) new UnsatisfiedLinkError(
88                      "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE);
89          }
90      }
91  
92      /**
93       * Returns the cause of unavailability.
94       *
95       * @return the cause if unavailable. {@code null} if available.
96       */
97      @Nullable
98      public static Throwable unavailabilityCause() {
99          return UNAVAILABILITY_CAUSE;
100     }
101 
102     static Map.Entry<ChannelOption<?>, Object>[] toOptionsArray(Map<ChannelOption<?>, Object> opts) {
103         return new HashMap<>(opts).entrySet().toArray(EMPTY_OPTION_ARRAY);
104     }
105 
106     static Map.Entry<AttributeKey<?>, Object>[] toAttributesArray(Map<AttributeKey<?>, Object> attributes) {
107         return new LinkedHashMap<>(attributes).entrySet().toArray(EMPTY_ATTRIBUTE_ARRAY);
108     }
109 
110     private static void setAttributes(Channel channel, Map.Entry<AttributeKey<?>, Object>[] attrs) {
111         for (Map.Entry<AttributeKey<?>, Object> e: attrs) {
112             @SuppressWarnings("unchecked")
113             AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
114             channel.attr(key).set(e.getValue());
115         }
116     }
117 
118     private static void setChannelOptions(
119             Channel channel, Map.Entry<ChannelOption<?>, Object>[] options, InternalLogger logger) {
120         for (Map.Entry<ChannelOption<?>, Object> e: options) {
121             setChannelOption(channel, e.getKey(), e.getValue(), logger);
122         }
123     }
124 
125     @SuppressWarnings("unchecked")
126     private static void setChannelOption(
127             Channel channel, ChannelOption<?> option, Object value, InternalLogger logger) {
128         try {
129             if (!channel.config().setOption((ChannelOption<Object>) option, value)) {
130                 logger.warn("Unknown channel option '{}' for channel '{}'", option, channel);
131             }
132         } catch (Throwable t) {
133             logger.warn(
134                     "Failed to set channel option '{}' with value '{}' for channel '{}'", option, value, channel, t);
135         }
136     }
137 
138     /**
139      * Allow to specify a {@link ChannelOption} which is used for the {@link QuicStreamChannel} instances once they got
140      * created. Use a value of {@code null} to remove a previous set {@link ChannelOption}.
141      */
142     static <T> void updateOptions(Map<ChannelOption<?>, Object> options, ChannelOption<T> option, @Nullable T value) {
143         ObjectUtil.checkNotNull(option, "option");
144         if (value == null) {
145             options.remove(option);
146         } else {
147             options.put(option, value);
148         }
149     }
150 
151     /**
152      * Allow to specify an initial attribute of the newly created {@link QuicStreamChannel}. If the {@code value} is
153      * {@code null}, the attribute of the specified {@code key} is removed.
154      */
155     static <T> void updateAttributes(Map<AttributeKey<?>, Object> attributes, AttributeKey<T> key, @Nullable T value) {
156         ObjectUtil.checkNotNull(key, "key");
157         if (value == null) {
158             attributes.remove(key);
159         } else {
160             attributes.put(key, value);
161         }
162     }
163 
164     static void setupChannel(Channel ch, Map.Entry<ChannelOption<?>, Object>[] options,
165                              Map.Entry<AttributeKey<?>, Object>[] attrs, @Nullable ChannelHandler handler,
166                              InternalLogger logger) {
167         Quic.setChannelOptions(ch, options, logger);
168         Quic.setAttributes(ch, attrs);
169         if (handler != null) {
170             ch.pipeline().addLast(handler);
171         }
172     }
173 
174     private Quic() { }
175 }