View Javadoc
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(&quot;/tmp/myfile.txt&quot;));
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       *            trailing headers. Ownership is transferred to this {@link HttpChunkedInput}.
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          try {
83              input.close();
84          } finally {
85              if (!sentLastChunk) {
86                  sentLastChunk = true;
87                  lastHttpContent.release();
88              }
89          }
90      }
91  
92      @Deprecated
93      @Override
94      public HttpContent readChunk(ChannelHandlerContext ctx) throws Exception {
95          return readChunk(ctx.alloc());
96      }
97  
98      @Override
99      public HttpContent readChunk(ByteBufAllocator allocator) throws Exception {
100         if (input.isEndOfInput()) {
101             if (sentLastChunk) {
102                 return null;
103             } else {
104                 // Send last chunk for this input
105                 sentLastChunk = true;
106                 return lastHttpContent;
107             }
108         } else {
109             ByteBuf buf = input.readChunk(allocator);
110             if (buf == null) {
111                 return null;
112             }
113             return new DefaultHttpContent(buf);
114         }
115     }
116 
117     @Override
118     public long length() {
119         return input.length();
120     }
121 
122     @Override
123     public long progress() {
124         return input.progress();
125     }
126 }