1 /* 2 * Copyright 2014 The Netty Project 3 * 4 * The Netty Project licenses this file to you under the Apache License, 5 * version 2.0 (the "License"); you may not use this file except in compliance 6 * with the License. You may obtain a copy of the License at: 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 package io.netty.handler.codec.http; 17 18 import io.netty.buffer.ByteBuf; 19 import io.netty.channel.ChannelHandlerContext; 20 import io.netty.handler.stream.ChunkedInput; 21 22 /** 23 * A {@link ChunkedInput} that fetches data chunk by chunk for use with HTTP chunked transfers. 24 * <p> 25 * Each chunk from the input data will be wrapped within a {@link HttpContent}. At the end of the input data, 26 * {@link LastHttpContent} will be written. 27 * <p> 28 * Ensure that your HTTP response header contains {@code Transfer-Encoding: chunked}. 29 * <p> 30 * <pre> 31 * public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { 32 * HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); 33 * response.headers().set(TRANSFER_ENCODING, CHUNKED); 34 * ctx.write(response); 35 * 36 * HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput( 37 * new ChunkedFile("/tmp/myfile.txt")); 38 * ChannelFuture sendFileFuture = ctx.write(httpChunkWriter); 39 * } 40 * </pre> 41 */ 42 public class HttpChunkedInput implements ChunkedInput<HttpContent> { 43 44 private final ChunkedInput<ByteBuf> input; 45 private final LastHttpContent lastHttpContent; 46 private boolean sentLastChunk; 47 48 /** 49 * Creates a new instance using the specified input. 50 * @param input {@link ChunkedInput} containing data to write 51 */ 52 public HttpChunkedInput(ChunkedInput<ByteBuf> input) { 53 this.input = input; 54 lastHttpContent = LastHttpContent.EMPTY_LAST_CONTENT; 55 } 56 57 /** 58 * Creates a new instance using the specified input. {@code lastHttpContent} will be written as the terminating 59 * chunk. 60 * @param input {@link ChunkedInput} containing data to write 61 * @param lastHttpContent {@link LastHttpContent} that will be written as the terminating chunk. Use this for 62 * training headers. 63 */ 64 public HttpChunkedInput(ChunkedInput<ByteBuf> input, LastHttpContent lastHttpContent) { 65 this.input = input; 66 this.lastHttpContent = lastHttpContent; 67 } 68 69 @Override 70 public boolean isEndOfInput() throws Exception { 71 if (input.isEndOfInput()) { 72 // Only end of input after last HTTP chunk has been sent 73 return sentLastChunk; 74 } else { 75 return false; 76 } 77 } 78 79 @Override 80 public void close() throws Exception { 81 input.close(); 82 } 83 84 @Override 85 public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { 86 if (input.isEndOfInput()) { 87 if (sentLastChunk) { 88 return null; 89 } else { 90 // Send last chunk for this input 91 sentLastChunk = true; 92 return lastHttpContent; 93 } 94 } else { 95 ByteBuf buf = input.readChunk(ctx); 96 if (buf == null) { 97 return null; 98 } 99 return new DefaultHttpContent(buf); 100 } 101 } 102 }