View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.http2.impl.nio;
29  
30  import java.nio.ByteBuffer;
31  
32  import org.apache.hc.core5.http.ConnectionClosedException;
33  import org.apache.hc.core5.http2.H2ConnectionException;
34  import org.apache.hc.core5.http2.H2CorruptFrameException;
35  import org.apache.hc.core5.http2.ReadableByteChannelMock;
36  import org.apache.hc.core5.http2.WritableByteChannelMock;
37  import org.apache.hc.core5.http2.frame.FrameConsts;
38  import org.apache.hc.core5.http2.frame.FrameFlag;
39  import org.apache.hc.core5.http2.frame.FrameType;
40  import org.apache.hc.core5.http2.frame.RawFrame;
41  import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics;
42  import org.junit.jupiter.api.Assertions;
43  import org.junit.jupiter.api.Test;
44  
45  public class TestFrameInOutBuffers {
46  
47      @Test
48      public void testReadWriteFrame() throws Exception {
49          final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024);
50          final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024);
51  
52          final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1,
53                  ByteBuffer.wrap(new byte[]{1,2,3,4,5}));
54          outbuffer.write(frame, writableChannel);
55  
56          final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
57          final byte[] bytes = writableChannel.toByteArray();
58          Assertions.assertEquals(FrameConsts.HEAD_LEN + 5, bytes.length);
59  
60          Assertions.assertEquals(1, outbuffer.getMetrics().getFramesTransferred());
61          Assertions.assertEquals(bytes.length, outbuffer.getMetrics().getBytesTransferred());
62  
63          final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(bytes);
64          final RawFrame frame2 = inBuffer.read(readableChannel);
65          Assertions.assertEquals(FrameType.DATA.getValue(), frame2.getType());
66          Assertions.assertEquals(0, frame2.getFlags());
67          Assertions.assertEquals(1L, frame2.getStreamId());
68          final ByteBuffer payload2 = frame2.getPayloadContent();
69          Assertions.assertNotNull(payload2);
70          Assertions.assertEquals(5, payload2.remaining());
71          Assertions.assertEquals(1, payload2.get());
72          Assertions.assertEquals(2, payload2.get());
73          Assertions.assertEquals(3, payload2.get());
74          Assertions.assertEquals(4, payload2.get());
75          Assertions.assertEquals(5, payload2.get());
76          Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024)));
77  
78          Assertions.assertEquals(1, inBuffer.getMetrics().getFramesTransferred());
79          Assertions.assertEquals(bytes.length, inBuffer.getMetrics().getBytesTransferred());
80  
81          final RawFrame frame3 = inBuffer.read(ByteBuffer.wrap(bytes), readableChannel);
82          Assertions.assertEquals(FrameType.DATA.getValue(), frame3.getType());
83          Assertions.assertEquals(0, frame3.getFlags());
84          Assertions.assertEquals(1L, frame3.getStreamId());
85          final ByteBuffer payload3 = frame3.getPayloadContent();
86          Assertions.assertNotNull(payload3);
87          Assertions.assertEquals(5, payload3.remaining());
88          Assertions.assertEquals(1, payload3.get());
89          Assertions.assertEquals(2, payload3.get());
90          Assertions.assertEquals(3, payload3.get());
91          Assertions.assertEquals(4, payload3.get());
92          Assertions.assertEquals(5, payload3.get());
93  
94          Assertions.assertEquals(2, inBuffer.getMetrics().getFramesTransferred());
95          Assertions.assertEquals(bytes.length * 2, inBuffer.getMetrics().getBytesTransferred());
96      }
97  
98      @Test
99      public void testPartialFrameWrite() throws Exception {
100         final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024, FrameConsts.HEAD_LEN + 10);
101         final FrameOutputBuffer outbuffer = new FrameOutputBuffer(16 * 1024);
102         final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), FrameFlag.END_STREAM.getValue(), 5,
103                 ByteBuffer.wrap(new byte[]{'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}));
104 
105         outbuffer.write(frame, writableChannel);
106         Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57},
107                 writableChannel.toByteArray());
108 
109         Assertions.assertFalse(outbuffer.isEmpty());
110         outbuffer.flush(writableChannel);
111         Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57},
112                 writableChannel.toByteArray());
113 
114         writableChannel.flush();
115         outbuffer.flush(writableChannel);
116         Assertions.assertArrayEquals(new byte[] {0,0,16,0,1,0,0,0,5,48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102},
117                 writableChannel.toByteArray());
118     }
119 
120     @Test
121     public void testReadFrameMultiple() throws Exception {
122         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
123         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(
124                 new byte[] {
125                         0,0,10,0,8,0,0,0,8,4,0,1,2,3,4,0,0,0,0,
126                         0,0,10,0,9,0,0,0,8,4,5,6,7,8,9,0,0,0,0
127                 });
128 
129         final RawFrame frame1 = inBuffer.read(readableChannel);
130         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType()));
131         Assertions.assertEquals(8, frame1.getFlags());
132         Assertions.assertEquals(8, frame1.getStreamId());
133         final ByteBuffer payload1 = frame1.getPayloadContent();
134         Assertions.assertNotNull(payload1);
135         Assertions.assertEquals(5, payload1.remaining());
136         Assertions.assertEquals(0, payload1.get());
137         Assertions.assertEquals(1, payload1.get());
138         Assertions.assertEquals(2, payload1.get());
139         Assertions.assertEquals(3, payload1.get());
140         Assertions.assertEquals(4, payload1.get());
141 
142         final RawFrame frame2 = inBuffer.read(readableChannel);
143         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType()));
144         Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame2.getFlags());
145         Assertions.assertEquals(8, frame2.getStreamId());
146         final ByteBuffer payload2 = frame2.getPayloadContent();
147         Assertions.assertNotNull(payload2);
148         Assertions.assertEquals(5, payload2.remaining());
149         Assertions.assertEquals(5, payload2.get());
150         Assertions.assertEquals(6, payload2.get());
151         Assertions.assertEquals(7, payload2.get());
152         Assertions.assertEquals(8, payload2.get());
153         Assertions.assertEquals(9, payload2.get());
154 
155         Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024)));
156     }
157 
158     @Test
159     public void testReadFrameMultipleSmallBuffer() throws Exception {
160         final FrameInputBuffer inBuffer = new FrameInputBuffer(new BasicH2TransportMetrics(), 20, 10);
161         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(
162                 new byte[] {
163                         0,0,10,0,8,0,0,0,8,4,1,1,1,1,1,0,0,0,0,
164                         0,0,5,0,0,0,0,0,8,2,2,2,2,2,
165                         0,0,10,0,9,0,0,0,8,4,3,3,3,3,3,0,0,0,0
166                 });
167 
168         final RawFrame frame1 = inBuffer.read(readableChannel);
169         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame1.getType()));
170         Assertions.assertEquals(8, frame1.getFlags());
171         Assertions.assertEquals(8, frame1.getStreamId());
172         final ByteBuffer payload1 = frame1.getPayloadContent();
173         Assertions.assertNotNull(payload1);
174         Assertions.assertEquals(5, payload1.remaining());
175         Assertions.assertEquals(1, payload1.get());
176         Assertions.assertEquals(1, payload1.get());
177         Assertions.assertEquals(1, payload1.get());
178         Assertions.assertEquals(1, payload1.get());
179         Assertions.assertEquals(1, payload1.get());
180 
181         final RawFrame frame2 = inBuffer.read(readableChannel);
182         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame2.getType()));
183         Assertions.assertEquals(0, frame2.getFlags());
184         Assertions.assertEquals(8, frame2.getStreamId());
185         final ByteBuffer payload2 = frame2.getPayloadContent();
186         Assertions.assertNotNull(payload2);
187         Assertions.assertEquals(5, payload2.remaining());
188         Assertions.assertEquals(2, payload2.get());
189         Assertions.assertEquals(2, payload2.get());
190         Assertions.assertEquals(2, payload2.get());
191         Assertions.assertEquals(2, payload2.get());
192         Assertions.assertEquals(2, payload2.get());
193 
194         final RawFrame frame3 = inBuffer.read(readableChannel);
195         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame3.getType()));
196         Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame3.getFlags());
197         Assertions.assertEquals(8, frame3.getStreamId());
198         final ByteBuffer payload3 = frame3.getPayloadContent();
199         Assertions.assertNotNull(payload3);
200         Assertions.assertEquals(5, payload3.remaining());
201         Assertions.assertEquals(3, payload3.get());
202         Assertions.assertEquals(3, payload3.get());
203         Assertions.assertEquals(3, payload3.get());
204         Assertions.assertEquals(3, payload3.get());
205         Assertions.assertEquals(3, payload3.get());
206 
207         Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024)));
208     }
209 
210     @Test
211     public void testReadFramePartialReads() throws Exception {
212         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
213         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(
214                 new byte[] {0,0},
215                 new byte[] {10,0,9,0},
216                 new byte[] {0,0,8},
217                 new byte[] {4},
218                 new byte[] {1,2,3,4},
219                 new byte[] {5,0},
220                 new byte[] {0,0,0});
221 
222         final RawFrame frame = inBuffer.read(readableChannel);
223         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType()));
224         Assertions.assertEquals(FrameFlag.of(FrameFlag.END_STREAM, FrameFlag.PADDED), frame.getFlags());
225         Assertions.assertEquals(8, frame.getStreamId());
226         final ByteBuffer payload = frame.getPayloadContent();
227         Assertions.assertNotNull(payload);
228         Assertions.assertEquals(5, payload.remaining());
229         Assertions.assertEquals(1, payload.get());
230         Assertions.assertEquals(2, payload.get());
231         Assertions.assertEquals(3, payload.get());
232         Assertions.assertEquals(4, payload.get());
233         Assertions.assertEquals(5, payload.get());
234 
235         Assertions.assertEquals(-1, readableChannel.read(ByteBuffer.allocate(1024)));
236     }
237 
238     @Test
239     public void testReadEmptyFrame() throws Exception {
240         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
241         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(
242                 new byte[] {0,0,0,0,0,0,0,0,0});
243 
244         final RawFrame frame = inBuffer.read(readableChannel);
245         Assertions.assertEquals(FrameType.DATA, FrameType.valueOf(frame.getType()));
246         Assertions.assertEquals(0, frame.getFlags());
247         Assertions.assertEquals(0, frame.getStreamId());
248         final ByteBuffer payload = frame.getPayloadContent();
249         Assertions.assertNull(payload);
250     }
251 
252     @Test
253     public void testReadFrameConnectionClosed() throws Exception {
254         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
255         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(new byte[] {});
256 
257         Assertions.assertNull(inBuffer.read(readableChannel));
258         Assertions.assertThrows(ConnectionClosedException.class, () ->
259                 inBuffer.read(readableChannel));
260     }
261 
262     @Test
263     public void testReadFrameCorruptFrame() throws Exception {
264         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
265         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(new byte[] {0,0});
266 
267         Assertions.assertThrows(H2CorruptFrameException.class, () ->
268                 inBuffer.read(readableChannel));
269     }
270 
271     @Test
272     public void testWriteFrameExceedingLimit() throws Exception {
273         final WritableByteChannelMock writableChannel = new WritableByteChannelMock(1024);
274         final FrameOutputBuffer outbuffer = new FrameOutputBuffer(1024);
275 
276         final RawFrame frame = new RawFrame(FrameType.DATA.getValue(), 0, 1,
277                 ByteBuffer.wrap(new byte[2048]));
278         Assertions.assertThrows(IllegalArgumentException.class, () ->
279                 outbuffer.write(frame, writableChannel));
280     }
281 
282     @Test
283     public void testReadFrameExceedingLimit() throws Exception {
284         final FrameInputBuffer inBuffer = new FrameInputBuffer(16 * 1024);
285         final ReadableByteChannelMock readableChannel = new ReadableByteChannelMock(
286                 new byte[] {0,-128,-128,0,0,0,0,0,1});
287 
288         Assertions.assertThrows(H2ConnectionException.class, () ->
289                 inBuffer.read(readableChannel));
290     }
291 
292     @Test
293     public void testOutputBufferResize() throws Exception {
294         final FrameOutputBuffer outBuffer = new FrameOutputBuffer(16 * 1024);
295         Assertions.assertEquals(16 * 1024, outBuffer.getMaxFramePayloadSize());
296         outBuffer.resize(1024);
297         Assertions.assertEquals(1024, outBuffer.getMaxFramePayloadSize());
298     }
299 
300 }
301