package org.jooby.internal.netty;
import static io.netty.channel.ChannelFutureListener.CLOSE;
import static org.easymock.EasyMock.aryEq;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.DefaultFileRegion;
import io.netty.channel.EventLoop;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedInput;
import io.netty.handler.stream.ChunkedNioFile;
import io.netty.handler.stream.ChunkedStream;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.Attribute;
@RunWith(PowerMockRunner.class)
@PrepareForTest({NettyResponse.class, DefaultFullHttpResponse.class, Unpooled.class,
DefaultHttpHeaders.class, DefaultHttpResponse.class, ChunkedStream.class,
DefaultFileRegion.class })
public class NettyResponseTest {
private Block channel = unit -> {
Channel channel = unit.mock(Channel.class);
unit.registerMock(Channel.class, channel);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.channel()).andReturn(channel).times(1, 2);
};
byte[] bytes = "bytes".getBytes();
ByteBuffer buffer = ByteBuffer.wrap(bytes);
int bufferSize = 8192;
private Block wrapBytes = unit -> {
ByteBuf buffer = unit.get(ByteBuf.class);
unit.mockStatic(Unpooled.class);
expect(Unpooled.wrappedBuffer(bytes)).andReturn(buffer);
};
private Block wrapBuffer = unit -> {
ByteBuf buf = unit.get(ByteBuf.class);
unit.mockStatic(Unpooled.class);
expect(Unpooled.wrappedBuffer(buffer)).andReturn(buf);
};
private Block headers = unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
unit.registerMock(DefaultHttpHeaders.class, headers);
};
private Block deflen = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
ByteBuf buffer = unit.get(ByteBuf.class);
expect(buffer.readableBytes()).andReturn(bytes.length);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(false);
expect(headers.remove(HttpHeaderNames.TRANSFER_ENCODING)).andReturn(headers);
expect(headers.set(HttpHeaderNames.CONTENT_LENGTH, bytes.length)).andReturn(headers);
};
private Block connkeep = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE)).andReturn(headers);
};
private Block len = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(true);
};
private Block fullResponse = unit -> {
DefaultFullHttpResponse rsp = unit.mockConstructor(
DefaultFullHttpResponse.class,
new Class[]{HttpVersion.class, HttpResponseStatus.class, ByteBuf.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK, unit.get(ByteBuf.class));
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
};
private Block keeAliveWithLen = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(true);
};
private Block noKeepAlive = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(true);
ChannelFuture future = unit.get(ChannelFuture.class);
expect(future.addListener(CLOSE)).andReturn(future);
};
private Block noKeepAliveNoLen = unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(false);
ChannelFuture future = unit.get(ChannelFuture.class);
expect(future.addListener(CLOSE)).andReturn(future);
};
@SuppressWarnings("unchecked")
private Block async = unit -> {
Channel channel = unit.get(Channel.class);
Attribute<Boolean> async = unit.mock(Attribute.class);
expect(async.get()).andReturn(false);
expect(channel.attr(NettyRequest.ASYNC)).andReturn(async);
};
@SuppressWarnings("unchecked")
private Block setNeedFlush = unit -> {
Attribute<Boolean> needFlush = unit.mock(Attribute.class);
needFlush.set(false);
Channel channel = unit.get(Channel.class);
expect(channel.attr(NettyRequest.NEED_FLUSH)).andReturn(needFlush);
};
@Test
public void defaults() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive);
});
}
@Test
public void headers() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
List<String> v = Arrays.asList("h1");
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.getAll("h")).andReturn(v);
})
.run(unit -> {
assertEquals(v, new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize,
keepAlive)
.headers("h"));
});
}
@Test
public void noHeaders() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
List<String> v = Collections.emptyList();
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.getAll("h")).andReturn(null);
})
.run(unit -> {
assertEquals(v, new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize,
keepAlive)
.headers("h"));
});
}
@Test
public void header() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.get("h")).andReturn("v");
})
.run(unit -> {
assertEquals(Optional.of("v"),
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.header("h"));
});
}
@Test
public void noHeader() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.get("h")).andReturn(null);
})
.run(unit -> {
assertEquals(Optional.empty(),
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.header("h"));
});
}
@Test
public void setHeader() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.set("h", "v")).andReturn(headers);
})
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.header("h", "v");
});
}
@Test
public void setHeaders() throws Exception {
int bufferSize = 8192;
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class)
.expect(unit -> {
DefaultHttpHeaders headers = unit.mockConstructor(DefaultHttpHeaders.class);
expect(headers.remove("h")).andReturn(headers);
expect(headers.add("h", Arrays.asList("v1", "v2"))).andReturn(headers);
})
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.header("h", Arrays.asList("v1", "v2"));
});
}
@Test
public void sendBytesSetDefLenAndKeepAlive() throws Exception {
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(wrapBytes)
.expect(headers)
.expect(deflen)
.expect(connkeep)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(keeAliveWithLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(bytes);
});
}
@Test
public void sendBufferSetDefLenAndKeepAlive() throws Exception {
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(wrapBuffer)
.expect(headers)
.expect(deflen)
.expect(connkeep)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(keeAliveWithLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(buffer);
});
}
@Test
public void sendBytesKeepAlive() throws Exception {
boolean keepAlive = true;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(wrapBytes)
.expect(headers)
.expect(len)
.expect(connkeep)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(keeAliveWithLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(bytes);
});
}
@Test
public void sendBytesNoKeepAlive() throws Exception {
boolean keepAlive = false;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(wrapBytes)
.expect(headers)
.expect(len)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(noKeepAlive)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(bytes);
});
}
@Test
public void sendBytesKeepAliveOffNoLen() throws Exception {
boolean keepAlive = false;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(wrapBytes)
.expect(headers)
.expect(len)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(bytes);
});
}
@Test
public void sendEmptyStream() throws Exception {
boolean keepAlive = false;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class, InputStream.class)
.expect(unit -> {
InputStream stream = unit.get(InputStream.class);
expect(stream.read(unit.capture(byte[].class), eq(0), eq(bufferSize))).andReturn(-1);
})
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(unit.get(InputStream.class));
}, unit -> {
assertEquals(bufferSize, unit.captured(byte[].class).iterator().next().length);
});
}
@Test
public void sendHeadChunk() throws Exception {
boolean keepAlive = false;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class, InputStream.class)
.expect(channel)
.expect(
unit -> {
InputStream stream = unit.get(InputStream.class);
expect(stream.read(unit.capture(byte[].class), eq(0), eq(bytes.length))).andReturn(
bytes.length / 2);
expect(stream.read(unit.capture(byte[].class), eq(2), eq(3))).andReturn(-1);
})
.expect(unit -> {
ByteBuf buffer = unit.get(ByteBuf.class);
unit.mockStatic(Unpooled.class);
expect(Unpooled.wrappedBuffer(aryEq(new byte[bytes.length]), eq(0),
eq(bytes.length / 2))).andReturn(buffer);
})
.expect(headers)
.expect(len)
.expect(fullResponse)
.expect(async)
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bytes.length, keepAlive)
.send(unit.get(InputStream.class));
});
}
@Test
public void sendChunks() throws Exception {
boolean keepAlive = true;
int bufferSize = 10;
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class, InputStream.class)
.expect(channel)
.expect(unit -> {
InputStream stream = unit.get(InputStream.class);
expect(stream.read(unit.capture(byte[].class), eq(0), eq(bufferSize))).andReturn(
bufferSize);
})
.expect(unit -> {
ByteBuf buffer = unit.get(ByteBuf.class);
unit.mockStatic(Unpooled.class);
expect(Unpooled.wrappedBuffer(aryEq(new byte[bufferSize]), eq(0), eq(bufferSize)))
.andReturn(buffer);
})
.expect(headers)
.expect(unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(false);
expect(headers.set(HttpHeaderNames.TRANSFER_ENCODING, HttpHeaderValues.CHUNKED))
.andReturn(headers);
})
.expect(setNeedFlush)
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultHttpResponse.class,
new Class[]{HttpVersion.class,
HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
EventLoop loop = unit.mock(EventLoop.class);
loop.execute(unit.capture(Runnable.class));
Channel channel = unit.get(Channel.class);
expect(channel.eventLoop()).andReturn(loop);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(ByteBuf.class))).andReturn(future);
})
.expect(unit -> {
ChunkedStream chunkedStream = unit.mockConstructor(ChunkedStream.class, new Class[]{
InputStream.class, int.class }, unit.get(InputStream.class), bufferSize);
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(chunkedStream)).andReturn(future);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)).andReturn(future);
})
.expect(unit -> {
ChannelPipeline pipeline = unit.mock(ChannelPipeline.class);
expect(pipeline.get("chunker")).andReturn(null);
expect(pipeline.addAfter(eq("codec"), eq("chunker"), isA(ChunkedWriteHandler.class)))
.andReturn(pipeline);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.pipeline()).andReturn(pipeline);
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(unit.get(InputStream.class));
}, unit -> {
unit.captured(Runnable.class).iterator().next().run();
});
}
@Test
public void sendFileChannel() throws Exception {
boolean keepAlive = false;
FileChannel fchannel = newFileChannel(8192);
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(headers)
.expect(unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(false);
expect(headers.remove(HttpHeaderNames.TRANSFER_ENCODING)).andReturn(headers);
expect(headers.set(HttpHeaderNames.CONTENT_LENGTH, 8192L)).andReturn(headers);
})
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultHttpResponse.class,
new Class[]{HttpVersion.class,
HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(
unit -> {
DefaultFileRegion region = unit.mockConstructor(DefaultFileRegion.class,
new Class[]{FileChannel.class, long.class, long.class }, fchannel, 0L,
fchannel.size());
unit.registerMock(DefaultFileRegion.class, region);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(DefaultFileRegion.class))).andReturn(future);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)).andReturn(future);
})
.expect(setNeedFlush)
.expect(unit -> {
EventLoop loop = unit.mock(EventLoop.class);
loop.execute(unit.capture(Runnable.class));
Channel chn = unit.get(Channel.class);
expect(chn.eventLoop()).andReturn(loop);
})
.expect(unit -> {
ChannelPipeline pipeline = unit.mock(ChannelPipeline.class);
expect(pipeline.get(SslHandler.class)).andReturn(null);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.pipeline()).andReturn(pipeline);
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(fchannel);
}, unit -> {
unit.captured(Runnable.class).iterator().next().run();
});
}
@Test
public void sendFileChannelSSL() throws Exception {
boolean keepAlive = false;
FileChannel fchannel = newFileChannel(8192);
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(headers)
.expect(unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(false);
expect(headers.remove(HttpHeaderNames.TRANSFER_ENCODING)).andReturn(headers);
expect(headers.set(HttpHeaderNames.CONTENT_LENGTH, 8192L)).andReturn(headers);
})
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultHttpResponse.class,
new Class[]{HttpVersion.class,
HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(
unit -> {
ChunkedNioFile nioFile = unit.mockConstructor(ChunkedNioFile.class,
new Class[]{FileChannel.class, long.class, long.class, int.class },
fchannel, 0L, fchannel.size(), 8192);
HttpChunkedInput chunked = unit.mockConstructor(HttpChunkedInput.class,
new Class[]{ChunkedInput.class }, nioFile);
unit.registerMock(HttpChunkedInput.class, chunked);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(unit.get(HttpChunkedInput.class))).andReturn(future);
})
.expect(setNeedFlush)
.expect(unit -> {
EventLoop loop = unit.mock(EventLoop.class);
loop.execute(unit.capture(Runnable.class));
Channel chn = unit.get(Channel.class);
expect(chn.eventLoop()).andReturn(loop);
})
.expect(unit -> {
ChannelPipeline pipeline = unit.mock(ChannelPipeline.class);
expect(pipeline.get("chunker")).andReturn(null);
expect(pipeline.get(SslHandler.class)).andReturn(unit.mock(SslHandler.class));
expect(pipeline.addAfter(eq("codec"), eq("chunker"), isA(ChunkedWriteHandler.class)))
.andReturn(pipeline);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.pipeline()).andReturn(pipeline);
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(fchannel);
}, unit -> {
unit.captured(Runnable.class).iterator().next().run();
});
}
@Test
public void sendFileChannelNoLen() throws Exception {
boolean keepAlive = false;
FileChannel fchannel = newFileChannel(8192);
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(headers)
.expect(unit -> {
DefaultHttpHeaders headers = unit.get(DefaultHttpHeaders.class);
expect(headers.contains(HttpHeaderNames.CONTENT_LENGTH)).andReturn(true);
})
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultHttpResponse.class,
new Class[]{HttpVersion.class,
HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(future);
})
.expect(
unit -> {
DefaultFileRegion region = unit.mockConstructor(DefaultFileRegion.class,
new Class[]{FileChannel.class, long.class, long.class }, fchannel, 0L,
fchannel.size());
unit.registerMock(DefaultFileRegion.class, region);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(DefaultFileRegion.class))).andReturn(future);
})
.expect(unit -> {
ChannelFuture future = unit.get(ChannelFuture.class);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT)).andReturn(future);
})
.expect(unit -> {
ChannelPipeline pipeline = unit.mock(ChannelPipeline.class);
expect(pipeline.get(SslHandler.class)).andReturn(null);
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.pipeline()).andReturn(pipeline);
})
.expect(setNeedFlush)
.expect(noKeepAliveNoLen)
.expect(unit -> {
EventLoop loop = unit.mock(EventLoop.class);
loop.execute(unit.capture(Runnable.class));
Channel chn = unit.get(Channel.class);
expect(chn.eventLoop()).andReturn(loop);
})
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class), bufferSize, keepAlive)
.send(fchannel);
}, unit -> {
unit.captured(Runnable.class).iterator().next().run();
});
}
@Test
public void status() throws Exception {
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(headers)
.run(unit -> {
NettyResponse rsp = new NettyResponse(unit.get(ChannelHandlerContext.class),
bufferSize, true);
assertEquals(200, rsp.statusCode());
rsp.statusCode(201);
assertEquals(201, rsp.statusCode());
});
}
@Test
public void end() throws Exception {
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(channel)
.expect(headers)
.expect(unit -> {
Channel ctx = unit.get(Channel.class);
expect(ctx.attr(NettyWebSocket.KEY)).andReturn(null);
})
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultFullHttpResponse.class,
new Class[]{HttpVersion.class, HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(
unit.get(ChannelFuture.class));
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class),
bufferSize, true)
.end();
});
}
@SuppressWarnings("unchecked")
@Test
public void end2() throws Exception {
new MockUnit(ChannelHandlerContext.class, ByteBuf.class, ChannelFuture.class)
.expect(headers)
.expect(channel)
.expect(unit -> {
Attribute<NettyWebSocket> attr = unit.mock(Attribute.class);
expect(attr.get()).andReturn(null);
Channel ctx = unit.get(Channel.class);
expect(ctx.attr(NettyWebSocket.KEY)).andReturn(attr);
})
.expect(unit -> {
DefaultHttpResponse rsp = unit.mockConstructor(DefaultFullHttpResponse.class,
new Class[]{HttpVersion.class, HttpResponseStatus.class },
HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpHeaders headers = unit.mock(HttpHeaders.class);
expect(headers.set(unit.get(DefaultHttpHeaders.class))).andReturn(headers);
expect(rsp.headers()).andReturn(headers);
unit.registerMock(HttpResponse.class, rsp);
})
.expect(unit -> {
ChannelHandlerContext ctx = unit.get(ChannelHandlerContext.class);
expect(ctx.write(unit.get(HttpResponse.class))).andReturn(
unit.get(ChannelFuture.class));
})
.expect(noKeepAliveNoLen)
.run(unit -> {
new NettyResponse(unit.get(ChannelHandlerContext.class),
bufferSize, true)
.end();
});
}
private FileChannel newFileChannel(final int size) {
return new FileChannel() {
@Override
public int read(final ByteBuffer dst) throws IOException {
return 0;
}
@Override
public long read(final ByteBuffer[] dsts, final int offset, final int length)
throws IOException {
return 0;
}
@Override
public int write(final ByteBuffer src) throws IOException {
return 0;
}
@Override
public long write(final ByteBuffer[] srcs, final int offset, final int length)
throws IOException {
return 0;
}
@Override
public long position() throws IOException {
return 0;
}
@Override
public FileChannel position(final long newPosition) throws IOException {
return null;
}
@Override
public long size() throws IOException {
return size;
}
@Override
public FileChannel truncate(final long size) throws IOException {
return null;
}
@Override
public void force(final boolean metaData) throws IOException {
}
@Override
public long transferTo(final long position, final long count,
final WritableByteChannel target)
throws IOException {
return 0;
}
@Override
public long transferFrom(final ReadableByteChannel src, final long position, final long count)
throws IOException {
return 0;
}
@Override
public int read(final ByteBuffer dst, final long position) throws IOException {
return 0;
}
@Override
public int write(final ByteBuffer src, final long position) throws IOException {
return 0;
}
@Override
public MappedByteBuffer map(final MapMode mode, final long position, final long size)
throws IOException {
return null;
}
@Override
public FileLock lock(final long position, final long size, final boolean shared)
throws IOException {
return null;
}
@Override
public FileLock tryLock(final long position, final long size, final boolean shared)
throws IOException {
return null;
}
@Override
protected void implCloseChannel() throws IOException {
}
};
}
}