2009-03-10 09:42:19 +01:00
|
|
|
/*
|
2012-06-04 22:31:44 +02:00
|
|
|
* Copyright 2012 The Netty Project
|
2009-03-10 09:42:19 +01:00
|
|
|
*
|
2011-12-09 06:18:34 +01:00
|
|
|
* The Netty Project licenses this file to you under the Apache License,
|
|
|
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
|
|
* with the License. You may obtain a copy of the License at:
|
2009-03-10 09:42:19 +01:00
|
|
|
*
|
2012-06-04 22:31:44 +02:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2009-03-10 09:42:19 +01:00
|
|
|
*
|
2009-08-28 09:15:49 +02:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
2011-12-09 06:18:34 +01:00
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
2009-08-28 09:15:49 +02:00
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
* under the License.
|
2009-03-10 09:42:19 +01:00
|
|
|
*/
|
2011-12-09 04:38:59 +01:00
|
|
|
package io.netty.handler.codec.http;
|
2009-03-10 09:42:19 +01:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
import io.netty.buffer.ByteBuf;
|
2012-06-11 10:02:00 +02:00
|
|
|
import io.netty.buffer.Unpooled;
|
2013-12-16 13:44:44 +01:00
|
|
|
import io.netty.channel.ChannelFuture;
|
|
|
|
import io.netty.channel.ChannelFutureListener;
|
2011-12-09 04:38:59 +01:00
|
|
|
import io.netty.channel.ChannelHandler;
|
2012-06-07 07:52:33 +02:00
|
|
|
import io.netty.channel.ChannelHandlerContext;
|
2011-12-09 04:38:59 +01:00
|
|
|
import io.netty.channel.ChannelPipeline;
|
2014-07-04 11:48:23 +02:00
|
|
|
import io.netty.handler.codec.DecoderResult;
|
2014-06-04 11:34:57 +02:00
|
|
|
import io.netty.handler.codec.MessageAggregator;
|
2014-02-20 20:35:23 +01:00
|
|
|
import io.netty.handler.codec.TooLongFrameException;
|
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
2009-03-10 09:42:19 +01:00
|
|
|
|
2016-11-04 22:22:16 +01:00
|
|
|
import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
|
2015-08-04 00:09:44 +02:00
|
|
|
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
|
2017-08-11 01:50:00 +02:00
|
|
|
import static io.netty.handler.codec.http.HttpHeaderNames.EXPECT;
|
2015-08-22 17:25:57 +02:00
|
|
|
import static io.netty.handler.codec.http.HttpUtil.getContentLength;
|
2015-08-04 00:09:44 +02:00
|
|
|
|
2009-03-10 09:42:19 +01:00
|
|
|
/**
|
2013-01-16 05:22:50 +01:00
|
|
|
* A {@link ChannelHandler} that aggregates an {@link HttpMessage}
|
2014-04-17 15:42:47 +02:00
|
|
|
* and its following {@link HttpContent}s into a single {@link FullHttpRequest}
|
|
|
|
* or {@link FullHttpResponse} (depending on if it used to handle requests or responses)
|
|
|
|
* with no following {@link HttpContent}s. It is useful when you don't want to take
|
2009-06-19 17:39:34 +02:00
|
|
|
* care of HTTP messages whose transfer encoding is 'chunked'. Insert this
|
2016-08-15 03:17:42 +02:00
|
|
|
* handler after {@link HttpResponseDecoder} in the {@link ChannelPipeline} if being used to handle
|
|
|
|
* responses, or after {@link HttpRequestDecoder} and {@link HttpResponseEncoder} in the
|
|
|
|
* {@link ChannelPipeline} if being used to handle requests.
|
|
|
|
* <blockquote>
|
|
|
|
* <pre>
|
|
|
|
* {@link ChannelPipeline} p = ...;
|
|
|
|
* ...
|
|
|
|
* p.addLast("decoder", <b>new {@link HttpRequestDecoder}()</b>);
|
|
|
|
* p.addLast("encoder", <b>new {@link HttpResponseEncoder}()</b>);
|
|
|
|
* p.addLast("aggregator", <b>new {@link HttpObjectAggregator}(1048576)</b>);
|
|
|
|
* ...
|
|
|
|
* p.addLast("handler", new HttpRequestHandler());
|
|
|
|
* </pre>
|
|
|
|
* </blockquote>
|
|
|
|
* <p>
|
|
|
|
* For convenience, consider putting a {@link HttpServerCodec} before the {@link HttpObjectAggregator}
|
|
|
|
* as it functions as both a {@link HttpRequestDecoder} and a {@link HttpResponseEncoder}.
|
|
|
|
* </p>
|
|
|
|
* Be aware that {@link HttpObjectAggregator} may end up sending a {@link HttpResponse}:
|
|
|
|
* <table border summary="Possible Responses">
|
|
|
|
* <tbody>
|
|
|
|
* <tr>
|
|
|
|
* <th>Response Status</th>
|
|
|
|
* <th>Condition When Sent</th>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>100 Continue</td>
|
|
|
|
* <td>A '100-continue' expectation is received and the 'content-length' doesn't exceed maxContentLength</td>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>417 Expectation Failed</td>
|
|
|
|
* <td>A '100-continue' expectation is received and the 'content-length' exceeds maxContentLength</td>
|
|
|
|
* </tr>
|
|
|
|
* <tr>
|
|
|
|
* <td>413 Request Entity Too Large</td>
|
|
|
|
* <td>Either the 'content-length' or the bytes received so far exceed maxContentLength</td>
|
|
|
|
* </tr>
|
|
|
|
* </tbody>
|
|
|
|
* </table>
|
|
|
|
*
|
|
|
|
* @see FullHttpRequest
|
|
|
|
* @see FullHttpResponse
|
|
|
|
* @see HttpResponseDecoder
|
|
|
|
* @see HttpServerCodec
|
2009-03-10 09:42:19 +01:00
|
|
|
*/
|
2014-06-04 11:34:57 +02:00
|
|
|
public class HttpObjectAggregator
|
|
|
|
extends MessageAggregator<HttpObject, HttpMessage, HttpContent, FullHttpMessage> {
|
2014-02-20 20:35:23 +01:00
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpObjectAggregator.class);
|
2015-08-04 00:09:44 +02:00
|
|
|
private static final FullHttpResponse CONTINUE =
|
|
|
|
new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER);
|
|
|
|
private static final FullHttpResponse EXPECTATION_FAILED = new DefaultFullHttpResponse(
|
|
|
|
HttpVersion.HTTP_1_1, HttpResponseStatus.EXPECTATION_FAILED, Unpooled.EMPTY_BUFFER);
|
2016-11-04 22:22:16 +01:00
|
|
|
private static final FullHttpResponse TOO_LARGE_CLOSE = new DefaultFullHttpResponse(
|
2014-02-07 17:59:51 +01:00
|
|
|
HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
|
2016-11-04 22:22:16 +01:00
|
|
|
private static final FullHttpResponse TOO_LARGE = new DefaultFullHttpResponse(
|
|
|
|
HttpVersion.HTTP_1_1, HttpResponseStatus.REQUEST_ENTITY_TOO_LARGE, Unpooled.EMPTY_BUFFER);
|
2010-10-19 07:40:44 +02:00
|
|
|
|
2014-02-20 20:35:23 +01:00
|
|
|
static {
|
2015-08-04 00:09:44 +02:00
|
|
|
EXPECTATION_FAILED.headers().set(CONTENT_LENGTH, 0);
|
|
|
|
TOO_LARGE.headers().set(CONTENT_LENGTH, 0);
|
2016-11-04 22:22:16 +01:00
|
|
|
|
|
|
|
TOO_LARGE_CLOSE.headers().set(CONTENT_LENGTH, 0);
|
|
|
|
TOO_LARGE_CLOSE.headers().set(CONNECTION, HttpHeaderValues.CLOSE);
|
2014-02-20 20:35:23 +01:00
|
|
|
}
|
|
|
|
|
2015-08-04 00:09:44 +02:00
|
|
|
private final boolean closeOnExpectationFailed;
|
|
|
|
|
2009-06-19 17:35:19 +02:00
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
2015-08-04 00:09:44 +02:00
|
|
|
* @param maxContentLength the maximum length of the aggregated content in bytes.
|
|
|
|
* If the length of the aggregated content exceeds this value,
|
|
|
|
* {@link #handleOversizedMessage(ChannelHandlerContext, HttpMessage)} will be called.
|
2009-06-19 17:35:19 +02:00
|
|
|
*/
|
2013-01-14 16:52:30 +01:00
|
|
|
public HttpObjectAggregator(int maxContentLength) {
|
2015-08-04 00:09:44 +02:00
|
|
|
this(maxContentLength, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
|
|
|
* @param maxContentLength the maximum length of the aggregated content in bytes.
|
|
|
|
* If the length of the aggregated content exceeds this value,
|
|
|
|
* {@link #handleOversizedMessage(ChannelHandlerContext, HttpMessage)} will be called.
|
|
|
|
* @param closeOnExpectationFailed If a 100-continue response is detected but the content length is too large
|
|
|
|
* then {@code true} means close the connection. otherwise the connection will remain open and data will be
|
|
|
|
* consumed and discarded until the next request is received.
|
|
|
|
*/
|
|
|
|
public HttpObjectAggregator(int maxContentLength, boolean closeOnExpectationFailed) {
|
2014-06-04 11:34:57 +02:00
|
|
|
super(maxContentLength);
|
2015-08-04 00:09:44 +02:00
|
|
|
this.closeOnExpectationFailed = closeOnExpectationFailed;
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected boolean isStartMessage(HttpObject msg) throws Exception {
|
|
|
|
return msg instanceof HttpMessage;
|
2012-07-03 10:37:05 +02:00
|
|
|
}
|
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected boolean isContentMessage(HttpObject msg) throws Exception {
|
|
|
|
return msg instanceof HttpContent;
|
2012-07-03 10:37:05 +02:00
|
|
|
}
|
|
|
|
|
2012-05-20 07:19:11 +02:00
|
|
|
@Override
|
2014-06-04 11:34:57 +02:00
|
|
|
protected boolean isLastContentMessage(HttpContent msg) throws Exception {
|
|
|
|
return msg instanceof LastHttpContent;
|
|
|
|
}
|
2009-03-10 09:42:19 +01:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected boolean isAggregated(HttpObject msg) throws Exception {
|
|
|
|
return msg instanceof FullHttpMessage;
|
|
|
|
}
|
2012-07-03 10:37:05 +02:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
2015-08-04 00:09:44 +02:00
|
|
|
protected boolean isContentLengthInvalid(HttpMessage start, int maxContentLength) {
|
2017-06-19 17:16:39 +02:00
|
|
|
try {
|
|
|
|
return getContentLength(start, -1L) > maxContentLength;
|
|
|
|
} catch (final NumberFormatException e) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-08-04 00:09:44 +02:00
|
|
|
}
|
|
|
|
|
2017-10-14 15:49:45 +02:00
|
|
|
private static Object continueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) {
|
Correct expect header handling
Motivation:
Today, the HTTP codec in Netty responds to HTTP/1.1 requests containing
an "expect: 100-continue" header and a content-length that exceeds the
max content length for the server with a 417 status (Expectation
Failed). This is a violation of the HTTP specification. The purpose of
this commit is to address this situation by modifying the HTTP codec to
respond in this situation with a 413 status (Request Entity Too
Large). Additionally, the HTTP codec ignores expectations in the expect
header that are currently unsupported. This commit also addresses this
situation by responding with a 417 status.
Handling the expect header is tricky business as the specification (RFC
2616) is more complicated than it needs to be. The specification defines
the legitimate values for this header as "100-continue" and defines the
notion of expectatation extensions. Further, the specification defines a
417 status (Expectation Failed) and this is where implementations go
astray. The intent of the specification was for servers to respond with
417 status when they do not support the expectation in the expect
header.
The key sentence from the specification follows:
The server MUST respond with a 417 (Expectation Failed) status if
any of the expectations cannot be met or, if there are other
problems with the request, some other 4xx status.
That is, a server should respond with a 417 status if and only if there
is an expectation that the server does not support (whether it be
100-continue, or another expectation extension), and should respond with
another 4xx status code if the expectation is supported but there is
something else wrong with the request.
Modifications:
This commit modifies the HTTP codec by changing the handling for the
expect header in the HTTP object aggregator. In particular, the codec
will now respond with 417 status if any expectation other than
100-continue is present in the expect header, the codec will respond
with 413 status if the 100-continue expectation is present in the expect
header and the content-length is larger than the max content length for
the aggregator, and otherwise the codec will respond with 100 status.
Result:
The HTTP codec can now be used to correctly reply to clients that send a
100-continue expectation with a content-length that is too large for the
server with a 413 status, and servers that use the HTTP codec will now
no longer ignore expectations that are not supported (any value other
than 100-continue).
2017-02-14 18:09:52 +01:00
|
|
|
if (HttpUtil.isUnsupportedExpectation(start)) {
|
|
|
|
// if the request contains an unsupported expectation, we return 417
|
|
|
|
pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);
|
|
|
|
return EXPECTATION_FAILED.retainedDuplicate();
|
|
|
|
} else if (HttpUtil.is100ContinueExpected(start)) {
|
|
|
|
// if the request contains 100-continue but the content-length is too large, we return 413
|
2016-02-01 02:24:14 +01:00
|
|
|
if (getContentLength(start, -1L) <= maxContentLength) {
|
2016-04-14 10:31:48 +02:00
|
|
|
return CONTINUE.retainedDuplicate();
|
2015-08-04 00:09:44 +02:00
|
|
|
}
|
|
|
|
pipeline.fireUserEventTriggered(HttpExpectationFailedEvent.INSTANCE);
|
Correct expect header handling
Motivation:
Today, the HTTP codec in Netty responds to HTTP/1.1 requests containing
an "expect: 100-continue" header and a content-length that exceeds the
max content length for the server with a 417 status (Expectation
Failed). This is a violation of the HTTP specification. The purpose of
this commit is to address this situation by modifying the HTTP codec to
respond in this situation with a 413 status (Request Entity Too
Large). Additionally, the HTTP codec ignores expectations in the expect
header that are currently unsupported. This commit also addresses this
situation by responding with a 417 status.
Handling the expect header is tricky business as the specification (RFC
2616) is more complicated than it needs to be. The specification defines
the legitimate values for this header as "100-continue" and defines the
notion of expectatation extensions. Further, the specification defines a
417 status (Expectation Failed) and this is where implementations go
astray. The intent of the specification was for servers to respond with
417 status when they do not support the expectation in the expect
header.
The key sentence from the specification follows:
The server MUST respond with a 417 (Expectation Failed) status if
any of the expectations cannot be met or, if there are other
problems with the request, some other 4xx status.
That is, a server should respond with a 417 status if and only if there
is an expectation that the server does not support (whether it be
100-continue, or another expectation extension), and should respond with
another 4xx status code if the expectation is supported but there is
something else wrong with the request.
Modifications:
This commit modifies the HTTP codec by changing the handling for the
expect header in the HTTP object aggregator. In particular, the codec
will now respond with 417 status if any expectation other than
100-continue is present in the expect header, the codec will respond
with 413 status if the 100-continue expectation is present in the expect
header and the content-length is larger than the max content length for
the aggregator, and otherwise the codec will respond with 100 status.
Result:
The HTTP codec can now be used to correctly reply to clients that send a
100-continue expectation with a content-length that is too large for the
server with a 413 status, and servers that use the HTTP codec will now
no longer ignore expectations that are not supported (any value other
than 100-continue).
2017-02-14 18:09:52 +01:00
|
|
|
return TOO_LARGE.retainedDuplicate();
|
2015-08-04 00:09:44 +02:00
|
|
|
}
|
Correct expect header handling
Motivation:
Today, the HTTP codec in Netty responds to HTTP/1.1 requests containing
an "expect: 100-continue" header and a content-length that exceeds the
max content length for the server with a 417 status (Expectation
Failed). This is a violation of the HTTP specification. The purpose of
this commit is to address this situation by modifying the HTTP codec to
respond in this situation with a 413 status (Request Entity Too
Large). Additionally, the HTTP codec ignores expectations in the expect
header that are currently unsupported. This commit also addresses this
situation by responding with a 417 status.
Handling the expect header is tricky business as the specification (RFC
2616) is more complicated than it needs to be. The specification defines
the legitimate values for this header as "100-continue" and defines the
notion of expectatation extensions. Further, the specification defines a
417 status (Expectation Failed) and this is where implementations go
astray. The intent of the specification was for servers to respond with
417 status when they do not support the expectation in the expect
header.
The key sentence from the specification follows:
The server MUST respond with a 417 (Expectation Failed) status if
any of the expectations cannot be met or, if there are other
problems with the request, some other 4xx status.
That is, a server should respond with a 417 status if and only if there
is an expectation that the server does not support (whether it be
100-continue, or another expectation extension), and should respond with
another 4xx status code if the expectation is supported but there is
something else wrong with the request.
Modifications:
This commit modifies the HTTP codec by changing the handling for the
expect header in the HTTP object aggregator. In particular, the codec
will now respond with 417 status if any expectation other than
100-continue is present in the expect header, the codec will respond
with 413 status if the 100-continue expectation is present in the expect
header and the content-length is larger than the max content length for
the aggregator, and otherwise the codec will respond with 100 status.
Result:
The HTTP codec can now be used to correctly reply to clients that send a
100-continue expectation with a content-length that is too large for the
server with a 413 status, and servers that use the HTTP codec will now
no longer ignore expectations that are not supported (any value other
than 100-continue).
2017-02-14 18:09:52 +01:00
|
|
|
|
2015-08-04 00:09:44 +02:00
|
|
|
return null;
|
2014-06-04 11:34:57 +02:00
|
|
|
}
|
2012-09-28 08:26:38 +02:00
|
|
|
|
2017-08-11 01:50:00 +02:00
|
|
|
@Override
|
|
|
|
protected Object newContinueResponse(HttpMessage start, int maxContentLength, ChannelPipeline pipeline) {
|
|
|
|
Object response = continueResponse(start, maxContentLength, pipeline);
|
|
|
|
// we're going to respond based on the request expectation so there's no
|
|
|
|
// need to propagate the expectation further.
|
|
|
|
if (response != null) {
|
|
|
|
start.headers().remove(EXPECT);
|
|
|
|
}
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
2015-08-04 00:09:44 +02:00
|
|
|
protected boolean closeAfterContinueResponse(Object msg) {
|
|
|
|
return closeOnExpectationFailed && ignoreContentAfterContinueResponse(msg);
|
2014-06-04 11:34:57 +02:00
|
|
|
}
|
2010-08-26 07:06:40 +02:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
2015-08-04 00:09:44 +02:00
|
|
|
protected boolean ignoreContentAfterContinueResponse(Object msg) {
|
Correct expect header handling
Motivation:
Today, the HTTP codec in Netty responds to HTTP/1.1 requests containing
an "expect: 100-continue" header and a content-length that exceeds the
max content length for the server with a 417 status (Expectation
Failed). This is a violation of the HTTP specification. The purpose of
this commit is to address this situation by modifying the HTTP codec to
respond in this situation with a 413 status (Request Entity Too
Large). Additionally, the HTTP codec ignores expectations in the expect
header that are currently unsupported. This commit also addresses this
situation by responding with a 417 status.
Handling the expect header is tricky business as the specification (RFC
2616) is more complicated than it needs to be. The specification defines
the legitimate values for this header as "100-continue" and defines the
notion of expectatation extensions. Further, the specification defines a
417 status (Expectation Failed) and this is where implementations go
astray. The intent of the specification was for servers to respond with
417 status when they do not support the expectation in the expect
header.
The key sentence from the specification follows:
The server MUST respond with a 417 (Expectation Failed) status if
any of the expectations cannot be met or, if there are other
problems with the request, some other 4xx status.
That is, a server should respond with a 417 status if and only if there
is an expectation that the server does not support (whether it be
100-continue, or another expectation extension), and should respond with
another 4xx status code if the expectation is supported but there is
something else wrong with the request.
Modifications:
This commit modifies the HTTP codec by changing the handling for the
expect header in the HTTP object aggregator. In particular, the codec
will now respond with 417 status if any expectation other than
100-continue is present in the expect header, the codec will respond
with 413 status if the 100-continue expectation is present in the expect
header and the content-length is larger than the max content length for
the aggregator, and otherwise the codec will respond with 100 status.
Result:
The HTTP codec can now be used to correctly reply to clients that send a
100-continue expectation with a content-length that is too large for the
server with a 413 status, and servers that use the HTTP codec will now
no longer ignore expectations that are not supported (any value other
than 100-continue).
2017-02-14 18:09:52 +01:00
|
|
|
if (msg instanceof HttpResponse) {
|
|
|
|
final HttpResponse httpResponse = (HttpResponse) msg;
|
|
|
|
return httpResponse.status().codeClass().equals(HttpStatusClass.CLIENT_ERROR);
|
|
|
|
}
|
|
|
|
return false;
|
2014-06-04 11:34:57 +02:00
|
|
|
}
|
2010-08-26 07:06:40 +02:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected FullHttpMessage beginAggregation(HttpMessage start, ByteBuf content) throws Exception {
|
|
|
|
assert !(start instanceof FullHttpMessage);
|
|
|
|
|
2015-08-22 17:25:57 +02:00
|
|
|
HttpUtil.setTransferEncodingChunked(start, false);
|
2014-06-04 11:34:57 +02:00
|
|
|
|
2014-07-04 11:48:23 +02:00
|
|
|
AggregatedFullHttpMessage ret;
|
2014-06-04 11:34:57 +02:00
|
|
|
if (start instanceof HttpRequest) {
|
2014-07-04 11:48:23 +02:00
|
|
|
ret = new AggregatedFullHttpRequest((HttpRequest) start, content, null);
|
2014-06-28 06:27:21 +02:00
|
|
|
} else if (start instanceof HttpResponse) {
|
2014-07-04 11:48:23 +02:00
|
|
|
ret = new AggregatedFullHttpResponse((HttpResponse) start, content, null);
|
2010-04-16 06:22:00 +02:00
|
|
|
} else {
|
2013-01-16 05:22:50 +01:00
|
|
|
throw new Error();
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|
2014-06-04 11:34:57 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2014-02-20 22:41:54 +01:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected void aggregate(FullHttpMessage aggregated, HttpContent content) throws Exception {
|
|
|
|
if (content instanceof LastHttpContent) {
|
|
|
|
// Merge trailing headers into the message.
|
2014-07-04 11:48:23 +02:00
|
|
|
((AggregatedFullHttpMessage) aggregated).setTrailingHeaders(((LastHttpContent) content).trailingHeaders());
|
2014-02-20 20:35:23 +01:00
|
|
|
}
|
2014-02-07 17:59:51 +01:00
|
|
|
}
|
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
@Override
|
|
|
|
protected void finishAggregation(FullHttpMessage aggregated) throws Exception {
|
2014-12-24 21:48:41 +01:00
|
|
|
// Set the 'Content-Length' header. If one isn't already set.
|
|
|
|
// This is important as HEAD responses will use a 'Content-Length' header which
|
|
|
|
// does not match the actual body, but the number of bytes that would be
|
|
|
|
// transmitted if a GET would have been used.
|
|
|
|
//
|
|
|
|
// See rfc2616 14.13 Content-Length
|
2015-08-22 17:25:57 +02:00
|
|
|
if (!HttpUtil.isContentLengthSet(aggregated)) {
|
2014-12-24 21:48:41 +01:00
|
|
|
aggregated.headers().set(
|
2016-04-14 10:31:48 +02:00
|
|
|
CONTENT_LENGTH,
|
2014-12-24 21:48:41 +01:00
|
|
|
String.valueOf(aggregated.content().readableBytes()));
|
|
|
|
}
|
2014-06-04 11:34:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void handleOversizedMessage(final ChannelHandlerContext ctx, HttpMessage oversized) throws Exception {
|
|
|
|
if (oversized instanceof HttpRequest) {
|
2014-02-20 20:35:23 +01:00
|
|
|
// send back a 413 and close the connection
|
2014-02-07 17:59:51 +01:00
|
|
|
|
2014-02-20 20:35:23 +01:00
|
|
|
// If the client started to send data already, close because it's impossible to recover.
|
2014-10-07 15:56:15 +02:00
|
|
|
// If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open.
|
2014-06-04 11:34:57 +02:00
|
|
|
if (oversized instanceof FullHttpMessage ||
|
2015-08-22 17:25:57 +02:00
|
|
|
!HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
|
2016-11-04 22:22:16 +01:00
|
|
|
ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
|
|
|
|
future.addListener(new ChannelFutureListener() {
|
|
|
|
@Override
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
|
|
|
|
}
|
|
|
|
ctx.close();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(new ChannelFutureListener() {
|
|
|
|
@Override
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
|
|
|
|
ctx.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2014-02-20 20:35:23 +01:00
|
|
|
}
|
2014-02-20 00:31:53 +01:00
|
|
|
|
2014-06-04 11:34:57 +02:00
|
|
|
// If an oversized request was handled properly and the connection is still alive
|
|
|
|
// (i.e. rejected 100-continue). the decoder should prepare to handle a new message.
|
|
|
|
HttpObjectDecoder decoder = ctx.pipeline().get(HttpObjectDecoder.class);
|
|
|
|
if (decoder != null) {
|
|
|
|
decoder.reset();
|
|
|
|
}
|
|
|
|
} else if (oversized instanceof HttpResponse) {
|
|
|
|
ctx.close();
|
|
|
|
throw new TooLongFrameException("Response entity too large: " + oversized);
|
2014-02-20 00:31:53 +01:00
|
|
|
} else {
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
}
|
2014-07-04 11:48:23 +02:00
|
|
|
|
2016-04-14 10:31:48 +02:00
|
|
|
private abstract static class AggregatedFullHttpMessage implements FullHttpMessage {
|
2014-07-04 11:48:23 +02:00
|
|
|
protected final HttpMessage message;
|
2015-04-16 13:45:32 +02:00
|
|
|
private final ByteBuf content;
|
2014-07-04 11:48:23 +02:00
|
|
|
private HttpHeaders trailingHeaders;
|
|
|
|
|
2014-12-31 11:24:53 +01:00
|
|
|
AggregatedFullHttpMessage(HttpMessage message, ByteBuf content, HttpHeaders trailingHeaders) {
|
2014-07-04 11:48:23 +02:00
|
|
|
this.message = message;
|
2015-04-16 13:45:32 +02:00
|
|
|
this.content = content;
|
2014-07-04 11:48:23 +02:00
|
|
|
this.trailingHeaders = trailingHeaders;
|
|
|
|
}
|
2014-12-31 11:24:53 +01:00
|
|
|
|
2014-07-04 11:48:23 +02:00
|
|
|
@Override
|
|
|
|
public HttpHeaders trailingHeaders() {
|
2014-12-31 11:24:53 +01:00
|
|
|
HttpHeaders trailingHeaders = this.trailingHeaders;
|
|
|
|
if (trailingHeaders == null) {
|
2015-09-03 00:10:58 +02:00
|
|
|
return EmptyHttpHeaders.INSTANCE;
|
2014-12-31 11:24:53 +01:00
|
|
|
} else {
|
|
|
|
return trailingHeaders;
|
|
|
|
}
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
2014-12-31 11:24:53 +01:00
|
|
|
void setTrailingHeaders(HttpHeaders trailingHeaders) {
|
2014-07-04 11:48:23 +02:00
|
|
|
this.trailingHeaders = trailingHeaders;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpVersion getProtocolVersion() {
|
|
|
|
return message.protocolVersion();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpVersion protocolVersion() {
|
|
|
|
return message.protocolVersion();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpMessage setProtocolVersion(HttpVersion version) {
|
|
|
|
message.setProtocolVersion(version);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpHeaders headers() {
|
|
|
|
return message.headers();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public DecoderResult decoderResult() {
|
|
|
|
return message.decoderResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public DecoderResult getDecoderResult() {
|
|
|
|
return message.decoderResult();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void setDecoderResult(DecoderResult result) {
|
|
|
|
message.setDecoderResult(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2015-04-16 13:45:32 +02:00
|
|
|
public ByteBuf content() {
|
|
|
|
return content;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public int refCnt() {
|
|
|
|
return content.refCnt();
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpMessage retain() {
|
2015-04-16 13:45:32 +02:00
|
|
|
content.retain();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpMessage retain(int increment) {
|
|
|
|
content.retain(increment);
|
2014-07-04 11:48:23 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpMessage touch(Object hint) {
|
2015-04-16 13:45:32 +02:00
|
|
|
content.touch(hint);
|
2014-07-04 11:48:23 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpMessage touch() {
|
2015-04-16 13:45:32 +02:00
|
|
|
content.touch();
|
2014-07-04 11:48:23 +02:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2015-04-16 13:45:32 +02:00
|
|
|
@Override
|
|
|
|
public boolean release() {
|
|
|
|
return content.release();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean release(int decrement) {
|
|
|
|
return content.release(decrement);
|
|
|
|
}
|
|
|
|
|
2014-07-04 11:48:23 +02:00
|
|
|
@Override
|
|
|
|
public abstract FullHttpMessage copy();
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public abstract FullHttpMessage duplicate();
|
2016-04-14 10:31:48 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public abstract FullHttpMessage retainedDuplicate();
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final class AggregatedFullHttpRequest extends AggregatedFullHttpMessage implements FullHttpRequest {
|
|
|
|
|
2014-12-31 11:24:53 +01:00
|
|
|
AggregatedFullHttpRequest(HttpRequest request, ByteBuf content, HttpHeaders trailingHeaders) {
|
2014-07-04 11:48:23 +02:00
|
|
|
super(request, content, trailingHeaders);
|
|
|
|
}
|
|
|
|
|
2016-04-14 10:31:48 +02:00
|
|
|
@Override
|
|
|
|
public FullHttpRequest copy() {
|
|
|
|
return replace(content().copy());
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
2015-01-20 01:48:11 +01:00
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpRequest duplicate() {
|
|
|
|
return replace(content().duplicate());
|
2015-01-20 01:48:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpRequest retainedDuplicate() {
|
|
|
|
return replace(content().retainedDuplicate());
|
2015-01-20 01:48:11 +01:00
|
|
|
}
|
|
|
|
|
2014-07-04 11:48:23 +02:00
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpRequest replace(ByteBuf content) {
|
|
|
|
DefaultFullHttpRequest dup = new DefaultFullHttpRequest(protocolVersion(), method(), uri(), content);
|
|
|
|
dup.headers().set(headers());
|
|
|
|
dup.trailingHeaders().set(trailingHeaders());
|
2017-02-05 00:56:40 +01:00
|
|
|
dup.setDecoderResult(decoderResult());
|
2016-04-14 10:31:48 +02:00
|
|
|
return dup;
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest retain(int increment) {
|
|
|
|
super.retain(increment);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest retain() {
|
|
|
|
super.retain();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest touch() {
|
|
|
|
super.touch();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest touch(Object hint) {
|
|
|
|
super.touch(hint);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest setMethod(HttpMethod method) {
|
|
|
|
((HttpRequest) message).setMethod(method);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest setUri(String uri) {
|
|
|
|
((HttpRequest) message).setUri(uri);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpMethod getMethod() {
|
|
|
|
return ((HttpRequest) message).method();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String getUri() {
|
|
|
|
return ((HttpRequest) message).uri();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpMethod method() {
|
|
|
|
return getMethod();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public String uri() {
|
|
|
|
return getUri();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpRequest setProtocolVersion(HttpVersion version) {
|
|
|
|
super.setProtocolVersion(version);
|
|
|
|
return this;
|
|
|
|
}
|
2014-12-31 10:32:48 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return HttpMessageUtil.appendFullRequest(new StringBuilder(256), this).toString();
|
|
|
|
}
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static final class AggregatedFullHttpResponse extends AggregatedFullHttpMessage
|
|
|
|
implements FullHttpResponse {
|
2014-12-31 11:24:53 +01:00
|
|
|
|
|
|
|
AggregatedFullHttpResponse(HttpResponse message, ByteBuf content, HttpHeaders trailingHeaders) {
|
2014-07-04 11:48:23 +02:00
|
|
|
super(message, content, trailingHeaders);
|
|
|
|
}
|
|
|
|
|
2016-04-14 10:31:48 +02:00
|
|
|
@Override
|
|
|
|
public FullHttpResponse copy() {
|
|
|
|
return replace(content().copy());
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
2015-01-20 01:48:11 +01:00
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpResponse duplicate() {
|
|
|
|
return replace(content().duplicate());
|
2015-01-20 01:48:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpResponse retainedDuplicate() {
|
|
|
|
return replace(content().retainedDuplicate());
|
2015-01-20 01:48:11 +01:00
|
|
|
}
|
|
|
|
|
2014-07-04 11:48:23 +02:00
|
|
|
@Override
|
2016-04-14 10:31:48 +02:00
|
|
|
public FullHttpResponse replace(ByteBuf content) {
|
|
|
|
DefaultFullHttpResponse dup = new DefaultFullHttpResponse(getProtocolVersion(), getStatus(), content);
|
|
|
|
dup.headers().set(headers());
|
|
|
|
dup.trailingHeaders().set(trailingHeaders());
|
2017-02-05 00:56:40 +01:00
|
|
|
dup.setDecoderResult(decoderResult());
|
2016-04-14 10:31:48 +02:00
|
|
|
return dup;
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse setStatus(HttpResponseStatus status) {
|
|
|
|
((HttpResponse) message).setStatus(status);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpResponseStatus getStatus() {
|
|
|
|
return ((HttpResponse) message).status();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public HttpResponseStatus status() {
|
|
|
|
return getStatus();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse setProtocolVersion(HttpVersion version) {
|
|
|
|
super.setProtocolVersion(version);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse retain(int increment) {
|
|
|
|
super.retain(increment);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse retain() {
|
|
|
|
super.retain();
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse touch(Object hint) {
|
|
|
|
super.touch(hint);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public FullHttpResponse touch() {
|
|
|
|
super.touch();
|
|
|
|
return this;
|
|
|
|
}
|
2014-12-31 10:32:48 +01:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public String toString() {
|
|
|
|
return HttpMessageUtil.appendFullResponse(new StringBuilder(256), this).toString();
|
|
|
|
}
|
2014-07-04 11:48:23 +02:00
|
|
|
}
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|