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 private static final byte METHOD_FLAG_HEAD = 1;
63 private static final byte METHOD_FLAG_CONNECT = 2;
64 private static final byte METHOD_FLAG_OTHER = 3;
65
66
67
68 private static final int METHOD_FLAG_BITS = 2;
69 private static final int INLINE_QUEUE_CAPACITY = Long.SIZE / METHOD_FLAG_BITS;
70
71
72
73
74
75
76
77
78
79
80 private long methodQueue;
81 private int methodQueueSize;
82 private Queue<Byte> methodOverflowQueue;
83 private final int maxPipelineDepth;
84
85
86
87
88 private boolean mustCloseAfterResponse;
89
90
91
92
93
94
95 public HttpServerCodec() {
96 this(DEFAULT_MAX_INITIAL_LINE_LENGTH, DEFAULT_MAX_HEADER_SIZE, DEFAULT_MAX_CHUNK_SIZE);
97 }
98
99
100
101
102 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
103 this(new HttpDecoderConfig()
104 .setMaxInitialLineLength(maxInitialLineLength)
105 .setMaxHeaderSize(maxHeaderSize)
106 .setMaxChunkSize(maxChunkSize));
107 }
108
109
110
111
112
113
114
115 @Deprecated
116 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders) {
117 this(new HttpDecoderConfig()
118 .setMaxInitialLineLength(maxInitialLineLength)
119 .setMaxHeaderSize(maxHeaderSize)
120 .setMaxChunkSize(maxChunkSize)
121 .setValidateHeaders(validateHeaders));
122 }
123
124
125
126
127
128
129
130 @Deprecated
131 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
132 int initialBufferSize) {
133 this(new HttpDecoderConfig()
134 .setMaxInitialLineLength(maxInitialLineLength)
135 .setMaxHeaderSize(maxHeaderSize)
136 .setMaxChunkSize(maxChunkSize)
137 .setValidateHeaders(validateHeaders)
138 .setInitialBufferSize(initialBufferSize));
139 }
140
141
142
143
144
145
146
147 @Deprecated
148 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
149 int initialBufferSize, boolean allowDuplicateContentLengths) {
150 this(new HttpDecoderConfig()
151 .setMaxInitialLineLength(maxInitialLineLength)
152 .setMaxHeaderSize(maxHeaderSize)
153 .setMaxChunkSize(maxChunkSize)
154 .setValidateHeaders(validateHeaders)
155 .setInitialBufferSize(initialBufferSize)
156 .setAllowDuplicateContentLengths(allowDuplicateContentLengths));
157 }
158
159
160
161
162
163
164
165 @Deprecated
166 public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize, boolean validateHeaders,
167 int initialBufferSize, boolean allowDuplicateContentLengths, boolean allowPartialChunks) {
168 this(new HttpDecoderConfig()
169 .setMaxInitialLineLength(maxInitialLineLength)
170 .setMaxHeaderSize(maxHeaderSize)
171 .setMaxChunkSize(maxChunkSize)
172 .setValidateHeaders(validateHeaders)
173 .setInitialBufferSize(initialBufferSize)
174 .setAllowDuplicateContentLengths(allowDuplicateContentLengths)
175 .setAllowPartialChunks(allowPartialChunks));
176 }
177
178
179
180
181 public HttpServerCodec(HttpDecoderConfig config) {
182 this(config, DEFAULT_MAX_PIPELINE_DEPTH);
183 }
184
185
186
187
188
189
190
191
192
193 public HttpServerCodec(HttpDecoderConfig config, int maxPipelineDepth) {
194 this.maxPipelineDepth = ObjectUtil.checkPositive(maxPipelineDepth, "maxPipelineDepth");
195 init(new HttpServerRequestDecoder(config), new HttpServerResponseEncoder());
196 }
197
198
199
200
201
202 @Override
203 public void upgradeFrom(ChannelHandlerContext ctx) {
204 ctx.pipeline().remove(this);
205 }
206
207 private boolean enqueueMethod(HttpMethod method) {
208 Queue<Byte> overflowQueue = methodOverflowQueue;
209 int currentDepth = methodQueueSize + (overflowQueue != null ? overflowQueue.size() : 0);
210 if (currentDepth >= maxPipelineDepth) {
211 return false;
212 }
213
214 final byte flag;
215 if (HttpMethod.HEAD.equals(method)) {
216 flag = METHOD_FLAG_HEAD;
217 } else if (HttpMethod.CONNECT.equals(method)) {
218 flag = METHOD_FLAG_CONNECT;
219 } else {
220 flag = METHOD_FLAG_OTHER;
221 }
222
223
224 if (overflowQueue != null) {
225 overflowQueue.add(flag);
226 return true;
227 }
228
229 if (methodQueueSize < INLINE_QUEUE_CAPACITY) {
230 methodQueue |= (long) flag << (methodQueueSize << 1);
231 methodQueueSize++;
232 } else {
233 overflowQueue = new ArrayDeque<>(4);
234 overflowQueue.add(flag);
235 methodOverflowQueue = overflowQueue;
236 }
237 return true;
238 }
239
240 private byte pollMethod() {
241 if (methodQueueSize != 0) {
242
243 byte flag = (byte) (methodQueue & 0x3L);
244 methodQueue >>>= METHOD_FLAG_BITS;
245 methodQueueSize--;
246 return flag;
247 }
248
249 Queue<Byte> overflowQueue = methodOverflowQueue;
250 if (overflowQueue != null) {
251 Byte flag = overflowQueue.poll();
252 if (overflowQueue.isEmpty()) {
253 methodOverflowQueue = null;
254 }
255 return flag != null ? flag : METHOD_FLAG_OTHER;
256 }
257
258 return METHOD_FLAG_OTHER;
259 }
260
261 private final class HttpServerRequestDecoder extends HttpRequestDecoder {
262 private boolean discard;
263
264 HttpServerRequestDecoder(HttpDecoderConfig config) {
265 super(config);
266 }
267
268 @Override
269 protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, List<Object> out) throws Exception {
270 if (discard) {
271 buffer.skipBytes(buffer.readableBytes());
272 return;
273 }
274 int oldSize = out.size();
275 super.decode(ctx, buffer, out);
276 int size = out.size();
277 for (int i = oldSize; i < size; i++) {
278 Object obj = out.get(i);
279 if (obj instanceof HttpRequest) {
280 if (!enqueueMethod(((HttpRequest) obj).method())) {
281
282
283 mustCloseAfterResponse = true;
284 discard = true;
285 ReferenceCountUtil.release(obj);
286 while (++i < size) {
287 ReferenceCountUtil.release(out.get(i));
288 }
289 out.clear();
290 throw new IllegalStateException("maxPipelineDepth exceeded: " + maxPipelineDepth);
291 }
292 }
293 }
294 }
295
296 @Override
297 protected void handleTransferEncodingChunkedWithContentLength(HttpMessage message) {
298 super.handleTransferEncodingChunkedWithContentLength(message);
299 mustCloseAfterResponse = true;
300 }
301 }
302
303 private final class HttpServerResponseEncoder extends HttpResponseEncoder {
304
305 private byte methodFlag;
306
307 @Override
308 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
309 if (mustCloseAfterResponse && msg instanceof LastHttpContent) {
310 mustCloseAfterResponse = false;
311 promise = promise.unvoid().addListener(ChannelFutureListener.CLOSE);
312 }
313 super.write(ctx, msg, promise);
314 }
315
316 @Override
317 protected void sanitizeHeadersBeforeEncode(HttpResponse msg, boolean isAlwaysEmpty) {
318 if (!isAlwaysEmpty && methodFlag == METHOD_FLAG_CONNECT
319 && msg.status().codeClass() == HttpStatusClass.SUCCESS) {
320
321
322 msg.headers().remove(HttpHeaderNames.TRANSFER_ENCODING);
323 return;
324 }
325
326 super.sanitizeHeadersBeforeEncode(msg, isAlwaysEmpty);
327 }
328
329 @Override
330 protected boolean isContentAlwaysEmpty(HttpResponse msg) {
331 if (msg.status().codeClass() == HttpStatusClass.INFORMATIONAL) {
332
333
334
335 return super.isContentAlwaysEmpty(msg);
336 }
337 methodFlag = pollMethod();
338 return methodFlag == METHOD_FLAG_HEAD || super.isContentAlwaysEmpty(msg);
339 }
340 }
341 }