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.buffer.ByteBuf;
19  import io.netty.util.internal.ObjectUtil;
20  
21  import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_HEADER_LIST_SIZE;
22  import static io.netty.handler.codec.http2.Http2Error.COMPRESSION_ERROR;
23  import static io.netty.handler.codec.http2.Http2Error.INTERNAL_ERROR;
24  import static io.netty.handler.codec.http2.Http2Exception.connectionError;
25  
26  public class DefaultHttp2HeadersDecoder implements Http2HeadersDecoder, Http2HeadersDecoder.Configuration {
27      private static final float HEADERS_COUNT_WEIGHT_NEW = 1 / 5f;
28      private static final float HEADERS_COUNT_WEIGHT_HISTORICAL = 1 - HEADERS_COUNT_WEIGHT_NEW;
29  
30      private final HpackDecoder hpackDecoder;
31      private final boolean validateHeaders;
32      private final boolean validateHeaderValues;
33      private long maxHeaderListSizeGoAway;
34  
35      /**
36       * Used to calculate an exponential moving average of header sizes to get an estimate of how large the data
37       * structure for storing headers should be.
38       */
39      private float headerArraySizeAccumulator = 8;
40  
41      /**
42       * Create a new instance with default settings.
43       * Header validation is enabled by default.
44       */
45      public DefaultHttp2HeadersDecoder() {
46          this(true);
47      }
48  
49      /**
50       * Create a new instance.
51       * @param validateHeaders {@code true} to validate headers are valid according to the RFC.
52       * Validates both header name and header value.
53       */
54      public DefaultHttp2HeadersDecoder(boolean validateHeaders) {
55          this(validateHeaders, validateHeaders, DEFAULT_HEADER_LIST_SIZE);
56      }
57  
58      /**
59       * Create a new instance.
60       *
61       * @param validateHeaders {@code true} to validate headers are valid according to the RFC.
62       * This validates everything except header values.
63       * @param validateHeaderValues {@code true} to validate that header <em>values</em> are valid according to the RFC.
64       * Since this is potentially expensive, it can be enabled separately from {@code validateHeaders}.
65       */
66      public DefaultHttp2HeadersDecoder(boolean validateHeaders, boolean validateHeaderValues) {
67          this(validateHeaders, validateHeaderValues, DEFAULT_HEADER_LIST_SIZE);
68      }
69  
70      /**
71       * Create a new instance.
72       * @param validateHeaders {@code true} to validate headers are valid according to the RFC.
73       * Validates both header name and header value.
74       * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer.
75       *  This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
76       *  allows a lower than advertised limit from being enforced, and the default limit is unlimited
77       *  (which is dangerous).
78       */
79      public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize) {
80          this(validateHeaders, validateHeaders, new HpackDecoder(maxHeaderListSize));
81      }
82  
83      /**
84       * Create a new instance.
85       * @param validateHeaders {@code true} to validate headers are valid according to the RFC.
86       * This validates everything except header values.
87       * @param validateHeaderValues {@code true} to validate that header <em>values</em> are valid according to the RFC.
88       * Since this is potentially expensive, it can be enabled separately from {@code validateHeaders}.
89       * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer.
90       *  This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
91       *  allows a lower than advertised limit from being enforced, and the default limit is unlimited
92       *  (which is dangerous).
93       */
94      public DefaultHttp2HeadersDecoder(boolean validateHeaders, boolean validateHeaderValues, long maxHeaderListSize) {
95          this(validateHeaders, validateHeaderValues, new HpackDecoder(maxHeaderListSize));
96      }
97  
98      /**
99       * Create a new instance.
100      * @param validateHeaders {@code true} to validate headers are valid according to the RFC.
101      * Validates both header name and header value.
102      * @param maxHeaderListSize This is the only setting that can be configured before notifying the peer.
103      *  This is because <a href="https://tools.ietf.org/html/rfc7540#section-6.5.1">SETTINGS_MAX_HEADER_LIST_SIZE</a>
104      *  allows a lower than advertised limit from being enforced, and the default limit is unlimited
105      *  (which is dangerous).
106      * @param initialHuffmanDecodeCapacity Does nothing, do not use.
107      */
108     public DefaultHttp2HeadersDecoder(boolean validateHeaders, long maxHeaderListSize,
109                                       @Deprecated int initialHuffmanDecodeCapacity) {
110         this(validateHeaders, validateHeaders, new HpackDecoder(maxHeaderListSize));
111     }
112 
113     /**
114      * Exposed for testing only! Default values used in the initial settings frame are overridden intentionally
115      * for testing but violate the RFC if used outside the scope of testing.
116      */
117     DefaultHttp2HeadersDecoder(boolean validateHeaders, boolean validateHeaderValues, HpackDecoder hpackDecoder) {
118         this.hpackDecoder = ObjectUtil.checkNotNull(hpackDecoder, "hpackDecoder");
119         this.validateHeaders = validateHeaders;
120         this.validateHeaderValues = validateHeaderValues;
121         maxHeaderListSizeGoAway =
122                 Http2CodecUtil.calculateMaxHeaderListSizeGoAway(hpackDecoder.getMaxHeaderListSize());
123     }
124 
125     @Override
126     public void maxHeaderTableSize(long max) throws Http2Exception {
127         hpackDecoder.setMaxHeaderTableSize(max);
128     }
129 
130     @Override
131     public long maxHeaderTableSize() {
132         return hpackDecoder.getMaxHeaderTableSize();
133     }
134 
135     @Override
136     public void maxHeaderListSize(long max, long goAwayMax) throws Http2Exception {
137         if (goAwayMax < max || goAwayMax < 0) {
138             throw connectionError(INTERNAL_ERROR, "Header List Size GO_AWAY %d must be non-negative and >= %d",
139                     goAwayMax, max);
140         }
141         hpackDecoder.setMaxHeaderListSize(max);
142         maxHeaderListSizeGoAway = goAwayMax;
143     }
144 
145     @Override
146     public long maxHeaderListSize() {
147         return hpackDecoder.getMaxHeaderListSize();
148     }
149 
150     @Override
151     public long maxHeaderListSizeGoAway() {
152         return maxHeaderListSizeGoAway;
153     }
154 
155     @Override
156     public Configuration configuration() {
157         return this;
158     }
159 
160     @Override
161     public Http2Headers decodeHeaders(int streamId, ByteBuf headerBlock) throws Http2Exception {
162         try {
163             final Http2Headers headers = newHeaders();
164             hpackDecoder.decode(streamId, headerBlock, headers, validateHeaders);
165             headerArraySizeAccumulator = HEADERS_COUNT_WEIGHT_NEW * headers.size() +
166                                          HEADERS_COUNT_WEIGHT_HISTORICAL * headerArraySizeAccumulator;
167             return headers;
168         } catch (Http2Exception e) {
169             throw e;
170         } catch (Throwable e) {
171             // Default handler for any other types of errors that may have occurred. For example,
172             // the Header builder throws IllegalArgumentException if the key or value was invalid
173             // for any reason (e.g. the key was an invalid pseudo-header).
174             throw connectionError(COMPRESSION_ERROR, e, "Error decoding headers: %s", e.getMessage());
175         }
176     }
177 
178     /**
179      * A weighted moving average estimating how many headers are expected during the decode process.
180      * @return an estimate of how many headers are expected during the decode process.
181      */
182     protected final int numberOfHeadersGuess() {
183         return (int) headerArraySizeAccumulator;
184     }
185 
186     /**
187      * Determines if the headers should be validated as a result of the decode operation.
188      * <p>
189      * <strong>Note:</strong> This does not include validation of header <em>values</em>, since that is potentially
190      * expensive to do. Value validation is instead {@linkplain #validateHeaderValues() enabled separately}.
191      *
192      * @return {@code true} if the headers should be validated as a result of the decode operation.
193      */
194     protected final boolean validateHeaders() {
195         return validateHeaders;
196     }
197 
198     /**
199      * Determines if the header values should be validated as a result of the decode operation.
200      * <p>
201      * <strong>Note:</strong> This <em>only</em> validates the values of headers. All other header validations are
202      * instead {@linkplain #validateHeaders() enabled separately}.
203      *
204      * @return {@code true} if the header values should be validated as a result of the decode operation.
205      */
206     protected boolean validateHeaderValues() { // Not 'final' due to backwards compatibility.
207         return validateHeaderValues;
208     }
209 
210     /**
211      * Create a new {@link Http2Headers} object which will store the results of the decode operation.
212      * @return a new {@link Http2Headers} object which will store the results of the decode operation.
213      */
214     protected Http2Headers newHeaders() {
215         return new DefaultHttp2Headers(validateHeaders, validateHeaderValues, (int) headerArraySizeAccumulator);
216     }
217 }