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 boolean 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 return 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 boolean parse(ByteBuffer buffer)
91 {
92 if (LOG.isDebugEnabled())
93 LOG.debug("Response {} {} content {} {}", request, FCGI.StreamType.STD_OUT, state, buffer);
94
95 int remaining = buffer.remaining();
96 while (remaining > 0)
97 {
98 switch (state)
99 {
100 case HEADERS:
101 {
102 if (httpParser.parseNext(buffer))
103 state = State.CONTENT_MODE;
104 remaining = buffer.remaining();
105 break;
106 }
107 case CONTENT_MODE:
108 {
109
110
111
112
113 boolean rawContent = fields.size() == 0 ||
114 (fields.get(HttpHeader.CONTENT_LENGTH) == null &&
115 fields.get(HttpHeader.TRANSFER_ENCODING) == null);
116 state = rawContent ? State.RAW_CONTENT : State.HTTP_CONTENT;
117 break;
118 }
119 case RAW_CONTENT:
120 {
121 if (notifyContent(buffer))
122 return true;
123 remaining = 0;
124 break;
125 }
126 case HTTP_CONTENT:
127 {
128 if (httpParser.parseNext(buffer))
129 return true;
130 remaining = buffer.remaining();
131 break;
132 }
133 default:
134 {
135 throw new IllegalStateException();
136 }
137 }
138 }
139 return false;
140 }
141
142 @Override
143 public int getHeaderCacheSize()
144 {
145
146 return 0;
147 }
148
149 @Override
150 public boolean startResponse(HttpVersion version, int status, String reason)
151 {
152
153 throw new IllegalStateException();
154 }
155
156 @Override
157 public boolean parsedHeader(HttpField httpField)
158 {
159 try
160 {
161 String name = httpField.getName();
162 if ("Status".equalsIgnoreCase(name))
163 {
164 if (!seenResponseCode)
165 {
166 seenResponseCode = true;
167
168
169
170 String value = httpField.getValue();
171 String[] parts = value.split(" ");
172 String status = parts[0];
173 int code = Integer.parseInt(status);
174 httpParser.setResponseStatus(code);
175 String reason = parts.length > 1 ? value.substring(status.length()) : HttpStatus.getMessage(code);
176
177 notifyBegin(code, reason.trim());
178 notifyHeaders(fields);
179 }
180 }
181 else
182 {
183 fields.add(httpField);
184 if (seenResponseCode)
185 notifyHeader(httpField);
186 }
187 }
188 catch (Throwable x)
189 {
190 if (LOG.isDebugEnabled())
191 LOG.debug("Exception while invoking listener " + listener, x);
192 }
193 return false;
194 }
195
196 private void notifyBegin(int code, String reason)
197 {
198 try
199 {
200 listener.onBegin(request, code, reason);
201 }
202 catch (Throwable x)
203 {
204 if (LOG.isDebugEnabled())
205 LOG.debug("Exception while invoking listener " + listener, x);
206 }
207 }
208
209 private void notifyHeader(HttpField httpField)
210 {
211 try
212 {
213 listener.onHeader(request, httpField);
214 }
215 catch (Throwable x)
216 {
217 if (LOG.isDebugEnabled())
218 LOG.debug("Exception while invoking listener " + listener, x);
219 }
220 }
221
222 private void notifyHeaders(HttpFields fields)
223 {
224 if (fields != null)
225 {
226 for (HttpField field : fields)
227 notifyHeader(field);
228 }
229 }
230
231 private void notifyHeaders()
232 {
233 try
234 {
235 listener.onHeaders(request);
236 }
237 catch (Throwable x)
238 {
239 if (LOG.isDebugEnabled())
240 LOG.debug("Exception while invoking listener " + listener, x);
241 }
242 }
243
244 @Override
245 public boolean headerComplete()
246 {
247 if (!seenResponseCode)
248 {
249
250 notifyBegin(200, "OK");
251 notifyHeaders(fields);
252 }
253 notifyHeaders();
254
255 return true;
256 }
257
258 @Override
259 public boolean content(ByteBuffer buffer)
260 {
261 return notifyContent(buffer);
262 }
263
264 private boolean notifyContent(ByteBuffer buffer)
265 {
266 try
267 {
268 return listener.onContent(request, FCGI.StreamType.STD_OUT, buffer);
269 }
270 catch (Throwable x)
271 {
272 if (LOG.isDebugEnabled())
273 LOG.debug("Exception while invoking listener " + listener, x);
274 return false;
275 }
276 }
277
278 @Override
279 public boolean messageComplete()
280 {
281
282
283 return true;
284 }
285
286 @Override
287 public void earlyEOF()
288 {
289
290 }
291
292 @Override
293 public void badMessage(int status, String reason)
294 {
295
296 }
297 }
298
299
300 private static class FCGIHttpParser extends HttpParser
301 {
302 private FCGIHttpParser(ResponseHandler<ByteBuffer> handler)
303 {
304 super(handler, 65 * 1024, true);
305 reset();
306 }
307
308 @Override
309 public void reset()
310 {
311 super.reset();
312 setResponseStatus(200);
313 setState(State.HEADER);
314 }
315
316 @Override
317 protected void setResponseStatus(int status)
318 {
319 super.setResponseStatus(status);
320 }
321 }
322
323 private enum State
324 {
325 HEADERS, CONTENT_MODE, RAW_CONTENT, HTTP_CONTENT
326 }
327 }