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