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.netty5.handler.codec.http;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.buffer.api.BufferAllocator;
20  import io.netty5.handler.stream.ChunkedInput;
21  import io.netty5.util.Resource;
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   *     HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput(
38   *         new ChunkedFile(&quot;/tmp/myfile.txt&quot;));
39   *     Future&lt;Void&gt; sendFileFuture = ctx.write(httpChunkWriter);
40   * }
41   * </pre>
42   */
43  public class HttpChunkedInput implements ChunkedInput<HttpContent<?>> {
44      private final ChunkedInput<Buffer> input;
45      private final LastHttpContent<?> lastHttpContent;
46      private boolean sentLastChunk;
47  
48      /**
49       * Creates a new instance using the specified input. {@code lastHttpContent} will be written as the terminating
50       * chunk.
51       * @param input {@link ChunkedInput} containing data to write
52       * @param lastHttpContent {@link LastHttpContent} that will be written as the terminating chunk. Use this for
53       * training headers.
54       */
55      public HttpChunkedInput(ChunkedInput<Buffer> input, LastHttpContent<?> lastHttpContent) {
56          this.input = input;
57          this.lastHttpContent = lastHttpContent;
58      }
59  
60      @Override
61      public boolean isEndOfInput() throws Exception {
62          if (input.isEndOfInput()) {
63              // Only end of input after last HTTP chunk has been sent
64              return sentLastChunk;
65          } else {
66              return false;
67          }
68      }
69  
70      @Override
71      public void close() throws Exception {
72          // Using try-with-resources to correctly handle exceptions from close().
73          try (input) {
74              if (!sentLastChunk && Resource.isAccessible(lastHttpContent, false)) {
75                  Resource.dispose(lastHttpContent);
76              }
77          }
78      }
79  
80      @Override
81      public HttpContent<?> readChunk(BufferAllocator allocator) throws Exception {
82          if (input.isEndOfInput()) {
83              if (sentLastChunk) {
84                  return null;
85              } else {
86                  // Send last chunk for this input
87                  sentLastChunk = true;
88                  return lastHttpContent;
89              }
90          } else {
91              Buffer buf = input.readChunk(allocator);
92              if (buf == null) {
93                  return null;
94              }
95              return new DefaultHttpContent(buf);
96          }
97      }
98  
99      @Override
100     public long length() {
101         return input.length();
102     }
103 
104     @Override
105     public long progress() {
106         return input.progress();
107     }
108 }