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 }