1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.client;
20
21 import java.io.IOException;
22 import java.net.URI;
23 import java.nio.ByteBuffer;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Enumeration;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.concurrent.atomic.AtomicReference;
31
32 import org.eclipse.jetty.client.api.Response;
33 import org.eclipse.jetty.client.api.Result;
34 import org.eclipse.jetty.http.HttpField;
35 import org.eclipse.jetty.http.HttpHeader;
36 import org.eclipse.jetty.http.HttpStatus;
37 import org.eclipse.jetty.util.BufferUtil;
38 import org.eclipse.jetty.util.Callback;
39 import org.eclipse.jetty.util.CountingCallback;
40 import org.eclipse.jetty.util.log.Log;
41 import org.eclipse.jetty.util.log.Logger;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68 public abstract class HttpReceiver
69 {
70 protected static final Logger LOG = Log.getLogger(HttpReceiver.class);
71
72 private final AtomicReference<ResponseState> responseState = new AtomicReference<>(ResponseState.IDLE);
73 private final HttpChannel channel;
74 private ContentDecoder decoder;
75 private Throwable failure;
76
77 protected HttpReceiver(HttpChannel channel)
78 {
79 this.channel = channel;
80 }
81
82 protected HttpChannel getHttpChannel()
83 {
84 return channel;
85 }
86
87 protected HttpExchange getHttpExchange()
88 {
89 return channel.getHttpExchange();
90 }
91
92 protected HttpDestination getHttpDestination()
93 {
94 return channel.getHttpDestination();
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108 protected boolean responseBegin(HttpExchange exchange)
109 {
110 if (!updateResponseState(ResponseState.IDLE, ResponseState.TRANSIENT))
111 return false;
112
113 HttpConversation conversation = exchange.getConversation();
114 HttpResponse response = exchange.getResponse();
115
116 HttpDestination destination = getHttpDestination();
117 HttpClient client = destination.getHttpClient();
118 ProtocolHandler protocolHandler = client.findProtocolHandler(exchange.getRequest(), response);
119 Response.Listener handlerListener = null;
120 if (protocolHandler != null)
121 {
122 handlerListener = protocolHandler.getResponseListener();
123 if (LOG.isDebugEnabled())
124 LOG.debug("Found protocol handler {}", protocolHandler);
125 }
126 exchange.getConversation().updateResponseListeners(handlerListener);
127
128 if (LOG.isDebugEnabled())
129 LOG.debug("Response begin {}", response);
130 ResponseNotifier notifier = destination.getResponseNotifier();
131 notifier.notifyBegin(conversation.getResponseListeners(), response);
132
133 if (updateResponseState(ResponseState.TRANSIENT, ResponseState.BEGIN))
134 return true;
135
136 terminateResponse(exchange);
137 return false;
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152 protected boolean responseHeader(HttpExchange exchange, HttpField field)
153 {
154 out: while (true)
155 {
156 ResponseState current = responseState.get();
157 switch (current)
158 {
159 case BEGIN:
160 case HEADER:
161 {
162 if (updateResponseState(current, ResponseState.TRANSIENT))
163 break out;
164 break;
165 }
166 default:
167 {
168 return false;
169 }
170 }
171 }
172
173 HttpResponse response = exchange.getResponse();
174 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
175 boolean process = notifier.notifyHeader(exchange.getConversation().getResponseListeners(), response, field);
176 if (process)
177 {
178 response.getHeaders().add(field);
179 HttpHeader fieldHeader = field.getHeader();
180 if (fieldHeader != null)
181 {
182 switch (fieldHeader)
183 {
184 case SET_COOKIE:
185 case SET_COOKIE2:
186 {
187 storeCookie(exchange.getRequest().getURI(), field);
188 break;
189 }
190 default:
191 {
192 break;
193 }
194 }
195 }
196 }
197
198 if (updateResponseState(ResponseState.TRANSIENT, ResponseState.HEADER))
199 return true;
200
201 terminateResponse(exchange);
202 return false;
203 }
204
205 protected void storeCookie(URI uri, HttpField field)
206 {
207 try
208 {
209 String value = field.getValue();
210 if (value != null)
211 {
212 Map<String, List<String>> header = new HashMap<>(1);
213 header.put(field.getHeader().asString(), Collections.singletonList(value));
214 getHttpDestination().getHttpClient().getCookieManager().put(uri, header);
215 }
216 }
217 catch (IOException x)
218 {
219 if (LOG.isDebugEnabled())
220 LOG.debug(x);
221 }
222 }
223
224
225
226
227
228
229
230
231
232 protected boolean responseHeaders(HttpExchange exchange)
233 {
234 out: while (true)
235 {
236 ResponseState current = responseState.get();
237 switch (current)
238 {
239 case BEGIN:
240 case HEADER:
241 {
242 if (updateResponseState(current, ResponseState.TRANSIENT))
243 break out;
244 break;
245 }
246 default:
247 {
248 return false;
249 }
250 }
251 }
252
253 HttpResponse response = exchange.getResponse();
254 if (LOG.isDebugEnabled())
255 LOG.debug("Response headers {}{}{}", response, System.lineSeparator(), response.getHeaders().toString().trim());
256 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
257 notifier.notifyHeaders(exchange.getConversation().getResponseListeners(), response);
258
259 Enumeration<String> contentEncodings = response.getHeaders().getValues(HttpHeader.CONTENT_ENCODING.asString(), ",");
260 if (contentEncodings != null)
261 {
262 for (ContentDecoder.Factory factory : getHttpDestination().getHttpClient().getContentDecoderFactories())
263 {
264 while (contentEncodings.hasMoreElements())
265 {
266 if (factory.getEncoding().equalsIgnoreCase(contentEncodings.nextElement()))
267 {
268 this.decoder = factory.newContentDecoder();
269 break;
270 }
271 }
272 }
273 }
274
275 if (updateResponseState(ResponseState.TRANSIENT, ResponseState.HEADERS))
276 return true;
277
278 terminateResponse(exchange);
279 return false;
280 }
281
282
283
284
285
286
287
288
289
290
291 protected boolean responseContent(HttpExchange exchange, ByteBuffer buffer, final Callback callback)
292 {
293 out: while (true)
294 {
295 ResponseState current = responseState.get();
296 switch (current)
297 {
298 case HEADERS:
299 case CONTENT:
300 {
301 if (updateResponseState(current, ResponseState.TRANSIENT))
302 break out;
303 break;
304 }
305 default:
306 {
307 return false;
308 }
309 }
310 }
311
312 HttpResponse response = exchange.getResponse();
313 if (LOG.isDebugEnabled())
314 LOG.debug("Response content {}{}{}", response, System.lineSeparator(), BufferUtil.toDetailString(buffer));
315
316 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
317 List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
318
319 ContentDecoder decoder = this.decoder;
320 if (decoder == null)
321 {
322 notifier.notifyContent(listeners, response, buffer, callback);
323 }
324 else
325 {
326 List<ByteBuffer> decodeds = new ArrayList<>(2);
327 while (buffer.hasRemaining())
328 {
329 ByteBuffer decoded = decoder.decode(buffer);
330 if (!decoded.hasRemaining())
331 continue;
332 decodeds.add(decoded);
333 if (LOG.isDebugEnabled())
334 LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded));
335 }
336
337 if (decodeds.isEmpty())
338 {
339 callback.succeeded();
340 }
341 else
342 {
343 int size = decodeds.size();
344 CountingCallback counter = new CountingCallback(callback, size);
345 for (int i = 0; i < size; ++i)
346 notifier.notifyContent(listeners, response, decodeds.get(i), counter);
347 }
348 }
349
350 if (updateResponseState(ResponseState.TRANSIENT, ResponseState.CONTENT))
351 return true;
352
353 terminateResponse(exchange);
354 return false;
355 }
356
357
358
359
360
361
362
363
364
365
366 protected boolean responseSuccess(HttpExchange exchange)
367 {
368
369
370 if (!exchange.responseComplete(null))
371 return false;
372
373 responseState.set(ResponseState.IDLE);
374
375
376 reset();
377
378 HttpResponse response = exchange.getResponse();
379 if (LOG.isDebugEnabled())
380 LOG.debug("Response success {}", response);
381 List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
382 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
383 notifier.notifySuccess(listeners, response);
384
385
386
387 if (exchange.getResponse().getStatus() == HttpStatus.CONTINUE_100)
388 return true;
389
390
391
392 Result result = exchange.terminateResponse();
393 terminateResponse(exchange, result);
394
395 return true;
396 }
397
398
399
400
401
402
403
404
405
406 protected boolean responseFailure(Throwable failure)
407 {
408 HttpExchange exchange = getHttpExchange();
409
410
411
412
413 if (exchange == null)
414 return false;
415
416
417
418 if (exchange.responseComplete(failure))
419 return abort(exchange, failure);
420
421 return false;
422 }
423
424 private void terminateResponse(HttpExchange exchange)
425 {
426 Result result = exchange.terminateResponse();
427 terminateResponse(exchange, result);
428 }
429
430 private void terminateResponse(HttpExchange exchange, Result result)
431 {
432 HttpResponse response = exchange.getResponse();
433
434 if (LOG.isDebugEnabled())
435 LOG.debug("Response complete {}", response);
436
437 if (result != null)
438 {
439 boolean ordered = getHttpDestination().getHttpClient().isStrictEventOrdering();
440 if (!ordered)
441 channel.exchangeTerminated(exchange, result);
442 if (LOG.isDebugEnabled())
443 LOG.debug("Request/Response {}: {}", failure == null ? "succeeded" : "failed", result);
444 List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
445 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
446 notifier.notifyComplete(listeners, result);
447 if (ordered)
448 channel.exchangeTerminated(exchange, result);
449 }
450 }
451
452
453
454
455
456
457
458
459 protected void reset()
460 {
461 decoder = null;
462 }
463
464
465
466
467
468
469
470
471 protected void dispose()
472 {
473 decoder = null;
474 }
475
476 public boolean abort(HttpExchange exchange, Throwable failure)
477 {
478
479 boolean terminate;
480 out: while (true)
481 {
482 ResponseState current = responseState.get();
483 switch (current)
484 {
485 case FAILURE:
486 {
487 return false;
488 }
489 default:
490 {
491 if (updateResponseState(current, ResponseState.FAILURE))
492 {
493 terminate = current != ResponseState.TRANSIENT;
494 break out;
495 }
496 break;
497 }
498 }
499 }
500
501 this.failure = failure;
502
503 dispose();
504
505 HttpResponse response = exchange.getResponse();
506 if (LOG.isDebugEnabled())
507 LOG.debug("Response failure {} {} on {}: {}", response, exchange, getHttpChannel(), failure);
508 List<Response.ResponseListener> listeners = exchange.getConversation().getResponseListeners();
509 ResponseNotifier notifier = getHttpDestination().getResponseNotifier();
510 notifier.notifyFailure(listeners, response, failure);
511
512 if (terminate)
513 {
514
515
516 Result result = exchange.terminateResponse();
517 terminateResponse(exchange, result);
518 }
519 else
520 {
521 if (LOG.isDebugEnabled())
522 LOG.debug("Concurrent failure: response termination skipped, performed by helpers");
523 }
524
525 return true;
526 }
527
528 private boolean updateResponseState(ResponseState from, ResponseState to)
529 {
530 boolean updated = responseState.compareAndSet(from, to);
531 if (!updated)
532 {
533 if (LOG.isDebugEnabled())
534 LOG.debug("State update failed: {} -> {}: {}", from, to, responseState.get());
535 }
536 return updated;
537 }
538
539 @Override
540 public String toString()
541 {
542 return String.format("%s@%x(rsp=%s,failure=%s)",
543 getClass().getSimpleName(),
544 hashCode(),
545 responseState,
546 failure);
547 }
548
549
550
551
552 private enum ResponseState
553 {
554
555
556
557 TRANSIENT,
558
559
560
561 IDLE,
562
563
564
565 BEGIN,
566
567
568
569 HEADER,
570
571
572
573 HEADERS,
574
575
576
577 CONTENT,
578
579
580
581 FAILURE
582 }
583 }