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