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 * https://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.buffer.ByteBufAllocator; 20 import io.netty.channel.ChannelHandlerContext; 21 import io.netty.handler.stream.ChunkedInput; 22 23 /** 24 * A {@link ChunkedInput} that fetches data chunk by chunk for use with HTTP chunked transfers. 25 * <p> 26 * Each chunk from the input data will be wrapped within a {@link HttpContent}. At the end of the input data, 27 * {@link LastHttpContent} will be written. 28 * <p> 29 * Ensure that your HTTP response header contains {@code Transfer-Encoding: chunked}. 30 * <p> 31 * <pre> 32 * public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { 33 * HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); 34 * response.headers().set(TRANSFER_ENCODING, CHUNKED); 35 * ctx.write(response); 36 * 37 * HttpChunkedInput httpChunkWriter = new HttpChunkedInput( 38 * new ChunkedFile("/tmp/myfile.txt")); 39 * ChannelFuture sendFileFuture = ctx.write(httpChunkWriter); 40 * } 41 * </pre> 42 */ 43 public class HttpChunkedInput implements ChunkedInput<HttpContent> { 44 45 private final ChunkedInput<ByteBuf> input; 46 private final LastHttpContent lastHttpContent; 47 private boolean sentLastChunk; 48 49 /** 50 * Creates a new instance using the specified input. 51 * @param input {@link ChunkedInput} containing data to write 52 */ 53 public HttpChunkedInput(ChunkedInput<ByteBuf> input) { 54 this.input = input; 55 lastHttpContent = LastHttpContent.EMPTY_LAST_CONTENT; 56 } 57 58 /** 59 * Creates a new instance using the specified input. {@code lastHttpContent} will be written as the terminating 60 * chunk. 61 * @param input {@link ChunkedInput} containing data to write 62 * @param lastHttpContent {@link LastHttpContent} that will be written as the terminating chunk. Use this for 63 * training headers. 64 */ 65 public HttpChunkedInput(ChunkedInput<ByteBuf> input, LastHttpContent lastHttpContent) { 66 this.input = input; 67 this.lastHttpContent = lastHttpContent; 68 } 69 70 @Override 71 public boolean isEndOfInput() throws Exception { 72 if (input.isEndOfInput()) { 73 // Only end of input after last HTTP chunk has been sent 74 return sentLastChunk; 75 } else { 76 return false; 77 } 78 } 79 80 @Override 81 public void close() throws Exception { 82 input.close(); 83 } 84 85 @Deprecated 86 @Override 87 public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception { 88 return readChunk(ctx.alloc()); 89 } 90 91 @Override 92 public HttpContent readChunk(ByteBufAllocator allocator) throws Exception { 93 if (input.isEndOfInput()) { 94 if (sentLastChunk) { 95 return null; 96 } else { 97 // Send last chunk for this input 98 sentLastChunk = true; 99 return lastHttpContent; 100 } 101 } else { 102 ByteBuf buf = input.readChunk(allocator); 103 if (buf == null) { 104 return null; 105 } 106 return new DefaultHttpContent(buf); 107 } 108 } 109 110 @Override 111 public long length() { 112 return input.length(); 113 } 114 115 @Override 116 public long progress() { 117 return input.progress(); 118 } 119 }