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
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 void 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 }
194
195 private void notifyBegin(int code, String reason)
196 {
197 try
198 {
199 listener.onBegin(request, code, reason);
200 }
201 catch (Throwable x)
202 {
203 if (LOG.isDebugEnabled())
204 LOG.debug("Exception while invoking listener " + listener, x);
205 }
206 }
207
208 private void notifyHeader(HttpField httpField)
209 {
210 try
211 {
212 listener.onHeader(request, httpField);
213 }
214 catch (Throwable x)
215 {
216 if (LOG.isDebugEnabled())
217 LOG.debug("Exception while invoking listener " + listener, x);
218 }
219 }
220
221 private void notifyHeaders(HttpFields fields)
222 {
223 if (fields != null)
224 {
225 for (HttpField field : fields)
226 notifyHeader(field);
227 }
228 }
229
230 private void notifyHeaders()
231 {
232 try
233 {
234 listener.onHeaders(request);
235 }
236 catch (Throwable x)
237 {
238 if (LOG.isDebugEnabled())
239 LOG.debug("Exception while invoking listener " + listener, x);
240 }
241 }
242
243 @Override
244 public boolean headerComplete()
245 {
246 if (!seenResponseCode)
247 {
248
249 notifyBegin(200, "OK");
250 notifyHeaders(fields);
251 }
252 notifyHeaders();
253
254 return true;
255 }
256
257 @Override
258 public boolean content(ByteBuffer buffer)
259 {
260 return notifyContent(buffer);
261 }
262
263 private boolean notifyContent(ByteBuffer buffer)
264 {
265 try
266 {
267 return listener.onContent(request, FCGI.StreamType.STD_OUT, buffer);
268 }
269 catch (Throwable x)
270 {
271 if (LOG.isDebugEnabled())
272 LOG.debug("Exception while invoking listener " + listener, x);
273 return false;
274 }
275 }
276
277 @Override
278 public boolean messageComplete()
279 {
280
281
282 return true;
283 }
284
285 @Override
286 public void earlyEOF()
287 {
288
289 }
290
291 @Override
292 public void badMessage(int status, String reason)
293 {
294
295 }
296 }
297
298
299 private static class FCGIHttpParser extends HttpParser
300 {
301 private FCGIHttpParser(ResponseHandler handler)
302 {
303 super(handler, 65 * 1024, true);
304 reset();
305 }
306
307 @Override
308 public void reset()
309 {
310 super.reset();
311 setResponseStatus(200);
312 setState(State.HEADER);
313 }
314
315 @Override
316 protected void setResponseStatus(int status)
317 {
318 super.setResponseStatus(status);
319 }
320 }
321
322 private enum State
323 {
324 HEADERS, CONTENT_MODE, RAW_CONTENT, HTTP_CONTENT
325 }
326 }