View Javadoc
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      private boolean useRfc9112TransferEncoding = HttpObjectDecoder.RFC9112_TRANSFER_ENCODING;
39  
40      public int getInitialBufferSize() {
41          return initialBufferSize;
42      }
43  
44      /**
45       * Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers.
46       *
47       * @param initialBufferSize The buffer size in bytes.
48       * @return This decoder config.
49       */
50      public HttpDecoderConfig setInitialBufferSize(int initialBufferSize) {
51          checkPositive(initialBufferSize, "initialBufferSize");
52          this.initialBufferSize = initialBufferSize;
53          return this;
54      }
55  
56      public int getMaxInitialLineLength() {
57          return maxInitialLineLength;
58      }
59  
60      /**
61       * Set the maximum length of the first line of the HTTP header.
62       * This limits how much memory Netty will use when parsed the initial HTTP header line.
63       * You would typically set this to the same value as {@link #setMaxHeaderSize(int)}.
64       *
65       * @param maxInitialLineLength The maximum length, in bytes.
66       * @return This decoder config.
67       */
68      public HttpDecoderConfig setMaxInitialLineLength(int maxInitialLineLength) {
69          checkPositive(maxInitialLineLength, "maxInitialLineLength");
70          this.maxInitialLineLength = maxInitialLineLength;
71          return this;
72      }
73  
74      public int getMaxHeaderSize() {
75          return maxHeaderSize;
76      }
77  
78      /**
79       * Set the maximum line length of header lines.
80       * This limits how much memory Netty will use when parsing HTTP header key-value pairs.
81       * The limit applies to the sum of all the headers, so it applies equally to many short header-lines,
82       * or fewer but longer header lines.
83       * <p>
84       * You would typically set this to the same value as {@link #setMaxInitialLineLength(int)}.
85       *
86       * @param maxHeaderSize The maximum length, in bytes.
87       * @return This decoder config.
88       */
89      public HttpDecoderConfig setMaxHeaderSize(int maxHeaderSize) {
90          checkPositive(maxHeaderSize, "maxHeaderSize");
91          this.maxHeaderSize = maxHeaderSize;
92          return this;
93      }
94  
95      public int getMaxChunkSize() {
96          return maxChunkSize;
97      }
98  
99      /**
100      * Set the maximum chunk size.
101      * HTTP requests and responses can be quite large, in which case it's better to process the data as a stream of
102      * chunks.
103      * This sets the limit, in bytes, at which Netty will send a chunk down the pipeline.
104      *
105      * @param maxChunkSize The maximum chunk size, in bytes.
106      * @return This decoder config.
107      */
108     public HttpDecoderConfig setMaxChunkSize(int maxChunkSize) {
109         checkPositive(maxChunkSize, "maxChunkSize");
110         this.maxChunkSize = maxChunkSize;
111         return this;
112     }
113 
114     public boolean isChunkedSupported() {
115         return chunkedSupported;
116     }
117 
118     /**
119      * Set whether {@code Transfer-Encoding: Chunked} should be supported.
120      *
121      * @param chunkedSupported if {@code false}, then a {@code Transfer-Encoding: Chunked} header will produce an error,
122      * instead of a stream of chunks.
123      * @return This decoder config.
124      */
125     public HttpDecoderConfig setChunkedSupported(boolean chunkedSupported) {
126         this.chunkedSupported = chunkedSupported;
127         return this;
128     }
129 
130     public boolean isAllowPartialChunks() {
131         return allowPartialChunks;
132     }
133 
134     /**
135      * Set whether chunks can be split into multiple messages, if their chunk size exceeds the size of the input buffer.
136      *
137      * @param allowPartialChunks set to {@code false} to only allow sending whole chunks down the pipeline.
138      * @return This decoder config.
139      */
140     public HttpDecoderConfig setAllowPartialChunks(boolean allowPartialChunks) {
141         this.allowPartialChunks = allowPartialChunks;
142         return this;
143     }
144 
145     public HttpHeadersFactory getHeadersFactory() {
146         return headersFactory;
147     }
148 
149     /**
150      * Set the {@link HttpHeadersFactory} to use when creating new HTTP headers objects.
151      * The default headers factory is {@link DefaultHttpHeadersFactory#headersFactory()}.
152      * <p>
153      * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be
154      * shared across different decoders and decoder configs.
155      *
156      * @param headersFactory The header factory to use.
157      * @return This decoder config.
158      */
159     public HttpDecoderConfig setHeadersFactory(HttpHeadersFactory headersFactory) {
160         checkNotNull(headersFactory, "headersFactory");
161         this.headersFactory = headersFactory;
162         return this;
163     }
164 
165     public boolean isAllowDuplicateContentLengths() {
166         return allowDuplicateContentLengths;
167     }
168 
169     /**
170      * Set whether more than one {@code Content-Length} header is allowed.
171      * You usually want to disallow this (which is the default) as multiple {@code Content-Length} headers can indicate
172      * a request- or response-splitting attack.
173      *
174      * @param allowDuplicateContentLengths set to {@code true} to allow multiple content length headers.
175      * @return This decoder config.
176      */
177     public HttpDecoderConfig setAllowDuplicateContentLengths(boolean allowDuplicateContentLengths) {
178         this.allowDuplicateContentLengths = allowDuplicateContentLengths;
179         return this;
180     }
181 
182     /**
183      * Set whether header validation should be enabled or not.
184      * This works by changing the configured {@linkplain #setHeadersFactory(HttpHeadersFactory) header factory}
185      * and {@linkplain #setTrailersFactory(HttpHeadersFactory) trailer factory}.
186      * <p>
187      * You usually want header validation enabled (which is the default) in order to prevent request-/response-splitting
188      * attacks.
189      *
190      * @param validateHeaders set to {@code false} to disable header validation.
191      * @return This decoder config.
192      */
193     public HttpDecoderConfig setValidateHeaders(boolean validateHeaders) {
194         DefaultHttpHeadersFactory noValidation = DefaultHttpHeadersFactory.headersFactory().withValidation(false);
195         headersFactory = validateHeaders ? DefaultHttpHeadersFactory.headersFactory() : noValidation;
196         trailersFactory = validateHeaders ? DefaultHttpHeadersFactory.trailersFactory() : noValidation;
197         return this;
198     }
199 
200     public HttpHeadersFactory getTrailersFactory() {
201         return trailersFactory;
202     }
203 
204     /**
205      * Set the {@link HttpHeadersFactory} used to create HTTP trailers.
206      * This differs from {@link #setHeadersFactory(HttpHeadersFactory)} in that trailers have different validation
207      * requirements.
208      * The default trailer factory is {@link DefaultHttpHeadersFactory#headersFactory()}.
209      * <p>
210      * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be
211      * shared across different decoders and decoder configs.
212      *
213      * @param trailersFactory The headers factory to use for creating trailers.
214      * @return This decoder config.
215      */
216     public HttpDecoderConfig setTrailersFactory(HttpHeadersFactory trailersFactory) {
217         checkNotNull(trailersFactory, "trailersFactory");
218         this.trailersFactory = trailersFactory;
219         return this;
220     }
221 
222     public boolean isStrictLineParsing() {
223         return strictLineParsing;
224     }
225 
226     /**
227      * The RFC 9112 specification for the HTTP protocol says that the initial start-line, and the following header
228      * field-lines, must be separated by a Carriage Return (CR) and Line Feed (LF) octet pair, but also offers that
229      * implementations "MAY" accept just a Line Feed octet as a separator.
230      * <p>
231      * Parsing leniencies can increase compatibility with a wider range of implementations, but can also cause
232      * security vulnerabilities, when multiple systems disagree on the meaning of leniently parsed messages.
233      * <p>
234      * When <em>strict line parsing</em> is enabled ({@code true}), then Netty will enforce that start- and header
235      * field-lines MUST be separated by a CR LF octet pair, and will produce messagas with failed
236      * {@link io.netty.handler.codec.DecoderResult}s.
237      * <p>
238      * When <em>strict line parsing</em> is disabled ({@code false}), then Netty will accept lone LF octets as line
239      * seperators for the start- and header field-lines.
240      * <p>
241      * See <a href="https://datatracker.ietf.org/doc/html/rfc9112#name-message-format">RFC 9112 Section 2.1</a>.
242      * @param strictLineParsing Whether <em>strict line parsing</em> should be enabled ({@code true}),
243      * or not ({@code false}).
244      * @return This decoder config.
245      */
246     public HttpDecoderConfig setStrictLineParsing(boolean strictLineParsing) {
247         this.strictLineParsing = strictLineParsing;
248         return this;
249     }
250 
251     public boolean isUseRfc9112TransferEncoding() {
252         return useRfc9112TransferEncoding;
253     }
254 
255     /**
256      * The RFC 9112 specification is more strict than RFC 7230 with regards to having {@code Transfer-Encoding} and
257      * {@code Content-Length} headers in the same HTTP message. Senders are now forbidden from including both headers
258      * in the same message, while servers may reject such requests. When this setting is set to {@code true}, which
259      * is the default, then such messages will be <em>rejected.</em>
260      * <p>
261      * When this setting is set to {@code false}, it restores the RFC 7230 behavior of instead removing any
262      * {@code Content-Length} headers when {@code Transfer-Encoding} headers are present.
263      * @param useRfc9112TransferEncoding Whether to reject messages with both {@code Transfer-Encoding} and
264      *                                   {@code Content-Length} headers.
265      * @return This decoder config.
266      * @see HttpObjectDecoder#handleTransferEncodingChunkedWithContentLength(HttpMessage)
267      */
268     public HttpDecoderConfig setUseRfc9112TransferEncoding(boolean useRfc9112TransferEncoding) {
269         this.useRfc9112TransferEncoding = useRfc9112TransferEncoding;
270         return this;
271     }
272 
273     @Override
274     public HttpDecoderConfig clone() {
275         try {
276             return (HttpDecoderConfig) super.clone();
277         } catch (CloneNotSupportedException e) {
278             throw new AssertionError();
279         }
280     }
281 }