/*
* Copyright 2012 The Netty Project
*
* 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:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import org.junit.Test;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
public class HttpServerCodecTest {
/**
* Testcase for https://github.com/netty/netty/issues/433
*/
@Test
public void testUnfinishedChunkedHttpRequestIsLastFlag() throws Exception {
int maxChunkSize = 2000;
HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000, maxChunkSize);
EmbeddedChannel decoderEmbedder = new EmbeddedChannel(httpServerCodec);
int totalContentLength = maxChunkSize * 5;
decoderEmbedder.writeInbound(Unpooled.copiedBuffer(
"PUT /test HTTP/1.1\r\n" +
"Content-Length: " + totalContentLength + "\r\n" +
"\r\n", CharsetUtil.UTF_8));
int offeredContentLength = (int) (maxChunkSize * 2.5);
decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength));
decoderEmbedder.finish();
HttpMessage httpMessage = (HttpMessage) decoderEmbedder.readInbound();
assertNotNull(httpMessage);
boolean empty = true;
int totalBytesPolled = 0;
for (;;) {
HttpContent httpChunk = (HttpContent) decoderEmbedder.readInbound();
if (httpChunk == null) {
break;
}
empty = false;
totalBytesPolled += httpChunk.content().readableBytes();
assertFalse(httpChunk instanceof LastHttpContent);
httpChunk.release();
}
assertFalse(empty);
assertEquals(offeredContentLength, totalBytesPolled);
}
@Test
public void test100Continue() throws Exception {
EmbeddedChannel ch = new EmbeddedChannel(new HttpServerCodec(), new HttpObjectAggregator(1024));
// Send the request headers.
ch.writeInbound(Unpooled.copiedBuffer(
"PUT /upload-large HTTP/1.1\r\n" +
"Expect: 100-continue\r\n" +
"Content-Length: 1\r\n\r\n", CharsetUtil.UTF_8));
// Ensure the aggregator generates nothing.
assertThat(ch.readInbound(), is(nullValue()));
// Ensure the aggregator writes a 100 Continue response.
ByteBuf continueResponse = (ByteBuf) ch.readOutbound();
assertThat(continueResponse.toString(CharsetUtil.UTF_8), is("HTTP/1.1 100 Continue\r\n\r\n"));
continueResponse.release();
// But nothing more.
assertThat(ch.readOutbound(), is(nullValue()));
// Send the content of the request.
ch.writeInbound(Unpooled.wrappedBuffer(new byte[] { 42 }));
// Ensure the aggregator generates a full request.
FullHttpRequest req = (FullHttpRequest) ch.readInbound();
assertThat(req.headers().get(CONTENT_LENGTH), is("1"));
assertThat(req.content().readableBytes(), is(1));
assertThat(req.content().readByte(), is((byte) 42));
req.release();
// But nothing more.
assertThat(ch.readInbound(), is(nullValue()));
// Send the actual response.
FullHttpResponse res = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CREATED);
res.content().writeBytes("OK".getBytes(CharsetUtil.UTF_8));
res.headers().set(CONTENT_LENGTH, 2);
ch.writeOutbound(res);
// Ensure the encoder handles the response after handling 100 Continue.
ByteBuf encodedRes = (ByteBuf) ch.readOutbound();
assertThat(encodedRes.toString(CharsetUtil.UTF_8), is("HTTP/1.1 201 Created\r\nContent-Length: 2\r\n\r\nOK"));
encodedRes.release();
ch.finish();
}
private static ByteBuf prepareDataChunk(int size) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < size; ++i) {
sb.append('a');
}
return Unpooled.copiedBuffer(sb.toString(), CharsetUtil.UTF_8);
}
}