View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License, version 2.0 (the
5    * "License"); you may not use this file except in compliance with the License. You may obtain a
6    * 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 distributed under the License
11   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing permissions and limitations under
13   * the License.
14   */
15  package io.netty5.handler.codec.http2;
16  
17  import io.netty5.util.collection.CharObjectHashMap;
18  import io.netty5.util.internal.UnstableApi;
19  
20  import static io.netty5.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
21  import static io.netty5.handler.codec.http2.Http2CodecUtil.MAX_CONCURRENT_STREAMS;
22  import static io.netty5.handler.codec.http2.Http2CodecUtil.MAX_HEADER_LIST_SIZE;
23  import static io.netty5.handler.codec.http2.Http2CodecUtil.MAX_HEADER_TABLE_SIZE;
24  import static io.netty5.handler.codec.http2.Http2CodecUtil.MAX_INITIAL_WINDOW_SIZE;
25  import static io.netty5.handler.codec.http2.Http2CodecUtil.MIN_CONCURRENT_STREAMS;
26  import static io.netty5.handler.codec.http2.Http2CodecUtil.MIN_HEADER_LIST_SIZE;
27  import static io.netty5.handler.codec.http2.Http2CodecUtil.MIN_HEADER_TABLE_SIZE;
28  import static io.netty5.handler.codec.http2.Http2CodecUtil.MIN_INITIAL_WINDOW_SIZE;
29  import static io.netty5.handler.codec.http2.Http2CodecUtil.NUM_STANDARD_SETTINGS;
30  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_ENABLE_PUSH;
31  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_HEADER_TABLE_SIZE;
32  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_INITIAL_WINDOW_SIZE;
33  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_CONCURRENT_STREAMS;
34  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_FRAME_SIZE;
35  import static io.netty5.handler.codec.http2.Http2CodecUtil.SETTINGS_MAX_HEADER_LIST_SIZE;
36  import static io.netty5.handler.codec.http2.Http2CodecUtil.isMaxFrameSizeValid;
37  import static java.util.Objects.requireNonNull;
38  
39  /**
40   * Settings for one endpoint in an HTTP/2 connection. Each of the values are optional as defined in
41   * the spec for the SETTINGS frame. Permits storage of arbitrary key/value pairs but provides helper
42   * methods for standard settings.
43   */
44  @UnstableApi
45  public final class Http2Settings extends CharObjectHashMap<Long> {
46      /**
47       * Default capacity based on the number of standard settings from the HTTP/2 spec, adjusted so that adding all of
48       * the standard settings will not cause the map capacity to change.
49       */
50      private static final int DEFAULT_CAPACITY = (int) (NUM_STANDARD_SETTINGS / DEFAULT_LOAD_FACTOR) + 1;
51      private static final Long FALSE = 0L;
52      private static final Long TRUE = 1L;
53  
54      public Http2Settings() {
55          this(DEFAULT_CAPACITY);
56      }
57  
58      public Http2Settings(int initialCapacity, float loadFactor) {
59          super(initialCapacity, loadFactor);
60      }
61  
62      public Http2Settings(int initialCapacity) {
63          super(initialCapacity);
64      }
65  
66      /**
67       * Adds the given setting key/value pair. For standard settings defined by the HTTP/2 spec, performs
68       * validation on the values.
69       *
70       * @throws IllegalArgumentException if verification for a standard HTTP/2 setting fails.
71       */
72      @Override
73      public Long put(char key, Long value) {
74          verifyStandardSetting(key, value);
75          return super.put(key, value);
76      }
77  
78      /**
79       * Gets the {@code SETTINGS_HEADER_TABLE_SIZE} value. If unavailable, returns {@code null}.
80       */
81      public Long headerTableSize() {
82          return get(SETTINGS_HEADER_TABLE_SIZE);
83      }
84  
85      /**
86       * Sets the {@code SETTINGS_HEADER_TABLE_SIZE} value.
87       *
88       * @throws IllegalArgumentException if verification of the setting fails.
89       */
90      public Http2Settings headerTableSize(long value) {
91          put(SETTINGS_HEADER_TABLE_SIZE, Long.valueOf(value));
92          return this;
93      }
94  
95      /**
96       * Gets the {@code SETTINGS_ENABLE_PUSH} value. If unavailable, returns {@code null}.
97       */
98      public Boolean pushEnabled() {
99          Long value = get(SETTINGS_ENABLE_PUSH);
100         if (value == null) {
101             return null;
102         }
103         return TRUE.equals(value);
104     }
105 
106     /**
107      * Sets the {@code SETTINGS_ENABLE_PUSH} value.
108      */
109     public Http2Settings pushEnabled(boolean enabled) {
110         put(SETTINGS_ENABLE_PUSH, enabled ? TRUE : FALSE);
111         return this;
112     }
113 
114     /**
115      * Gets the {@code SETTINGS_MAX_CONCURRENT_STREAMS} value. If unavailable, returns {@code null}.
116      */
117     public Long maxConcurrentStreams() {
118         return get(SETTINGS_MAX_CONCURRENT_STREAMS);
119     }
120 
121     /**
122      * Sets the {@code SETTINGS_MAX_CONCURRENT_STREAMS} value.
123      *
124      * @throws IllegalArgumentException if verification of the setting fails.
125      */
126     public Http2Settings maxConcurrentStreams(long value) {
127         put(SETTINGS_MAX_CONCURRENT_STREAMS, Long.valueOf(value));
128         return this;
129     }
130 
131     /**
132      * Gets the {@code SETTINGS_INITIAL_WINDOW_SIZE} value. If unavailable, returns {@code null}.
133      */
134     public Integer initialWindowSize() {
135         return getIntValue(SETTINGS_INITIAL_WINDOW_SIZE);
136     }
137 
138     /**
139      * Sets the {@code SETTINGS_INITIAL_WINDOW_SIZE} value.
140      *
141      * @throws IllegalArgumentException if verification of the setting fails.
142      */
143     public Http2Settings initialWindowSize(int value) {
144         put(SETTINGS_INITIAL_WINDOW_SIZE, Long.valueOf(value));
145         return this;
146     }
147 
148     /**
149      * Gets the {@code SETTINGS_MAX_FRAME_SIZE} value. If unavailable, returns {@code null}.
150      */
151     public Integer maxFrameSize() {
152         return getIntValue(SETTINGS_MAX_FRAME_SIZE);
153     }
154 
155     /**
156      * Sets the {@code SETTINGS_MAX_FRAME_SIZE} value.
157      *
158      * @throws IllegalArgumentException if verification of the setting fails.
159      */
160     public Http2Settings maxFrameSize(int value) {
161         put(SETTINGS_MAX_FRAME_SIZE, Long.valueOf(value));
162         return this;
163     }
164 
165     /**
166      * Gets the {@code SETTINGS_MAX_HEADER_LIST_SIZE} value. If unavailable, returns {@code null}.
167      */
168     public Long maxHeaderListSize() {
169         return get(SETTINGS_MAX_HEADER_LIST_SIZE);
170     }
171 
172     /**
173      * Sets the {@code SETTINGS_MAX_HEADER_LIST_SIZE} value.
174      *
175      * @throws IllegalArgumentException if verification of the setting fails.
176      */
177     public Http2Settings maxHeaderListSize(long value) {
178         put(SETTINGS_MAX_HEADER_LIST_SIZE, Long.valueOf(value));
179         return this;
180     }
181 
182     /**
183      * Clears and then copies the given settings into this object.
184      */
185     public Http2Settings copyFrom(Http2Settings settings) {
186         clear();
187         putAll(settings);
188         return this;
189     }
190 
191     /**
192      * A helper method that returns {@link Long#intValue()} on the return of {@link #get(char)}, if present. Note that
193      * if the range of the value exceeds {@link Integer#MAX_VALUE}, the {@link #get(char)} method should
194      * be used instead to avoid truncation of the value.
195      */
196     public Integer getIntValue(char key) {
197         Long value = get(key);
198         if (value == null) {
199             return null;
200         }
201         return value.intValue();
202     }
203 
204     private static void verifyStandardSetting(int key, Long value) {
205         requireNonNull(value, "value");
206         switch (key) {
207             case SETTINGS_HEADER_TABLE_SIZE:
208                 if (value < MIN_HEADER_TABLE_SIZE || value > MAX_HEADER_TABLE_SIZE) {
209                     throw new IllegalArgumentException("Setting HEADER_TABLE_SIZE is invalid: " + value);
210                 }
211                 break;
212             case SETTINGS_ENABLE_PUSH:
213                 if (value != 0L && value != 1L) {
214                     throw new IllegalArgumentException("Setting ENABLE_PUSH is invalid: " + value);
215                 }
216                 break;
217             case SETTINGS_MAX_CONCURRENT_STREAMS:
218                 if (value < MIN_CONCURRENT_STREAMS || value > MAX_CONCURRENT_STREAMS) {
219                     throw new IllegalArgumentException(
220                             "Setting MAX_CONCURRENT_STREAMS is invalid: " + value);
221                 }
222                 break;
223             case SETTINGS_INITIAL_WINDOW_SIZE:
224                 if (value < MIN_INITIAL_WINDOW_SIZE || value > MAX_INITIAL_WINDOW_SIZE) {
225                     throw new IllegalArgumentException("Setting INITIAL_WINDOW_SIZE is invalid: "
226                             + value);
227                 }
228                 break;
229             case SETTINGS_MAX_FRAME_SIZE:
230                 if (!isMaxFrameSizeValid(value.intValue())) {
231                     throw new IllegalArgumentException("Setting MAX_FRAME_SIZE is invalid: " + value);
232                 }
233                 break;
234             case SETTINGS_MAX_HEADER_LIST_SIZE:
235                 if (value < MIN_HEADER_LIST_SIZE || value > MAX_HEADER_LIST_SIZE) {
236                     throw new IllegalArgumentException("Setting MAX_HEADER_LIST_SIZE is invalid: " + value);
237                 }
238                 break;
239             default:
240                 // Non-standard HTTP/2 setting - don't do validation.
241                 break;
242         }
243     }
244 
245     @Override
246     protected String keyToString(char key) {
247         switch (key) {
248             case SETTINGS_HEADER_TABLE_SIZE:
249                 return "HEADER_TABLE_SIZE";
250             case SETTINGS_ENABLE_PUSH:
251                 return "ENABLE_PUSH";
252             case SETTINGS_MAX_CONCURRENT_STREAMS:
253                 return "MAX_CONCURRENT_STREAMS";
254             case SETTINGS_INITIAL_WINDOW_SIZE:
255                 return "INITIAL_WINDOW_SIZE";
256             case SETTINGS_MAX_FRAME_SIZE:
257                 return "MAX_FRAME_SIZE";
258             case SETTINGS_MAX_HEADER_LIST_SIZE:
259                 return "MAX_HEADER_LIST_SIZE";
260             default:
261                 // Unknown keys.
262                 return super.keyToString(key);
263         }
264     }
265 
266     public static Http2Settings defaultSettings() {
267         return new Http2Settings().maxHeaderListSize(DEFAULT_HEADER_LIST_SIZE);
268     }
269 }