1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http2.impl.io;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.nio.ByteBuffer;
33
34 import org.apache.hc.core5.http.ConnectionClosedException;
35 import org.apache.hc.core5.http2.H2ConnectionException;
36 import org.apache.hc.core5.http2.H2CorruptFrameException;
37 import org.apache.hc.core5.http2.H2Error;
38 import org.apache.hc.core5.http2.H2TransportMetrics;
39 import org.apache.hc.core5.http2.frame.FrameConsts;
40 import org.apache.hc.core5.http2.frame.FrameFlag;
41 import org.apache.hc.core5.http2.frame.RawFrame;
42 import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics;
43 import org.apache.hc.core5.util.Args;
44
45
46
47
48
49
50 public final class FrameInputBuffer {
51
52 private final BasicH2TransportMetrics metrics;
53 private final int maxFramePayloadSize;
54 private final byte[] buffer;
55
56 private int off;
57 private int dataLen;
58
59 FrameInputBuffer(final BasicH2TransportMetrics metrics, final int bufferLen, final int maxFramePayloadSize) {
60 Args.notNull(metrics, "HTTP2 transport metrics");
61 Args.positive(maxFramePayloadSize, "Maximum payload size");
62 this.metrics = metrics;
63 this.maxFramePayloadSize = maxFramePayloadSize;
64 this.buffer = new byte[bufferLen];
65 this.dataLen = 0;
66 }
67
68 public FrameInputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) {
69 this(metrics, FrameConsts.HEAD_LEN + maxFramePayloadSize, maxFramePayloadSize);
70 }
71
72 public FrameInputBuffer(final int maxFramePayloadSize) {
73 this(new BasicH2TransportMetrics(), maxFramePayloadSize);
74 }
75
76 boolean hasData() {
77 return this.dataLen > 0;
78 }
79
80 void fillBuffer(final InputStream inStream, final int requiredLen) throws IOException {
81 while (dataLen < requiredLen) {
82 if (off > 0) {
83 System.arraycopy(buffer, off, buffer, 0, dataLen);
84 off = 0;
85 }
86 final int bytesRead = inStream.read(buffer, off + dataLen, buffer.length - dataLen);
87 if (bytesRead == -1) {
88 if (dataLen > 0) {
89 throw new H2CorruptFrameException("Corrupt or incomplete HTTP2 frame");
90 }
91 throw new ConnectionClosedException();
92 }
93 dataLen += bytesRead;
94 this.metrics.incrementBytesTransferred(bytesRead);
95 }
96 }
97
98 public RawFrame read(final InputStream inStream) throws IOException {
99
100 fillBuffer(inStream, FrameConsts.HEAD_LEN);
101 final int payloadOff = FrameConsts.HEAD_LEN;
102
103 final int payloadLen = (buffer[off] & 0xff) << 16 | (buffer[off + 1] & 0xff) << 8 | (buffer[off + 2] & 0xff);
104 final int type = buffer[off + 3] & 0xff;
105 final int flags = buffer[off + 4] & 0xff;
106 final int streamId = Math.abs(buffer[off + 5] & 0xff) << 24 | (buffer[off + 6] & 0xff << 16) | (buffer[off + 7] & 0xff) << 8 | (buffer[off + 8] & 0xff);
107 if (payloadLen > maxFramePayloadSize) {
108 throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum");
109 }
110
111 final int frameLen = payloadOff + payloadLen;
112 fillBuffer(inStream, frameLen);
113
114 if ((flags & FrameFlag.PADDED.getValue()) > 0) {
115 if (payloadLen == 0) {
116 throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding");
117 }
118 final int padding = buffer[off + FrameConsts.HEAD_LEN] & 0xff;
119 if (payloadLen < padding + 1) {
120 throw new H2ConnectionException(H2Error.PROTOCOL_ERROR, "Inconsistent padding");
121 }
122 }
123
124 final ByteBuffer payload = payloadLen > 0 ? ByteBuffer.wrap(buffer, off + payloadOff, payloadLen) : null;
125 final RawFrame frame = new RawFrame(type, flags, streamId, payload);
126
127 off += frameLen;
128 dataLen -= frameLen;
129
130 this.metrics.incrementFramesTransferred();
131
132 return frame;
133 }
134
135 public H2TransportMetrics getMetrics() {
136 return metrics;
137 }
138
139 }