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