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