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