View Javadoc
1   /*
2    * Copyright 2016 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.http2;
17  
18  import io.netty5.buffer.api.Buffer;
19  import io.netty5.buffer.api.BufferClosedException;
20  import io.netty5.buffer.api.DefaultBufferAllocators;
21  import io.netty5.util.Send;
22  import io.netty5.util.internal.StringUtil;
23  import io.netty5.util.internal.UnstableApi;
24  
25  import static io.netty5.handler.codec.http2.Http2CodecUtil.verifyPadding;
26  import static java.util.Objects.requireNonNull;
27  
28  /**
29   * The default {@link Http2DataFrame} implementation.
30   */
31  @UnstableApi
32  public final class DefaultHttp2DataFrame extends AbstractHttp2StreamFrame implements Http2DataFrame {
33      private final Buffer content;
34      private final boolean endStream;
35      private final int padding;
36      private final int initialFlowControlledBytes;
37  
38      /**
39       * Equivalent to {@code new DefaultHttp2DataFrame(content, false)}.
40       *
41       * @param content non-{@code null} payload
42       */
43      public DefaultHttp2DataFrame(Send<Buffer> content) {
44          this(content, false);
45      }
46  
47      /**
48       * Equivalent to {@code new DefaultHttp2DataFrame(Unpooled.EMPTY_BUFFER, endStream)}.
49       *
50       * @param endStream whether this data should terminate the stream
51       */
52      public DefaultHttp2DataFrame(boolean endStream) {
53          this(DefaultBufferAllocators.onHeapAllocator().allocate(0), endStream, 0);
54      }
55  
56      /**
57       * Equivalent to {@code new DefaultHttp2DataFrame(content, endStream, 0)}.
58       *
59       * @param content non-{@code null} payload
60       * @param endStream whether this data should terminate the stream
61       */
62      public DefaultHttp2DataFrame(Send<Buffer> content, boolean endStream) {
63          this(content, endStream, 0);
64      }
65  
66      /**
67       * Construct a new data message.
68       *
69       * @param content non-{@code null} payload
70       * @param endStream whether this data should terminate the stream
71       * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
72       *                256 (inclusive).
73       */
74      public DefaultHttp2DataFrame(Send<Buffer> content, boolean endStream, int padding) {
75          this(content.receive(), endStream, padding);
76      }
77  
78      private DefaultHttp2DataFrame(Buffer content, boolean endStream, int padding) {
79          this.content = requireNonNull(content, "content");
80          this.endStream = endStream;
81          verifyPadding(padding);
82          this.padding = padding;
83          if (content().readableBytes() + (long) padding > Integer.MAX_VALUE) {
84              throw new IllegalArgumentException("content + padding must be <= Integer.MAX_VALUE");
85          }
86          initialFlowControlledBytes = content().readableBytes() + padding;
87      }
88  
89      @Override
90      public DefaultHttp2DataFrame stream(Http2FrameStream stream) {
91          super.stream(stream);
92          return this;
93      }
94  
95      @Override
96      public String name() {
97          return "DATA";
98      }
99  
100     @Override
101     public boolean isEndStream() {
102         return endStream;
103     }
104 
105     @Override
106     public int padding() {
107         return padding;
108     }
109 
110     @Override
111     public Buffer content() {
112         if (content.isAccessible()) {
113             return content;
114         }
115         throw new BufferClosedException();
116     }
117 
118     @Override
119     public int initialFlowControlledBytes() {
120         return initialFlowControlledBytes;
121     }
122 
123     @Override
124     public DefaultHttp2DataFrame copy() {
125         return new DefaultHttp2DataFrame(content.copy(), endStream, padding);
126     }
127 
128     @Override
129     public String toString() {
130         return StringUtil.simpleClassName(this) + "(stream=" + stream() + ", content=" + content
131                + ", endStream=" + endStream + ", padding=" + padding + ')';
132     }
133 
134     @Override
135     public Send<Http2DataFrame> send() {
136         return content.send().map(Http2DataFrame.class,
137                                   content -> new DefaultHttp2DataFrame(content, endStream, padding));
138     }
139 
140     @Override
141     public void close() {
142         content.close();
143     }
144 
145     @Override
146     public boolean isAccessible() {
147         return content.isAccessible();
148     }
149 
150     @Override
151     public DefaultHttp2DataFrame touch(Object hint) {
152         content.touch(hint);
153         return this;
154     }
155 
156     @Override
157     public boolean equals(Object o) {
158         if (!(o instanceof DefaultHttp2DataFrame)) {
159             return false;
160         }
161         DefaultHttp2DataFrame other = (DefaultHttp2DataFrame) o;
162         return super.equals(other) && content.equals(other.content())
163             && endStream == other.endStream && padding == other.padding;
164     }
165 
166     @Override
167     public int hashCode() {
168         int hash = super.hashCode();
169         hash = hash * 31 + content.hashCode();
170         hash = hash * 31 + (endStream ? 0 : 1);
171         hash = hash * 31 + padding;
172         return hash;
173     }
174 }