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.nio.ByteBuffer;
22 import java.util.concurrent.atomic.AtomicReference;
23
24 import org.eclipse.jetty.client.api.ContentProvider;
25 import org.eclipse.jetty.client.api.Request;
26 import org.eclipse.jetty.client.api.Result;
27 import org.eclipse.jetty.http.HttpHeader;
28 import org.eclipse.jetty.http.HttpHeaderValue;
29 import org.eclipse.jetty.util.BufferUtil;
30 import org.eclipse.jetty.util.Callback;
31 import org.eclipse.jetty.util.IteratingCallback;
32 import org.eclipse.jetty.util.log.Log;
33 import org.eclipse.jetty.util.log.Logger;
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public abstract class HttpSender implements AsyncContentProvider.Listener
59 {
60 protected static final Logger LOG = Log.getLogger(HttpSender.class);
61
62 private final AtomicReference<RequestState> requestState = new AtomicReference<>(RequestState.QUEUED);
63 private final AtomicReference<SenderState> senderState = new AtomicReference<>(SenderState.IDLE);
64 private final Callback commitCallback = new CommitCallback();
65 private final IteratingCallback contentCallback = new ContentCallback();
66 private final Callback lastCallback = new LastContentCallback();
67 private final HttpChannel channel;
68 private HttpContent content;
69 private Throwable failure;
70
71 protected HttpSender(HttpChannel channel)
72 {
73 this.channel = channel;
74 }
75
76 protected HttpChannel getHttpChannel()
77 {
78 return channel;
79 }
80
81 protected HttpExchange getHttpExchange()
82 {
83 return channel.getHttpExchange();
84 }
85
86 @Override
87 public void onContent()
88 {
89 HttpExchange exchange = getHttpExchange();
90 if (exchange == null)
91 return;
92
93 while (true)
94 {
95 SenderState current = senderState.get();
96 switch (current)
97 {
98 case IDLE:
99 {
100 SenderState newSenderState = SenderState.SENDING;
101 if (updateSenderState(current, newSenderState))
102 {
103 if (LOG.isDebugEnabled())
104 LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
105 contentCallback.iterate();
106 return;
107 }
108 break;
109 }
110 case SENDING:
111 {
112 SenderState newSenderState = SenderState.SENDING_WITH_CONTENT;
113 if (updateSenderState(current, newSenderState))
114 {
115 if (LOG.isDebugEnabled())
116 LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
117 return;
118 }
119 break;
120 }
121 case EXPECTING:
122 {
123 SenderState newSenderState = SenderState.EXPECTING_WITH_CONTENT;
124 if (updateSenderState(current, newSenderState))
125 {
126 if (LOG.isDebugEnabled())
127 LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
128 return;
129 }
130 break;
131 }
132 case PROCEEDING:
133 {
134 SenderState newSenderState = SenderState.PROCEEDING_WITH_CONTENT;
135 if (updateSenderState(current, newSenderState))
136 {
137 if (LOG.isDebugEnabled())
138 LOG.debug("Deferred content available, {} -> {}", current, newSenderState);
139 return;
140 }
141 break;
142 }
143 case SENDING_WITH_CONTENT:
144 case EXPECTING_WITH_CONTENT:
145 case PROCEEDING_WITH_CONTENT:
146 case WAITING:
147 {
148 if (LOG.isDebugEnabled())
149 LOG.debug("Deferred content available, {}", current);
150 return;
151 }
152 default:
153 {
154 throw illegalSenderState(current);
155 }
156 }
157 }
158 }
159
160 public void send(HttpExchange exchange)
161 {
162 Request request = exchange.getRequest();
163 if (!queuedToBegin(request))
164 return;
165
166 ContentProvider contentProvider = request.getContent();
167 HttpContent content = this.content = new HttpContent(contentProvider);
168
169 SenderState newSenderState = SenderState.SENDING;
170 if (expects100Continue(request))
171 newSenderState = content.hasContent() ? SenderState.EXPECTING_WITH_CONTENT : SenderState.EXPECTING;
172 if (!updateSenderState(SenderState.IDLE, newSenderState))
173 throw illegalSenderState(SenderState.IDLE);
174
175
176
177 if (contentProvider instanceof AsyncContentProvider)
178 ((AsyncContentProvider)contentProvider).setListener(this);
179
180 if (!beginToHeaders(request))
181 return;
182
183 sendHeaders(exchange, content, commitCallback);
184 }
185
186 protected boolean expects100Continue(Request request)
187 {
188 return request.getHeaders().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString());
189 }
190
191 protected boolean queuedToBegin(Request request)
192 {
193 if (!updateRequestState(RequestState.QUEUED, RequestState.TRANSIENT))
194 return false;
195 if (LOG.isDebugEnabled())
196 LOG.debug("Request begin {}", request);
197 RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier();
198 notifier.notifyBegin(request);
199 if (!updateRequestState(RequestState.TRANSIENT, RequestState.BEGIN))
200 terminateRequest(getHttpExchange(), failure);
201 return true;
202 }
203
204 protected boolean beginToHeaders(Request request)
205 {
206 if (!updateRequestState(RequestState.BEGIN, RequestState.TRANSIENT))
207 return false;
208 if (LOG.isDebugEnabled())
209 LOG.debug("Request headers {}{}{}", request, System.getProperty("line.separator"), request.getHeaders().toString().trim());
210 RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier();
211 notifier.notifyHeaders(request);
212 if (!updateRequestState(RequestState.TRANSIENT, RequestState.HEADERS))
213 terminateRequest(getHttpExchange(), failure);
214 return true;
215 }
216
217 protected boolean headersToCommit(Request request)
218 {
219 if (!updateRequestState(RequestState.HEADERS, RequestState.TRANSIENT))
220 return false;
221 if (LOG.isDebugEnabled())
222 LOG.debug("Request committed {}", request);
223 RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier();
224 notifier.notifyCommit(request);
225 if (!updateRequestState(RequestState.TRANSIENT, RequestState.COMMIT))
226 terminateRequest(getHttpExchange(), failure);
227 return true;
228 }
229
230 protected boolean someToContent(Request request, ByteBuffer content)
231 {
232 RequestState current = requestState.get();
233 switch (current)
234 {
235 case COMMIT:
236 case CONTENT:
237 {
238 if (!updateRequestState(current, RequestState.TRANSIENT_CONTENT))
239 return false;
240 if (LOG.isDebugEnabled())
241 LOG.debug("Request content {}{}{}", request, System.getProperty("line.separator"), BufferUtil.toDetailString(content));
242 RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier();
243 notifier.notifyContent(request, content);
244 if (!updateRequestState(RequestState.TRANSIENT_CONTENT, RequestState.CONTENT))
245 terminateRequest(getHttpExchange(), failure);
246 return true;
247 }
248 default:
249 {
250 return false;
251 }
252 }
253 }
254
255 protected boolean someToSuccess(HttpExchange exchange)
256 {
257 RequestState current = requestState.get();
258 switch (current)
259 {
260 case COMMIT:
261 case CONTENT:
262 {
263
264
265 boolean completed = exchange.requestComplete();
266 if (!completed)
267 return false;
268
269 requestState.set(RequestState.QUEUED);
270
271
272 reset();
273
274
275
276 Result result = exchange.terminateRequest(null);
277
278 Request request = exchange.getRequest();
279 if (LOG.isDebugEnabled())
280 LOG.debug("Request success {}", request);
281 HttpDestination destination = getHttpChannel().getHttpDestination();
282 destination.getRequestNotifier().notifySuccess(exchange.getRequest());
283
284 terminateRequest(exchange, null, result);
285
286 return true;
287 }
288 default:
289 {
290 return false;
291 }
292 }
293 }
294
295 protected boolean anyToFailure(Throwable failure)
296 {
297 HttpExchange exchange = getHttpExchange();
298 if (exchange == null)
299 return false;
300
301
302
303 boolean completed = exchange.requestComplete();
304 if (!completed)
305 return false;
306
307 this.failure = failure;
308
309
310 RequestState current;
311 boolean fail;
312 while (true)
313 {
314 current = requestState.get();
315 if (updateRequestState(current, RequestState.FAILURE))
316 {
317 fail = current != RequestState.TRANSIENT && current != RequestState.TRANSIENT_CONTENT;
318 break;
319 }
320 }
321
322 dispose();
323
324
325
326 Result result = exchange.terminateRequest(failure);
327
328 Request request = exchange.getRequest();
329 if (LOG.isDebugEnabled())
330 LOG.debug("Request failure {} {}", exchange, failure);
331 HttpDestination destination = getHttpChannel().getHttpDestination();
332 destination.getRequestNotifier().notifyFailure(request, failure);
333
334 if (fail)
335 {
336 terminateRequest(exchange, failure, result);
337 }
338 else
339 {
340 if (LOG.isDebugEnabled())
341 LOG.debug("Concurrent failure: request termination skipped, performed by helpers");
342 }
343
344 return true;
345 }
346
347 private void terminateRequest(HttpExchange exchange, Throwable failure)
348 {
349 if (exchange != null)
350 {
351 Result result = exchange.terminateRequest(failure);
352 terminateRequest(exchange, failure, result);
353 }
354 }
355
356 private void terminateRequest(HttpExchange exchange, Throwable failure, Result result)
357 {
358 Request request = exchange.getRequest();
359
360 if (LOG.isDebugEnabled())
361 LOG.debug("Terminating request {}", request);
362
363 if (result == null)
364 {
365 if (failure != null)
366 {
367 if (LOG.isDebugEnabled())
368 LOG.debug("Response failure from request {}", exchange);
369 getHttpChannel().abortResponse(failure);
370 }
371 }
372 else
373 {
374 HttpDestination destination = getHttpChannel().getHttpDestination();
375 boolean ordered = destination.getHttpClient().isStrictEventOrdering();
376 if (!ordered)
377 channel.exchangeTerminated(result);
378 if (LOG.isDebugEnabled())
379 LOG.debug("Request/Response {} {}", failure == null ? "succeeded" : "failed", request);
380 HttpConversation conversation = exchange.getConversation();
381 destination.getResponseNotifier().notifyComplete(conversation.getResponseListeners(), result);
382 if (ordered)
383 channel.exchangeTerminated(result);
384 }
385 }
386
387
388
389
390
391
392
393
394
395
396
397
398 protected abstract void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback);
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416 protected abstract void sendContent(HttpExchange exchange, HttpContent content, Callback callback);
417
418 protected void reset()
419 {
420 content.close();
421 content = null;
422 senderState.set(SenderState.IDLE);
423 }
424
425 protected void dispose()
426 {
427 HttpContent content = this.content;
428 if (content != null)
429 content.close();
430 }
431
432 public void proceed(HttpExchange exchange, Throwable failure)
433 {
434 if (!expects100Continue(exchange.getRequest()))
435 return;
436
437 if (failure != null)
438 {
439 anyToFailure(failure);
440 return;
441 }
442
443 while (true)
444 {
445 SenderState current = senderState.get();
446 switch (current)
447 {
448 case EXPECTING:
449 {
450
451 if (updateSenderState(current, SenderState.PROCEEDING))
452 {
453 if (LOG.isDebugEnabled())
454 LOG.debug("Proceeding while expecting");
455 return;
456 }
457 break;
458 }
459 case EXPECTING_WITH_CONTENT:
460 {
461
462
463
464
465
466
467 if (updateSenderState(current, SenderState.PROCEEDING_WITH_CONTENT))
468 {
469 if (LOG.isDebugEnabled())
470 LOG.debug("Proceeding while scheduled");
471 return;
472 }
473 break;
474 }
475 case WAITING:
476 {
477
478 if (!updateSenderState(current, SenderState.SENDING))
479 throw illegalSenderState(current);
480 if (LOG.isDebugEnabled())
481 LOG.debug("Proceeding while waiting");
482 contentCallback.iterate();
483 return;
484 }
485 default:
486 {
487 throw illegalSenderState(current);
488 }
489 }
490 }
491 }
492
493 public boolean abort(Throwable failure)
494 {
495 RequestState current = requestState.get();
496 boolean abortable = isBeforeCommit(current) || isSending(current);
497 return abortable && anyToFailure(failure);
498 }
499
500 private boolean updateRequestState(RequestState from, RequestState to)
501 {
502 boolean updated = requestState.compareAndSet(from, to);
503 if (!updated)
504 LOG.debug("RequestState update failed: {} -> {}: {}", from, to, requestState.get());
505 return updated;
506 }
507
508 private boolean updateSenderState(SenderState from, SenderState to)
509 {
510 boolean updated = senderState.compareAndSet(from, to);
511 if (!updated)
512 LOG.debug("SenderState update failed: {} -> {}: {}", from, to, senderState.get());
513 return updated;
514 }
515
516 private boolean isBeforeCommit(RequestState requestState)
517 {
518 switch (requestState)
519 {
520 case TRANSIENT:
521 case QUEUED:
522 case BEGIN:
523 case HEADERS:
524 return true;
525 default:
526 return false;
527 }
528 }
529
530 private boolean isSending(RequestState requestState)
531 {
532 switch (requestState)
533 {
534 case TRANSIENT_CONTENT:
535 case COMMIT:
536 case CONTENT:
537 return true;
538 default:
539 return false;
540 }
541 }
542
543 private RuntimeException illegalSenderState(SenderState current)
544 {
545 return new IllegalStateException("Expected " + current + " found " + senderState.get() + " instead");
546 }
547
548 @Override
549 public String toString()
550 {
551 return String.format("%s@%x(req=%s,snd=%s)",
552 getClass().getSimpleName(),
553 hashCode(),
554 requestState,
555 senderState);
556 }
557
558
559
560
561 private enum RequestState
562 {
563
564
565
566 TRANSIENT,
567
568
569
570 TRANSIENT_CONTENT,
571
572
573
574 QUEUED,
575
576
577
578 BEGIN,
579
580
581
582 HEADERS,
583
584
585
586 COMMIT,
587
588
589
590 CONTENT,
591
592
593
594 FAILURE
595 }
596
597
598
599
600 private enum SenderState
601 {
602
603
604
605 IDLE,
606
607
608
609 SENDING,
610
611
612
613 SENDING_WITH_CONTENT,
614
615
616
617 EXPECTING,
618
619
620
621 EXPECTING_WITH_CONTENT,
622
623
624
625 WAITING,
626
627
628
629 PROCEEDING,
630
631
632
633 PROCEEDING_WITH_CONTENT
634 }
635
636 private class CommitCallback implements Callback
637 {
638 @Override
639 public void succeeded()
640 {
641 try
642 {
643 content.succeeded();
644 process();
645 }
646
647 catch (Exception x)
648 {
649 anyToFailure(x);
650 }
651 }
652
653 @Override
654 public void failed(Throwable failure)
655 {
656 content.failed(failure);
657 anyToFailure(failure);
658 }
659
660 private void process() throws Exception
661 {
662 HttpExchange exchange = getHttpExchange();
663 if (exchange == null)
664 return;
665
666 Request request = exchange.getRequest();
667 if (!headersToCommit(request))
668 return;
669
670 HttpContent content = HttpSender.this.content;
671
672 if (!content.hasContent())
673 {
674
675 someToSuccess(exchange);
676 }
677 else
678 {
679
680 ByteBuffer contentBuffer = content.getContent();
681 if (contentBuffer != null)
682 {
683 if (!someToContent(request, contentBuffer))
684 return;
685 }
686
687 while (true)
688 {
689 SenderState current = senderState.get();
690 switch (current)
691 {
692 case SENDING:
693 {
694 contentCallback.iterate();
695 return;
696 }
697 case SENDING_WITH_CONTENT:
698 {
699
700 updateSenderState(current, SenderState.SENDING);
701 break;
702 }
703 case EXPECTING:
704 {
705
706 if (updateSenderState(current, SenderState.WAITING))
707 return;
708 break;
709 }
710 case EXPECTING_WITH_CONTENT:
711 {
712
713
714 if (updateSenderState(current, SenderState.WAITING))
715 return;
716 break;
717 }
718 case PROCEEDING:
719 {
720
721
722 if (updateSenderState(current, SenderState.IDLE))
723 return;
724 break;
725 }
726 case PROCEEDING_WITH_CONTENT:
727 {
728
729
730 updateSenderState(current, SenderState.SENDING);
731 break;
732 }
733 default:
734 {
735 throw illegalSenderState(current);
736 }
737 }
738 }
739 }
740 }
741 }
742
743 private class ContentCallback extends IteratingCallback
744 {
745 @Override
746 protected Action process() throws Exception
747 {
748 HttpExchange exchange = getHttpExchange();
749 if (exchange == null)
750 return Action.IDLE;
751
752 HttpContent content = HttpSender.this.content;
753 while (true)
754 {
755 boolean advanced = content.advance();
756 boolean consumed = content.isConsumed();
757 if (LOG.isDebugEnabled())
758 LOG.debug("Content {} consumed {} for {}", advanced, consumed, exchange.getRequest());
759
760 if (advanced)
761 {
762 sendContent(exchange, content, this);
763 return Action.SCHEDULED;
764 }
765
766 if (consumed)
767 {
768 sendContent(exchange, content, lastCallback);
769 return Action.IDLE;
770 }
771
772 SenderState current = HttpSender.this.senderState.get();
773 switch (current)
774 {
775 case SENDING:
776 {
777 if (updateSenderState(current, SenderState.IDLE))
778 {
779 if (LOG.isDebugEnabled())
780 LOG.debug("Content is deferred for {}", exchange.getRequest());
781 return Action.IDLE;
782 }
783 break;
784 }
785 case SENDING_WITH_CONTENT:
786 {
787 updateSenderState(current, SenderState.SENDING);
788 break;
789 }
790 default:
791 {
792 throw illegalSenderState(current);
793 }
794 }
795 }
796 }
797
798 @Override
799 public void succeeded()
800 {
801 ByteBuffer buffer = content.getContent();
802 someToContent(getHttpExchange().getRequest(), buffer);
803 content.succeeded();
804 super.succeeded();
805 }
806
807 @Override
808 public void onCompleteFailure(Throwable failure)
809 {
810 content.failed(failure);
811 anyToFailure(failure);
812 }
813
814 @Override
815 protected void onCompleteSuccess()
816 {
817
818
819 }
820 }
821
822 private class LastContentCallback implements Callback
823 {
824 @Override
825 public void succeeded()
826 {
827 content.succeeded();
828 HttpExchange exchange = getHttpExchange();
829 if (exchange == null)
830 return;
831 someToSuccess(exchange);
832 }
833
834 @Override
835 public void failed(Throwable failure)
836 {
837 content.failed(failure);
838 anyToFailure(failure);
839 }
840 }
841 }