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