1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.codec.http;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.ChannelFutureListener;
20 import io.netty.channel.ChannelHandlerContext;
21 import io.netty.channel.ChannelPromise;
22 import io.netty.channel.CombinedChannelDuplexHandler;
23 import io.netty.util.ReferenceCountUtil;
24 import io.netty.util.internal.ObjectUtil;
25
26 import java.util.ArrayDeque;
27 import java.util.List;
28 import java.util.Queue;
29
30 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_CHUNK_SIZE;
31 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_HEADER_SIZE;
32 import static io.netty.handler.codec.http.HttpObjectDecoder.DEFAULT_MAX_INITIAL_LINE_LENGTH;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public final class HttpServerCodec extends CombinedChannelDuplexHandler<HttpRequestDecoder, HttpResponseEncoder>
53 implements HttpServerUpgradeHandler.SourceCodec {
54
55
56
57
58
59
60 static final int DEFAULT_MAX_PIPELINE_DEPTH = 128;
61
62
63 private final Queue<HttpMethod> queue = new ArrayDeque<HttpMethod>();
64 private final int maxPipelineDepth;
65
66
67
68
69 private boolean mustCloseAfterResponse;
70
71
72
73
74
75
76 public HttpServerCodec() {
77 this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE);
78 }
79
80
81
82
83 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
84 this(new HttpDecoderConfig()
85 .setMaxInitialLineLength(maxInitialLineLength)
86 .setMaxHeaderSize(maxHeaderSize)
87 .setMaxChunkSize(maxChunkSize));
88 }
89
90
91
92
93
94
95
96 @Deprecated
97 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
98 this(new HttpDecoderConfig()
99 .setMaxInitialLineLength(maxInitialLineLength)
100 .setMaxHeaderSize(maxHeaderSize)
101 .setMaxChunkSize(maxChunkSize)
102 .setValidateHeaders(validateHeaders));
103 }
104
105
106
107
108
109
110
111 @Deprecated
112 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
113 int initialBufferSize) {
114 this(new HttpDecoderConfig()
115 .setMaxInitialLineLength(maxInitialLineLength)
116 .setMaxHeaderSize(maxHeaderSize)
117 .setMaxChunkSize(maxChunkSize)
118 .setValidateHeaders(validateHeaders)
119 .setInitialBufferSize(initialBufferSize));
120 }
121
122
123
124
125
126
127
128 @Deprecated
129 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
130 int initialBufferSize, boolean allowDuplicateContentLengths) {
131 this(new HttpDecoderConfig()
132 .setMaxInitialLineLength(maxInitialLineLength)
133 .setMaxHeaderSize(maxHeaderSize)
134 .setMaxChunkSize(maxChunkSize)
135 .setValidateHeaders(validateHeaders)
136 .setInitialBufferSize(initialBufferSize)
137 .setAllowDuplicateContentLengths(allowDuplicateContentLengths));
138 }
139
140
141
142
143
144
145
146 @Deprecated
147 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
148 int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks) {
149 this(new HttpDecoderConfig()
150 .setMaxInitialLineLength(maxInitialLineLength)
151 .setMaxHeaderSize(maxHeaderSize)
152 .setMaxChunkSize(maxChunkSize)
153 .setValidateHeaders(validateHeaders)
154 .setInitialBufferSize(initialBufferSize)
155 .setAllowDuplicateContentLengths(allowDuplicateContentLengths)
156 .setAllowPartialChunks(allowPartialChunks));
157 }
158
159
160
161
162 public HttpServerCodec(HttpDecoderConfig config) {
163 this(config, DEFAULT_MAX_PIPELINE_DEPTH);
164 }
165
166
167
168
169
170
171
172
173
174 public HttpServerCodec(HttpDecoderConfig config, int maxPipelineDepth) {
175 this.maxPipelineDepth = ObjectUtil.checkPositive(maxPipelineDepth, "maxPipelineDepth");
176 init(new HttpServerRequestDecoder(config), new HttpServerResponseEncoder());
177 }
178
179
180
181
182
183 @Override
184 public void upgradeFrom(ChannelHandlerContext ctx) {
185 ctx.pipeline().remove(this);
186 }
187
188 private boolean enqueueMethod(HttpMethod method) {
189 int currentDepth = queue.size();
190 if (currentDepth >= maxPipelineDepth) {
191 return false;
192 }
193
194 queue.add(method);
195 return true;
196 }
197
198 private final class HttpServerRequestDecoder extends HttpRequestDecoder {
199 private boolean discard;
200
201 HttpServerRequestDecoder(HttpDecoderConfig config) {
202 super(config);
203 }
204
205 @Override
206 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
207 if (discard) {
208 buffer.skipBytes(buffer.readableBytes());
209 return;
210 }
211 int oldSize = out.size();
212 super.decode(ctx, buffer, out);
213 int size = out.size();
214 for (int i = oldSize; i < size; i++) {
215 Object obj = out.get(i);
216 if (obj instanceof HttpRequest) {
217 if (!enqueueMethod(((HttpRequest) obj).method())) {
218
219
220 mustCloseAfterResponse = true;
221 discard = true;
222 ReferenceCountUtil.release(obj);
223 while (++i < size) {
224 ReferenceCountUtil.release(out.get(i));
225 }
226 out.clear();
227 throw new IllegalStateException("maxPipelineDepth exceeded: " + maxPipelineDepth);
228 }
229 }
230 }
231 }
232
233 @Override
234 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
235 super.handleTransferEncodingChunkedWithContentLength(message);
236 mustCloseAfterResponse = true;
237 }
238 }
239
240 private final class HttpServerResponseEncoder extends HttpResponseEncoder {
241
242 private HttpMethod method;
243
244 @Override
245 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
246 if (mustCloseAfterResponse && msg instanceof LastHttpContent) {
247 mustCloseAfterResponse = false;
248 promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);
249 }
250 super.write(ctx, msg, promise);
251 }
252
253 @Override
254 protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) {
255 if (!isAlwaysEmpty && HttpMethod.CONNECT.equals(method)
256 && msg.status().codeClass() == HttpStatusClass.SUCCESS) {
257
258
259 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
260 return;
261 }
262
263 super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty);
264 }
265
266 @Override
267 protected boolean isContentAlwaysEmpty(@SuppressWarnings("unused") HttpResponse msg) {
268 if (msg.status().codeClass() == HttpStatusClass.INFORMATIONAL) {
269
270
271
272 return super.isContentAlwaysEmpty(msg);
273 }
274 method = queue.poll();
275 return HttpMethod.HEAD.equals(method) || super.isContentAlwaysEmpty(msg);
276 }
277 }
278 }