1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.http2.parser;
20
21 import java.nio.ByteBuffer;
22
23 import org.eclipse.jetty.http2.ErrorCode;
24 import org.eclipse.jetty.http2.frames.GoAwayFrame;
25
26 public class GoAwayBodyParser extends BodyParser
27 {
28 private State state = State.PREPARE;
29 private int cursor;
30 private int length;
31 private int lastStreamId;
32 private int error;
33 private byte[] payload;
34
35 public GoAwayBodyParser(HeaderParser headerParser, Parser.Listener listener)
36 {
37 super(headerParser, listener);
38 }
39
40 private void reset()
41 {
42 state = State.PREPARE;
43 cursor = 0;
44 length = 0;
45 lastStreamId = 0;
46 error = 0;
47 payload = null;
48 }
49
50 @Override
51 public boolean parse(ByteBuffer buffer)
52 {
53 while (buffer.hasRemaining())
54 {
55 switch (state)
56 {
57 case PREPARE:
58 {
59 state = State.LAST_STREAM_ID;
60 length = getBodyLength();
61 break;
62 }
63 case LAST_STREAM_ID:
64 {
65 if (buffer.remaining() >= 4)
66 {
67 lastStreamId = buffer.getInt();
68 lastStreamId &= 0x7F_FF_FF_FF;
69 state = State.ERROR;
70 length -= 4;
71 if (length <= 0)
72 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
73 }
74 else
75 {
76 state = State.LAST_STREAM_ID_BYTES;
77 cursor = 4;
78 }
79 break;
80 }
81 case LAST_STREAM_ID_BYTES:
82 {
83 int currByte = buffer.get() & 0xFF;
84 --cursor;
85 lastStreamId += currByte << (8 * cursor);
86 --length;
87 if (cursor > 0 && length <= 0)
88 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
89 if (cursor == 0)
90 {
91 lastStreamId &= 0x7F_FF_FF_FF;
92 state = State.ERROR;
93 if (length == 0)
94 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
95 }
96 break;
97 }
98 case ERROR:
99 {
100 if (buffer.remaining() >= 4)
101 {
102 error = buffer.getInt();
103 state = State.PAYLOAD;
104 length -= 4;
105 if (length < 0)
106 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
107 if (length == 0)
108 return onGoAway(lastStreamId, error, null);
109 }
110 else
111 {
112 state = State.ERROR_BYTES;
113 cursor = 4;
114 }
115 break;
116 }
117 case ERROR_BYTES:
118 {
119 int currByte = buffer.get() & 0xFF;
120 --cursor;
121 error += currByte << (8 * cursor);
122 --length;
123 if (cursor > 0 && length <= 0)
124 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_go_away_frame");
125 if (cursor == 0)
126 {
127 state = State.PAYLOAD;
128 if (length == 0)
129 return onGoAway(lastStreamId, error, null);
130 }
131 break;
132 }
133 case PAYLOAD:
134 {
135 payload = new byte[length];
136 if (buffer.remaining() >= length)
137 {
138 buffer.get(payload);
139 return onGoAway(lastStreamId, error, payload);
140 }
141 else
142 {
143 state = State.PAYLOAD_BYTES;
144 cursor = length;
145 }
146 break;
147 }
148 case PAYLOAD_BYTES:
149 {
150 payload[payload.length - cursor] = buffer.get();
151 --cursor;
152 if (cursor == 0)
153 return onGoAway(lastStreamId, error, payload);
154 break;
155 }
156 default:
157 {
158 throw new IllegalStateException();
159 }
160 }
161 }
162 return false;
163 }
164
165 private boolean onGoAway(int lastStreamId, int error, byte[] payload)
166 {
167 GoAwayFrame frame = new GoAwayFrame(lastStreamId, error, payload);
168 reset();
169 notifyGoAway(frame);
170 return true;
171 }
172
173 private enum State
174 {
175 PREPARE, LAST_STREAM_ID, LAST_STREAM_ID_BYTES, ERROR, ERROR_BYTES, PAYLOAD, PAYLOAD_BYTES
176 }
177 }