1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.client;
15
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.net.URI;
19 import java.util.concurrent.atomic.AtomicInteger;
20
21 import org.eclipse.jetty.client.security.SecurityListener;
22 import org.eclipse.jetty.http.HttpFields;
23 import org.eclipse.jetty.http.HttpHeaders;
24 import org.eclipse.jetty.http.HttpMethods;
25 import org.eclipse.jetty.http.HttpSchemes;
26 import org.eclipse.jetty.http.HttpURI;
27 import org.eclipse.jetty.http.HttpVersions;
28 import org.eclipse.jetty.io.Buffer;
29 import org.eclipse.jetty.io.BufferCache.CachedBuffer;
30 import org.eclipse.jetty.io.ByteArrayBuffer;
31 import org.eclipse.jetty.io.Connection;
32 import org.eclipse.jetty.io.EndPoint;
33 import org.eclipse.jetty.util.log.Log;
34 import org.eclipse.jetty.util.log.Logger;
35 import org.eclipse.jetty.util.thread.Timeout;
36
37
38
39
40
41
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
69 public class HttpExchange
70 {
71 private static final Logger LOG = Log.getLogger(HttpExchange.class);
72
73 public static final int STATUS_START = 0;
74 public static final int STATUS_WAITING_FOR_CONNECTION = 1;
75 public static final int STATUS_WAITING_FOR_COMMIT = 2;
76 public static final int STATUS_SENDING_REQUEST = 3;
77 public static final int STATUS_WAITING_FOR_RESPONSE = 4;
78 public static final int STATUS_PARSING_HEADERS = 5;
79 public static final int STATUS_PARSING_CONTENT = 6;
80 public static final int STATUS_COMPLETED = 7;
81 public static final int STATUS_EXPIRED = 8;
82 public static final int STATUS_EXCEPTED = 9;
83 public static final int STATUS_CANCELLING = 10;
84 public static final int STATUS_CANCELLED = 11;
85
86
87 private String _method = HttpMethods.GET;
88 private Buffer _scheme = HttpSchemes.HTTP_BUFFER;
89 private String _uri;
90 private int _version = HttpVersions.HTTP_1_1_ORDINAL;
91 private Address _address;
92 private final HttpFields _requestFields = new HttpFields();
93 private Buffer _requestContent;
94 private InputStream _requestContentSource;
95
96 private AtomicInteger _status = new AtomicInteger(STATUS_START);
97 private Buffer _requestContentChunk;
98 private boolean _retryStatus = false;
99
100 private boolean _configureListeners = true;
101 private HttpEventListener _listener = new Listener();
102 private volatile HttpConnection _connection;
103
104 private Address _localAddress = null;
105
106
107 private long _timeout = -1;
108 private volatile Timeout.Task _timeoutTask;
109
110 private long _lastStateChange=-1;
111 private long _sent=-1;
112
113 boolean _onRequestCompleteDone;
114 boolean _onResponseCompleteDone;
115 boolean _onDone;
116
117 protected void expire(HttpDestination destination)
118 {
119 if (getStatus() < HttpExchange.STATUS_COMPLETED)
120 setStatus(HttpExchange.STATUS_EXPIRED);
121
122 destination.exchangeExpired(this);
123 HttpConnection connection = _connection;
124 if (connection != null)
125 connection.exchangeExpired(this);
126 }
127
128 public int getStatus()
129 {
130 return _status.get();
131 }
132
133
134
135
136
137
138 @Deprecated
139 public void waitForStatus(int status) throws InterruptedException
140 {
141 throw new UnsupportedOperationException();
142 }
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public int waitForDone () throws InterruptedException
160 {
161 synchronized (this)
162 {
163 while (!isDone())
164 this.wait();
165 return _status.get();
166 }
167 }
168
169 public void reset()
170 {
171
172
173 synchronized(this)
174 {
175 _timeoutTask=null;
176 _onRequestCompleteDone=false;
177 _onResponseCompleteDone=false;
178 _onDone=false;
179 setStatus(STATUS_START);
180 }
181 }
182
183 void setStatus(int newStatus)
184 {
185 try
186 {
187 int oldStatus = _status.get();
188 boolean set = false;
189 if (oldStatus!=newStatus)
190 {
191 _lastStateChange=System.currentTimeMillis();
192 if (newStatus==STATUS_SENDING_REQUEST)
193 _sent=_lastStateChange;
194 }
195
196
197 switch (oldStatus)
198 {
199 case STATUS_START:
200 switch (newStatus)
201 {
202 case STATUS_START:
203 case STATUS_WAITING_FOR_CONNECTION:
204 case STATUS_WAITING_FOR_COMMIT:
205 case STATUS_CANCELLING:
206 case STATUS_EXCEPTED:
207 set=_status.compareAndSet(oldStatus,newStatus);
208 break;
209 }
210 break;
211 case STATUS_WAITING_FOR_CONNECTION:
212 switch (newStatus)
213 {
214 case STATUS_WAITING_FOR_COMMIT:
215 case STATUS_CANCELLING:
216 case STATUS_EXCEPTED:
217 set=_status.compareAndSet(oldStatus,newStatus);
218 break;
219 case STATUS_EXPIRED:
220 if (set=_status.compareAndSet(oldStatus,newStatus))
221 getEventListener().onExpire();
222 break;
223 }
224 break;
225 case STATUS_WAITING_FOR_COMMIT:
226 switch (newStatus)
227 {
228 case STATUS_SENDING_REQUEST:
229 case STATUS_CANCELLING:
230 case STATUS_EXCEPTED:
231 set=_status.compareAndSet(oldStatus,newStatus);
232 break;
233 case STATUS_EXPIRED:
234 if (set=_status.compareAndSet(oldStatus,newStatus))
235 getEventListener().onExpire();
236 break;
237 }
238 break;
239 case STATUS_SENDING_REQUEST:
240 switch (newStatus)
241 {
242 case STATUS_WAITING_FOR_RESPONSE:
243 if (set=_status.compareAndSet(oldStatus,newStatus))
244 getEventListener().onRequestCommitted();
245 break;
246 case STATUS_CANCELLING:
247 case STATUS_EXCEPTED:
248 set=_status.compareAndSet(oldStatus,newStatus);
249 break;
250 case STATUS_EXPIRED:
251 if (set=_status.compareAndSet(oldStatus,newStatus))
252 getEventListener().onExpire();
253 break;
254 }
255 break;
256 case STATUS_WAITING_FOR_RESPONSE:
257 switch (newStatus)
258 {
259 case STATUS_PARSING_HEADERS:
260 case STATUS_CANCELLING:
261 case STATUS_EXCEPTED:
262 set=_status.compareAndSet(oldStatus,newStatus);
263 break;
264 case STATUS_EXPIRED:
265 if (set=_status.compareAndSet(oldStatus,newStatus))
266 getEventListener().onExpire();
267 break;
268 }
269 break;
270 case STATUS_PARSING_HEADERS:
271 switch (newStatus)
272 {
273 case STATUS_PARSING_CONTENT:
274 if (set=_status.compareAndSet(oldStatus,newStatus))
275 getEventListener().onResponseHeaderComplete();
276 break;
277 case STATUS_CANCELLING:
278 case STATUS_EXCEPTED:
279 set=_status.compareAndSet(oldStatus,newStatus);
280 break;
281 case STATUS_EXPIRED:
282 if (set=_status.compareAndSet(oldStatus,newStatus))
283 getEventListener().onExpire();
284 break;
285 }
286 break;
287 case STATUS_PARSING_CONTENT:
288 switch (newStatus)
289 {
290 case STATUS_COMPLETED:
291 if (set=_status.compareAndSet(oldStatus,newStatus))
292 getEventListener().onResponseComplete();
293 break;
294 case STATUS_CANCELLING:
295 case STATUS_EXCEPTED:
296 set=_status.compareAndSet(oldStatus,newStatus);
297 break;
298 case STATUS_EXPIRED:
299 if (set=_status.compareAndSet(oldStatus,newStatus))
300 getEventListener().onExpire();
301 break;
302 }
303 break;
304 case STATUS_COMPLETED:
305 switch (newStatus)
306 {
307 case STATUS_START:
308 case STATUS_EXCEPTED:
309 case STATUS_WAITING_FOR_RESPONSE:
310 set=_status.compareAndSet(oldStatus,newStatus);
311 break;
312 case STATUS_CANCELLING:
313 case STATUS_EXPIRED:
314
315 set=true;
316 break;
317 }
318 break;
319 case STATUS_CANCELLING:
320 switch (newStatus)
321 {
322 case STATUS_EXCEPTED:
323 case STATUS_CANCELLED:
324 if (set=_status.compareAndSet(oldStatus,newStatus))
325 done();
326 break;
327 default:
328
329 set=true;
330 break;
331 }
332 break;
333 case STATUS_EXCEPTED:
334 case STATUS_EXPIRED:
335 case STATUS_CANCELLED:
336 switch (newStatus)
337 {
338 case STATUS_START:
339 set=_status.compareAndSet(oldStatus,newStatus);
340 break;
341 default:
342 set=true;
343 break;
344 }
345 break;
346 default:
347
348 throw new AssertionError(oldStatus + " => " + newStatus);
349 }
350
351 if (!set)
352 throw new IllegalStateException(toState(oldStatus) + " => " + toState(newStatus));
353 }
354 catch (IOException x)
355 {
356 LOG.warn(x);
357 }
358 }
359
360 public boolean isDone()
361 {
362 synchronized (this)
363 {
364 return _onDone;
365 }
366 }
367
368
369
370
371 @Deprecated
372 public boolean isDone (int status)
373 {
374 return isDone();
375 }
376
377 public HttpEventListener getEventListener()
378 {
379 return _listener;
380 }
381
382 public void setEventListener(HttpEventListener listener)
383 {
384 _listener=listener;
385 }
386
387 public void setTimeout( long timeout )
388 {
389 _timeout = timeout;
390 }
391
392 public long getTimeout()
393 {
394 return _timeout;
395 }
396
397
398
399
400 public void setURL(String url)
401 {
402 setURI(URI.create(url));
403 }
404
405
406
407
408 public void setAddress(Address address)
409 {
410 _address = address;
411 }
412
413
414
415
416 public Address getAddress()
417 {
418 return _address;
419 }
420
421
422
423
424
425
426
427
428
429 public Address getLocalAddress()
430 {
431 return _localAddress;
432 }
433
434
435
436
437 public void setScheme(Buffer scheme)
438 {
439 _scheme = scheme;
440 }
441
442
443
444
445 public void setScheme(String scheme)
446 {
447 if (scheme != null)
448 {
449 if (HttpSchemes.HTTP.equalsIgnoreCase(scheme))
450 setScheme(HttpSchemes.HTTP_BUFFER);
451 else if (HttpSchemes.HTTPS.equalsIgnoreCase(scheme))
452 setScheme(HttpSchemes.HTTPS_BUFFER);
453 else
454 setScheme(new ByteArrayBuffer(scheme));
455 }
456 }
457
458
459
460
461 public Buffer getScheme()
462 {
463 return _scheme;
464 }
465
466
467
468
469 public void setVersion(int version)
470 {
471 _version = version;
472 }
473
474
475
476
477 public void setVersion(String version)
478 {
479 CachedBuffer v = HttpVersions.CACHE.get(version);
480 if (v == null)
481 _version = 10;
482 else
483 _version = v.getOrdinal();
484 }
485
486
487
488
489
490 public int getVersion()
491 {
492 return _version;
493 }
494
495
496
497
498 public void setMethod(String method)
499 {
500 _method = method;
501 }
502
503
504
505
506 public String getMethod()
507 {
508 return _method;
509 }
510
511
512
513
514
515
516 @Deprecated
517 public String getURI()
518 {
519 return getRequestURI();
520 }
521
522
523
524
525 public String getRequestURI()
526 {
527 return _uri;
528 }
529
530
531
532
533
534
535
536
537 @Deprecated
538 public void setURI(String uri)
539 {
540 setRequestURI(uri);
541 }
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558 public void setRequestURI(String uri)
559 {
560 _uri = uri;
561 }
562
563
564
565
566
567 public void setURI(URI uri)
568 {
569 if (!uri.isAbsolute())
570 throw new IllegalArgumentException("!Absolute URI: "+uri);
571
572 if (uri.isOpaque())
573 throw new IllegalArgumentException("Opaque URI: "+uri);
574
575 String scheme = uri.getScheme();
576 int port = uri.getPort();
577 if (port <= 0)
578 port = "https".equalsIgnoreCase(scheme)?443:80;
579
580 setScheme(scheme);
581 setAddress(new Address(uri.getHost(),port));
582
583 HttpURI httpUri = new HttpURI(uri);
584 String completePath = httpUri.getCompletePath();
585 setRequestURI(completePath==null ? "/" : completePath);
586 }
587
588
589
590
591
592
593 public void addRequestHeader(String name, String value)
594 {
595 getRequestFields().add(name,value);
596 }
597
598
599
600
601
602
603 public void addRequestHeader(Buffer name, Buffer value)
604 {
605 getRequestFields().add(name,value);
606 }
607
608
609
610
611
612
613 public void setRequestHeader(String name, String value)
614 {
615 getRequestFields().put(name, value);
616 }
617
618
619
620
621
622
623 public void setRequestHeader(Buffer name, Buffer value)
624 {
625 getRequestFields().put(name, value);
626 }
627
628
629
630
631 public void setRequestContentType(String value)
632 {
633 getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER, value);
634 }
635
636
637
638
639 public HttpFields getRequestFields()
640 {
641 return _requestFields;
642 }
643
644
645
646
647 public void setRequestContent(Buffer requestContent)
648 {
649 _requestContent = requestContent;
650 }
651
652
653
654
655 public void setRequestContentSource(InputStream stream)
656 {
657 _requestContentSource = stream;
658 if (_requestContentSource != null && _requestContentSource.markSupported())
659 _requestContentSource.mark(Integer.MAX_VALUE);
660 }
661
662
663
664
665 public InputStream getRequestContentSource()
666 {
667 return _requestContentSource;
668 }
669
670 public Buffer getRequestContentChunk() throws IOException
671 {
672 synchronized (this)
673 {
674 if (_requestContentChunk == null)
675 _requestContentChunk = new ByteArrayBuffer(4096);
676 else
677 {
678 if (_requestContentChunk.hasContent())
679 throw new IllegalStateException();
680 _requestContentChunk.clear();
681 }
682
683 int read = _requestContentChunk.capacity();
684 int length = _requestContentSource.read(_requestContentChunk.array(),0,read);
685 if (length >= 0)
686 {
687 _requestContentChunk.setPutIndex(length);
688 return _requestContentChunk;
689 }
690 return null;
691 }
692 }
693
694
695
696
697 public Buffer getRequestContent()
698 {
699 return _requestContent;
700 }
701
702
703
704
705 public boolean getRetryStatus()
706 {
707 return _retryStatus;
708 }
709
710
711
712
713 public void setRetryStatus(boolean retryStatus)
714 {
715 _retryStatus = retryStatus;
716 }
717
718
719
720
721
722
723
724
725
726
727 public void cancel()
728 {
729 setStatus(STATUS_CANCELLING);
730 abort();
731 }
732
733 private void done()
734 {
735 synchronized(this)
736 {
737 disassociate();
738 _onDone=true;
739 notifyAll();
740 }
741 }
742
743 private void abort()
744 {
745 HttpConnection httpConnection = _connection;
746 if (httpConnection != null)
747 {
748 try
749 {
750
751
752 httpConnection.close();
753 }
754 catch (IOException x)
755 {
756 LOG.debug(x);
757 }
758 finally
759 {
760 disassociate();
761 }
762 }
763 }
764
765 void associate(HttpConnection connection)
766 {
767 if (connection.getEndPoint().getLocalHost() != null)
768 _localAddress = new Address(connection.getEndPoint().getLocalHost(), connection.getEndPoint().getLocalPort());
769
770 _connection = connection;
771 if (getStatus() == STATUS_CANCELLING)
772 abort();
773 }
774
775 boolean isAssociated()
776 {
777 return this._connection != null;
778 }
779
780 HttpConnection disassociate()
781 {
782 HttpConnection result = _connection;
783 this._connection = null;
784 if (getStatus() == STATUS_CANCELLING)
785 setStatus(STATUS_CANCELLED);
786 return result;
787 }
788
789 public static String toState(int s)
790 {
791 String state;
792 switch(s)
793 {
794 case STATUS_START: state="START"; break;
795 case STATUS_WAITING_FOR_CONNECTION: state="CONNECTING"; break;
796 case STATUS_WAITING_FOR_COMMIT: state="CONNECTED"; break;
797 case STATUS_SENDING_REQUEST: state="SENDING"; break;
798 case STATUS_WAITING_FOR_RESPONSE: state="WAITING"; break;
799 case STATUS_PARSING_HEADERS: state="HEADERS"; break;
800 case STATUS_PARSING_CONTENT: state="CONTENT"; break;
801 case STATUS_COMPLETED: state="COMPLETED"; break;
802 case STATUS_EXPIRED: state="EXPIRED"; break;
803 case STATUS_EXCEPTED: state="EXCEPTED"; break;
804 case STATUS_CANCELLING: state="CANCELLING"; break;
805 case STATUS_CANCELLED: state="CANCELLED"; break;
806 default: state="UNKNOWN";
807 }
808 return state;
809 }
810
811 @Override
812 public String toString()
813 {
814 String state=toState(getStatus());
815 long now=System.currentTimeMillis();
816 long forMs = now -_lastStateChange;
817 String s= String.format("%s@%x=%s//%s%s#%s(%dms)",getClass().getSimpleName(),hashCode(),_method,_address,_uri,state,forMs);
818 if (getStatus()>=STATUS_SENDING_REQUEST)
819 s+="sent="+(now-_sent)+"ms";
820 return s;
821 }
822
823
824
825 protected Connection onSwitchProtocol(EndPoint endp) throws IOException
826 {
827 return null;
828 }
829
830
831
832
833
834
835 protected void onRequestCommitted() throws IOException
836 {
837 }
838
839
840
841
842
843
844 protected void onRequestComplete() throws IOException
845 {
846 }
847
848
849
850
851
852
853
854
855
856 protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
857 {
858 }
859
860
861
862
863
864
865
866
867 protected void onResponseHeader(Buffer name, Buffer value) throws IOException
868 {
869 }
870
871
872
873
874
875
876 protected void onResponseHeaderComplete() throws IOException
877 {
878 }
879
880
881
882
883
884
885
886 protected void onResponseContent(Buffer content) throws IOException
887 {
888 }
889
890
891
892
893
894
895 protected void onResponseComplete() throws IOException
896 {
897 }
898
899
900
901
902
903
904
905 protected void onConnectionFailed(Throwable x)
906 {
907 LOG.warn("CONNECTION FAILED " + this,x);
908 }
909
910
911
912
913
914
915 protected void onException(Throwable x)
916 {
917 LOG.warn("EXCEPTION " + this,x);
918 }
919
920
921
922
923
924 protected void onExpire()
925 {
926 LOG.warn("EXPIRED " + this);
927 }
928
929
930
931
932
933
934 protected void onRetry() throws IOException
935 {
936 if (_requestContentSource != null)
937 {
938 if (_requestContentSource.markSupported())
939 {
940 _requestContent = null;
941 _requestContentSource.reset();
942 }
943 else
944 {
945 throw new IOException("Unsupported retry attempt");
946 }
947 }
948 }
949
950
951
952
953
954
955 public boolean configureListeners()
956 {
957 return _configureListeners;
958 }
959
960
961
962
963 public void setConfigureListeners(boolean autoConfigure)
964 {
965 this._configureListeners = autoConfigure;
966 }
967
968 protected void scheduleTimeout(final HttpDestination destination)
969 {
970 assert _timeoutTask == null;
971
972 _timeoutTask = new Timeout.Task()
973 {
974 @Override
975 public void expired()
976 {
977 HttpExchange.this.expire(destination);
978 }
979 };
980
981 HttpClient httpClient = destination.getHttpClient();
982 long timeout = getTimeout();
983 if (timeout > 0)
984 httpClient.schedule(_timeoutTask, timeout);
985 else
986 httpClient.schedule(_timeoutTask);
987 }
988
989 protected void cancelTimeout(HttpClient httpClient)
990 {
991 Timeout.Task task = _timeoutTask;
992 if (task != null)
993 httpClient.cancel(task);
994 _timeoutTask = null;
995 }
996
997 private class Listener implements HttpEventListener
998 {
999 public void onConnectionFailed(Throwable ex)
1000 {
1001 try
1002 {
1003 HttpExchange.this.onConnectionFailed(ex);
1004 }
1005 finally
1006 {
1007 done();
1008 }
1009 }
1010
1011 public void onException(Throwable ex)
1012 {
1013 try
1014 {
1015 HttpExchange.this.onException(ex);
1016 }
1017 finally
1018 {
1019 done();
1020 }
1021 }
1022
1023 public void onExpire()
1024 {
1025 try
1026 {
1027 HttpExchange.this.onExpire();
1028 }
1029 finally
1030 {
1031 done();
1032 }
1033 }
1034
1035 public void onRequestCommitted() throws IOException
1036 {
1037 HttpExchange.this.onRequestCommitted();
1038 }
1039
1040 public void onRequestComplete() throws IOException
1041 {
1042 try
1043 {
1044 HttpExchange.this.onRequestComplete();
1045 }
1046 finally
1047 {
1048 synchronized(HttpExchange.this)
1049 {
1050 _onRequestCompleteDone = true;
1051
1052
1053 _onDone |= _onResponseCompleteDone;
1054 if (_onDone)
1055 disassociate();
1056 HttpExchange.this.notifyAll();
1057 }
1058 }
1059 }
1060
1061 public void onResponseComplete() throws IOException
1062 {
1063 try
1064 {
1065 HttpExchange.this.onResponseComplete();
1066 }
1067 finally
1068 {
1069 synchronized(HttpExchange.this)
1070 {
1071 _onResponseCompleteDone = true;
1072
1073
1074 _onDone |= _onRequestCompleteDone;
1075 if (_onDone)
1076 disassociate();
1077 HttpExchange.this.notifyAll();
1078 }
1079 }
1080 }
1081
1082 public void onResponseContent(Buffer content) throws IOException
1083 {
1084 HttpExchange.this.onResponseContent(content);
1085 }
1086
1087 public void onResponseHeader(Buffer name, Buffer value) throws IOException
1088 {
1089 HttpExchange.this.onResponseHeader(name,value);
1090 }
1091
1092 public void onResponseHeaderComplete() throws IOException
1093 {
1094 HttpExchange.this.onResponseHeaderComplete();
1095 }
1096
1097 public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
1098 {
1099 HttpExchange.this.onResponseStatus(version,status,reason);
1100 }
1101
1102 public void onRetry()
1103 {
1104 HttpExchange.this.setRetryStatus( true );
1105 try
1106 {
1107 HttpExchange.this.onRetry();
1108 }
1109 catch (IOException e)
1110 {
1111 LOG.debug(e);
1112 }
1113 }
1114 }
1115
1116
1117
1118
1119 @Deprecated
1120 public static class CachedExchange extends org.eclipse.jetty.client.CachedExchange
1121 {
1122 public CachedExchange(boolean cacheFields)
1123 {
1124 super(cacheFields);
1125 }
1126 }
1127
1128
1129
1130
1131 @Deprecated
1132 public static class ContentExchange extends org.eclipse.jetty.client.ContentExchange
1133 {
1134 }
1135 }