* all limit parameters are mandatory to create HTTP decoders now for better security
* chunks are not merged anymore for better security * modified HTTP client example to handle HTTP chunks
This commit is contained in:
parent
4a72aafd56
commit
a5ebbfb111
@ -43,7 +43,7 @@ public class HttpClientPipelineFactory implements ChannelPipelineFactory {
|
|||||||
public ChannelPipeline getPipeline() throws Exception {
|
public ChannelPipeline getPipeline() throws Exception {
|
||||||
// Create a default pipeline implementation.
|
// Create a default pipeline implementation.
|
||||||
ChannelPipeline pipeline = pipeline();
|
ChannelPipeline pipeline = pipeline();
|
||||||
pipeline.addLast("decoder", new HttpResponseDecoder());
|
pipeline.addLast("decoder", new HttpResponseDecoder(8192, 8192, 8192));
|
||||||
pipeline.addLast("encoder", new HttpRequestEncoder());
|
pipeline.addLast("encoder", new HttpRequestEncoder());
|
||||||
pipeline.addLast("handler", handler);
|
pipeline.addLast("handler", handler);
|
||||||
return pipeline;
|
return pipeline;
|
||||||
|
@ -93,7 +93,6 @@ public class HttpRequestHandler extends SimpleChannelHandler {
|
|||||||
|
|
||||||
if (request.isChunked()) {
|
if (request.isChunked()) {
|
||||||
readingChunks = true;
|
readingChunks = true;
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
ChannelBuffer content = request.getContent();
|
ChannelBuffer content = request.getContent();
|
||||||
if (content.readable()) {
|
if (content.readable()) {
|
||||||
@ -107,7 +106,6 @@ public class HttpRequestHandler extends SimpleChannelHandler {
|
|||||||
readingChunks = false;
|
readingChunks = false;
|
||||||
responseContent.append("END OF CONTENT\r\n");
|
responseContent.append("END OF CONTENT\r\n");
|
||||||
writeResponse(e);
|
writeResponse(e);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
responseContent.append("CHUNK: " + chunk.getContent().toString("UTF-8") + "\r\n");
|
responseContent.append("CHUNK: " + chunk.getContent().toString("UTF-8") + "\r\n");
|
||||||
}
|
}
|
||||||
|
@ -21,23 +21,59 @@
|
|||||||
*/
|
*/
|
||||||
package org.jboss.netty.example.http;
|
package org.jboss.netty.example.http;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
|
||||||
import org.jboss.netty.channel.ChannelHandlerContext;
|
import org.jboss.netty.channel.ChannelHandlerContext;
|
||||||
import org.jboss.netty.channel.ChannelPipelineCoverage;
|
import org.jboss.netty.channel.ChannelPipelineCoverage;
|
||||||
import org.jboss.netty.channel.MessageEvent;
|
import org.jboss.netty.channel.MessageEvent;
|
||||||
import org.jboss.netty.channel.SimpleChannelHandler;
|
import org.jboss.netty.channel.SimpleChannelHandler;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpChunk;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author The Netty Project (netty-dev@lists.jboss.org)
|
* @author The Netty Project (netty-dev@lists.jboss.org)
|
||||||
* @author Andy Taylor (andy.taylor@jboss.org)
|
* @author Andy Taylor (andy.taylor@jboss.org)
|
||||||
|
* @author Trustin Lee (tlee@redhat.com)
|
||||||
*/
|
*/
|
||||||
@ChannelPipelineCoverage("all")
|
@ChannelPipelineCoverage("one")
|
||||||
public class HttpResponseHandler extends SimpleChannelHandler {
|
public class HttpResponseHandler extends SimpleChannelHandler {
|
||||||
|
|
||||||
|
private volatile boolean readingChunks;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
||||||
HttpResponse response = (HttpResponse) e.getMessage();
|
if (!readingChunks) {
|
||||||
System.out.println(response.getContent().toString(Charset.defaultCharset().name()));
|
HttpResponse response = (HttpResponse) e.getMessage();
|
||||||
|
|
||||||
|
System.out.println("STATUS: " + response.getStatus());
|
||||||
|
System.out.println("VERSION: " + response.getProtocolVersion());
|
||||||
|
System.out.println();
|
||||||
|
|
||||||
|
if (!response.getHeaderNames().isEmpty()) {
|
||||||
|
for (String name: response.getHeaderNames()) {
|
||||||
|
for (String value: response.getHeaders(name)) {
|
||||||
|
System.out.println("HEADER: " + name + " = " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response.getStatus().getCode() == 200 && response.isChunked()) {
|
||||||
|
readingChunks = true;
|
||||||
|
System.out.println("CHUNKED CONTENT:");
|
||||||
|
} else {
|
||||||
|
ChannelBuffer content = response.getContent();
|
||||||
|
if (content.readable()) {
|
||||||
|
System.out.println("CONTENT:");
|
||||||
|
System.out.println(content.toString("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HttpChunk chunk = (HttpChunk) e.getMessage();
|
||||||
|
if (chunk.isLast()) {
|
||||||
|
readingChunks = false;
|
||||||
|
} else {
|
||||||
|
System.out.println(chunk.getContent().toString("UTF-8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,10 +77,6 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
READ_CHUNK_FOOTER;
|
READ_CHUNK_FOOTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected HttpMessageDecoder() {
|
|
||||||
this(8192, 8192, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected HttpMessageDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
protected HttpMessageDecoder(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||||
super(State.SKIP_CONTROL_CHARS, true);
|
super(State.SKIP_CONTROL_CHARS, true);
|
||||||
if (maxInitialLineLength <= 0) {
|
if (maxInitialLineLength <= 0) {
|
||||||
@ -95,7 +91,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
}
|
}
|
||||||
if (maxChunkSize < 0) {
|
if (maxChunkSize < 0) {
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
"maxChunkSize must not be a negative integer: " +
|
"maxChunkSize must be a positive integer: " +
|
||||||
maxChunkSize);
|
maxChunkSize);
|
||||||
}
|
}
|
||||||
this.maxInitialLineLength = maxInitialLineLength;
|
this.maxInitialLineLength = maxInitialLineLength;
|
||||||
@ -103,10 +99,6 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
this.maxChunkSize = maxChunkSize;
|
this.maxChunkSize = maxChunkSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canGenerateChunks() {
|
|
||||||
return maxChunkSize > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception {
|
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, State state) throws Exception {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
@ -135,12 +127,8 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
checkpoint(nextState);
|
checkpoint(nextState);
|
||||||
if (nextState == State.READ_CHUNK_SIZE) {
|
if (nextState == State.READ_CHUNK_SIZE) {
|
||||||
// Chunked encoding
|
// Chunked encoding
|
||||||
if (canGenerateChunks()) {
|
// Generate HttpMessage first. HttpChunks will follow.
|
||||||
// Generate HttpMessage first. HttpChunks will follow.
|
return message;
|
||||||
return message;
|
|
||||||
} else {
|
|
||||||
// Merge all chunks.
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
int contentLength = message.getContentLength(-1);
|
int contentLength = message.getContentLength(-1);
|
||||||
if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {
|
if (contentLength == 0 || contentLength == -1 && isDecodingRequest()) {
|
||||||
@ -148,10 +136,9 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
return reset();
|
return reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (canGenerateChunks()) {
|
switch (nextState) {
|
||||||
// Emulate chunked encoding if the content is too large or
|
case READ_FIXED_LENGTH_CONTENT:
|
||||||
// the content length is indefinite.
|
if (contentLength > maxChunkSize) {
|
||||||
if (contentLength > maxChunkSize && nextState == State.READ_FIXED_LENGTH_CONTENT) {
|
|
||||||
// Generate HttpMessage first. HttpChunks will follow.
|
// Generate HttpMessage first. HttpChunks will follow.
|
||||||
checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS);
|
checkpoint(State.READ_FIXED_LENGTH_CONTENT_AS_CHUNKS);
|
||||||
message.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
|
message.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
|
||||||
@ -159,12 +146,16 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
// state reads data chunk by chunk.
|
// state reads data chunk by chunk.
|
||||||
chunkSize = message.getContentLength(-1);
|
chunkSize = message.getContentLength(-1);
|
||||||
return message;
|
return message;
|
||||||
} else if (nextState == State.READ_VARIABLE_LENGTH_CONTENT) {
|
}
|
||||||
|
break;
|
||||||
|
case READ_VARIABLE_LENGTH_CONTENT:
|
||||||
|
if (buffer.readableBytes() > maxChunkSize) {
|
||||||
// Generate HttpMessage first. HttpChunks will follow.
|
// Generate HttpMessage first. HttpChunks will follow.
|
||||||
checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS);
|
checkpoint(State.READ_VARIABLE_LENGTH_CONTENT_AS_CHUNKS);
|
||||||
message.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
|
message.addHeader(HttpHeaders.Names.TRANSFER_ENCODING, HttpHeaders.Values.CHUNKED);
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We return null here, this forces decode to be called again where we will decode the content
|
// We return null here, this forces decode to be called again where we will decode the content
|
||||||
@ -231,31 +222,17 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder<HttpMessageDec
|
|||||||
if (chunkSize == 0) {
|
if (chunkSize == 0) {
|
||||||
checkpoint(State.READ_CHUNK_FOOTER);
|
checkpoint(State.READ_CHUNK_FOOTER);
|
||||||
return null;
|
return null;
|
||||||
} else if (canGenerateChunks()) {
|
} else if (chunkSize > maxChunkSize) {
|
||||||
if (chunkSize <= maxChunkSize) {
|
// A chunk is too large. Split them into multiple chunks again.
|
||||||
checkpoint(State.READ_CHUNKED_CONTENT);
|
checkpoint(State.READ_CHUNKED_CONTENT_AS_CHUNKS);
|
||||||
} else {
|
|
||||||
// A chunk is too large. Split them into multiple chunks again.
|
|
||||||
checkpoint(State.READ_CHUNKED_CONTENT_AS_CHUNKS);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
checkpoint(State.READ_CHUNKED_CONTENT);
|
checkpoint(State.READ_CHUNKED_CONTENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case READ_CHUNKED_CONTENT: {
|
case READ_CHUNKED_CONTENT: {
|
||||||
if (canGenerateChunks()) {
|
HttpChunk chunk = new DefaultHttpChunk(buffer.readBytes(chunkSize));
|
||||||
HttpChunk chunk = new DefaultHttpChunk(buffer.readBytes(chunkSize));
|
checkpoint(State.READ_CHUNK_DELIMITER);
|
||||||
checkpoint(State.READ_CHUNK_DELIMITER);
|
return chunk;
|
||||||
return chunk;
|
|
||||||
} else {
|
|
||||||
if (content == null) {
|
|
||||||
content = ChannelBuffers.dynamicBuffer(
|
|
||||||
chunkSize, channel.getConfig().getBufferFactory());
|
|
||||||
}
|
|
||||||
content.writeBytes(buffer, chunkSize);
|
|
||||||
checkpoint(State.READ_CHUNK_DELIMITER);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case READ_CHUNKED_CONTENT_AS_CHUNKS: {
|
case READ_CHUNKED_CONTENT_AS_CHUNKS: {
|
||||||
int chunkSize = this.chunkSize;
|
int chunkSize = this.chunkSize;
|
||||||
|
@ -32,10 +32,6 @@ package org.jboss.netty.handler.codec.http;
|
|||||||
*/
|
*/
|
||||||
public class HttpRequestDecoder extends HttpMessageDecoder {
|
public class HttpRequestDecoder extends HttpMessageDecoder {
|
||||||
|
|
||||||
public HttpRequestDecoder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpRequestDecoder(
|
public HttpRequestDecoder(
|
||||||
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||||
super(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
super(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
||||||
|
@ -32,10 +32,6 @@ package org.jboss.netty.handler.codec.http;
|
|||||||
*/
|
*/
|
||||||
public class HttpResponseDecoder extends HttpMessageDecoder {
|
public class HttpResponseDecoder extends HttpMessageDecoder {
|
||||||
|
|
||||||
public HttpResponseDecoder() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HttpResponseDecoder(
|
public HttpResponseDecoder(
|
||||||
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
|
||||||
super(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
super(maxInitialLineLength, maxHeaderSize, maxChunkSize);
|
||||||
|
Loading…
Reference in New Issue
Block a user