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