package org.jboss.resteasy.plugins.server.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultHttpContent;
import java.io.IOException;
import java.io.OutputStream;
import org.jboss.resteasy.plugins.server.netty.i18n.Messages;
/**
* Class to help application that are built to write to an
* OutputStream to chunk the content
*
* <pre>
* {@code
DefaultHttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
HttpHeaders.setTransferEncodingChunked(response);
response.headers().set(CONTENT_TYPE, "application/octet-stream");
//other headers
ctx.write(response);
// code of the application that use the ChunkOutputStream
// Don't forget to close the ChunkOutputStream after use!
ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT).addListener(ChannelFutureListener.CLOSE);
}
</pre>
* @author tbussier
*
*/
public class ChunkOutputStream extends OutputStream {
final ByteBuf buffer;
final ChannelHandlerContext ctx;
final NettyHttpResponse response;
ChunkOutputStream(NettyHttpResponse response, ChannelHandlerContext ctx, int chunksize) {
this.response = response;
if (chunksize < 1) {
throw new IllegalArgumentException(Messages.MESSAGES.chunkSizeMustBeAtLeastOne());
}
this.buffer = Unpooled.buffer(0, chunksize);
this.ctx = ctx;
}
@Override
public void write(int b) throws IOException {
if (buffer.maxWritableBytes() < 1) {
flush();
}
buffer.writeByte(b);
}
public void reset()
{
if (response.isCommitted()) throw new IllegalStateException(Messages.MESSAGES.responseIsCommitted());
buffer.clear();
}
@Override
public void close() throws IOException {
flush();
super.close();
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
int dataLengthLeftToWrite = len;
int dataToWriteOffset = off;
int spaceLeftInCurrentChunk;
while ((spaceLeftInCurrentChunk = buffer.maxWritableBytes()) < dataLengthLeftToWrite) {
buffer.writeBytes(b, dataToWriteOffset, spaceLeftInCurrentChunk);
dataToWriteOffset = dataToWriteOffset + spaceLeftInCurrentChunk;
dataLengthLeftToWrite = dataLengthLeftToWrite - spaceLeftInCurrentChunk;
flush();
}
if (dataLengthLeftToWrite > 0) {
buffer.writeBytes(b, dataToWriteOffset, dataLengthLeftToWrite);
}
}
@Override
public void flush() throws IOException {
int readable = buffer.readableBytes();
if (readable == 0) return;
if (!response.isCommitted()) response.prepareChunkStream();
ctx.writeAndFlush(new DefaultHttpContent(buffer.copy()));
buffer.clear();
super.flush();
}
}