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