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