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.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   * Frame input buffer for HTTP/2 blocking connections.
47   *
48   * @since 5.0
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 RawFramehttp2/frame/RawFrame.html#RawFrame">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 }