Ported the HTTP snoop example to the new API
- Replaced pipeline factories with initializers - Ported essential parts related with HTTP to the new API - Replaced ChannelHandlerAdapter.combine() with CombinedChannelHandler - Fixed a bug where ReplayingDecoder does not notify the next handler - Fixed a bug where ReplayingDecoder calls wrong callDecode() method - Added a destination buffer as an argument to AbstractChannel.doRead() for easier implementation - Fixed a bug where NioSocketChannel did not try to increase the inbound buffer size (moved the logic to AbstractChannel)
This commit is contained in:
parent
e846505ceb
commit
af37ec4f23
@ -15,23 +15,19 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.channel.Channels.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.buffer.ChannelBuffers;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.Channels;
|
||||
import io.netty.channel.MessageEvent;
|
||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* A {@link ChannelHandler} that aggregates an {@link HttpMessage}
|
||||
* and its following {@link HttpChunk}s into a single {@link HttpMessage} with
|
||||
@ -50,7 +46,7 @@ import io.netty.util.CharsetUtil;
|
||||
* @apiviz.landmark
|
||||
* @apiviz.has io.netty.handler.codec.http.HttpChunk oneway - - filters out
|
||||
*/
|
||||
public class HttpChunkAggregator extends SimpleChannelUpstreamHandler {
|
||||
public class HttpChunkAggregator extends MessageToMessageDecoder<Object, HttpMessage> {
|
||||
|
||||
private static final ChannelBuffer CONTINUE = ChannelBuffers.copiedBuffer(
|
||||
"HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII);
|
||||
@ -75,11 +71,9 @@ public class HttpChunkAggregator extends SimpleChannelUpstreamHandler {
|
||||
this.maxContentLength = maxContentLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
|
||||
throws Exception {
|
||||
|
||||
Object msg = e.getMessage();
|
||||
@Override
|
||||
public HttpMessage decode(ChannelInboundHandlerContext<Object> ctx, Object msg) throws Exception {
|
||||
HttpMessage currentMessage = this.currentMessage;
|
||||
|
||||
if (msg instanceof HttpMessage) {
|
||||
@ -91,7 +85,7 @@ public class HttpChunkAggregator extends SimpleChannelUpstreamHandler {
|
||||
// No need to notify the upstream handlers - just log.
|
||||
// If decoding a response, just throw an exception.
|
||||
if (is100ContinueExpected(m)) {
|
||||
write(ctx, succeededFuture(ctx.channel()), CONTINUE.duplicate());
|
||||
ctx.write(CONTINUE.duplicate());
|
||||
}
|
||||
|
||||
if (m.isChunked()) {
|
||||
@ -103,12 +97,13 @@ public class HttpChunkAggregator extends SimpleChannelUpstreamHandler {
|
||||
m.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
|
||||
}
|
||||
m.setChunked(false);
|
||||
m.setContent(ChannelBuffers.dynamicBuffer(e.channel().getConfig().getBufferFactory()));
|
||||
m.setContent(ChannelBuffers.dynamicBuffer());
|
||||
this.currentMessage = m;
|
||||
return null;
|
||||
} else {
|
||||
// Not a chunked message - pass through.
|
||||
this.currentMessage = null;
|
||||
ctx.sendUpstream(e);
|
||||
return m;
|
||||
}
|
||||
} else if (msg instanceof HttpChunk) {
|
||||
// Sanity check
|
||||
@ -149,12 +144,15 @@ public class HttpChunkAggregator extends SimpleChannelUpstreamHandler {
|
||||
HttpHeaders.Names.CONTENT_LENGTH,
|
||||
String.valueOf(content.readableBytes()));
|
||||
|
||||
// All done - generate the event.
|
||||
Channels.fireMessageReceived(ctx, currentMessage, e.getRemoteAddress());
|
||||
// All done
|
||||
return currentMessage;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
// Neither HttpMessage or HttpChunk
|
||||
ctx.sendUpstream(e);
|
||||
throw new IllegalStateException(
|
||||
"Only " + HttpMessage.class.getSimpleName() + " and " +
|
||||
HttpChunk.class.getSimpleName() + " are accepted: " + msg.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,16 +15,14 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import java.util.Queue;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelDownstreamHandler;
|
||||
import io.netty.channel.ChannelEvent;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelUpstreamHandler;
|
||||
import io.netty.channel.ChannelInboundHandlerContext;
|
||||
import io.netty.channel.ChannelOutboundHandlerContext;
|
||||
import io.netty.channel.CombinedChannelHandler;
|
||||
import io.netty.util.internal.QueueFactory;
|
||||
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* A combination of {@link HttpRequestEncoder} and {@link HttpResponseDecoder}
|
||||
* which enables easier client side HTTP implementation. {@link HttpClientCodec}
|
||||
@ -38,8 +36,7 @@ import io.netty.util.internal.QueueFactory;
|
||||
* @apiviz.has io.netty.handler.codec.http.HttpResponseDecoder
|
||||
* @apiviz.has io.netty.handler.codec.http.HttpRequestEncoder
|
||||
*/
|
||||
public class HttpClientCodec implements ChannelUpstreamHandler,
|
||||
ChannelDownstreamHandler {
|
||||
public class HttpClientCodec extends CombinedChannelHandler {
|
||||
|
||||
/** A queue that is used for correlating a request and a response. */
|
||||
final Queue<HttpMethod> queue = QueueFactory.createQueue(HttpMethod.class);
|
||||
@ -47,9 +44,6 @@ public class HttpClientCodec implements ChannelUpstreamHandler,
|
||||
/** If true, decoding stops (i.e. pass-through) */
|
||||
volatile boolean done;
|
||||
|
||||
private final HttpRequestEncoder encoder = new Encoder();
|
||||
private final HttpResponseDecoder decoder;
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default decoder options
|
||||
* ({@code maxInitialLineLength (4096}}, {@code maxHeaderSize (8192)}, and
|
||||
@ -64,33 +58,19 @@ public class HttpClientCodec implements ChannelUpstreamHandler,
|
||||
*/
|
||||
public HttpClientCodec(
|
||||
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||
decoder = new Decoder(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||
throws Exception {
|
||||
decoder.handleUpstream(ctx, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||
throws Exception {
|
||||
encoder.handleDownstream(ctx, e);
|
||||
init(
|
||||
new Decoder(maxInitialLineLength, maxHeaderSize, maxChunkSize),
|
||||
new Encoder());
|
||||
}
|
||||
|
||||
private final class Encoder extends HttpRequestEncoder {
|
||||
|
||||
Encoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object encode(ChannelHandlerContext ctx, Channel channel,
|
||||
Object msg) throws Exception {
|
||||
public void encode(
|
||||
ChannelOutboundHandlerContext<Object> ctx, Object msg, ChannelBuffer out) throws Exception {
|
||||
if (msg instanceof HttpRequest && !done) {
|
||||
queue.offer(((HttpRequest) msg).getMethod());
|
||||
}
|
||||
return super.encode(ctx, channel, msg);
|
||||
super.encode(ctx, msg, out);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,12 +81,12 @@ public class HttpClientCodec implements ChannelUpstreamHandler,
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, Channel channel,
|
||||
ChannelBuffer buffer, State state) throws Exception {
|
||||
public Object decode(
|
||||
ChannelInboundHandlerContext<Byte> ctx, ChannelBuffer buffer) throws Exception {
|
||||
if (done) {
|
||||
return buffer.readBytes(actualReadableBytes());
|
||||
} else {
|
||||
return super.decode(ctx, channel, buffer, state);
|
||||
return super.decode(ctx, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,16 +15,15 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.buffer.ChannelBuffers;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.ReplayingDecoder;
|
||||
import io.netty.handler.codec.TooLongFrameException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Decodes {@link ChannelBuffer}s into {@link HttpMessage}s and
|
||||
* {@link HttpChunk}s.
|
||||
@ -98,7 +97,7 @@ import io.netty.handler.codec.TooLongFrameException;
|
||||
* implement all abstract methods properly.
|
||||
* @apiviz.landmark
|
||||
*/
|
||||
public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDecoder.State> {
|
||||
public abstract class HttpMessageDecoder extends ReplayingDecoder<Object, HttpMessageDecoder.State> {
|
||||
|
||||
private final int maxInitialLineLength;
|
||||
private final int maxHeaderSize;
|
||||
@ -143,7 +142,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
protected HttpMessageDecoder(
|
||||
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||
|
||||
super(State.SKIP_CONTROL_CHARS, true);
|
||||
super(State.SKIP_CONTROL_CHARS);
|
||||
|
||||
if (maxInitialLineLength <= 0) {
|
||||
throw new IllegalArgumentException(
|
||||
@ -166,8 +165,8 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception {
|
||||
switch (state) {
|
||||
public Object decode(ChannelInboundHandlerContext<Byte> ctx, ChannelBuffer buffer) throws Exception {
|
||||
switch (state()) {
|
||||
case SKIP_CONTROL_CHARS: {
|
||||
try {
|
||||
skipControlCharacters(buffer);
|
||||
@ -237,7 +236,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
}
|
||||
case READ_VARIABLE_LENGTH_CONTENT: {
|
||||
if (content == null) {
|
||||
content = ChannelBuffers.dynamicBuffer(channel.getConfig().getBufferFactory());
|
||||
content = ChannelBuffers.dynamicBuffer();
|
||||
}
|
||||
//this will cause a replay error until the channel is closed where this will read what's left in the buffer
|
||||
content.writeBytes(buffer.readBytes(buffer.readableBytes()));
|
||||
@ -368,10 +367,10 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
if (msg instanceof HttpResponse) {
|
||||
HttpResponse res = (HttpResponse) msg;
|
||||
int code = res.getStatus().getCode();
|
||||
|
||||
|
||||
// Correctly handle return codes of 1xx.
|
||||
//
|
||||
// See:
|
||||
//
|
||||
// See:
|
||||
// - http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html Section 4.4
|
||||
// - https://github.com/netty/netty/issues/222
|
||||
if (code >= 100 && code < 200) {
|
||||
@ -404,7 +403,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
return message;
|
||||
}
|
||||
|
||||
private void skipControlCharacters(ChannelBuffer buffer) {
|
||||
private static void skipControlCharacters(ChannelBuffer buffer) {
|
||||
for (;;) {
|
||||
char c = (char) buffer.readUnsignedByte();
|
||||
if (!Character.isISOControl(c) &&
|
||||
@ -556,7 +555,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
protected abstract boolean isDecodingRequest();
|
||||
protected abstract HttpMessage createMessage(String[] initialLine) throws Exception;
|
||||
|
||||
private int getChunkSize(String hex) {
|
||||
private static int getChunkSize(String hex) {
|
||||
hex = hex.trim();
|
||||
for (int i = 0; i < hex.length(); i ++) {
|
||||
char c = hex.charAt(i);
|
||||
@ -569,7 +568,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
return Integer.parseInt(hex, 16);
|
||||
}
|
||||
|
||||
private String readLine(ChannelBuffer buffer, int maxLineLength) throws TooLongFrameException {
|
||||
private static String readLine(ChannelBuffer buffer, int maxLineLength) throws TooLongFrameException {
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
int lineLength = 0;
|
||||
while (true) {
|
||||
@ -597,7 +596,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
}
|
||||
}
|
||||
|
||||
private String[] splitInitialLine(String sb) {
|
||||
private static String[] splitInitialLine(String sb) {
|
||||
int aStart;
|
||||
int aEnd;
|
||||
int bStart;
|
||||
@ -620,7 +619,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
cStart < cEnd? sb.substring(cStart, cEnd) : "" };
|
||||
}
|
||||
|
||||
private String[] splitHeader(String sb) {
|
||||
private static String[] splitHeader(String sb) {
|
||||
final int length = sb.length();
|
||||
int nameStart;
|
||||
int nameEnd;
|
||||
@ -658,7 +657,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
};
|
||||
}
|
||||
|
||||
private int findNonWhitespace(String sb, int offset) {
|
||||
private static int findNonWhitespace(String sb, int offset) {
|
||||
int result;
|
||||
for (result = offset; result < sb.length(); result ++) {
|
||||
if (!Character.isWhitespace(sb.charAt(result))) {
|
||||
@ -668,7 +667,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
return result;
|
||||
}
|
||||
|
||||
private int findWhitespace(String sb, int offset) {
|
||||
private static int findWhitespace(String sb, int offset) {
|
||||
int result;
|
||||
for (result = offset; result < sb.length(); result ++) {
|
||||
if (Character.isWhitespace(sb.charAt(result))) {
|
||||
@ -678,7 +677,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
||||
return result;
|
||||
}
|
||||
|
||||
private int findEndOfString(String sb) {
|
||||
private static int findEndOfString(String sb) {
|
||||
int result;
|
||||
for (result = sb.length(); result > 0; result --) {
|
||||
if (!Character.isWhitespace(sb.charAt(result - 1))) {
|
||||
|
@ -17,19 +17,17 @@ package io.netty.handler.codec.http;
|
||||
|
||||
import static io.netty.buffer.ChannelBuffers.*;
|
||||
import static io.netty.handler.codec.http.HttpCodecUtil.*;
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.channel.ChannelOutboundHandlerContext;
|
||||
import io.netty.handler.codec.MessageToStreamEncoder;
|
||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Map;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.buffer.ChannelBuffers;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Names;
|
||||
import io.netty.handler.codec.http.HttpHeaders.Values;
|
||||
import io.netty.handler.codec.oneone.OneToOneEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
/**
|
||||
* Encodes an {@link HttpMessage} or an {@link HttpChunk} into
|
||||
* a {@link ChannelBuffer}.
|
||||
@ -44,7 +42,7 @@ import io.netty.util.CharsetUtil;
|
||||
* implement all abstract methods properly.
|
||||
* @apiviz.landmark
|
||||
*/
|
||||
public abstract class HttpMessageEncoder extends OneToOneEncoder {
|
||||
public abstract class HttpMessageEncoder extends MessageToStreamEncoder<Object> {
|
||||
|
||||
private static final ChannelBuffer LAST_CHUNK =
|
||||
copiedBuffer("0\r\n\r\n", CharsetUtil.US_ASCII);
|
||||
@ -58,7 +56,7 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
|
||||
public void encode(ChannelOutboundHandlerContext<Object> ctx, Object msg, ChannelBuffer out) throws Exception {
|
||||
if (msg instanceof HttpMessage) {
|
||||
HttpMessage m = (HttpMessage) msg;
|
||||
boolean chunked;
|
||||
@ -72,70 +70,59 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
|
||||
} else {
|
||||
chunked = this.chunked = HttpCodecUtil.isTransferEncodingChunked(m);
|
||||
}
|
||||
ChannelBuffer header = ChannelBuffers.dynamicBuffer(
|
||||
channel.getConfig().getBufferFactory());
|
||||
encodeInitialLine(header, m);
|
||||
encodeHeaders(header, m);
|
||||
header.writeByte(CR);
|
||||
header.writeByte(LF);
|
||||
out.markWriterIndex();
|
||||
encodeInitialLine(out, m);
|
||||
encodeHeaders(out, m);
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
|
||||
ChannelBuffer content = m.getContent();
|
||||
if (!content.readable()) {
|
||||
return header; // no content
|
||||
} else if (chunked) {
|
||||
throw new IllegalArgumentException(
|
||||
"HttpMessage.content must be empty " +
|
||||
"if Transfer-Encoding is chunked.");
|
||||
} else {
|
||||
return wrappedBuffer(header, content);
|
||||
if (content.readable()) {
|
||||
if (chunked) {
|
||||
out.resetWriterIndex();
|
||||
throw new IllegalArgumentException(
|
||||
"HttpMessage.content must be empty " +
|
||||
"if Transfer-Encoding is chunked.");
|
||||
} else {
|
||||
out.writeBytes(content, content.readerIndex(), content.readableBytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (msg instanceof HttpChunk) {
|
||||
} else if (msg instanceof HttpChunk) {
|
||||
HttpChunk chunk = (HttpChunk) msg;
|
||||
if (chunked) {
|
||||
if (chunk.isLast()) {
|
||||
chunked = false;
|
||||
if (chunk instanceof HttpChunkTrailer) {
|
||||
ChannelBuffer trailer = ChannelBuffers.dynamicBuffer(
|
||||
channel.getConfig().getBufferFactory());
|
||||
trailer.writeByte((byte) '0');
|
||||
trailer.writeByte(CR);
|
||||
trailer.writeByte(LF);
|
||||
encodeTrailingHeaders(trailer, (HttpChunkTrailer) chunk);
|
||||
trailer.writeByte(CR);
|
||||
trailer.writeByte(LF);
|
||||
return trailer;
|
||||
out.writeByte((byte) '0');
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
encodeTrailingHeaders(out, (HttpChunkTrailer) chunk);
|
||||
out.writeByte(CR);
|
||||
out.writeByte(LF);
|
||||
} else {
|
||||
return LAST_CHUNK.duplicate();
|
||||
out.writeBytes(LAST_CHUNK, LAST_CHUNK.readerIndex(), LAST_CHUNK.readableBytes());
|
||||
}
|
||||
} else {
|
||||
ChannelBuffer content = chunk.getContent();
|
||||
int contentLength = content.readableBytes();
|
||||
|
||||
return wrappedBuffer(
|
||||
copiedBuffer(
|
||||
Integer.toHexString(contentLength),
|
||||
CharsetUtil.US_ASCII),
|
||||
wrappedBuffer(CRLF),
|
||||
content.slice(content.readerIndex(), contentLength),
|
||||
wrappedBuffer(CRLF));
|
||||
out.writeBytes(copiedBuffer(Integer.toHexString(contentLength), CharsetUtil.US_ASCII));
|
||||
out.writeBytes(CRLF);
|
||||
out.writeBytes(content, content.readerIndex(), contentLength);
|
||||
out.writeBytes(CRLF);
|
||||
}
|
||||
} else {
|
||||
if (chunk.isLast()) {
|
||||
return null;
|
||||
} else {
|
||||
return chunk.getContent();
|
||||
if (!chunk.isLast()) {
|
||||
ChannelBuffer chunkContent = chunk.getContent();
|
||||
out.writeBytes(chunkContent, chunkContent.readerIndex(), chunkContent.readableBytes());
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new UnsupportedMessageTypeException(msg, HttpMessage.class, HttpChunk.class);
|
||||
}
|
||||
|
||||
// Unknown message type.
|
||||
return msg;
|
||||
}
|
||||
|
||||
private void encodeHeaders(ChannelBuffer buf, HttpMessage message) {
|
||||
private static void encodeHeaders(ChannelBuffer buf, HttpMessage message) {
|
||||
try {
|
||||
for (Map.Entry<String, String> h: message.getHeaders()) {
|
||||
encodeHeader(buf, h.getKey(), h.getValue());
|
||||
@ -145,7 +132,7 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeTrailingHeaders(ChannelBuffer buf, HttpChunkTrailer trailer) {
|
||||
private static void encodeTrailingHeaders(ChannelBuffer buf, HttpChunkTrailer trailer) {
|
||||
try {
|
||||
for (Map.Entry<String, String> h: trailer.getHeaders()) {
|
||||
encodeHeader(buf, h.getKey(), h.getValue());
|
||||
@ -155,7 +142,7 @@ public abstract class HttpMessageEncoder extends OneToOneEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeHeader(ChannelBuffer buf, String header, String value)
|
||||
private static void encodeHeader(ChannelBuffer buf, String header, String value)
|
||||
throws UnsupportedEncodingException {
|
||||
buf.writeBytes(header.getBytes("ASCII"));
|
||||
buf.writeByte(COLON);
|
||||
|
@ -15,10 +15,7 @@
|
||||
*/
|
||||
package io.netty.handler.codec.http;
|
||||
|
||||
import io.netty.channel.ChannelDownstreamHandler;
|
||||
import io.netty.channel.ChannelEvent;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelUpstreamHandler;
|
||||
import io.netty.channel.CombinedChannelHandler;
|
||||
|
||||
/**
|
||||
* A combination of {@link HttpRequestDecoder} and {@link HttpResponseEncoder}
|
||||
@ -28,11 +25,7 @@ import io.netty.channel.ChannelUpstreamHandler;
|
||||
* @apiviz.has io.netty.handler.codec.http.HttpRequestDecoder
|
||||
* @apiviz.has io.netty.handler.codec.http.HttpResponseEncoder
|
||||
*/
|
||||
public class HttpServerCodec implements ChannelUpstreamHandler,
|
||||
ChannelDownstreamHandler {
|
||||
|
||||
private final HttpRequestDecoder decoder;
|
||||
private final HttpResponseEncoder encoder = new HttpResponseEncoder();
|
||||
public class HttpServerCodec extends CombinedChannelHandler {
|
||||
|
||||
/**
|
||||
* Creates a new instance with the default decoder options
|
||||
@ -48,18 +41,8 @@ public class HttpServerCodec implements ChannelUpstreamHandler,
|
||||
*/
|
||||
public HttpServerCodec(
|
||||
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||
decoder = new HttpRequestDecoder(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||
throws Exception {
|
||||
decoder.handleUpstream(ctx, e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
|
||||
throws Exception {
|
||||
encoder.handleDownstream(ctx, e);
|
||||
super(
|
||||
new HttpRequestDecoder(maxInitialLineLength, maxHeaderSize, maxChunkSize),
|
||||
new HttpResponseEncoder());
|
||||
}
|
||||
}
|
||||
|
@ -339,6 +339,25 @@ public abstract class ReplayingDecoder<O, S extends Enum<S>> extends StreamToMes
|
||||
return oldState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the actual number of readable bytes in the internal cumulative
|
||||
* buffer of this decoder. You usually do not need to rely on this value
|
||||
* to write a decoder. Use it only when you muse use it at your own risk.
|
||||
* This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}.
|
||||
*/
|
||||
protected int actualReadableBytes() {
|
||||
return internalBuffer().readableBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the internal cumulative buffer of this decoder. You usually
|
||||
* do not need to access the internal buffer directly to write a decoder.
|
||||
* Use it only when you must use it at your own risk.
|
||||
*/
|
||||
protected ChannelBuffer internalBuffer() {
|
||||
return cumulation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBufferHolder<Byte> newInboundBuffer(
|
||||
ChannelInboundHandlerContext<Byte> ctx) throws Exception {
|
||||
@ -360,8 +379,7 @@ public abstract class ReplayingDecoder<O, S extends Enum<S>> extends StreamToMes
|
||||
|
||||
try {
|
||||
if (unfoldAndAdd(ctx, ctx.nextIn(), decodeLast(ctx, replayable))) {
|
||||
in.discardReadBytes();
|
||||
ctx.fireInboundBufferUpdated();
|
||||
fireInboundBufferUpdated(ctx, in);
|
||||
}
|
||||
} catch (Signal replay) {
|
||||
// Ignore
|
||||
@ -377,8 +395,10 @@ public abstract class ReplayingDecoder<O, S extends Enum<S>> extends StreamToMes
|
||||
ctx.fireChannelInactive();
|
||||
}
|
||||
|
||||
private void callDecode(ChannelInboundHandlerContext<Byte> ctx) {
|
||||
@Override
|
||||
protected void callDecode(ChannelInboundHandlerContext<Byte> ctx) {
|
||||
ChannelBuffer in = cumulation;
|
||||
boolean decoded = false;
|
||||
while (in.readable()) {
|
||||
try {
|
||||
int oldReaderIndex = checkpoint = in.readerIndex();
|
||||
@ -422,8 +442,15 @@ public abstract class ReplayingDecoder<O, S extends Enum<S>> extends StreamToMes
|
||||
}
|
||||
|
||||
// A successful decode
|
||||
MessageToMessageEncoder.unfoldAndAdd(ctx, ctx.nextIn(), result);
|
||||
if (unfoldAndAdd(ctx, ctx.nextIn(), result)) {
|
||||
decoded = true;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (decoded) {
|
||||
decoded = false;
|
||||
fireInboundBufferUpdated(ctx, in);
|
||||
}
|
||||
|
||||
if (t instanceof CodecException) {
|
||||
ctx.fireExceptionCaught(t);
|
||||
} else {
|
||||
@ -431,5 +458,15 @@ public abstract class ReplayingDecoder<O, S extends Enum<S>> extends StreamToMes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (decoded) {
|
||||
fireInboundBufferUpdated(ctx, in);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireInboundBufferUpdated(ChannelInboundHandlerContext<Byte> ctx, ChannelBuffer in) {
|
||||
checkpoint -= in.readerIndex();
|
||||
in.discardReadBytes();
|
||||
ctx.fireInboundBufferUpdated();
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public abstract class StreamToMessageDecoder<O> extends ChannelInboundHandlerAda
|
||||
ctx.fireChannelInactive();
|
||||
}
|
||||
|
||||
private void callDecode(ChannelInboundHandlerContext<Byte> ctx) {
|
||||
protected void callDecode(ChannelInboundHandlerContext<Byte> ctx) {
|
||||
ChannelBuffer in = ctx.in().byteBuffer();
|
||||
|
||||
boolean decoded = false;
|
||||
@ -75,6 +75,12 @@ public abstract class StreamToMessageDecoder<O> extends ChannelInboundHandlerAda
|
||||
break;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
if (decoded) {
|
||||
decoded = false;
|
||||
in.discardReadBytes();
|
||||
ctx.fireInboundBufferUpdated();
|
||||
}
|
||||
|
||||
if (t instanceof CodecException) {
|
||||
ctx.fireExceptionCaught(t);
|
||||
} else {
|
||||
|
@ -15,14 +15,10 @@
|
||||
*/
|
||||
package io.netty.example.http.snoop;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import io.netty.bootstrap.ClientBootstrap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.socket.nio.NioClientSocketChannelFactory;
|
||||
import io.netty.channel.ChannelBootstrap;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.socket.nio.SelectorEventLoop;
|
||||
import io.netty.handler.codec.http.CookieEncoder;
|
||||
import io.netty.handler.codec.http.DefaultHttpRequest;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
@ -30,6 +26,9 @@ import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A simple HTTP client that prints out the content of the HTTP response to
|
||||
* {@link System#out} to test {@link HttpSnoopServer}.
|
||||
@ -42,7 +41,7 @@ public class HttpSnoopClient {
|
||||
this.uri = uri;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
public void run() throws Exception {
|
||||
String scheme = uri.getScheme() == null? "http" : uri.getScheme();
|
||||
String host = uri.getHost() == null? "localhost" : uri.getHost();
|
||||
int port = uri.getPort();
|
||||
@ -62,45 +61,38 @@ public class HttpSnoopClient {
|
||||
boolean ssl = scheme.equalsIgnoreCase("https");
|
||||
|
||||
// Configure the client.
|
||||
ClientBootstrap bootstrap = new ClientBootstrap(
|
||||
new NioClientSocketChannelFactory(
|
||||
Executors.newCachedThreadPool()));
|
||||
ChannelBootstrap b = new ChannelBootstrap();
|
||||
try {
|
||||
b.eventLoop(new SelectorEventLoop())
|
||||
.channel(new NioSocketChannel())
|
||||
.initializer(new HttpSnoopClientInitializer(ssl))
|
||||
.remoteAddress(new InetSocketAddress(host, port));
|
||||
|
||||
// Set up the event pipeline factory.
|
||||
bootstrap.setPipelineFactory(new HttpSnoopClientPipelineFactory(ssl));
|
||||
// Make the connection attempt.
|
||||
Channel ch = b.connect().sync().channel();
|
||||
|
||||
// Start the connection attempt.
|
||||
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
|
||||
// Prepare the HTTP request.
|
||||
HttpRequest request = new DefaultHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
|
||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
|
||||
// Wait until the connection attempt succeeds or fails.
|
||||
Channel channel = future.awaitUninterruptibly().channel();
|
||||
if (!future.isSuccess()) {
|
||||
future.cause().printStackTrace();
|
||||
bootstrap.releaseExternalResources();
|
||||
return;
|
||||
// Set some example cookies.
|
||||
CookieEncoder httpCookieEncoder = new CookieEncoder(false);
|
||||
httpCookieEncoder.addCookie("my-cookie", "foo");
|
||||
httpCookieEncoder.addCookie("another-cookie", "bar");
|
||||
request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode());
|
||||
|
||||
// Send the HTTP request.
|
||||
ch.write(request);
|
||||
|
||||
// Wait for the server to close the connection.
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
// Shut down executor threads to exit.
|
||||
b.shutdown();
|
||||
}
|
||||
|
||||
// Prepare the HTTP request.
|
||||
HttpRequest request = new DefaultHttpRequest(
|
||||
HttpVersion.HTTP_1_1, HttpMethod.GET, uri.getRawPath());
|
||||
request.setHeader(HttpHeaders.Names.HOST, host);
|
||||
request.setHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE);
|
||||
request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
|
||||
|
||||
// Set some example cookies.
|
||||
CookieEncoder httpCookieEncoder = new CookieEncoder(false);
|
||||
httpCookieEncoder.addCookie("my-cookie", "foo");
|
||||
httpCookieEncoder.addCookie("another-cookie", "bar");
|
||||
request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode());
|
||||
|
||||
// Send the HTTP request.
|
||||
channel.write(request);
|
||||
|
||||
// Wait for the server to close the connection.
|
||||
channel.getCloseFuture().awaitUninterruptibly();
|
||||
|
||||
// Shut down executor threads to exit.
|
||||
bootstrap.releaseExternalResources();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
@ -16,21 +16,44 @@
|
||||
package io.netty.example.http.snoop;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.MessageEvent;
|
||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
||||
import io.netty.channel.ChannelBufferHolder;
|
||||
import io.netty.channel.ChannelBufferHolders;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInboundHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpChunk;
|
||||
import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class HttpSnoopClientHandler extends SimpleChannelUpstreamHandler {
|
||||
import java.util.Queue;
|
||||
|
||||
public class HttpSnoopClientHandler extends ChannelInboundHandlerAdapter<Object> {
|
||||
|
||||
private boolean readingChunks;
|
||||
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
||||
public ChannelBufferHolder<Object> newInboundBuffer(
|
||||
ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
return ChannelBufferHolders.messageBuffer();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void inboundBufferUpdated(ChannelInboundHandlerContext<Object> ctx)
|
||||
throws Exception {
|
||||
Queue<Object> in = ctx.in().messageBuffer();
|
||||
while (handleMessage(in.poll())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleMessage(Object msg) throws Exception {
|
||||
if (msg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readingChunks) {
|
||||
HttpResponse response = (HttpResponse) e.getMessage();
|
||||
HttpResponse response = (HttpResponse) msg;
|
||||
|
||||
System.out.println("STATUS: " + response.getStatus());
|
||||
System.out.println("VERSION: " + response.getProtocolVersion());
|
||||
@ -57,7 +80,7 @@ public class HttpSnoopClientHandler extends SimpleChannelUpstreamHandler {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HttpChunk chunk = (HttpChunk) e.getMessage();
|
||||
HttpChunk chunk = (HttpChunk) msg;
|
||||
if (chunk.isLast()) {
|
||||
readingChunks = false;
|
||||
System.out.println("} END OF CHUNKED CONTENT");
|
||||
@ -66,5 +89,14 @@ public class HttpSnoopClientHandler extends SimpleChannelUpstreamHandler {
|
||||
System.out.flush();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(
|
||||
ChannelInboundHandlerContext<Object> ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
@ -15,51 +15,49 @@
|
||||
*/
|
||||
package io.netty.example.http.snoop;
|
||||
|
||||
import static io.netty.channel.Channels.*;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.example.securechat.SecureChatSslContextFactory;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.logging.LogLevel;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPipelineFactory;
|
||||
import io.netty.example.securechat.SecureChatSslContextFactory;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import io.netty.logging.InternalLogLevel;
|
||||
|
||||
public class HttpSnoopClientPipelineFactory implements ChannelPipelineFactory {
|
||||
public class HttpSnoopClientInitializer extends ChannelInitializer {
|
||||
|
||||
private final boolean ssl;
|
||||
|
||||
public HttpSnoopClientPipelineFactory(boolean ssl) {
|
||||
public HttpSnoopClientInitializer(boolean ssl) {
|
||||
this.ssl = ssl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
public void initChannel(Channel ch) throws Exception {
|
||||
// Create a default pipeline implementation.
|
||||
ChannelPipeline pipeline = pipeline();
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
pipeline.addLast("log", new LoggingHandler(InternalLogLevel.INFO));
|
||||
p.addLast("log", new LoggingHandler(LogLevel.INFO));
|
||||
// Enable HTTPS if necessary.
|
||||
if (ssl) {
|
||||
SSLEngine engine =
|
||||
SecureChatSslContextFactory.getClientContext().createSSLEngine();
|
||||
engine.setUseClientMode(true);
|
||||
|
||||
pipeline.addLast("ssl", new SslHandler(engine));
|
||||
// FIXME: Port SslHandler to the new API
|
||||
//p.addLast("ssl", new SslHandler(engine));
|
||||
}
|
||||
|
||||
pipeline.addLast("codec", new HttpClientCodec());
|
||||
p.addLast("codec", new HttpClientCodec());
|
||||
|
||||
// Remove the following line if you don't want automatic content decompression.
|
||||
pipeline.addLast("inflater", new HttpContentDecompressor());
|
||||
// FIXME: Port HttpContentDecompressor to the new API
|
||||
//p.addLast("inflater", new HttpContentDecompressor());
|
||||
|
||||
// Uncomment the following line if you don't want to handle HttpChunks.
|
||||
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
|
||||
|
||||
pipeline.addLast("handler", new HttpSnoopClientHandler());
|
||||
return pipeline;
|
||||
p.addLast("handler", new HttpSnoopClientHandler());
|
||||
}
|
||||
}
|
@ -15,11 +15,12 @@
|
||||
*/
|
||||
package io.netty.example.http.snoop;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.Executors;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ServerChannelBootstrap;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.SelectorEventLoop;
|
||||
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannelFactory;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* An HTTP server that sends back the content of the received HTTP request
|
||||
@ -33,20 +34,24 @@ public class HttpSnoopServer {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
public void run() throws Exception {
|
||||
// Configure the server.
|
||||
ServerBootstrap bootstrap = new ServerBootstrap(
|
||||
new NioServerSocketChannelFactory(
|
||||
Executors.newCachedThreadPool()));
|
||||
ServerChannelBootstrap b = new ServerChannelBootstrap();
|
||||
|
||||
// Set up the event pipeline factory.
|
||||
bootstrap.setPipelineFactory(new HttpSnoopServerPipelineFactory());
|
||||
try {
|
||||
b.eventLoop(new SelectorEventLoop(), new SelectorEventLoop())
|
||||
.channel(new NioServerSocketChannel())
|
||||
.childInitializer(new HttpSnoopServerInitializer())
|
||||
.localAddress(new InetSocketAddress(port));
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
bootstrap.bind(new InetSocketAddress(port));
|
||||
Channel ch = b.bind().sync().channel();
|
||||
ch.closeFuture().sync();
|
||||
} finally {
|
||||
b.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
public static void main(String[] args) throws Exception {
|
||||
int port;
|
||||
if (args.length > 0) {
|
||||
port = Integer.parseInt(args[0]);
|
||||
|
@ -19,20 +19,14 @@ import static io.netty.handler.codec.http.HttpHeaders.*;
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
|
||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.buffer.ChannelBuffers;
|
||||
import io.netty.channel.ChannelBufferHolder;
|
||||
import io.netty.channel.ChannelBufferHolders;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ExceptionEvent;
|
||||
import io.netty.channel.MessageEvent;
|
||||
import io.netty.channel.SimpleChannelUpstreamHandler;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.ChannelInboundHandlerContext;
|
||||
import io.netty.handler.codec.http.Cookie;
|
||||
import io.netty.handler.codec.http.CookieDecoder;
|
||||
import io.netty.handler.codec.http.CookieEncoder;
|
||||
@ -45,7 +39,13 @@ import io.netty.handler.codec.http.HttpResponse;
|
||||
import io.netty.handler.codec.http.QueryStringDecoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
|
||||
public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter<Object> {
|
||||
|
||||
private HttpRequest request;
|
||||
private boolean readingChunks;
|
||||
@ -53,12 +53,30 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
private final StringBuilder buf = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
||||
public ChannelBufferHolder<Object> newInboundBuffer(
|
||||
ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
return ChannelBufferHolders.messageBuffer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inboundBufferUpdated(ChannelInboundHandlerContext<Object> ctx)
|
||||
throws Exception {
|
||||
Queue<Object> in = ctx.in().messageBuffer();
|
||||
while (handleMessage(ctx, in.poll())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handleMessage(ChannelInboundHandlerContext<Object> ctx, Object msg) throws Exception {
|
||||
if (msg == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readingChunks) {
|
||||
HttpRequest request = this.request = (HttpRequest) e.getMessage();
|
||||
HttpRequest request = this.request = (HttpRequest) msg;
|
||||
|
||||
if (is100ContinueExpected(request)) {
|
||||
send100Continue(e);
|
||||
send100Continue(ctx);
|
||||
}
|
||||
|
||||
buf.setLength(0);
|
||||
@ -94,10 +112,10 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
if (content.readable()) {
|
||||
buf.append("CONTENT: " + content.toString(CharsetUtil.UTF_8) + "\r\n");
|
||||
}
|
||||
writeResponse(e);
|
||||
writeResponse(ctx);
|
||||
}
|
||||
} else {
|
||||
HttpChunk chunk = (HttpChunk) e.getMessage();
|
||||
HttpChunk chunk = (HttpChunk) msg;
|
||||
if (chunk.isLast()) {
|
||||
readingChunks = false;
|
||||
buf.append("END OF CONTENT\r\n");
|
||||
@ -113,14 +131,16 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
buf.append("\r\n");
|
||||
}
|
||||
|
||||
writeResponse(e);
|
||||
writeResponse(ctx);
|
||||
} else {
|
||||
buf.append("CHUNK: " + chunk.getContent().toString(CharsetUtil.UTF_8) + "\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void writeResponse(MessageEvent e) {
|
||||
private void writeResponse(ChannelInboundHandlerContext<Object> ctx) {
|
||||
// Decide whether to close the connection or not.
|
||||
boolean keepAlive = isKeepAlive(request);
|
||||
|
||||
@ -152,7 +172,7 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
}
|
||||
|
||||
// Write the response.
|
||||
ChannelFuture future = e.channel().write(response);
|
||||
ChannelFuture future = ctx.write(response);
|
||||
|
||||
// Close the non-keep-alive connection after the write operation is done.
|
||||
if (!keepAlive) {
|
||||
@ -160,15 +180,15 @@ public class HttpSnoopServerHandler extends SimpleChannelUpstreamHandler {
|
||||
}
|
||||
}
|
||||
|
||||
private void send100Continue(MessageEvent e) {
|
||||
private static void send100Continue(ChannelInboundHandlerContext<Object> ctx) {
|
||||
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, CONTINUE);
|
||||
e.channel().write(response);
|
||||
ctx.write(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
|
||||
throws Exception {
|
||||
e.cause().printStackTrace();
|
||||
e.channel().close();
|
||||
public void exceptionCaught(
|
||||
ChannelInboundHandlerContext<Object> ctx, Throwable cause) throws Exception {
|
||||
cause.printStackTrace();
|
||||
ctx.close();
|
||||
}
|
||||
}
|
||||
|
@ -15,32 +15,30 @@
|
||||
*/
|
||||
package io.netty.example.http.snoop;
|
||||
|
||||
import static io.netty.channel.Channels.*;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.ChannelPipelineFactory;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
|
||||
public class HttpSnoopServerPipelineFactory implements ChannelPipelineFactory {
|
||||
public class HttpSnoopServerInitializer extends ChannelInitializer {
|
||||
@Override
|
||||
public ChannelPipeline getPipeline() throws Exception {
|
||||
public void initChannel(Channel ch) throws Exception {
|
||||
// Create a default pipeline implementation.
|
||||
ChannelPipeline pipeline = pipeline();
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
|
||||
// Uncomment the following line if you want HTTPS
|
||||
//SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine();
|
||||
//engine.setUseClientMode(false);
|
||||
//pipeline.addLast("ssl", new SslHandler(engine));
|
||||
//p.addLast("ssl", new SslHandler(engine));
|
||||
|
||||
pipeline.addLast("decoder", new HttpRequestDecoder());
|
||||
p.addLast("decoder", new HttpRequestDecoder());
|
||||
// Uncomment the following line if you don't want to handle HttpChunks.
|
||||
//pipeline.addLast("aggregator", new HttpChunkAggregator(1048576));
|
||||
pipeline.addLast("encoder", new HttpResponseEncoder());
|
||||
p.addLast("encoder", new HttpResponseEncoder());
|
||||
// Remove the following line if you don't want automatic content compression.
|
||||
pipeline.addLast("deflater", new HttpContentCompressor());
|
||||
pipeline.addLast("handler", new HttpSnoopServerHandler());
|
||||
return pipeline;
|
||||
// FIXME: Port HttpContentCompressor to the new API
|
||||
//p.addLast("deflater", new HttpContentCompressor());
|
||||
p.addLast("handler", new HttpSnoopServerHandler());
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.channel;
|
||||
|
||||
import io.netty.buffer.ChannelBuffer;
|
||||
import io.netty.logging.InternalLogger;
|
||||
import io.netty.logging.InternalLoggerFactory;
|
||||
import io.netty.util.DefaultAttributeMap;
|
||||
@ -636,19 +637,23 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||
@Override
|
||||
public void read() {
|
||||
assert eventLoop().inEventLoop();
|
||||
|
||||
final ChannelBufferHolder<Object> buf = pipeline().nextIn();
|
||||
final boolean hasByteBuffer = buf.hasByteBuffer();
|
||||
|
||||
long readAmount = 0;
|
||||
boolean closed = false;
|
||||
try {
|
||||
for (;;) {
|
||||
int localReadAmount = doRead();
|
||||
int localReadAmount = doRead(buf);
|
||||
if (localReadAmount > 0) {
|
||||
readAmount += localReadAmount;
|
||||
continue;
|
||||
}
|
||||
if (localReadAmount == 0) {
|
||||
break;
|
||||
}
|
||||
if (localReadAmount < 0) {
|
||||
expandReadBuffer(buf, hasByteBuffer);
|
||||
} else if (localReadAmount == 0) {
|
||||
if (!expandReadBuffer(buf, hasByteBuffer)) {
|
||||
break;
|
||||
}
|
||||
} else if (localReadAmount < 0) {
|
||||
closed = true;
|
||||
break;
|
||||
}
|
||||
@ -851,7 +856,22 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha
|
||||
protected abstract void doClose() throws Exception;
|
||||
protected abstract void doDeregister() throws Exception;
|
||||
|
||||
protected abstract int doRead() throws Exception;
|
||||
protected abstract int doRead(ChannelBufferHolder<Object> buf) throws Exception;
|
||||
protected abstract int doFlush(boolean lastSpin) throws Exception;
|
||||
protected abstract boolean inEventLoopDrivenFlush();
|
||||
|
||||
private static boolean expandReadBuffer(ChannelBufferHolder<Object> buf, boolean hasByteBuffer) {
|
||||
if (!hasByteBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ChannelBuffer byteBuf = buf.byteBuffer();
|
||||
if (!byteBuf.writable()) {
|
||||
// FIXME: Use a sensible value.
|
||||
byteBuf.ensureWritableBytes(128);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -4,145 +4,6 @@ import java.net.SocketAddress;
|
||||
|
||||
public abstract class ChannelHandlerAdapter<I, O> implements ChannelInboundHandler<I>, ChannelOutboundHandler<O> {
|
||||
|
||||
public static <I, O> ChannelHandlerAdapter<I, O> combine(
|
||||
ChannelInboundHandler<I> inboundHandler, ChannelOutboundHandler<O> outboundHandler) {
|
||||
if (inboundHandler == null) {
|
||||
throw new NullPointerException("inboundHandler");
|
||||
}
|
||||
if (outboundHandler == null) {
|
||||
throw new NullPointerException("outboundHandler");
|
||||
}
|
||||
|
||||
final ChannelInboundHandler<I> in = inboundHandler;
|
||||
final ChannelOutboundHandler<O> out = outboundHandler;
|
||||
return new ChannelHandlerAdapter<I, O>() {
|
||||
|
||||
@Override
|
||||
public ChannelBufferHolder<I> newInboundBuffer(
|
||||
ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
return in.newInboundBuffer(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBufferHolder<O> newOutboundBuffer(
|
||||
ChannelOutboundHandlerContext<O> ctx) throws Exception {
|
||||
return out.newOutboundBuffer(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.beforeAdd(ctx);
|
||||
} finally {
|
||||
out.beforeAdd(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.afterAdd(ctx);
|
||||
} finally {
|
||||
out.afterAdd(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeRemove(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.beforeRemove(ctx);
|
||||
} finally {
|
||||
out.beforeRemove(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.afterRemove(ctx);
|
||||
} finally {
|
||||
out.afterRemove(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
in.channelRegistered(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
in.channelUnregistered(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
in.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
in.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(
|
||||
ChannelInboundHandlerContext<I> ctx, Throwable cause) throws Exception {
|
||||
in.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(
|
||||
ChannelInboundHandlerContext<I> ctx, Object evt) throws Exception {
|
||||
in.userEventTriggered(ctx, evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inboundBufferUpdated(ChannelInboundHandlerContext<I> ctx) throws Exception {
|
||||
in.inboundBufferUpdated(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(
|
||||
ChannelOutboundHandlerContext<O> ctx,
|
||||
SocketAddress localAddress, ChannelFuture future) throws Exception {
|
||||
out.bind(ctx, localAddress, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(
|
||||
ChannelOutboundHandlerContext<O> ctx,
|
||||
SocketAddress remoteAddress, SocketAddress localAddress,
|
||||
ChannelFuture future) throws Exception {
|
||||
out.connect(ctx, remoteAddress, localAddress, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(
|
||||
ChannelOutboundHandlerContext<O> ctx, ChannelFuture future) throws Exception {
|
||||
out.disconnect(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(
|
||||
ChannelOutboundHandlerContext<O> ctx, ChannelFuture future) throws Exception {
|
||||
out.close(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(
|
||||
ChannelOutboundHandlerContext<O> ctx, ChannelFuture future) throws Exception {
|
||||
out.deregister(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(
|
||||
ChannelOutboundHandlerContext<O> ctx, ChannelFuture future) throws Exception {
|
||||
out.flush(ctx, future);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
// Do nothing by default.
|
||||
|
@ -0,0 +1,166 @@
|
||||
package io.netty.channel;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
public class CombinedChannelHandler implements ChannelInboundHandler<Object>,
|
||||
ChannelOutboundHandler<Object> {
|
||||
|
||||
private ChannelOutboundHandler<Object> out;
|
||||
private ChannelInboundHandler<Object> in;
|
||||
|
||||
public CombinedChannelHandler() {
|
||||
// User will call init in the subclass constructor.
|
||||
}
|
||||
|
||||
public CombinedChannelHandler(
|
||||
ChannelInboundHandler<?> inboundHandler, ChannelOutboundHandler<?> outboundHandler) {
|
||||
init(inboundHandler, outboundHandler);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void init(ChannelInboundHandler<?> inboundHandler,
|
||||
ChannelOutboundHandler<?> outboundHandler) {
|
||||
if (inboundHandler == null) {
|
||||
throw new NullPointerException("inboundHandler");
|
||||
}
|
||||
if (outboundHandler == null) {
|
||||
throw new NullPointerException("outboundHandler");
|
||||
}
|
||||
|
||||
if (in != null) {
|
||||
throw new IllegalStateException("init() cannot be called more than once.");
|
||||
}
|
||||
|
||||
in = (ChannelInboundHandler<Object>) inboundHandler;
|
||||
out = (ChannelOutboundHandler<Object>) outboundHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBufferHolder<Object> newInboundBuffer(
|
||||
ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
return in.newInboundBuffer(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelBufferHolder<Object> newOutboundBuffer(
|
||||
ChannelOutboundHandlerContext<Object> ctx) throws Exception {
|
||||
return out.newOutboundBuffer(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
if (in == null) {
|
||||
throw new IllegalStateException(
|
||||
"not initialized yet - call init() in the constructor of the subclass");
|
||||
}
|
||||
|
||||
try {
|
||||
in.beforeAdd(ctx);
|
||||
} finally {
|
||||
out.beforeAdd(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterAdd(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.afterAdd(ctx);
|
||||
} finally {
|
||||
out.afterAdd(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeRemove(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.beforeRemove(ctx);
|
||||
} finally {
|
||||
out.beforeRemove(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterRemove(ChannelHandlerContext ctx) throws Exception {
|
||||
try {
|
||||
in.afterRemove(ctx);
|
||||
} finally {
|
||||
out.afterRemove(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
in.channelRegistered(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelUnregistered(ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
in.channelUnregistered(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelActive(ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
in.channelActive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
in.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(
|
||||
ChannelInboundHandlerContext<Object> ctx, Throwable cause) throws Exception {
|
||||
in.exceptionCaught(ctx, cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void userEventTriggered(
|
||||
ChannelInboundHandlerContext<Object> ctx, Object evt) throws Exception {
|
||||
in.userEventTriggered(ctx, evt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inboundBufferUpdated(ChannelInboundHandlerContext<Object> ctx) throws Exception {
|
||||
in.inboundBufferUpdated(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(
|
||||
ChannelOutboundHandlerContext<Object> ctx,
|
||||
SocketAddress localAddress, ChannelFuture future) throws Exception {
|
||||
out.bind(ctx, localAddress, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(
|
||||
ChannelOutboundHandlerContext<Object> ctx,
|
||||
SocketAddress remoteAddress, SocketAddress localAddress,
|
||||
ChannelFuture future) throws Exception {
|
||||
out.connect(ctx, remoteAddress, localAddress, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(
|
||||
ChannelOutboundHandlerContext<Object> ctx, ChannelFuture future) throws Exception {
|
||||
out.disconnect(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(
|
||||
ChannelOutboundHandlerContext<Object> ctx, ChannelFuture future) throws Exception {
|
||||
out.close(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(
|
||||
ChannelOutboundHandlerContext<Object> ctx, ChannelFuture future) throws Exception {
|
||||
out.deregister(ctx, future);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush(
|
||||
ChannelOutboundHandlerContext<Object> ctx, ChannelFuture future) throws Exception {
|
||||
out.flush(ctx, future);
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
package io.netty.channel.socket.nio;
|
||||
|
||||
import io.netty.channel.AbstractServerChannel;
|
||||
import io.netty.channel.ChannelBufferHolder;
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.socket.DefaultServerSocketChannelConfig;
|
||||
@ -127,12 +128,12 @@ public class NioServerSocketChannel extends AbstractServerChannel
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doRead() throws Exception {
|
||||
protected int doRead(ChannelBufferHolder<Object> buf) throws Exception {
|
||||
java.nio.channels.SocketChannel ch = javaChannel().accept();
|
||||
if (ch == null) {
|
||||
return 0;
|
||||
}
|
||||
pipeline().nextIn().messageBuffer().add(new NioSocketChannel(this, null, ch));
|
||||
buf.messageBuffer().add(new NioSocketChannel(this, null, ch));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@ -166,9 +166,9 @@ public class NioSocketChannel extends AbstractNioChannel implements io.netty.cha
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int doRead() throws Exception {
|
||||
ChannelBuffer buf = pipeline().nextIn().byteBuffer();
|
||||
return buf.writeBytes(javaChannel(), buf.writableBytes());
|
||||
protected int doRead(ChannelBufferHolder<Object> buf) throws Exception {
|
||||
ChannelBuffer byteBuf = buf.byteBuffer();
|
||||
return byteBuf.writeBytes(javaChannel(), byteBuf.writableBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
Loading…
Reference in New Issue
Block a user