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