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  
38      public int getInitialBufferSize() {
39          return initialBufferSize;
40      }
41  
42      /**
43       * Set the initial size of the temporary buffer used when parsing the lines of the HTTP headers.
44       *
45       * @param initialBufferSize The buffer size in bytes.
46       * @return This decoder config.
47       */
48      public HttpDecoderConfig setInitialBufferSize(int initialBufferSize) {
49          checkPositive(initialBufferSize, "initialBufferSize");
50          this.initialBufferSize = initialBufferSize;
51          return this;
52      }
53  
54      public int getMaxInitialLineLength() {
55          return maxInitialLineLength;
56      }
57  
58      /**
59       * Set the maximum length of the first line of the HTTP header.
60       * This limits how much memory Netty will use when parsed the initial HTTP header line.
61       * You would typically set this to the same value as {@link #setMaxHeaderSize(int)}.
62       *
63       * @param maxInitialLineLength The maximum length, in bytes.
64       * @return This decoder config.
65       */
66      public HttpDecoderConfig setMaxInitialLineLength(int maxInitialLineLength) {
67          checkPositive(maxInitialLineLength, "maxInitialLineLength");
68          this.maxInitialLineLength = maxInitialLineLength;
69          return this;
70      }
71  
72      public int getMaxHeaderSize() {
73          return maxHeaderSize;
74      }
75  
76      /**
77       * Set the maximum line length of header lines.
78       * This limits how much memory Netty will use when parsing HTTP header key-value pairs.
79       * The limit applies to the sum of all the headers, so it applies equally to many short header-lines,
80       * or fewer but longer header lines.
81       * <p>
82       * You would typically set this to the same value as {@link #setMaxInitialLineLength(int)}.
83       *
84       * @param maxHeaderSize The maximum length, in bytes.
85       * @return This decoder config.
86       */
87      public HttpDecoderConfig setMaxHeaderSize(int maxHeaderSize) {
88          checkPositive(maxHeaderSize, "maxHeaderSize");
89          this.maxHeaderSize = maxHeaderSize;
90          return this;
91      }
92  
93      public int getMaxChunkSize() {
94          return maxChunkSize;
95      }
96  
97      /**
98       * Set the maximum chunk size.
99       * HTTP requests and responses can be quite large, in which case it's better to process the data as a stream of
100      * chunks.
101      * This sets the limit, in bytes, at which Netty will send a chunk down the pipeline.
102      *
103      * @param maxChunkSize The maximum chunk size, in bytes.
104      * @return This decoder config.
105      */
106     public HttpDecoderConfig setMaxChunkSize(int maxChunkSize) {
107         checkPositive(maxChunkSize, "maxChunkSize");
108         this.maxChunkSize = maxChunkSize;
109         return this;
110     }
111 
112     public boolean isChunkedSupported() {
113         return chunkedSupported;
114     }
115 
116     /**
117      * Set whether {@code Transfer-Encoding: Chunked} should be supported.
118      *
119      * @param chunkedSupported if {@code false}, then a {@code Transfer-Encoding: Chunked} header will produce an error,
120      * instead of a stream of chunks.
121      * @return This decoder config.
122      */
123     public HttpDecoderConfig setChunkedSupported(boolean chunkedSupported) {
124         this.chunkedSupported = chunkedSupported;
125         return this;
126     }
127 
128     public boolean isAllowPartialChunks() {
129         return allowPartialChunks;
130     }
131 
132     /**
133      * Set whether chunks can be split into multiple messages, if their chunk size exceeds the size of the input buffer.
134      *
135      * @param allowPartialChunks set to {@code false} to only allow sending whole chunks down the pipeline.
136      * @return This decoder config.
137      */
138     public HttpDecoderConfig setAllowPartialChunks(boolean allowPartialChunks) {
139         this.allowPartialChunks = allowPartialChunks;
140         return this;
141     }
142 
143     public HttpHeadersFactory getHeadersFactory() {
144         return headersFactory;
145     }
146 
147     /**
148      * Set the {@link HttpHeadersFactory} to use when creating new HTTP headers objects.
149      * The default headers factory is {@link DefaultHttpHeadersFactory#headersFactory()}.
150      * <p>
151      * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be
152      * shared across different decoders and decoder configs.
153      *
154      * @param headersFactory The header factory to use.
155      * @return This decoder config.
156      */
157     public HttpDecoderConfig setHeadersFactory(HttpHeadersFactory headersFactory) {
158         checkNotNull(headersFactory, "headersFactory");
159         this.headersFactory = headersFactory;
160         return this;
161     }
162 
163     public boolean isAllowDuplicateContentLengths() {
164         return allowDuplicateContentLengths;
165     }
166 
167     /**
168      * Set whether more than one {@code Content-Length} header is allowed.
169      * You usually want to disallow this (which is the default) as multiple {@code Content-Length} headers can indicate
170      * a request- or response-splitting attack.
171      *
172      * @param allowDuplicateContentLengths set to {@code true} to allow multiple content length headers.
173      * @return This decoder config.
174      */
175     public HttpDecoderConfig setAllowDuplicateContentLengths(boolean allowDuplicateContentLengths) {
176         this.allowDuplicateContentLengths = allowDuplicateContentLengths;
177         return this;
178     }
179 
180     /**
181      * Set whether header validation should be enabled or not.
182      * This works by changing the configured {@linkplain #setHeadersFactory(HttpHeadersFactory) header factory}
183      * and {@linkplain #setTrailersFactory(HttpHeadersFactory) trailer factory}.
184      * <p>
185      * You usually want header validation enabled (which is the default) in order to prevent request-/response-splitting
186      * attacks.
187      *
188      * @param validateHeaders set to {@code false} to disable header validation.
189      * @return This decoder config.
190      */
191     public HttpDecoderConfig setValidateHeaders(boolean validateHeaders) {
192         DefaultHttpHeadersFactory noValidation = DefaultHttpHeadersFactory.headersFactory().withValidation(false);
193         headersFactory = validateHeaders ? DefaultHttpHeadersFactory.headersFactory() : noValidation;
194         trailersFactory = validateHeaders ? DefaultHttpHeadersFactory.trailersFactory() : noValidation;
195         return this;
196     }
197 
198     public HttpHeadersFactory getTrailersFactory() {
199         return trailersFactory;
200     }
201 
202     /**
203      * Set the {@link HttpHeadersFactory} used to create HTTP trailers.
204      * This differs from {@link #setHeadersFactory(HttpHeadersFactory)} in that trailers have different validation
205      * requirements.
206      * The default trailer factory is {@link DefaultHttpHeadersFactory#headersFactory()}.
207      * <p>
208      * For the purpose of {@link #clone()}, it is assumed that the factory is either immutable, or can otherwise be
209      * shared across different decoders and decoder configs.
210      *
211      * @param trailersFactory The headers factory to use for creating trailers.
212      * @return This decoder config.
213      */
214     public HttpDecoderConfig setTrailersFactory(HttpHeadersFactory trailersFactory) {
215         checkNotNull(trailersFactory, "trailersFactory");
216         this.trailersFactory = trailersFactory;
217         return this;
218     }
219 
220     @Override
221     public HttpDecoderConfig clone() {
222         try {
223             return (HttpDecoderConfig) super.clone();
224         } catch (CloneNotSupportedException e) {
225             throw new AssertionError();
226         }
227     }
228 }