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 volatile int maxFramePayloadSize;
49 private volatile 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 public void expand(final int maxFramePayloadSize) {
64 this.maxFramePayloadSize = maxFramePayloadSize;
65 final ByteBuffer newBuffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize);
66 if (buffer.position() > 0) {
67 buffer.flip();
68 newBuffer.put(buffer);
69 }
70 buffer = newBuffer;
71 }
72
73 public void write(final RawFrame frame, final WritableByteChannel channel) throws IOException {
74 Args.notNull(frame, "Frame");
75
76 final ByteBuffer payload = frame.getPayload();
77 Args.check(payload == null || payload.remaining() <= maxFramePayloadSize, "Frame size exceeds maximum");
78 buffer.putInt((payload != null ? payload.remaining() << 8 : 0) | (frame.getType() & 0xff));
79 buffer.put((byte) (frame.getFlags() & 0xff));
80 buffer.putInt(frame.getStreamId());
81
82 if (payload != null) {
83 if (channel instanceof GatheringByteChannel) {
84 buffer.flip();
85 ((GatheringByteChannel) channel).write(new ByteBuffer[]{buffer, payload});
86 buffer.compact();
87 if (payload.hasRemaining()) {
88 buffer.put(payload);
89 }
90 } else {
91 buffer.put(payload);
92 }
93 }
94
95 flush(channel);
96
97 metrics.incrementFramesTransferred();
98 }
99
100 public void flush(final WritableByteChannel channel) throws IOException {
101 if (buffer.position() > 0) {
102 buffer.flip();
103 try {
104 final int bytesWritten = channel.write(buffer);
105 if (bytesWritten > 0) {
106 metrics.incrementBytesTransferred(bytesWritten);
107 }
108 } finally {
109 buffer.compact();
110 }
111 }
112 }
113
114 public boolean isEmpty() {
115 return buffer.position() == 0;
116 }
117
118 public H2TransportMetrics getMetrics() {
119 return metrics;
120 }
121
122 }