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.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.RawFrame;
39 import org.apache.hc.core5.http2.impl.BasicH2TransportMetrics;
40 import org.apache.hc.core5.util.Args;
41
42
43
44
45
46
47 public final class FrameOutputBuffer {
48
49 private final BasicH2TransportMetrics metrics;
50 private final int maxFramePayloadSize;
51 private final ByteBuffer buffer;
52
53 public FrameOutputBuffer(final BasicH2TransportMetrics metrics, final int maxFramePayloadSize) {
54 Args.notNull(metrics, "HTTP2 transport metrics");
55 Args.positive(maxFramePayloadSize, "Maximum payload size");
56 this.metrics = metrics;
57 this.maxFramePayloadSize = maxFramePayloadSize;
58 this.buffer = ByteBuffer.allocate(FrameConsts.HEAD_LEN + maxFramePayloadSize);
59 }
60
61 public FrameOutputBuffer(final int maxFramePayloadSize) {
62 this(new BasicH2TransportMetrics(), maxFramePayloadSize);
63 }
64
65 public void write(final RawFrame frame, final WritableByteChannel channel) throws IOException {
66 Args.notNull(frame, "Frame");
67
68 final ByteBuffer payload = frame.getPayload();
69 if (payload != null && payload.remaining() > maxFramePayloadSize) {
70 throw new H2ConnectionException(H2Error.FRAME_SIZE_ERROR, "Frame size exceeds maximum");
71 }
72
73 buffer.putInt((payload != null ? payload.remaining() << 8 : 0) | (frame.getType() & 0xff));
74 buffer.put((byte) (frame.getFlags() & 0xff));
75 buffer.putInt(frame.getStreamId());
76
77 if (payload != null) {
78 if (channel instanceof GatheringByteChannel) {
79 buffer.flip();
80 ((GatheringByteChannel) channel).write(new ByteBuffer[]{buffer, payload});
81 buffer.compact();
82 if (payload.hasRemaining()) {
83 buffer.put(payload);
84 }
85 } else {
86 buffer.put(payload);
87 }
88 }
89
90 flush(channel);
91
92 metrics.incrementFramesTransferred();
93 }
94
95 public void flush(final WritableByteChannel channel) throws IOException {
96 if (buffer.position() > 0) {
97 buffer.flip();
98 try {
99 final int bytesWritten = channel.write(buffer);
100 if (bytesWritten > 0) {
101 metrics.incrementBytesTransferred(bytesWritten);
102 }
103 } finally {
104 buffer.compact();
105 }
106 }
107 }
108
109 public boolean isEmpty() {
110 return buffer.position() == 0;
111 }
112
113 public H2TransportMetrics getMetrics() {
114 return metrics;
115 }
116
117 }