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
28 package org.apache.hc.core5.http2.frame;
29
30 import java.io.IOException;
31 import java.nio.ByteBuffer;
32 import java.nio.charset.StandardCharsets;
33 import java.util.Objects;
34
35 import org.apache.hc.core5.annotation.Internal;
36 import org.apache.hc.core5.http.Chars;
37 import org.apache.hc.core5.http2.H2Error;
38 import org.apache.hc.core5.http2.config.H2Param;
39
40 @Internal
41 public final class FramePrinter {
42
43 public void printFrameInfo(final RawFrame frame, final Appendable appendable) throws IOException {
44 appendable.append("stream ").append(Integer.toString(frame.getStreamId())).append(" frame: ");
45 final FrameType type = FrameType.valueOf(frame.getType());
46 appendable.append(Objects.toString(type))
47 .append(" (0x").append(Integer.toHexString(frame.getType())).append("); flags: ");
48 final int flags = frame.getFlags();
49 if (flags > 0) {
50 switch (type) {
51 case SETTINGS:
52 case PING:
53 if ((flags & FrameFlag.ACK.value) > 0) {
54 appendable.append(FrameFlag.ACK.name()).append(" ");
55 }
56 break;
57 case DATA:
58 if ((flags & FrameFlag.END_STREAM.value) > 0) {
59 appendable.append(FrameFlag.END_STREAM.name()).append(" ");
60 }
61 if ((flags & FrameFlag.PADDED.value) > 0) {
62 appendable.append(FrameFlag.PADDED.name()).append(" ");
63 }
64 break;
65 case HEADERS:
66 if ((flags & FrameFlag.END_STREAM.value) > 0) {
67 appendable.append(FrameFlag.END_STREAM.name()).append(" ");
68 }
69 if ((flags & FrameFlag.END_HEADERS.value) > 0) {
70 appendable.append(FrameFlag.END_HEADERS.name()).append(" ");
71 }
72 if ((flags & FrameFlag.PADDED.value) > 0) {
73 appendable.append(FrameFlag.PADDED.name()).append(" ");
74 }
75 if ((flags & FrameFlag.PRIORITY.value) > 0) {
76 appendable.append(FrameFlag.PRIORITY.name()).append(" ");
77 }
78 break;
79 case PUSH_PROMISE:
80 if ((flags & FrameFlag.END_HEADERS.value) > 0) {
81 appendable.append(FrameFlag.END_HEADERS.name()).append(" ");
82 }
83 if ((flags & FrameFlag.PADDED.value) > 0) {
84 appendable.append(FrameFlag.PADDED.name()).append(" ");
85 }
86 break;
87 case CONTINUATION:
88 if ((flags & FrameFlag.END_HEADERS.value) > 0) {
89 appendable.append(FrameFlag.END_HEADERS.name()).append(" ");
90 }
91 }
92 }
93 appendable.append("(0x").append(Integer.toHexString(flags)).append("); length: ").append(Integer.toString(frame.getLength()));
94 }
95
96 public void printPayload(final RawFrame frame, final Appendable appendable) throws IOException {
97
98 final FrameType type = FrameType.valueOf(frame.getType());
99 final ByteBuffer buf = frame.getPayloadContent();
100 if (buf != null) {
101
102 switch (type) {
103 case SETTINGS:
104 if ((buf.remaining() % 6) == 0) {
105 while (buf.hasRemaining()) {
106 final int code = buf.getShort();
107 final H2Param param = H2Param.valueOf(code);
108 final int value = buf.getInt();
109 if (param != null) {
110 appendable.append(param.name());
111 } else {
112 appendable.append("0x").append(Integer.toHexString(code));
113 }
114 appendable.append(": ").append(Integer.toString(value)).append("\r\n");
115 }
116 } else {
117 appendable.append("Invalid\r\n");
118 }
119 break;
120 case RST_STREAM:
121 if (buf.remaining() == 4) {
122 appendable.append("Code ");
123 final int code = buf.getInt();
124 final H2Error error = H2Error.getByCode(code);
125 if (error != null) {
126 appendable.append(error.name());
127 } else {
128 appendable.append("0x").append(Integer.toHexString(code));
129 }
130 appendable.append("\r\n");
131 } else {
132 appendable.append("Invalid\r\n");
133 }
134 break;
135 case GOAWAY:
136 if (buf.remaining() >= 8) {
137 final int lastStream = buf.getInt();
138 appendable.append("Last stream ").append(Integer.toString(lastStream)).append("\r\n");
139 appendable.append("Code ");
140 final int code2 = buf.getInt();
141 final H2Error error2 = H2Error.getByCode(code2);
142 if (error2 != null) {
143 appendable.append(error2.name());
144 } else {
145 appendable.append("0x").append(Integer.toHexString(code2));
146 }
147 appendable.append("\r\n");
148 final byte[] tmp = new byte[buf.remaining()];
149 buf.get(tmp);
150 appendable.append(new String(tmp, StandardCharsets.US_ASCII));
151 appendable.append("\r\n");
152 } else {
153 appendable.append("Invalid\r\n");
154 }
155 break;
156 case WINDOW_UPDATE:
157 if (buf.remaining() == 4) {
158 final int increment = buf.getInt();
159 appendable.append("Increment ").append(Integer.toString(increment)).append("\r\n");
160 } else {
161 appendable.append("Invalid\r\n");
162 }
163 break;
164 case PUSH_PROMISE:
165 if (buf.remaining() > 4) {
166 final int streamId = buf.getInt();
167 appendable.append("Promised stream ").append(Integer.toString(streamId)).append("\r\n");
168 printData(buf, appendable);
169 } else {
170 appendable.append("Invalid\r\n");
171 }
172 break;
173 default:
174 printData(frame.getPayload(), appendable);
175 }
176 }
177 }
178
179 public void printData(final ByteBuffer data, final Appendable appendable) throws IOException {
180 final ByteBuffer buf = data.duplicate();
181 final byte[] line = new byte[16];
182 while (buf.hasRemaining()) {
183 final int chunk = Math.min(buf.remaining(), line.length);
184 buf.get(line, 0, chunk);
185
186 for (int i = 0; i < chunk; i++) {
187 final char ch = (char) line[i];
188 if (ch > Chars.SP && ch <= Chars.DEL) {
189 appendable.append(ch);
190 } else if (Character.isWhitespace(ch)) {
191 appendable.append(' ');
192 } else {
193 appendable.append('.');
194 }
195 }
196 for (int i = chunk; i < 17; i++) {
197 appendable.append(' ');
198 }
199
200 for (int i = 0; i < chunk; i++) {
201 appendable.append(' ');
202 final int b = line[i] & 0xff;
203 final String s = Integer.toHexString(b);
204 if (s.length() == 1) {
205 appendable.append("0");
206 }
207 appendable.append(s);
208 }
209 appendable.append("\r\n");
210 }
211 }
212
213 }