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
|
|
|
|
2012-07-03 10:37:05 +02:00
|
|
|
import io.netty.buffer.CompositeByteBuf;
|
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;
|
2012-09-28 08:26:38 +02:00
|
|
|
import io.netty.handler.codec.DecoderResult;
|
2012-05-20 07:19:11 +02:00
|
|
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
2014-02-20 20:35:23 +01:00
|
|
|
import io.netty.handler.codec.TooLongFrameException;
|
2014-02-07 17:59:51 +01:00
|
|
|
import io.netty.util.ReferenceCountUtil;
|
2014-02-20 20:35:23 +01:00
|
|
|
import io.netty.util.internal.logging.InternalLogger;
|
|
|
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
2009-03-10 09:42:19 +01:00
|
|
|
|
Remove MessageList from public API and change ChannelInbound/OutboundHandler accordingly
I must admit MesageList was pain in the ass. Instead of forcing a
handler always loop over the list of messages, this commit splits
messageReceived(ctx, list) into two event handlers:
- messageReceived(ctx, msg)
- mmessageReceivedLast(ctx)
When Netty reads one or more messages, messageReceived(ctx, msg) event
is triggered for each message. Once the current read operation is
finished, messageReceivedLast() is triggered to tell the handler that
the last messageReceived() was the last message in the current batch.
Similarly, for outbound, write(ctx, list) has been split into two:
- write(ctx, msg)
- flush(ctx, promise)
Instead of writing a list of message with a promise, a user is now
supposed to call write(msg) multiple times and then call flush() to
actually flush the buffered messages.
Please note that write() doesn't have a promise with it. You must call
flush() to get notified on completion. (or you can use writeAndFlush())
Other changes:
- Because MessageList is completely hidden, codec framework uses
List<Object> instead of MessageList as an output parameter.
2013-07-08 12:03:40 +02:00
|
|
|
import java.util.List;
|
2013-01-16 05:22:50 +01:00
|
|
|
|
2013-12-16 13:44:44 +01:00
|
|
|
import static io.netty.handler.codec.http.HttpHeaders.*;
|
|
|
|
|
2009-03-10 09:42:19 +01:00
|
|
|
/**
|
2013-01-16 05:22:50 +01:00
|
|
|
* A {@link ChannelHandler} that aggregates an {@link HttpMessage}
|
|
|
|
* and its following {@link HttpContent}s into a single {@link HttpMessage} with
|
2013-01-14 16:52:30 +01:00
|
|
|
* 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
|
2013-01-14 16:52:30 +01:00
|
|
|
* handler after {@link HttpObjectDecoder} in the {@link ChannelPipeline}:
|
2009-06-19 17:39:34 +02:00
|
|
|
* <pre>
|
2010-02-02 03:00:04 +01:00
|
|
|
* {@link ChannelPipeline} p = ...;
|
2009-06-19 17:39:34 +02:00
|
|
|
* ...
|
2014-02-09 17:10:09 +01:00
|
|
|
* p.addLast("encoder", new {@link HttpResponseEncoder}());
|
2010-02-02 03:00:04 +01:00
|
|
|
* p.addLast("decoder", new {@link HttpRequestDecoder}());
|
2013-01-14 16:52:30 +01:00
|
|
|
* p.addLast("aggregator", <b>new {@link HttpObjectAggregator}(1048576)</b>);
|
2009-06-19 17:39:34 +02:00
|
|
|
* ...
|
|
|
|
* p.addLast("handler", new HttpRequestHandler());
|
|
|
|
* </pre>
|
2009-03-10 09:42:19 +01:00
|
|
|
*/
|
2013-01-14 16:52:30 +01:00
|
|
|
public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
|
2014-02-20 20:35:23 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @deprecated Will be removed in the next minor version bump.
|
|
|
|
*/
|
|
|
|
@Deprecated
|
|
|
|
public static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS = 1024; // TODO: Make it private in the next bump.
|
|
|
|
|
|
|
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpObjectAggregator.class);
|
|
|
|
|
2014-02-07 17:59:51 +01:00
|
|
|
private static final FullHttpResponse CONTINUE = new DefaultFullHttpResponse(
|
|
|
|
HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE, Unpooled.EMPTY_BUFFER);
|
|
|
|
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 {
|
|
|
|
TOO_LARGE.headers().set(Names.CONTENT_LENGTH, 0);
|
|
|
|
}
|
|
|
|
|
2009-03-10 09:42:19 +01:00
|
|
|
private final int maxContentLength;
|
2013-01-16 05:22:50 +01:00
|
|
|
private FullHttpMessage currentMessage;
|
2014-02-20 20:35:23 +01:00
|
|
|
private boolean handlingOversizedMessage;
|
2009-03-10 09:42:19 +01:00
|
|
|
|
2012-07-03 10:37:05 +02:00
|
|
|
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
|
|
|
private ChannelHandlerContext ctx;
|
|
|
|
|
2009-06-19 17:35:19 +02:00
|
|
|
/**
|
|
|
|
* Creates a new instance.
|
|
|
|
*
|
|
|
|
* @param maxContentLength
|
|
|
|
* the maximum length of the aggregated content.
|
|
|
|
* If the length of the aggregated content exceeds this value,
|
2014-02-20 20:35:23 +01:00
|
|
|
* {@link #handleOversizedMessage(ChannelHandlerContext, HttpMessage)}
|
2014-02-07 17:59:51 +01:00
|
|
|
* will be called.
|
2009-06-19 17:35:19 +02:00
|
|
|
*/
|
2013-01-14 16:52:30 +01:00
|
|
|
public HttpObjectAggregator(int maxContentLength) {
|
2009-03-10 09:42:19 +01:00
|
|
|
if (maxContentLength <= 0) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"maxContentLength must be a positive integer: " +
|
|
|
|
maxContentLength);
|
|
|
|
}
|
|
|
|
this.maxContentLength = maxContentLength;
|
|
|
|
}
|
|
|
|
|
2012-07-03 10:37:05 +02:00
|
|
|
/**
|
|
|
|
* Returns the maximum number of components in the cumulation buffer. If the number of
|
|
|
|
* the components in the cumulation buffer exceeds this value, the components of the
|
|
|
|
* cumulation buffer are consolidated into a single component, involving memory copies.
|
2014-02-20 20:35:23 +01:00
|
|
|
* The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}.
|
2012-07-03 10:37:05 +02:00
|
|
|
*/
|
|
|
|
public final int getMaxCumulationBufferComponents() {
|
|
|
|
return maxCumulationBufferComponents;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the maximum number of components in the cumulation buffer. If the number of
|
|
|
|
* the components in the cumulation buffer exceeds this value, the components of the
|
|
|
|
* cumulation buffer are consolidated into a single component, involving memory copies.
|
2014-02-20 20:35:23 +01:00
|
|
|
* The default value of this property is {@value #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}
|
2012-07-03 10:37:05 +02:00
|
|
|
* and its minimum allowed value is {@code 2}.
|
|
|
|
*/
|
|
|
|
public final void setMaxCumulationBufferComponents(int maxCumulationBufferComponents) {
|
|
|
|
if (maxCumulationBufferComponents < 2) {
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
"maxCumulationBufferComponents: " + maxCumulationBufferComponents +
|
|
|
|
" (expected: >= 2)");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctx == null) {
|
|
|
|
this.maxCumulationBufferComponents = maxCumulationBufferComponents;
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException(
|
|
|
|
"decoder properties cannot be changed once the decoder is added to a pipeline.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-20 07:19:11 +02:00
|
|
|
@Override
|
2013-12-16 13:44:44 +01:00
|
|
|
protected void decode(final ChannelHandlerContext ctx, HttpObject msg, List<Object> out) throws Exception {
|
2013-01-16 05:22:50 +01:00
|
|
|
FullHttpMessage currentMessage = this.currentMessage;
|
|
|
|
|
|
|
|
if (msg instanceof HttpMessage) {
|
2014-02-20 20:35:23 +01:00
|
|
|
handlingOversizedMessage = false;
|
2013-01-16 05:22:50 +01:00
|
|
|
assert currentMessage == null;
|
2010-04-16 06:22:00 +02:00
|
|
|
|
2013-01-16 05:22:50 +01:00
|
|
|
HttpMessage m = (HttpMessage) msg;
|
2010-10-19 07:40:44 +02:00
|
|
|
|
2014-02-07 17:59:51 +01:00
|
|
|
// if content length is set, preemptively close if it's too large
|
|
|
|
if (isContentLengthSet(m)) {
|
|
|
|
if (getContentLength(m) > maxContentLength) {
|
|
|
|
// handle oversized message
|
2014-02-20 20:35:23 +01:00
|
|
|
invokeHandleOversizedMessage(ctx, m);
|
2014-02-07 17:59:51 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-19 07:40:44 +02:00
|
|
|
// Handle the 'Expect: 100-continue' header if necessary.
|
2014-02-20 20:35:23 +01:00
|
|
|
if (is100ContinueExpected(m)) {
|
2013-12-16 13:44:44 +01:00
|
|
|
ctx.writeAndFlush(CONTINUE).addListener(new ChannelFutureListener() {
|
|
|
|
@Override
|
|
|
|
public void operationComplete(ChannelFuture future) throws Exception {
|
|
|
|
if (!future.isSuccess()) {
|
|
|
|
ctx.fireExceptionCaught(future.cause());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2010-10-19 07:40:44 +02:00
|
|
|
}
|
|
|
|
|
2013-01-30 07:42:18 +01:00
|
|
|
if (!m.getDecoderResult().isSuccess()) {
|
2013-01-14 16:52:30 +01:00
|
|
|
removeTransferEncodingChunked(m);
|
2014-02-20 00:31:53 +01:00
|
|
|
out.add(toFullMessage(m));
|
2014-02-07 06:00:24 +01:00
|
|
|
this.currentMessage = null;
|
2013-04-03 11:32:33 +02:00
|
|
|
return;
|
2012-09-28 08:26:38 +02:00
|
|
|
}
|
2013-01-16 05:22:50 +01:00
|
|
|
if (msg instanceof HttpRequest) {
|
|
|
|
HttpRequest header = (HttpRequest) msg;
|
2013-01-30 07:42:18 +01:00
|
|
|
this.currentMessage = currentMessage = new DefaultFullHttpRequest(header.getProtocolVersion(),
|
|
|
|
header.getMethod(), header.getUri(), Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
2013-01-16 05:22:50 +01:00
|
|
|
} else if (msg instanceof HttpResponse) {
|
|
|
|
HttpResponse header = (HttpResponse) msg;
|
|
|
|
this.currentMessage = currentMessage = new DefaultFullHttpResponse(
|
2013-01-30 07:42:18 +01:00
|
|
|
header.getProtocolVersion(), header.getStatus(),
|
2013-01-16 05:22:50 +01:00
|
|
|
Unpooled.compositeBuffer(maxCumulationBufferComponents));
|
|
|
|
} else {
|
|
|
|
throw new Error();
|
2013-01-14 16:52:30 +01:00
|
|
|
}
|
2013-01-16 05:22:50 +01:00
|
|
|
|
2013-01-16 16:33:40 +01:00
|
|
|
currentMessage.headers().set(m.headers());
|
|
|
|
|
2013-01-14 16:52:30 +01:00
|
|
|
// A streamed message - initialize the cumulative buffer, and wait for incoming chunks.
|
2013-01-16 05:22:50 +01:00
|
|
|
removeTransferEncodingChunked(currentMessage);
|
2013-01-14 16:52:30 +01:00
|
|
|
} else if (msg instanceof HttpContent) {
|
2014-02-20 20:35:23 +01:00
|
|
|
if (handlingOversizedMessage) {
|
2013-05-09 20:11:34 +02:00
|
|
|
if (msg instanceof LastHttpContent) {
|
|
|
|
this.currentMessage = null;
|
|
|
|
}
|
2013-05-09 19:44:39 +02:00
|
|
|
// already detect the too long frame so just discard the content
|
|
|
|
return;
|
|
|
|
}
|
2013-07-10 22:10:48 +02:00
|
|
|
assert currentMessage != null;
|
2013-05-09 19:44:39 +02:00
|
|
|
|
2009-03-10 09:42:19 +01:00
|
|
|
// Merge the received chunk into the content of the current message.
|
2013-01-14 16:52:30 +01:00
|
|
|
HttpContent chunk = (HttpContent) msg;
|
2013-05-01 10:04:43 +02:00
|
|
|
CompositeByteBuf content = (CompositeByteBuf) currentMessage.content();
|
2009-03-10 09:42:19 +01:00
|
|
|
|
2013-05-01 10:04:43 +02:00
|
|
|
if (content.readableBytes() > maxContentLength - chunk.content().readableBytes()) {
|
2014-02-07 17:59:51 +01:00
|
|
|
// handle oversized message
|
2014-02-20 20:35:23 +01:00
|
|
|
invokeHandleOversizedMessage(ctx, currentMessage);
|
2014-02-07 17:59:51 +01:00
|
|
|
return;
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|
|
|
|
|
2012-07-03 10:37:05 +02:00
|
|
|
// Append the content of the chunk
|
2013-05-01 10:04:43 +02:00
|
|
|
if (chunk.content().isReadable()) {
|
2013-02-10 05:31:31 +01:00
|
|
|
chunk.retain();
|
2013-05-01 10:04:43 +02:00
|
|
|
content.addComponent(chunk.content());
|
|
|
|
content.writerIndex(content.writerIndex() + chunk.content().readableBytes());
|
2013-01-16 05:22:50 +01:00
|
|
|
}
|
2012-07-03 10:37:05 +02:00
|
|
|
|
2012-09-28 08:26:38 +02:00
|
|
|
final boolean last;
|
2013-01-30 07:42:18 +01:00
|
|
|
if (!chunk.getDecoderResult().isSuccess()) {
|
|
|
|
currentMessage.setDecoderResult(
|
2013-03-12 05:02:50 +01:00
|
|
|
DecoderResult.failure(chunk.getDecoderResult().cause()));
|
2012-09-28 08:26:38 +02:00
|
|
|
last = true;
|
|
|
|
} else {
|
2013-02-10 05:31:31 +01:00
|
|
|
last = chunk instanceof LastHttpContent;
|
2012-09-28 08:26:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (last) {
|
2010-08-26 07:06:40 +02:00
|
|
|
// Merge trailing headers into the message.
|
2013-01-14 16:52:30 +01:00
|
|
|
if (chunk instanceof LastHttpContent) {
|
|
|
|
LastHttpContent trailer = (LastHttpContent) chunk;
|
2013-01-16 16:33:40 +01:00
|
|
|
currentMessage.headers().add(trailer.trailingHeaders());
|
2010-08-26 07:06:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the 'Content-Length' header.
|
2013-01-16 05:22:50 +01:00
|
|
|
currentMessage.headers().set(
|
2009-03-10 09:42:19 +01:00
|
|
|
HttpHeaders.Names.CONTENT_LENGTH,
|
|
|
|
String.valueOf(content.readableBytes()));
|
2010-08-26 07:06:40 +02:00
|
|
|
|
2012-05-20 07:19:11 +02:00
|
|
|
// All done
|
2013-04-03 11:32:33 +02:00
|
|
|
out.add(currentMessage);
|
2014-02-07 06:00:24 +01:00
|
|
|
this.currentMessage = null;
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
}
|
2012-07-03 10:37:05 +02:00
|
|
|
|
2014-02-20 20:35:23 +01:00
|
|
|
private void invokeHandleOversizedMessage(ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
|
|
|
handlingOversizedMessage = true;
|
2014-02-07 17:59:51 +01:00
|
|
|
currentMessage = null;
|
2014-02-20 20:35:23 +01:00
|
|
|
try {
|
|
|
|
handleOversizedMessage(ctx, msg);
|
|
|
|
} finally {
|
2014-02-20 22:41:54 +01:00
|
|
|
// Release the message in case it is a full one.
|
2014-02-20 20:35:23 +01:00
|
|
|
ReferenceCountUtil.release(msg);
|
2014-02-20 22:41:54 +01:00
|
|
|
|
|
|
|
if (msg instanceof HttpRequest) {
|
|
|
|
// 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();
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 20:35:23 +01:00
|
|
|
}
|
2014-02-07 17:59:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invoked when an incoming request exceeds the maximum content length.
|
|
|
|
*
|
2014-02-20 20:35:23 +01:00
|
|
|
* The default behavior is:
|
|
|
|
* <ul>
|
|
|
|
* <li>Oversized request: Send a {@link HttpResponseStatus#REQUEST_ENTITY_TOO_LARGE} and close the connection
|
|
|
|
* if keep-alive is not enabled.</li>
|
|
|
|
* <li>Oversized response: Close the connection and raise {@link TooLongFrameException}.</li>
|
|
|
|
* </ul>
|
|
|
|
* Sub-classes may override this method to change the default behavior. The specified {@code msg} is released
|
|
|
|
* once this method returns.
|
2014-02-07 17:59:51 +01:00
|
|
|
*
|
|
|
|
* @param ctx the {@link ChannelHandlerContext}
|
|
|
|
* @param msg the accumulated HTTP message up to this point
|
|
|
|
*/
|
2014-02-20 20:35:23 +01:00
|
|
|
@SuppressWarnings("UnusedParameters")
|
|
|
|
protected void handleOversizedMessage(final ChannelHandlerContext ctx, HttpMessage msg) throws Exception {
|
|
|
|
if (msg instanceof HttpRequest) {
|
|
|
|
// send back a 413 and close the connection
|
|
|
|
ChannelFuture future = ctx.writeAndFlush(TOO_LARGE).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-07 17:59:51 +01:00
|
|
|
}
|
2014-02-20 20:35:23 +01:00
|
|
|
});
|
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.
|
|
|
|
// If 'Expect: 100-continue' is missing, close becuase it's impossible to recover.
|
|
|
|
// If keep-alive is off, no need to leave the connection open.
|
|
|
|
if (msg instanceof FullHttpMessage || !is100ContinueExpected(msg) || !isKeepAlive(msg)) {
|
|
|
|
future.addListener(ChannelFutureListener.CLOSE);
|
|
|
|
}
|
|
|
|
} else if (msg instanceof HttpResponse) {
|
|
|
|
ctx.close();
|
|
|
|
throw new TooLongFrameException("Response entity too large: " + msg);
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException();
|
2014-02-07 17:59:51 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-11 08:53:14 +02:00
|
|
|
@Override
|
|
|
|
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
// release current message if it is not null as it may be a left-over
|
|
|
|
if (currentMessage != null) {
|
|
|
|
currentMessage.release();
|
|
|
|
currentMessage = null;
|
|
|
|
}
|
2014-02-07 06:00:24 +01:00
|
|
|
|
|
|
|
super.channelInactive(ctx);
|
2013-06-11 08:53:14 +02:00
|
|
|
}
|
|
|
|
|
2012-07-19 13:23:55 +02:00
|
|
|
@Override
|
2013-04-05 15:46:18 +02:00
|
|
|
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
2012-07-03 10:37:05 +02:00
|
|
|
this.ctx = ctx;
|
|
|
|
}
|
2013-06-11 08:53:14 +02:00
|
|
|
|
|
|
|
@Override
|
|
|
|
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
|
|
|
|
super.handlerRemoved(ctx);
|
2014-02-07 06:00:24 +01:00
|
|
|
|
2013-06-11 08:53:14 +02:00
|
|
|
// release current message if it is not null as it may be a left-over as there is not much more we can do in
|
|
|
|
// this case
|
|
|
|
if (currentMessage != null) {
|
|
|
|
currentMessage.release();
|
|
|
|
currentMessage = null;
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 00:31:53 +01:00
|
|
|
|
|
|
|
private static FullHttpMessage toFullMessage(HttpMessage msg) {
|
|
|
|
if (msg instanceof FullHttpMessage) {
|
|
|
|
return ((FullHttpMessage) msg).retain();
|
|
|
|
}
|
|
|
|
|
|
|
|
FullHttpMessage fullMsg;
|
|
|
|
if (msg instanceof HttpRequest) {
|
|
|
|
HttpRequest req = (HttpRequest) msg;
|
|
|
|
fullMsg = new DefaultFullHttpRequest(
|
|
|
|
req.getProtocolVersion(), req.getMethod(), req.getUri(), Unpooled.EMPTY_BUFFER, false);
|
|
|
|
} else if (msg instanceof HttpResponse) {
|
|
|
|
HttpResponse res = (HttpResponse) msg;
|
|
|
|
fullMsg = new DefaultFullHttpResponse(
|
|
|
|
res.getProtocolVersion(), res.getStatus(), Unpooled.EMPTY_BUFFER, false);
|
|
|
|
} else {
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
|
|
|
|
return fullMsg;
|
|
|
|
}
|
2009-03-10 09:42:19 +01:00
|
|
|
}
|