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