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.PushPromiseFrame;
27
28 public class PushPromiseBodyParser extends BodyParser
29 {
30 private final HeaderBlockParser headerBlockParser;
31 private State state = State.PREPARE;
32 private int cursor;
33 private int length;
34 private int paddingLength;
35 private int streamId;
36
37 public PushPromiseBodyParser(HeaderParser headerParser, Parser.Listener listener, HeaderBlockParser headerBlockParser)
38 {
39 super(headerParser, listener);
40 this.headerBlockParser = headerBlockParser;
41 }
42
43 private void reset()
44 {
45 state = State.PREPARE;
46 cursor = 0;
47 length = 0;
48 paddingLength = 0;
49 streamId = 0;
50 }
51
52 @Override
53 public boolean parse(ByteBuffer buffer)
54 {
55 boolean loop = false;
56 while (buffer.hasRemaining() || loop)
57 {
58 switch (state)
59 {
60 case PREPARE:
61 {
62
63 if (getStreamId() == 0)
64 return connectionFailure(buffer, ErrorCode.PROTOCOL_ERROR.code, "invalid_push_promise_frame");
65
66
67 if (!hasFlag(Flags.END_HEADERS))
68 return connectionFailure(buffer, ErrorCode.INTERNAL_ERROR.code, "unsupported_push_promise_frame");
69
70 length = getBodyLength();
71
72 if (isPadding())
73 {
74 state = State.PADDING_LENGTH;
75 }
76 else
77 {
78 state = State.STREAM_ID;
79 }
80 break;
81 }
82 case PADDING_LENGTH:
83 {
84 paddingLength = buffer.get() & 0xFF;
85 --length;
86 length -= paddingLength;
87 state = State.STREAM_ID;
88 if (length < 4)
89 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_push_promise_frame");
90 break;
91 }
92 case STREAM_ID:
93 {
94 if (buffer.remaining() >= 4)
95 {
96 streamId = buffer.getInt();
97 streamId &= 0x7F_FF_FF_FF;
98 length -= 4;
99 state = State.HEADERS;
100 loop = length == 0;
101 }
102 else
103 {
104 state = State.STREAM_ID_BYTES;
105 cursor = 4;
106 }
107 break;
108 }
109 case STREAM_ID_BYTES:
110 {
111 int currByte = buffer.get() & 0xFF;
112 --cursor;
113 streamId += currByte << (8 * cursor);
114 --length;
115 if (cursor > 0 && length <= 0)
116 return connectionFailure(buffer, ErrorCode.FRAME_SIZE_ERROR.code, "invalid_push_promise_frame");
117 if (cursor == 0)
118 {
119 streamId &= 0x7F_FF_FF_FF;
120 state = State.HEADERS;
121 loop = length == 0;
122 }
123 break;
124 }
125 case HEADERS:
126 {
127 MetaData metaData = headerBlockParser.parse(buffer, length);
128 if (metaData != null)
129 {
130 state = State.PADDING;
131 loop = paddingLength == 0;
132 onPushPromise(streamId, metaData);
133 }
134 break;
135 }
136 case PADDING:
137 {
138 int size = Math.min(buffer.remaining(), paddingLength);
139 buffer.position(buffer.position() + size);
140 paddingLength -= size;
141 if (paddingLength == 0)
142 {
143 reset();
144 return true;
145 }
146 break;
147 }
148 default:
149 {
150 throw new IllegalStateException();
151 }
152 }
153 }
154 return false;
155 }
156
157 private void onPushPromise(int streamId, MetaData metaData)
158 {
159 PushPromiseFrame frame = new PushPromiseFrame(getStreamId(), streamId, metaData);
160 notifyPushPromise(frame);
161 }
162
163 private enum State
164 {
165 PREPARE, PADDING_LENGTH, STREAM_ID, STREAM_ID_BYTES, HEADERS, PADDING
166 }
167 }