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.OutputStream;
32  import java.nio.ByteBuffer;
33  
34  import org.apache.hc.core5.http2.H2ConnectionException;
35  import org.apache.hc.core5.http2.H2Error;
36  import org.apache.hc.core5.http2.H2TransportMetrics;
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.RawFrame;
40  import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics;
41  import org.apache.hc.core5.util.Args;
42  
43  /**
44   * Frame output buffer for HTTP/2 blocking connections.
45   *
46   * @since 5.0
47   */
48  public final class FrameOutputBuffer {
49  
50      private final BasicH2TransportMetrics metrics;
51      private final int maxFramePayloadSize;
52      private final byte[] buffer;
53  
54      public FrameOutputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) {
55          super();
56          Args.notNull(metrics, "HTTP2 transport metrics");
57          Args.positive(maxFramePayloadSize, "Maximum payload size");
58          this.metrics = metrics;
59          this.maxFramePayloadSize = maxFramePayloadSize;
60          this.buffer = new byte[FrameConsts.HEAD_LEN + maxFramePayloadSize + FrameConsts.MAX_PADDING + 1];
61      }
62  
63      public FrameOutputBuffer(final int maxFramePayloadSize) {
64          this(new BasicH2TransportMetrics(), maxFramePayloadSize);
65      }
66  
67      public void write(final RawFrame frame, final OutputStream outStream) throws IOException {
68          if (frame == null) {
69              return;
70          }
71          final int type = frame.getType();
72          final long streamId = frame.getStreamId();
73          final int flags = frame.getFlags();
74          final ByteBuffer payload = frame.getPayload();
75          final int payloadLen = payload != null ? payload.remaining() : 0;
76          if (payload != null && payload.remaining() > maxFramePayloadSize) {
77              throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum");
78          }
79          buffer[0] = (byte) (payloadLen >> 16 & 0xff);
80          buffer[1] = (byte) (payloadLen >> 8 & 0xff);
81          buffer[2] = (byte) (payloadLen & 0xff);
82  
83          buffer[3] = (byte) (type & 0xff);
84          buffer[4] = (byte) (flags & 0xff);
85  
86          buffer[5] = (byte) (streamId >> 24 & 0xff);
87          buffer[6] = (byte) (streamId >> 16 & 0xff);
88          buffer[7] = (byte) (streamId >> 8 & 0xff);
89          buffer[8] = (byte) (streamId & 0xff);
90  
91          int frameLen = FrameConsts.HEAD_LEN;
92          int padding = 0;
93          if ((flags & FrameFlag.PADDED.getValue()) > 0) {
94              padding = 16;
95              buffer[9] = (byte) (padding & 0xff);
96              frameLen++;
97          }
98          if (payload != null) {
99              payload.get(buffer, frameLen, payload.remaining());
100             frameLen += payloadLen;
101         }
102         for (int i = 0; i < padding; i++) {
103             buffer[frameLen++] = 0;
104         }
105         outStream.write(buffer, 0, frameLen);
106 
107         metrics.incrementFramesTransferred();
108         metrics.incrementBytesTransferred(frameLen);
109     }
110 
111     public H2TransportMetrics getMetrics() {
112         return metrics;
113     }
114 
115 }