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 }