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