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 package org.apache.hc.core5.http2.impl.nio;
28
29 import java.io.IOException;
30 import java.nio.ByteBuffer;
31 import java.nio.channels.GatheringByteChannel;
32 import java.nio.channels.WritableByteChannel;
33
34 import org.apache.hc.core5.http2.H2TransportMetrics;
35 import org.apache.hc.core5.http2.frame.FrameConsts;
36 import org.apache.hc.core5.http2.frame.RawFrame;
37 import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics;
38 import org.apache.hc.core5.util.Args;
39
40
41
42
43
44
45 public final class FrameOutputBuffer {
46
47 private final BasicH2TransportMetrics metrics;
48 private int maxFramePayloadSize;
49 private ByteBuffer buffer;
50
51 public FrameOutputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) {
52 Args.notNull(metrics, "HTTP2 transport metrics");
53 Args.positive(maxFramePayloadSize, "Maximum payload size");
54 this.metrics = metrics;
55 this.maxFramePayloadSize = maxFramePayloadSize;
56 this.buffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize);
57 }
58
59 public FrameOutputBuffer(final int maxFramePayloadSize) {
60 this(new BasicH2TransportMetrics(), maxFramePayloadSize);
61 }
62
63
64
65
66 @Deprecated
67 public void expand(final int maxFramePayloadSize) {
68 resize(maxFramePayloadSize);
69 }
70
71
72
73
74 public int getMaxFramePayloadSize() {
75 return maxFramePayloadSize;
76 }
77
78
79
80
81 public void resize(final int maxFramePayloadSize) {
82 if (buffer.capacity() == maxFramePayloadSize) {
83 return;
84 }
85 this.maxFramePayloadSize = maxFramePayloadSize;
86 final ByteBuffer newBuffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize);
87 if (buffer.position() > 0) {
88 buffer.flip();
89 newBuffer.put(buffer);
90 }
91 buffer = newBuffer;
92 }
93
94 public void write(final RawFrame frame, final WritableByteChannel channel) throws IOException {
95 Args.notNull(frame, "Frame");
96
97 final ByteBuffer payload = frame.getPayload();
98 Args.check(payload == null || payload.remaining() <= maxFramePayloadSize, "Frame size exceeds maximum");
99 buffer.putInt((payload != null ? payload.remaining() << 8 : 0) | (frame.getType() & 0xff));
100 buffer.put((byte) (frame.getFlags() & 0xff));
101 buffer.putInt(frame.getStreamId());
102
103 if (payload != null) {
104 if (channel instanceof GatheringByteChannel) {
105 buffer.flip();
106 ((GatheringByteChannel) channel).write(new ByteBuffer[]{buffer, payload});
107 buffer.compact();
108 if (payload.hasRemaining()) {
109 buffer.put(payload);
110 }
111 } else {
112 buffer.put(payload);
113 }
114 }
115
116 flush(channel);
117
118 metrics.incrementFramesTransferred();
119 }
120
121 public void flush(final WritableByteChannel channel) throws IOException {
122 if (buffer.position() > 0) {
123 buffer.flip();
124 try {
125 final int bytesWritten = channel.write(buffer);
126 if (bytesWritten > 0) {
127 metrics.incrementBytesTransferred(bytesWritten);
128 }
129 } finally {
130 buffer.compact();
131 }
132 }
133 }
134
135 public boolean isEmpty() {
136 return buffer.position() == 0;
137 }
138
139 public H2TransportMetrics getMetrics() {
140 return metrics;
141 }
142
143 }