1 /* 2 * Copyright 2023 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a 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 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package io.netty.handler.codec.http; 17 18 import static io.netty.util.internal.ObjectUtil.checkNotNull; 19 import static io.netty.util.internal.ObjectUtil.checkPositive; 20 21 /** 22 * A configuration object for specifying the behaviour of {@link HttpObjectDecoder} and its subclasses. 23 * <p> 24 * The {@link HttpDecoderConfig} objects are mutable to reduce allocation, 25 * but also {@link Cloneable} in case a defensive copy is needed. 26 */ 27 public final class HttpDecoderConfig implements Cloneable { 28 private int maxChunkSize = HttpObjectDecoder.DEFAULT_MAX_CHUNK_SIZE; 29 private boolean chunkedSupported = HttpObjectDecoder.DEFAULT_CHUNKED_SUPPORTED; 30 private boolean allowPartialChunks = HttpObjectDecoder.DEFAULT_ALLOW_PARTIAL_CHUNKS; 31 private HttpHeadersFactory headersFactory = DefaultHttpHeadersFactory.headersFactory(); 32 private HttpHeadersFactory trailersFactory = DefaultHttpHeadersFactory.trailersFactory(); 33 private boolean allowDuplicateContentLengths = HttpObjectDecoder.DEFAULT_ALLOW_DUPLICATE_CONTENT_LENGTHS; 34 private int maxInitialLineLength = HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH; 35 private int maxHeaderSize = HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE; 36 private int initialBufferSize = HttpObjectDecoder.DEFAULT_INITIAL_BUFFER_SIZE; 37 private boolean strictLineParsing = HttpObjectDecoder.DEFAULT_STRICT_LINE_PARSING; 38 39 public int getInitialBufferSize() { 40 return initialBufferSize; 41 } 42 43 /** 44 * Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers. 45 * 46 * @param initialBufferSize The buffer size in bytes. 47 * @return This decoder config. 48 */ 49 public HttpDecoderConfig setInitialBufferSize(int initialBufferSize) { 50 checkPositive(initialBufferSize, "initialBufferSize"); 51 this.initialBufferSize = initialBufferSize; 52 return this; 53 } 54 55 public int getMaxInitialLineLength() { 56 return maxInitialLineLength; 57 } 58 59 /** 60 * Set the maximum length of the first line of the HTTP header. 61 * This limits how much memory Netty will use when parsed the initial HTTP header line. 62 * You would typically set this to the same value as {@link #setMaxHeaderSize(int)}. 63 * 64 * @param maxInitialLineLength The maximum length, in bytes. 65 * @return This decoder config. 66 */ 67 public HttpDecoderConfig setMaxInitialLineLength(int maxInitialLineLength) { 68 checkPositive(maxInitialLineLength, "maxInitialLineLength"); 69 this.maxInitialLineLength = maxInitialLineLength; 70 return this; 71 } 72 73 public int getMaxHeaderSize() { 74 return maxHeaderSize; 75 } 76 77 /** 78 * Set the maximum line length of header lines. 79 * This limits how much memory Netty will use when parsing HTTP header key-value pairs. 80 * The limit applies to the sum of all the headers, so it applies equally to many short header-lines, 81 * or fewer but longer header lines. 82 * <p> 83 * You would typically set this to the same value as {@link #setMaxInitialLineLength(int)}. 84 * 85 * @param maxHeaderSize The maximum length, in bytes. 86 * @return This decoder config. 87 */ 88 public HttpDecoderConfig setMaxHeaderSize(int maxHeaderSize) { 89 checkPositive(maxHeaderSize, "maxHeaderSize"); 90 this.maxHeaderSize = maxHeaderSize; 91 return this; 92 } 93 94 public int getMaxChunkSize() { 95 return maxChunkSize; 96 } 97 98 /** 99 * Set the maximum chunk size. 100 * HTTP requests and responses can be quite large, in which case it's better to process the data as a stream of 101 * chunks. 102 * This sets the limit, in bytes, at which Netty will send a chunk down the pipeline. 103 * 104 * @param maxChunkSize The maximum chunk size, in bytes. 105 * @return This decoder config. 106 */ 107 public HttpDecoderConfig setMaxChunkSize(int maxChunkSize) { 108 checkPositive(maxChunkSize, "maxChunkSize"); 109 this.maxChunkSize = maxChunkSize; 110 return this; 111 } 112 113 public boolean isChunkedSupported() { 114 return chunkedSupported; 115 } 116 117 /** 118 * Set whether {@code Transfer-Encoding: Chunked} should be supported. 119 * 120 * @param chunkedSupported if {@code false}, then a {@code Transfer-Encoding: Chunked} header will produce an error, 121 * instead of a stream of chunks. 122 * @return This decoder config. 123 */ 124 public HttpDecoderConfig setChunkedSupported(boolean chunkedSupported) { 125 this.chunkedSupported = chunkedSupported; 126 return this; 127 } 128 129 public boolean isAllowPartialChunks() { 130 return allowPartialChunks; 131 } 132 133 /** 134 * Set whether chunks can be split into multiple messages, if their chunk size exceeds the size of the input buffer. 135 * 136 * @param allowPartialChunks set to {@code false} to only allow sending whole chunks down the pipeline. 137 * @return This decoder config. 138 */ 139 public HttpDecoderConfig setAllowPartialChunks(boolean allowPartialChunks) { 140 this.allowPartialChunks = allowPartialChunks; 141 return this; 142 } 143 144 public HttpHeadersFactory getHeadersFactory() { 145 return headersFactory; 146 } 147 148 /** 149 * Set the {@link HttpHeadersFactory} to use when creating new HTTP headers objects. 150 * The default headers factory is {@link DefaultHttpHeadersFactory#headersFactory()}. 151 * <p> 152 * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be 153 * shared across different decoders and decoder configs. 154 * 155 * @param headersFactory The header factory to use. 156 * @return This decoder config. 157 */ 158 public HttpDecoderConfig setHeadersFactory(HttpHeadersFactory headersFactory) { 159 checkNotNull(headersFactory, "headersFactory"); 160 this.headersFactory = headersFactory; 161 return this; 162 } 163 164 public boolean isAllowDuplicateContentLengths() { 165 return allowDuplicateContentLengths; 166 } 167 168 /** 169 * Set whether more than one {@code Content-Length} header is allowed. 170 * You usually want to disallow this (which is the default) as multiple {@code Content-Length} headers can indicate 171 * a request- or response-splitting attack. 172 * 173 * @param allowDuplicateContentLengths set to {@code true} to allow multiple content length headers. 174 * @return This decoder config. 175 */ 176 public HttpDecoderConfig setAllowDuplicateContentLengths(boolean allowDuplicateContentLengths) { 177 this.allowDuplicateContentLengths = allowDuplicateContentLengths; 178 return this; 179 } 180 181 /** 182 * Set whether header validation should be enabled or not. 183 * This works by changing the configured {@linkplain #setHeadersFactory(HttpHeadersFactory) header factory} 184 * and {@linkplain #setTrailersFactory(HttpHeadersFactory) trailer factory}. 185 * <p> 186 * You usually want header validation enabled (which is the default) in order to prevent request-/response-splitting 187 * attacks. 188 * 189 * @param validateHeaders set to {@code false} to disable header validation. 190 * @return This decoder config. 191 */ 192 public HttpDecoderConfig setValidateHeaders(boolean validateHeaders) { 193 DefaultHttpHeadersFactory noValidation = DefaultHttpHeadersFactory.headersFactory().withValidation(false); 194 headersFactory = validateHeaders ? DefaultHttpHeadersFactory.headersFactory() : noValidation; 195 trailersFactory = validateHeaders ? DefaultHttpHeadersFactory.trailersFactory() : noValidation; 196 return this; 197 } 198 199 public HttpHeadersFactory getTrailersFactory() { 200 return trailersFactory; 201 } 202 203 /** 204 * Set the {@link HttpHeadersFactory} used to create HTTP trailers. 205 * This differs from {@link #setHeadersFactory(HttpHeadersFactory)} in that trailers have different validation 206 * requirements. 207 * The default trailer factory is {@link DefaultHttpHeadersFactory#headersFactory()}. 208 * <p> 209 * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be 210 * shared across different decoders and decoder configs. 211 * 212 * @param trailersFactory The headers factory to use for creating trailers. 213 * @return This decoder config. 214 */ 215 public HttpDecoderConfig setTrailersFactory(HttpHeadersFactory trailersFactory) { 216 checkNotNull(trailersFactory, "trailersFactory"); 217 this.trailersFactory = trailersFactory; 218 return this; 219 } 220 221 public boolean isStrictLineParsing() { 222 return strictLineParsing; 223 } 224 225 /** 226 * The RFC 9112 specification for the HTTP protocol says that the initial start-line, and the following header 227 * field-lines, must be separated by a Carriage Return (CR) and Line Feed (LF) octet pair, but also offers that 228 * implementations "MAY" accept just a Line Feed octet as a separator. 229 * <p> 230 * Parsing leniencies can increase compatibility with a wider range of implementations, but can also cause 231 * security vulnerabilities, when multiple systems disagree on the meaning of leniently parsed messages. 232 * <p> 233 * When <em>strict line parsing</em> is enabled ({@code true}), then Netty will enforce that start- and header 234 * field-lines MUST be separated by a CR LF octet pair, and will produce messagas with failed 235 * {@link io.netty.handler.codec.DecoderResult}s. 236 * <p> 237 * When <em>strict line parsing</em> is disabled ({@code false}), then Netty will accept lone LF octets as line 238 * seperators for the start- and header field-lines. 239 * <p> 240 * See <a href="https://datatracker.ietf.org/doc/html/rfc9112#name-message-format">RFC 9112 Section 2.1</a>. 241 * @param strictLineParsing Whether <em>strict line parsing</em> should be enabled ({@code true}), 242 * or not ({@code false}). 243 * @return This decoder config. 244 */ 245 public HttpDecoderConfig setStrictLineParsing(boolean strictLineParsing) { 246 this.strictLineParsing = strictLineParsing; 247 return this; 248 } 249 250 @Override 251 public HttpDecoderConfig clone() { 252 try { 253 return (HttpDecoderConfig) super.clone(); 254 } catch (CloneNotSupportedException e) { 255 throw new AssertionError(); 256 } 257 } 258 }