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.http.MetaData;
24 import org.eclipse.jetty.http2.ErrorCode;
25 import org.eclipse.jetty.http2.Flags;
26 import org.eclipse.jetty.http2.frames.FrameType;
27 import org.eclipse.jetty.http2.frames.HeadersFrame;
28 import org.eclipse.jetty.http2.frames.PriorityFrame;
29 import org.eclipse.jetty.util.BufferUtil;
30
31 public class HeadersBodyParser extends BodyParser
32 {
33 private final HeaderBlockParser headerBlockParser;
34 private final HeaderBlockFragments headerBlockFragments;
35 private State state = State.PREPARE;
36 private int cursor;
37 private int length;
38 private int paddingLength;
39 private boolean exclusive;
40 private int parentStreamId;
41 private int weight;
42
43 public HeadersBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser, HeaderBlockFragments headerBlockFragments)
44 {
45 super(headerParser, listener);
46 this.headerBlockParser = headerBlockParser;
47 this.headerBlockFragments = headerBlockFragments;
48 }
49
50 private void reset()
51 {
52 state = State.PREPARE;
53 cursor = 0;
54 length = 0;
55 paddingLength = 0;
56 exclusive = false;
57 parentStreamId = 0;
58 weight = 0;
59 }
60
61 @Override
62 protected void emptyBody(ByteBuffer buffer)
63 {
64 if (hasFlag(Flags.END_HEADERS))
65 {
66 MetaData metaData = headerBlockParser.parse(BufferUtil.EMPTY_BUFFER, 0);
67 onHeaders(0, 0, false, metaData);
68 }
69 else
70 {
71 headerBlockFragments.setStreamId(getStreamId());
72 headerBlockFragments.setEndStream(isEndStream());
73 if (hasFlag(Flags.PRIORITY))
74 connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_priority_frame");
75 }
76 }
77
78 @Override
79 public boolean parse(ByteBuffer buffer)
80 {
81 boolean loop = false;
82 while (buffer.hasRemaining() || loop)
83 {
84 switch (state)
85 {
86 case PREPARE:
87 {
88
89 if (getStreamId() == 0)
90 return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_headers_frame");
91
92 length = getBodyLength();
93
94 if (isPadding())
95 {
96 state = State.PADDING_LENGTH;
97 }
98 else if (hasFlag(Flags.PRIORITY))
99 {
100 state = State.EXCLUSIVE;
101 }
102 else
103 {
104 state = State.HEADERS;
105 }
106 break;
107 }
108 case PADDING_LENGTH:
109 {
110 paddingLength = buffer.get() & 0xFF;
111 --length;
112 length -= paddingLength;
113 state = hasFlag(Flags.PRIORITY) ? State.EXCLUSIVE : State.HEADERS;
114 loop = length == 0;
115 if (length < 0)
116 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame_padding");
117 break;
118 }
119 case EXCLUSIVE:
120 {
121
122
123 int currByte = buffer.get(buffer.position());
124 exclusive = (currByte & 0x80) == 0x80;
125 state = State.PARENT_STREAM_ID;
126 break;
127 }
128 case PARENT_STREAM_ID:
129 {
130 if (buffer.remaining() >= 4)
131 {
132 parentStreamId = buffer.getInt();
133 parentStreamId &= 0x7F_FF_FF_FF;
134 length -= 4;
135 state = State.WEIGHT;
136 if (length < 1)
137 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
138 }
139 else
140 {
141 state = State.PARENT_STREAM_ID_BYTES;
142 cursor = 4;
143 }
144 break;
145 }
146 case PARENT_STREAM_ID_BYTES:
147 {
148 int currByte = buffer.get() & 0xFF;
149 --cursor;
150 parentStreamId += currByte << (8 * cursor);
151 --length;
152 if (cursor > 0 && length <= 0)
153 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
154 if (cursor == 0)
155 {
156 parentStreamId &= 0x7F_FF_FF_FF;
157 state = State.WEIGHT;
158 if (length < 1)
159 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_headers_frame");
160 }
161 break;
162 }
163 case WEIGHT:
164 {
165 weight = (buffer.get() & 0xFF) + 1;
166 --length;
167 state = State.HEADERS;
168 loop = length == 0;
169 break;
170 }
171 case HEADERS:
172 {
173 if (hasFlag(Flags.END_HEADERS))
174 {
175 MetaData metaData = headerBlockParser.parse(buffer, length);
176 if (metaData != null)
177 {
178 if (LOG.isDebugEnabled())
179 LOG.debug("Parsed {} frame hpack from {}", FrameType.HEADERS, buffer);
180 state = State.PADDING;
181 loop = paddingLength == 0;
182 onHeaders(parentStreamId, weight, exclusive, metaData);
183 }
184 }
185 else
186 {
187 int remaining = buffer.remaining();
188 if (remaining < length)
189 {
190 headerBlockFragments.storeFragment(buffer, remaining, false);
191 length -= remaining;
192 }
193 else
194 {
195 headerBlockFragments.setStreamId(getStreamId());
196 headerBlockFragments.setEndStream(isEndStream());
197 if (hasFlag(Flags.PRIORITY))
198 headerBlockFragments.setPriorityFrame(new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive));
199 headerBlockFragments.storeFragment(buffer, length, false);
200 state = State.PADDING;
201 loop = paddingLength == 0;
202 }
203 }
204 break;
205 }
206 case PADDING:
207 {
208 int size = Math.min(buffer.remaining(), paddingLength);
209 buffer.position(buffer.position() + size);
210 paddingLength -= size;
211 if (paddingLength == 0)
212 {
213 reset();
214 return true;
215 }
216 break;
217 }
218 default:
219 {
220 throw new IllegalStateException();
221 }
222 }
223 }
224 return false;
225 }
226
227 private void onHeaders(int parentStreamId, int weight, boolean exclusive, MetaData metaData)
228 {
229 PriorityFrame priorityFrame = null;
230 if (hasFlag(Flags.PRIORITY))
231 priorityFrame = new PriorityFrame(getStreamId(), parentStreamId, weight, exclusive);
232 HeadersFrame frame = new HeadersFrame(getStreamId(), metaData, priorityFrame, isEndStream());
233 notifyHeaders(frame);
234 }
235
236 private enum State
237 {
238 PREPARE, PADDING_LENGTH, EXCLUSIVE, PARENT_STREAM_ID, PARENT_STREAM_ID_BYTES, WEIGHT, HEADERS, PADDING
239 }
240 }