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  
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 }