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.io.EOFException;
22 import java.nio.ByteBuffer;
23 import java.util.Map;
24 import java.util.concurrent.ConcurrentHashMap;
25
26 import org.eclipse.jetty.fcgi.FCGI;
27 import org.eclipse.jetty.http.BadMessageException;
28 import org.eclipse.jetty.http.HttpField;
29 import org.eclipse.jetty.http.HttpFields;
30 import org.eclipse.jetty.http.HttpHeader;
31 import org.eclipse.jetty.http.HttpParser;
32 import org.eclipse.jetty.http.HttpStatus;
33 import org.eclipse.jetty.http.HttpVersion;
34 import org.eclipse.jetty.util.log.Log;
35 import org.eclipse.jetty.util.log.Logger;
36
37
38
39
40
41
42
43
44
45 public class ResponseContentParser extends StreamContentParser
46 {
47 private static final Logger LOG = Log.getLogger(ResponseContentParser.class);
48
49 private final Map<Integer, ResponseParser> parsers = new ConcurrentHashMap<>();
50 private final ClientParser.Listener listener;
51
52 public ResponseContentParser(HeaderParser headerParser, ClientParser.Listener listener)
53 {
54 super(headerParser, FCGI.StreamType.STD_OUT, listener);
55 this.listener = listener;
56 }
57
58 @Override
59 public void noContent()
60 {
61
62 }
63
64 @Override
65 protected boolean onContent(ByteBuffer buffer)
66 {
67 int request = getRequest();
68 ResponseParser parser = parsers.get(request);
69 if (parser == null)
70 {
71 parser = new ResponseParser(listener, request);
72 parsers.put(request, parser);
73 }
74 return parser.parse(buffer);
75 }
76
77 @Override
78 protected void end(int request)
79 {
80 super.end(request);
81 parsers.remove(request);
82 }
83
84 private class ResponseParser implements HttpParser.ResponseHandler
85 {
86 private final HttpFields fields = new HttpFields();
87 private ClientParser.Listener listener;
88 private final int request;
89 private final FCGIHttpParser httpParser;
90 private State state = State.HEADERS;
91 private boolean seenResponseCode;
92
93 private ResponseParser(ClientParser.Listener listener, int request)
94 {
95 this.listener = listener;
96 this.request = request;
97 this.httpParser = new FCGIHttpParser(this);
98 }
99
100 public boolean parse(ByteBuffer buffer)
101 {
102 int remaining = buffer.remaining();
103 while (remaining > 0)
104 {
105 if (LOG.isDebugEnabled())
106 LOG.debug("Response {} {}, state {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
107
108 switch (state)
109 {
110 case HEADERS:
111 {
112 if (httpParser.parseNext(buffer))
113 state = State.CONTENT_MODE;
114 remaining = buffer.remaining();
115 break;
116 }
117 case CONTENT_MODE:
118 {
119
120
121
122
123 boolean rawContent = fields.size() == 0 ||
124 (fields.get(HttpHeader.CONTENT_LENGTH) == null &&
125 fields.get(HttpHeader.TRANSFER_ENCODING) == null);
126 state = rawContent ? State.RAW_CONTENT : State.HTTP_CONTENT;
127 break;
128 }
129 case RAW_CONTENT:
130 {
131 if (notifyContent(buffer))
132 return true;
133 remaining = 0;
134 break;
135 }
136 case HTTP_CONTENT:
137 {
138 if (httpParser.parseNext(buffer))
139 return true;
140 remaining = buffer.remaining();
141 break;
142 }
143 default:
144 {
145 throw new IllegalStateException();
146 }
147 }
148 }
149 return false;
150 }
151
152 @Override
153 public int getHeaderCacheSize()
154 {
155
156 return 0;
157 }
158
159 @Override
160 public boolean startResponse(HttpVersion version, int status, String reason)
161 {
162
163 throw new IllegalStateException();
164 }
165
166 @Override
167 public void parsedHeader(HttpField httpField)
168 {
169 try
170 {
171 String name = httpField.getName();
172 if ("Status".equalsIgnoreCase(name))
173 {
174 if (!seenResponseCode)
175 {
176 seenResponseCode = true;
177
178
179
180 String value = httpField.getValue();
181 String[] parts = value.split(" ");
182 String status = parts[0];
183 int code = Integer.parseInt(status);
184 httpParser.setResponseStatus(code);
185 String reason = parts.length > 1 ? value.substring(status.length()) : HttpStatus.getMessage(code);
186
187 notifyBegin(code, reason.trim());
188 notifyHeaders(fields);
189 }
190 }
191 else
192 {
193 fields.add(httpField);
194 if (seenResponseCode)
195 notifyHeader(httpField);
196 }
197 }
198 catch (Throwable x)
199 {
200 if (LOG.isDebugEnabled())
201 LOG.debug("Exception while invoking listener " + listener, x);
202 }
203 }
204
205 private void notifyBegin(int code, String reason)
206 {
207 try
208 {
209 listener.onBegin(request, code, reason);
210 }
211 catch (Throwable x)
212 {
213 if (LOG.isDebugEnabled())
214 LOG.debug("Exception while invoking listener " + listener, x);
215 }
216 }
217
218 private void notifyHeader(HttpField httpField)
219 {
220 try
221 {
222 listener.onHeader(request, httpField);
223 }
224 catch (Throwable x)
225 {
226 if (LOG.isDebugEnabled())
227 LOG.debug("Exception while invoking listener " + listener, x);
228 }
229 }
230
231 private void notifyHeaders(HttpFields fields)
232 {
233 if (fields != null)
234 {
235 for (HttpField field : fields)
236 notifyHeader(field);
237 }
238 }
239
240 private void notifyHeaders()
241 {
242 try
243 {
244 listener.onHeaders(request);
245 }
246 catch (Throwable x)
247 {
248 if (LOG.isDebugEnabled())
249 LOG.debug("Exception while invoking listener " + listener, x);
250 }
251 }
252
253 @Override
254 public boolean headerComplete()
255 {
256 if (!seenResponseCode)
257 {
258
259 notifyBegin(200, "OK");
260 notifyHeaders(fields);
261 }
262 notifyHeaders();
263
264 return true;
265 }
266
267 @Override
268 public boolean content(ByteBuffer buffer)
269 {
270 return notifyContent(buffer);
271 }
272
273 private boolean notifyContent(ByteBuffer buffer)
274 {
275 try
276 {
277 return listener.onContent(request, FCGI.StreamType.STD_OUT, buffer);
278 }
279 catch (Throwable x)
280 {
281 if (LOG.isDebugEnabled())
282 LOG.debug("Exception while invoking listener " + listener, x);
283 return false;
284 }
285 }
286
287 @Override
288 public boolean messageComplete()
289 {
290
291
292 return false;
293 }
294
295 @Override
296 public void earlyEOF()
297 {
298 fail(new EOFException());
299 }
300
301 @Override
302 public void badMessage(int status, String reason)
303 {
304 fail(new BadMessageException(status, reason));
305 }
306
307 protected void fail(Throwable failure)
308 {
309 try
310 {
311 listener.onFailure(request, failure);
312 }
313 catch (Throwable x)
314 {
315 if (LOG.isDebugEnabled())
316 LOG.debug("Exception while invoking listener " + listener, x);
317 }
318 }
319 }
320
321
322 private static class FCGIHttpParser extends HttpParser
323 {
324 private FCGIHttpParser(ResponseHandler handler)
325 {
326 super(handler, 65 * 1024, true);
327 reset();
328 }
329
330 @Override
331 public void reset()
332 {
333 super.reset();
334 setResponseStatus(200);
335 setState(State.HEADER);
336 }
337
338 @Override
339 protected void setResponseStatus(int status)
340 {
341 super.setResponseStatus(status);
342 }
343 }
344
345 private enum State
346 {
347 HEADERS, CONTENT_MODE, RAW_CONTENT, HTTP_CONTENT
348 }
349 }