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.buffer.Unpooled;
20 import io.netty.channel.ChannelFuture;
21 import io.netty.channel.ChannelFutureListener;
22 import io.netty.channel.ChannelHandler;
23 import io.netty.channel.ChannelHandlerContext;
24 import io.netty.channel.ChannelPipeline;
25 import io.netty.handler.codec.DecoderResult;
26 import io.netty.handler.codec.MessageAggregator;
27 import io.netty.util.internal.logging.InternalLogger;
28 import io.netty.util.internal.logging.InternalLoggerFactory;
29
30 import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
31 import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
32 import static io.netty.handler.codec.http.HttpHeaderNames.EXPECT;
33 import static io.netty.handler.codec.http.HttpUtil.getContentLength;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public class HttpObjectAggregator
87 extends MessageAggregator<HttpObject, HttpMessage, HttpContent, FullHttpMessage> {
88 private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpObjectAggregator.class);
89 private static final FullHttpResponse CONTINUE =
90 new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER);
91 private static final FullHttpResponse EXPECTATION_FAILED = new DefaultFullHttpResponse(
92 HttpVersion.HTTP_1_1, HttpResponseStatus.EXPECTATION_FAILED, Unpooled.EMPTY_BUFFER);
93 private static final FullHttpResponse TOO_LARGE_CLOSE = new DefaultFullHttpResponse(
94 HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
95 private static final FullHttpResponse TOO_LARGE = new DefaultFullHttpResponse(
96 HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
97
98 static {
99 EXPECTATION_FAILED.headers().set(CONTENT_LENGTH, 0);
100 TOO_LARGE.headers().set(CONTENT_LENGTH, 0);
101
102 TOO_LARGE_CLOSE.headers().set(CONTENT_LENGTH, 0);
103 TOO_LARGE_CLOSE.headers().set(CONNECTION, HttpHeaderValues.CLOSE);
104 }
105
106 private final boolean closeOnExpectationFailed;
107
108
109
110
111
112
113
114 public HttpObjectAggregator(int maxContentLength) {
115 this(maxContentLength, false);
116 }
117
118
119
120
121
122
123
124
125
126
127 public HttpObjectAggregator(int maxContentLength, boolean closeOnExpectationFailed) {
128 super(maxContentLength);
129 this.closeOnExpectationFailed = closeOnExpectationFailed;
130 }
131
132 @Override
133 protected boolean isStartMessage(HttpObject msg) throws Exception {
134 return msg instanceof HttpMessage;
135 }
136
137 @Override
138 protected boolean isContentMessage(HttpObject msg) throws Exception {
139 return msg instanceof HttpContent;
140 }
141
142 @Override
143 protected boolean isLastContentMessage(HttpContent msg) throws Exception {
144 return msg instanceof LastHttpContent;
145 }
146
147 @Override
148 protected boolean isAggregated(HttpObject msg) throws Exception {
149 return msg instanceof FullHttpMessage;
150 }
151
152 @Override
153 protected boolean isContentLengthInvalid(HttpMessage start, int maxContentLength) {
154 try {
155 return getContentLength(start, -1L) > maxContentLength;
156 } catch (final NumberFormatException e) {
157 return false;
158 }
159 }
160
161 private Object continueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) {
162 if (HttpUtil.isUnsupportedExpectation(start)) {
163
164 pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);
165 return EXPECTATION_FAILED.retainedDuplicate();
166 } else if (HttpUtil.is100ContinueExpected(start)) {
167
168 if (!isContentLengthInvalid(start, maxContentLength)) {
169 return CONTINUE.retainedDuplicate();
170 }
171 pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);
172 return TOO_LARGE.retainedDuplicate();
173 }
174
175 return null;
176 }
177
178 @Override
179 protected Object newContinueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) {
180 Object response = continueResponse(start, maxContentLength, pipeline);
181
182
183 if (response != null) {
184 start.headers().remove(EXPECT);
185 }
186 return response;
187 }
188
189 @Override
190 protected boolean closeAfterContinueResponse(Object msg) {
191 return closeOnExpectationFailed && ignoreContentAfterContinueResponse(msg);
192 }
193
194 @Override
195 protected boolean ignoreContentAfterContinueResponse(Object msg) {
196 if (msg instanceof HttpResponse) {
197 final HttpResponse httpResponse = (HttpResponse) msg;
198 return httpResponse.status().codeClass().equals(HttpStatusClass.CLIENT_ERROR);
199 }
200 return false;
201 }
202
203 @Override
204 protected FullHttpMessage beginAggregation(HttpMessage start, ByteBuf content) throws Exception {
205 assert !(start instanceof FullHttpMessage);
206
207 HttpUtil.setTransferEncodingChunked(start, false);
208
209 AggregatedFullHttpMessage ret;
210 if (start instanceof HttpRequest) {
211 ret = new AggregatedFullHttpRequest((HttpRequest) start, content, null);
212 } else if (start instanceof HttpResponse) {
213 ret = new AggregatedFullHttpResponse((HttpResponse) start, content, null);
214 } else {
215 throw new Error();
216 }
217 return ret;
218 }
219
220 @Override
221 protected void aggregate(FullHttpMessage aggregated, HttpContent content) throws Exception {
222 if (content instanceof LastHttpContent) {
223
224 ((AggregatedFullHttpMessage) aggregated).setTrailingHeaders(((LastHttpContent) content).trailingHeaders());
225 }
226 }
227
228 @Override
229 protected void finishAggregation(FullHttpMessage aggregated) throws Exception {
230
231
232
233
234
235
236 if (!HttpUtil.isContentLengthSet(aggregated)) {
237 aggregated.headers().set(
238 CONTENT_LENGTH,
239 String.valueOf(aggregated.content().readableBytes()));
240 }
241 }
242
243 @Override
244 protected void handleOversizedMessage(final ChannelHandlerContext ctx, HttpMessage oversized) throws Exception {
245 if (oversized instanceof HttpRequest) {
246
247
248
249
250
251 if (oversized instanceof FullHttpMessage || !ctx.channel().config().isAutoRead() ||
252 !HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
253 ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
254 future.addListener(new ChannelFutureListener() {
255 @Override
256 public void operationComplete(ChannelFuture future) throws Exception {
257 if (!future.isSuccess()) {
258 logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
259 }
260 ctx.close();
261 }
262 });
263 } else {
264 ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(new ChannelFutureListener() {
265 @Override
266 public void operationComplete(ChannelFuture future) throws Exception {
267 if (!future.isSuccess()) {
268 logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
269 ctx.close();
270 }
271 }
272 });
273 }
274 } else if (oversized instanceof HttpResponse) {
275 ctx.close();
276 throw new TooLongHttpContentException("Response entity too large: " + oversized);
277 } else {
278 throw new IllegalStateException();
279 }
280 }
281
282 private abstract static class AggregatedFullHttpMessage implements FullHttpMessage {
283 protected final HttpMessage message;
284 private final ByteBuf content;
285 private HttpHeaders trailingHeaders;
286
287 AggregatedFullHttpMessage(HttpMessage message, ByteBuf content, HttpHeaders trailingHeaders) {
288 this.message = message;
289 this.content = content;
290 this.trailingHeaders = trailingHeaders;
291 }
292
293 @Override
294 public HttpHeaders trailingHeaders() {
295 HttpHeaders trailingHeaders = this.trailingHeaders;
296 if (trailingHeaders == null) {
297 return EmptyHttpHeaders.INSTANCE;
298 } else {
299 return trailingHeaders;
300 }
301 }
302
303 void setTrailingHeaders(HttpHeaders trailingHeaders) {
304 this.trailingHeaders = trailingHeaders;
305 }
306
307 @Override
308 public HttpVersion getProtocolVersion() {
309 return message.protocolVersion();
310 }
311
312 @Override
313 public HttpVersion protocolVersion() {
314 return message.protocolVersion();
315 }
316
317 @Override
318 public FullHttpMessage setProtocolVersion(HttpVersion version) {
319 message.setProtocolVersion(version);
320 return this;
321 }
322
323 @Override
324 public HttpHeaders headers() {
325 return message.headers();
326 }
327
328 @Override
329 public DecoderResult decoderResult() {
330 return message.decoderResult();
331 }
332
333 @Override
334 public DecoderResult getDecoderResult() {
335 return message.decoderResult();
336 }
337
338 @Override
339 public void setDecoderResult(DecoderResult result) {
340 message.setDecoderResult(result);
341 }
342
343 @Override
344 public ByteBuf content() {
345 return content;
346 }
347
348 @Override
349 public int refCnt() {
350 return content.refCnt();
351 }
352
353 @Override
354 public FullHttpMessage retain() {
355 content.retain();
356 return this;
357 }
358
359 @Override
360 public FullHttpMessage retain(int increment) {
361 content.retain(increment);
362 return this;
363 }
364
365 @Override
366 public FullHttpMessage touch(Object hint) {
367 content.touch(hint);
368 return this;
369 }
370
371 @Override
372 public FullHttpMessage touch() {
373 content.touch();
374 return this;
375 }
376
377 @Override
378 public boolean release() {
379 return content.release();
380 }
381
382 @Override
383 public boolean release(int decrement) {
384 return content.release(decrement);
385 }
386
387 @Override
388 public abstract FullHttpMessage copy();
389
390 @Override
391 public abstract FullHttpMessage duplicate();
392
393 @Override
394 public abstract FullHttpMessage retainedDuplicate();
395 }
396
397 private static final class AggregatedFullHttpRequest extends AggregatedFullHttpMessage implements FullHttpRequest {
398
399 AggregatedFullHttpRequest(HttpRequest request, ByteBuf content, HttpHeaders trailingHeaders) {
400 super(request, content, trailingHeaders);
401 }
402
403 @Override
404 public FullHttpRequest copy() {
405 return replace(content().copy());
406 }
407
408 @Override
409 public FullHttpRequest duplicate() {
410 return replace(content().duplicate());
411 }
412
413 @Override
414 public FullHttpRequest retainedDuplicate() {
415 return replace(content().retainedDuplicate());
416 }
417
418 @Override
419 public FullHttpRequest replace(ByteBuf content) {
420 DefaultFullHttpRequest dup = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content,
421 headers().copy(), trailingHeaders().copy());
422 dup.setDecoderResult(decoderResult());
423 return dup;
424 }
425
426 @Override
427 public FullHttpRequest retain(int increment) {
428 super.retain(increment);
429 return this;
430 }
431
432 @Override
433 public FullHttpRequest retain() {
434 super.retain();
435 return this;
436 }
437
438 @Override
439 public FullHttpRequest touch() {
440 super.touch();
441 return this;
442 }
443
444 @Override
445 public FullHttpRequest touch(Object hint) {
446 super.touch(hint);
447 return this;
448 }
449
450 @Override
451 public FullHttpRequest setMethod(HttpMethod method) {
452 ((HttpRequest) message).setMethod(method);
453 return this;
454 }
455
456 @Override
457 public FullHttpRequest setUri(String uri) {
458 ((HttpRequest) message).setUri(uri);
459 return this;
460 }
461
462 @Override
463 public HttpMethod getMethod() {
464 return ((HttpRequest) message).method();
465 }
466
467 @Override
468 public String getUri() {
469 return ((HttpRequest) message).uri();
470 }
471
472 @Override
473 public HttpMethod method() {
474 return getMethod();
475 }
476
477 @Override
478 public String uri() {
479 return getUri();
480 }
481
482 @Override
483 public FullHttpRequest setProtocolVersion(HttpVersion version) {
484 super.setProtocolVersion(version);
485 return this;
486 }
487
488 @Override
489 public String toString() {
490 return HttpMessageUtil.appendFullRequest(new StringBuilder(256), this).toString();
491 }
492 }
493
494 private static final class AggregatedFullHttpResponse extends AggregatedFullHttpMessage
495 implements FullHttpResponse {
496
497 AggregatedFullHttpResponse(HttpResponse message, ByteBuf content, HttpHeaders trailingHeaders) {
498 super(message, content, trailingHeaders);
499 }
500
501 @Override
502 public FullHttpResponse copy() {
503 return replace(content().copy());
504 }
505
506 @Override
507 public FullHttpResponse duplicate() {
508 return replace(content().duplicate());
509 }
510
511 @Override
512 public FullHttpResponse retainedDuplicate() {
513 return replace(content().retainedDuplicate());
514 }
515
516 @Override
517 public FullHttpResponse replace(ByteBuf content) {
518 DefaultFullHttpResponse dup = new DefaultFullHttpResponse(getProtocolVersion(), getStatus(), content,
519 headers().copy(), trailingHeaders().copy());
520 dup.setDecoderResult(decoderResult());
521 return dup;
522 }
523
524 @Override
525 public FullHttpResponse setStatus(HttpResponseStatus status) {
526 ((HttpResponse) message).setStatus(status);
527 return this;
528 }
529
530 @Override
531 public HttpResponseStatus getStatus() {
532 return ((HttpResponse) message).status();
533 }
534
535 @Override
536 public HttpResponseStatus status() {
537 return getStatus();
538 }
539
540 @Override
541 public FullHttpResponse setProtocolVersion(HttpVersion version) {
542 super.setProtocolVersion(version);
543 return this;
544 }
545
546 @Override
547 public FullHttpResponse retain(int increment) {
548 super.retain(increment);
549 return this;
550 }
551
552 @Override
553 public FullHttpResponse retain() {
554 super.retain();
555 return this;
556 }
557
558 @Override
559 public FullHttpResponse touch(Object hint) {
560 super.touch(hint);
561 return this;
562 }
563
564 @Override
565 public FullHttpResponse touch() {
566 super.touch();
567 return this;
568 }
569
570 @Override
571 public String toString() {
572 return HttpMessageUtil.appendFullResponse(new StringBuilder(256), this).toString();
573 }
574 }
575 }