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 static 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 (getContentLength(start, -1L) <= 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 if (oversized instanceof FullHttpMessage ||
251 !HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
252 ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
253 future.addListener(new ChannelFutureListener() {
254 @Override
255 public void operationComplete(ChannelFuture future) throws Exception {
256 if (!future.isSuccess()) {
257 logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
258 }
259 ctx.close();
260 }
261 });
262 } else {
263 ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(new ChannelFutureListener() {
264 @Override
265 public void operationComplete(ChannelFuture future) throws Exception {
266 if (!future.isSuccess()) {
267 logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
268 ctx.close();
269 }
270 }
271 });
272 }
273 } else if (oversized instanceof HttpResponse) {
274 ctx.close();
275 throw new TooLongHttpContentException("Response entity too large: " + oversized);
276 } else {
277 throw new IllegalStateException();
278 }
279 }
280
281 private abstract static class AggregatedFullHttpMessage implements FullHttpMessage {
282 protected final HttpMessage message;
283 private final ByteBuf content;
284 private HttpHeaders trailingHeaders;
285
286 AggregatedFullHttpMessage(HttpMessage message, ByteBuf content, HttpHeaders trailingHeaders) {
287 this.message = message;
288 this.content = content;
289 this.trailingHeaders = trailingHeaders;
290 }
291
292 @Override
293 public HttpHeaders trailingHeaders() {
294 HttpHeaders trailingHeaders = this.trailingHeaders;
295 if (trailingHeaders == null) {
296 return EmptyHttpHeaders.INSTANCE;
297 } else {
298 return trailingHeaders;
299 }
300 }
301
302 void setTrailingHeaders(HttpHeaders trailingHeaders) {
303 this.trailingHeaders = trailingHeaders;
304 }
305
306 @Override
307 public HttpVersion getProtocolVersion() {
308 return message.protocolVersion();
309 }
310
311 @Override
312 public HttpVersion protocolVersion() {
313 return message.protocolVersion();
314 }
315
316 @Override
317 public FullHttpMessage setProtocolVersion(HttpVersion version) {
318 message.setProtocolVersion(version);
319 return this;
320 }
321
322 @Override
323 public HttpHeaders headers() {
324 return message.headers();
325 }
326
327 @Override
328 public DecoderResult decoderResult() {
329 return message.decoderResult();
330 }
331
332 @Override
333 public DecoderResult getDecoderResult() {
334 return message.decoderResult();
335 }
336
337 @Override
338 public void setDecoderResult(DecoderResult result) {
339 message.setDecoderResult(result);
340 }
341
342 @Override
343 public ByteBuf content() {
344 return content;
345 }
346
347 @Override
348 public int refCnt() {
349 return content.refCnt();
350 }
351
352 @Override
353 public FullHttpMessage retain() {
354 content.retain();
355 return this;
356 }
357
358 @Override
359 public FullHttpMessage retain(int increment) {
360 content.retain(increment);
361 return this;
362 }
363
364 @Override
365 public FullHttpMessage touch(Object hint) {
366 content.touch(hint);
367 return this;
368 }
369
370 @Override
371 public FullHttpMessage touch() {
372 content.touch();
373 return this;
374 }
375
376 @Override
377 public boolean release() {
378 return content.release();
379 }
380
381 @Override
382 public boolean release(int decrement) {
383 return content.release(decrement);
384 }
385
386 @Override
387 public abstract FullHttpMessage copy();
388
389 @Override
390 public abstract FullHttpMessage duplicate();
391
392 @Override
393 public abstract FullHttpMessage retainedDuplicate();
394 }
395
396 private static final class AggregatedFullHttpRequest extends AggregatedFullHttpMessage implements FullHttpRequest {
397
398 AggregatedFullHttpRequest(HttpRequest request, ByteBuf content, HttpHeaders trailingHeaders) {
399 super(request, content, trailingHeaders);
400 }
401
402 @Override
403 public FullHttpRequest copy() {
404 return replace(content().copy());
405 }
406
407 @Override
408 public FullHttpRequest duplicate() {
409 return replace(content().duplicate());
410 }
411
412 @Override
413 public FullHttpRequest retainedDuplicate() {
414 return replace(content().retainedDuplicate());
415 }
416
417 @Override
418 public FullHttpRequest replace(ByteBuf content) {
419 DefaultFullHttpRequest dup = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content,
420 headers().copy(), trailingHeaders().copy());
421 dup.setDecoderResult(decoderResult());
422 return dup;
423 }
424
425 @Override
426 public FullHttpRequest retain(int increment) {
427 super.retain(increment);
428 return this;
429 }
430
431 @Override
432 public FullHttpRequest retain() {
433 super.retain();
434 return this;
435 }
436
437 @Override
438 public FullHttpRequest touch() {
439 super.touch();
440 return this;
441 }
442
443 @Override
444 public FullHttpRequest touch(Object hint) {
445 super.touch(hint);
446 return this;
447 }
448
449 @Override
450 public FullHttpRequest setMethod(HttpMethod method) {
451 ((HttpRequest) message).setMethod(method);
452 return this;
453 }
454
455 @Override
456 public FullHttpRequest setUri(String uri) {
457 ((HttpRequest) message).setUri(uri);
458 return this;
459 }
460
461 @Override
462 public HttpMethod getMethod() {
463 return ((HttpRequest) message).method();
464 }
465
466 @Override
467 public String getUri() {
468 return ((HttpRequest) message).uri();
469 }
470
471 @Override
472 public HttpMethod method() {
473 return getMethod();
474 }
475
476 @Override
477 public String uri() {
478 return getUri();
479 }
480
481 @Override
482 public FullHttpRequest setProtocolVersion(HttpVersion version) {
483 super.setProtocolVersion(version);
484 return this;
485 }
486
487 @Override
488 public String toString() {
489 return HttpMessageUtil.appendFullRequest(new StringBuilder(256), this).toString();
490 }
491 }
492
493 private static final class AggregatedFullHttpResponse extends AggregatedFullHttpMessage
494 implements FullHttpResponse {
495
496 AggregatedFullHttpResponse(HttpResponse message, ByteBuf content, HttpHeaders trailingHeaders) {
497 super(message, content, trailingHeaders);
498 }
499
500 @Override
501 public FullHttpResponse copy() {
502 return replace(content().copy());
503 }
504
505 @Override
506 public FullHttpResponse duplicate() {
507 return replace(content().duplicate());
508 }
509
510 @Override
511 public FullHttpResponse retainedDuplicate() {
512 return replace(content().retainedDuplicate());
513 }
514
515 @Override
516 public FullHttpResponse replace(ByteBuf content) {
517 DefaultFullHttpResponse dup = new DefaultFullHttpResponse(getProtocolVersion(), getStatus(), content,
518 headers().copy(), trailingHeaders().copy());
519 dup.setDecoderResult(decoderResult());
520 return dup;
521 }
522
523 @Override
524 public FullHttpResponse setStatus(HttpResponseStatus status) {
525 ((HttpResponse) message).setStatus(status);
526 return this;
527 }
528
529 @Override
530 public HttpResponseStatus getStatus() {
531 return ((HttpResponse) message).status();
532 }
533
534 @Override
535 public HttpResponseStatus status() {
536 return getStatus();
537 }
538
539 @Override
540 public FullHttpResponse setProtocolVersion(HttpVersion version) {
541 super.setProtocolVersion(version);
542 return this;
543 }
544
545 @Override
546 public FullHttpResponse retain(int increment) {
547 super.retain(increment);
548 return this;
549 }
550
551 @Override
552 public FullHttpResponse retain() {
553 super.retain();
554 return this;
555 }
556
557 @Override
558 public FullHttpResponse touch(Object hint) {
559 super.touch(hint);
560 return this;
561 }
562
563 @Override
564 public FullHttpResponse touch() {
565 super.touch();
566 return this;
567 }
568
569 @Override
570 public String toString() {
571 return HttpMessageUtil.appendFullResponse(new StringBuilder(256), this).toString();
572 }
573 }
574 }