1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.fcgi.parser;
20
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import java.nio.charset.StandardCharsets;
24
25 import org.eclipse.jetty.http.HttpField;
26 import org.eclipse.jetty.util.log.Log;
27 import org.eclipse.jetty.util.log.Logger;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61 public class ParamsContentParser extends ContentParser
62 {
63 private static final Logger LOG = Log.getLogger(ParamsContentParser.class);
64
65 private final ServerParser.Listener listener;
66 private State state = State.LENGTH;
67 private int cursor;
68 private int length;
69 private int nameLength;
70 private int valueLength;
71 private byte[] nameBytes;
72 private byte[] valueBytes;
73
74 public ParamsContentParser(HeaderParser headerParser, ServerParser.Listener listener)
75 {
76 super(headerParser);
77 this.listener = listener;
78 }
79
80 @Override
81 public Result parse(ByteBuffer buffer)
82 {
83 while (buffer.hasRemaining() || state == State.PARAM)
84 {
85 switch (state)
86 {
87 case LENGTH:
88 {
89 length = getContentLength();
90 state = State.NAME_LENGTH;
91 break;
92 }
93 case NAME_LENGTH:
94 {
95 if (isLargeLength(buffer))
96 {
97 if (buffer.remaining() >= 4)
98 {
99 nameLength = buffer.getInt() & 0x7F_FF;
100 state = State.VALUE_LENGTH;
101 length -= 4;
102 }
103 else
104 {
105 state = State.NAME_LENGTH_BYTES;
106 cursor = 0;
107 }
108 }
109 else
110 {
111 nameLength = buffer.get() & 0xFF;
112 state = State.VALUE_LENGTH;
113 --length;
114 }
115 break;
116 }
117 case NAME_LENGTH_BYTES:
118 {
119 int quarterInt = buffer.get() & 0xFF;
120 nameLength = (nameLength << 8) + quarterInt;
121 --length;
122 if (++cursor == 4)
123 {
124 nameLength &= 0x7F_FF;
125 state = State.VALUE_LENGTH;
126 }
127 break;
128 }
129 case VALUE_LENGTH:
130 {
131 if (isLargeLength(buffer))
132 {
133 if (buffer.remaining() >= 4)
134 {
135 valueLength = buffer.getInt() & 0x7F_FF;
136 state = State.NAME;
137 length -= 4;
138 }
139 else
140 {
141 state = State.VALUE_LENGTH_BYTES;
142 cursor = 0;
143 }
144 }
145 else
146 {
147 valueLength = buffer.get() & 0xFF;
148 state = State.NAME;
149 --length;
150 }
151 break;
152 }
153 case VALUE_LENGTH_BYTES:
154 {
155 int quarterInt = buffer.get() & 0xFF;
156 valueLength = (valueLength << 8) + quarterInt;
157 --length;
158 if (++cursor == 4)
159 {
160 valueLength &= 0x7F_FF;
161 state = State.NAME;
162 }
163 break;
164 }
165 case NAME:
166 {
167 nameBytes = new byte[nameLength];
168 if (buffer.remaining() >= nameLength)
169 {
170 buffer.get(nameBytes);
171 state = State.VALUE;
172 length -= nameLength;
173 }
174 else
175 {
176 state = State.NAME_BYTES;
177 cursor = 0;
178 }
179 break;
180 }
181 case NAME_BYTES:
182 {
183 nameBytes[cursor] = buffer.get();
184 --length;
185 if (++cursor == nameLength)
186 state = State.VALUE;
187 break;
188 }
189 case VALUE:
190 {
191 valueBytes = new byte[valueLength];
192 if (buffer.remaining() >= valueLength)
193 {
194 buffer.get(valueBytes);
195 state = State.PARAM;
196 length -= valueLength;
197 }
198 else
199 {
200 state = State.VALUE_BYTES;
201 cursor = 0;
202 }
203 break;
204 }
205 case VALUE_BYTES:
206 {
207 valueBytes[cursor] = buffer.get();
208 --length;
209 if (++cursor == valueLength)
210 state = State.PARAM;
211 break;
212 }
213 case PARAM:
214 {
215 Charset utf8 = StandardCharsets.UTF_8;
216 onParam(new String(nameBytes, utf8), new String(valueBytes, utf8));
217 partialReset();
218 if (length == 0)
219 {
220 reset();
221 return Result.COMPLETE;
222 }
223 break;
224 }
225 default:
226 {
227 throw new IllegalStateException();
228 }
229 }
230 }
231 return Result.PENDING;
232 }
233
234 @Override
235 public void noContent()
236 {
237 onParams();
238 }
239
240 protected void onParam(String name, String value)
241 {
242 try
243 {
244 listener.onHeader(getRequest(), new HttpField(name, value));
245 }
246 catch (Throwable x)
247 {
248 if (LOG.isDebugEnabled())
249 LOG.debug("Exception while invoking listener " + listener, x);
250 }
251 }
252
253 protected void onParams()
254 {
255 try
256 {
257 listener.onHeaders(getRequest());
258 }
259 catch (Throwable x)
260 {
261 if (LOG.isDebugEnabled())
262 LOG.debug("Exception while invoking listener " + listener, x);
263 }
264 }
265
266 private boolean isLargeLength(ByteBuffer buffer)
267 {
268 return (buffer.get(buffer.position()) & 0x80) == 0x80;
269 }
270
271 private void partialReset()
272 {
273 state = State.NAME_LENGTH;
274 cursor = 0;
275 nameLength = 0;
276 valueLength = 0;
277 nameBytes = null;
278 valueBytes = null;
279 }
280
281 private void reset()
282 {
283 partialReset();
284 state = State.LENGTH;
285 length = 0;
286 }
287
288 private enum State
289 {
290 LENGTH, NAME_LENGTH, NAME_LENGTH_BYTES, VALUE_LENGTH, VALUE_LENGTH_BYTES, NAME, NAME_BYTES, VALUE, VALUE_BYTES, PARAM
291 }
292 }