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    *   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(&quot;/tmp/myfile.txt&quot;));
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 }