blob: b97b9ffc50f0b0f60bcd5e3168e7a8da1838b550 [file] [log] [blame]
Jake Slack03928ae2014-05-13 18:41:56 -07001//
2// ========================================================================
3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4// ------------------------------------------------------------------------
5// All rights reserved. This program and the accompanying materials
6// are made available under the terms of the Eclipse Public License v1.0
7// and Apache License v2.0 which accompanies this distribution.
8//
9// The Eclipse Public License is available at
10// http://www.eclipse.org/legal/epl-v10.html
11//
12// The Apache License v2.0 is available at
13// http://www.opensource.org/licenses/apache2.0.php
14//
15// You may elect to redistribute this code under either of these licenses.
16// ========================================================================
17//
18
19package org.eclipse.jetty.client;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.net.URI;
24import java.util.concurrent.atomic.AtomicInteger;
25
26import org.eclipse.jetty.client.security.SecurityListener;
27import org.eclipse.jetty.http.HttpFields;
28import org.eclipse.jetty.http.HttpHeaders;
29import org.eclipse.jetty.http.HttpMethods;
30import org.eclipse.jetty.http.HttpSchemes;
31import org.eclipse.jetty.http.HttpURI;
32import org.eclipse.jetty.http.HttpVersions;
33import org.eclipse.jetty.io.Buffer;
34import org.eclipse.jetty.io.BufferCache.CachedBuffer;
35import org.eclipse.jetty.io.ByteArrayBuffer;
36import org.eclipse.jetty.io.Connection;
37import org.eclipse.jetty.io.EndPoint;
38import org.eclipse.jetty.util.log.Log;
39import org.eclipse.jetty.util.log.Logger;
40import org.eclipse.jetty.util.thread.Timeout;
41
42/**
43 * <p>
44 * An HTTP client API that encapsulates an exchange (a request and its response) with a HTTP server.
45 * </p>
46 *
47 * This object encapsulates:
48 * <ul>
49 * <li>The HTTP server address, see {@link #setAddress(Address)}, or {@link #setURI(URI)}, or {@link #setURL(String)})
50 * <li>The HTTP request method, URI and HTTP version (see {@link #setMethod(String)}, {@link #setRequestURI(String)}, and {@link #setVersion(int)})
51 * <li>The request headers (see {@link #addRequestHeader(String, String)} or {@link #setRequestHeader(String, String)})
52 * <li>The request content (see {@link #setRequestContent(Buffer)} or {@link #setRequestContentSource(InputStream)})
53 * <li>The status of the exchange (see {@link #getStatus()})
54 * <li>Callbacks to handle state changes (see the onXxx methods such as {@link #onRequestComplete()} or {@link #onResponseComplete()})
55 * <li>The ability to intercept callbacks (see {@link #setEventListener(HttpEventListener)}
56 * </ul>
57 *
58 * <p>
59 * The HttpExchange class is intended to be used by a developer wishing to have close asynchronous interaction with the the exchange.<br />
60 * Typically a developer will extend the HttpExchange class with a derived class that overrides some or all of the onXxx callbacks. <br />
61 * There are also some predefined HttpExchange subtypes that can be used as a basis, see {@link org.eclipse.jetty.client.ContentExchange} and
62 * {@link org.eclipse.jetty.client.CachedExchange}.
63 * </p>
64 *
65 * <p>
66 * Typically the HttpExchange is passed to the {@link HttpClient#send(HttpExchange)} method, which in turn selects a {@link HttpDestination} and calls its
67 * {@link HttpDestination#send(HttpExchange)}, which then creates or selects a {@link AbstractHttpConnection} and calls its {@link AbstractHttpConnection#send(HttpExchange)}. A
68 * developer may wish to directly call send on the destination or connection if they wish to bypass some handling provided (eg Cookie handling in the
69 * HttpDestination).
70 * </p>
71 *
72 * <p>
73 * In some circumstances, the HttpClient or HttpDestination may wish to retry a HttpExchange (eg. failed pipeline request, authentication retry or redirection).
74 * In such cases, the HttpClient and/or HttpDestination may insert their own HttpExchangeListener to intercept and filter the call backs intended for the
75 * HttpExchange.
76 * </p>
77 */
78public class HttpExchange
79{
80 static final Logger LOG = Log.getLogger(HttpExchange.class);
81
82 public static final int STATUS_START = 0;
83 public static final int STATUS_WAITING_FOR_CONNECTION = 1;
84 public static final int STATUS_WAITING_FOR_COMMIT = 2;
85 public static final int STATUS_SENDING_REQUEST = 3;
86 public static final int STATUS_WAITING_FOR_RESPONSE = 4;
87 public static final int STATUS_PARSING_HEADERS = 5;
88 public static final int STATUS_PARSING_CONTENT = 6;
89 public static final int STATUS_COMPLETED = 7;
90 public static final int STATUS_EXPIRED = 8;
91 public static final int STATUS_EXCEPTED = 9;
92 public static final int STATUS_CANCELLING = 10;
93 public static final int STATUS_CANCELLED = 11;
94
95 // HTTP protocol fields
96 private String _method = HttpMethods.GET;
97 private Buffer _scheme = HttpSchemes.HTTP_BUFFER;
98 private String _uri;
99 private int _version = HttpVersions.HTTP_1_1_ORDINAL;
100 private Address _address;
101 private final HttpFields _requestFields = new HttpFields();
102 private Buffer _requestContent;
103 private InputStream _requestContentSource;
104
105 private AtomicInteger _status = new AtomicInteger(STATUS_START);
106 private boolean _retryStatus = false;
107 // controls if the exchange will have listeners autoconfigured by the destination
108 private boolean _configureListeners = true;
109 private HttpEventListener _listener = new Listener();
110 private volatile AbstractHttpConnection _connection;
111
112 private Address _localAddress = null;
113
114 // a timeout for this exchange
115 private long _timeout = -1;
116 private volatile Timeout.Task _timeoutTask;
117 private long _lastStateChange=System.currentTimeMillis();
118 private long _sent=-1;
119 private int _lastState=-1;
120 private int _lastStatePeriod=-1;
121
122 boolean _onRequestCompleteDone;
123 boolean _onResponseCompleteDone;
124 boolean _onDone; // == onConnectionFail || onException || onExpired || onCancelled || onResponseCompleted && onRequestCompleted
125
126 protected void expire(HttpDestination destination)
127 {
128 AbstractHttpConnection connection = _connection;
129 if (getStatus() < HttpExchange.STATUS_COMPLETED)
130 setStatus(HttpExchange.STATUS_EXPIRED);
131 destination.exchangeExpired(this);
132 if (connection != null)
133 connection.exchangeExpired(this);
134 }
135
136 public int getStatus()
137 {
138 return _status.get();
139 }
140
141 /**
142 * @param status
143 * the status to wait for
144 * @throws InterruptedException
145 * if the waiting thread is interrupted
146 * @deprecated Use {@link #waitForDone()} instead
147 */
148 @Deprecated
149 public void waitForStatus(int status) throws InterruptedException
150 {
151 throw new UnsupportedOperationException();
152 }
153
154 /**
155 * Wait until the exchange is "done". Done is defined as when a final state has been passed to the HttpExchange via the associated onXxx call. Note that an
156 * exchange can transit a final state when being used as part of a dialog (eg {@link SecurityListener}. Done status is thus defined as:
157 *
158 * <pre>
159 * done == onConnectionFailed || onException || onExpire || onRequestComplete &amp;&amp; onResponseComplete
160 * </pre>
161 *
162 * @return the done status
163 * @throws InterruptedException
164 */
165 public int waitForDone() throws InterruptedException
166 {
167 synchronized (this)
168 {
169 while (!isDone())
170 this.wait();
171 return _status.get();
172 }
173 }
174
175 public void reset()
176 {
177 // TODO - this should do a cancel and wakeup everybody that was waiting.
178 // might need a version number concept
179 synchronized (this)
180 {
181 _timeoutTask = null;
182 _onRequestCompleteDone = false;
183 _onResponseCompleteDone = false;
184 _onDone = false;
185 setStatus(STATUS_START);
186 }
187 }
188
189 /* ------------------------------------------------------------ */
190 /**
191 * @param newStatus
192 * @return True if the status was actually set.
193 */
194 boolean setStatus(int newStatus)
195 {
196 boolean set = false;
197 try
198 {
199 int oldStatus = _status.get();
200 boolean ignored = false;
201 if (oldStatus != newStatus)
202 {
203 long now = System.currentTimeMillis();
204 _lastStatePeriod=(int)(now-_lastStateChange);
205 _lastState=oldStatus;
206 _lastStateChange=now;
207 if (newStatus==STATUS_SENDING_REQUEST)
208 _sent=_lastStateChange;
209 }
210
211 // State machine: from which old status you can go into which new status
212 switch (oldStatus)
213 {
214 case STATUS_START:
215 switch (newStatus)
216 {
217 case STATUS_START:
218 case STATUS_WAITING_FOR_CONNECTION:
219 case STATUS_WAITING_FOR_COMMIT:
220 case STATUS_CANCELLING:
221 case STATUS_EXCEPTED:
222 set = _status.compareAndSet(oldStatus,newStatus);
223 break;
224 case STATUS_EXPIRED:
225 set = setStatusExpired(newStatus,oldStatus);
226 break;
227 }
228 break;
229 case STATUS_WAITING_FOR_CONNECTION:
230 switch (newStatus)
231 {
232 case STATUS_WAITING_FOR_COMMIT:
233 case STATUS_CANCELLING:
234 case STATUS_EXCEPTED:
235 set = _status.compareAndSet(oldStatus,newStatus);
236 break;
237 case STATUS_EXPIRED:
238 set = setStatusExpired(newStatus,oldStatus);
239 break;
240 }
241 break;
242 case STATUS_WAITING_FOR_COMMIT:
243 switch (newStatus)
244 {
245 case STATUS_SENDING_REQUEST:
246 case STATUS_CANCELLING:
247 case STATUS_EXCEPTED:
248 set = _status.compareAndSet(oldStatus,newStatus);
249 break;
250 case STATUS_EXPIRED:
251 set = setStatusExpired(newStatus,oldStatus);
252 break;
253 }
254 break;
255 case STATUS_SENDING_REQUEST:
256 switch (newStatus)
257 {
258 case STATUS_WAITING_FOR_RESPONSE:
259 if (set = _status.compareAndSet(oldStatus,newStatus))
260 getEventListener().onRequestCommitted();
261 break;
262 case STATUS_CANCELLING:
263 case STATUS_EXCEPTED:
264 set = _status.compareAndSet(oldStatus,newStatus);
265 break;
266 case STATUS_EXPIRED:
267 set = setStatusExpired(newStatus,oldStatus);
268 break;
269 }
270 break;
271 case STATUS_WAITING_FOR_RESPONSE:
272 switch (newStatus)
273 {
274 case STATUS_PARSING_HEADERS:
275 case STATUS_CANCELLING:
276 case STATUS_EXCEPTED:
277 set = _status.compareAndSet(oldStatus,newStatus);
278 break;
279 case STATUS_EXPIRED:
280 set = setStatusExpired(newStatus,oldStatus);
281 break;
282 }
283 break;
284 case STATUS_PARSING_HEADERS:
285 switch (newStatus)
286 {
287 case STATUS_PARSING_CONTENT:
288 if (set = _status.compareAndSet(oldStatus,newStatus))
289 getEventListener().onResponseHeaderComplete();
290 break;
291 case STATUS_CANCELLING:
292 case STATUS_EXCEPTED:
293 set = _status.compareAndSet(oldStatus,newStatus);
294 break;
295 case STATUS_EXPIRED:
296 set = setStatusExpired(newStatus,oldStatus);
297 break;
298 }
299 break;
300 case STATUS_PARSING_CONTENT:
301 switch (newStatus)
302 {
303 case STATUS_COMPLETED:
304 if (set = _status.compareAndSet(oldStatus,newStatus))
305 getEventListener().onResponseComplete();
306 break;
307 case STATUS_CANCELLING:
308 case STATUS_EXCEPTED:
309 set = _status.compareAndSet(oldStatus,newStatus);
310 break;
311 case STATUS_EXPIRED:
312 set = setStatusExpired(newStatus,oldStatus);
313 break;
314 }
315 break;
316 case STATUS_COMPLETED:
317 switch (newStatus)
318 {
319 case STATUS_START:
320 case STATUS_EXCEPTED:
321 case STATUS_WAITING_FOR_RESPONSE:
322 set = _status.compareAndSet(oldStatus,newStatus);
323 break;
324 case STATUS_CANCELLING:
325 case STATUS_EXPIRED:
326 // Don't change the status, it's too late
327 ignored = true;
328 break;
329 }
330 break;
331 case STATUS_CANCELLING:
332 switch (newStatus)
333 {
334 case STATUS_EXCEPTED:
335 case STATUS_CANCELLED:
336 if (set = _status.compareAndSet(oldStatus,newStatus))
337 done();
338 break;
339 default:
340 // Ignore other statuses, we're cancelling
341 ignored = true;
342 break;
343 }
344 break;
345 case STATUS_EXCEPTED:
346 case STATUS_EXPIRED:
347 case STATUS_CANCELLED:
348 switch (newStatus)
349 {
350 case STATUS_START:
351 set = _status.compareAndSet(oldStatus,newStatus);
352 break;
353
354 case STATUS_COMPLETED:
355 ignored = true;
356 done();
357 break;
358
359 default:
360 ignored = true;
361 break;
362 }
363 break;
364 default:
365 // Here means I allowed to set a state that I don't recognize
366 throw new AssertionError(oldStatus + " => " + newStatus);
367 }
368
369 if (!set && !ignored)
370 throw new IllegalStateException(toState(oldStatus) + " => " + toState(newStatus));
371 LOG.debug("setStatus {} {}",newStatus,this);
372 }
373 catch (IOException x)
374 {
375 LOG.warn(x);
376 }
377 return set;
378 }
379
380 private boolean setStatusExpired(int newStatus, int oldStatus)
381 {
382 boolean set;
383 if (set = _status.compareAndSet(oldStatus,newStatus))
384 getEventListener().onExpire();
385 return set;
386 }
387
388 public boolean isDone()
389 {
390 synchronized (this)
391 {
392 return _onDone;
393 }
394 }
395
396 /**
397 * @deprecated
398 */
399 @Deprecated
400 public boolean isDone(int status)
401 {
402 return isDone();
403 }
404
405 public HttpEventListener getEventListener()
406 {
407 return _listener;
408 }
409
410 public void setEventListener(HttpEventListener listener)
411 {
412 _listener = listener;
413 }
414
415 public void setTimeout(long timeout)
416 {
417 _timeout = timeout;
418 }
419
420 public long getTimeout()
421 {
422 return _timeout;
423 }
424
425 /**
426 * @param url
427 * an absolute URL (for example 'http://localhost/foo/bar?a=1')
428 */
429 public void setURL(String url)
430 {
431 setURI(URI.create(url));
432 }
433
434 /**
435 * @param address
436 * the address of the server
437 */
438 public void setAddress(Address address)
439 {
440 _address = address;
441 }
442
443 /**
444 * @return the address of the server
445 */
446 public Address getAddress()
447 {
448 return _address;
449 }
450
451 /**
452 * the local address used by the connection
453 *
454 * Note: this method will not be populated unless the exchange has been executed by the HttpClient
455 *
456 * @return the local address used for the running of the exchange if available, null otherwise.
457 */
458 public Address getLocalAddress()
459 {
460 return _localAddress;
461 }
462
463 /**
464 * @param scheme
465 * the scheme of the URL (for example 'http')
466 */
467 public void setScheme(Buffer scheme)
468 {
469 _scheme = scheme;
470 }
471
472 /**
473 * @param scheme
474 * the scheme of the URL (for example 'http')
475 */
476 public void setScheme(String scheme)
477 {
478 if (scheme != null)
479 {
480 if (HttpSchemes.HTTP.equalsIgnoreCase(scheme))
481 setScheme(HttpSchemes.HTTP_BUFFER);
482 else if (HttpSchemes.HTTPS.equalsIgnoreCase(scheme))
483 setScheme(HttpSchemes.HTTPS_BUFFER);
484 else
485 setScheme(new ByteArrayBuffer(scheme));
486 }
487 }
488
489 /**
490 * @return the scheme of the URL
491 */
492 public Buffer getScheme()
493 {
494 return _scheme;
495 }
496
497 /**
498 * @param version
499 * the HTTP protocol version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
500 */
501 public void setVersion(int version)
502 {
503 _version = version;
504 }
505
506 /**
507 * @param version
508 * the HTTP protocol version as string
509 */
510 public void setVersion(String version)
511 {
512 CachedBuffer v = HttpVersions.CACHE.get(version);
513 if (v == null)
514 _version = 10;
515 else
516 _version = v.getOrdinal();
517 }
518
519 /**
520 * @return the HTTP protocol version as integer
521 * @see #setVersion(int)
522 */
523 public int getVersion()
524 {
525 return _version;
526 }
527
528 /**
529 * @param method
530 * the HTTP method (for example 'GET')
531 */
532 public void setMethod(String method)
533 {
534 _method = method;
535 }
536
537 /**
538 * @return the HTTP method
539 */
540 public String getMethod()
541 {
542 return _method;
543 }
544
545 /**
546 * @return request URI
547 * @see #getRequestURI()
548 * @deprecated
549 */
550 @Deprecated
551 public String getURI()
552 {
553 return getRequestURI();
554 }
555
556 /**
557 * @return request URI
558 */
559 public String getRequestURI()
560 {
561 return _uri;
562 }
563
564 /**
565 * Set the request URI
566 *
567 * @param uri
568 * new request URI
569 * @see #setRequestURI(String)
570 * @deprecated
571 */
572 @Deprecated
573 public void setURI(String uri)
574 {
575 setRequestURI(uri);
576 }
577
578 /**
579 * Set the request URI
580 *
581 * Per RFC 2616 sec5, Request-URI = "*" | absoluteURI | abs_path | authority<br/>
582 * where:<br/>
583 * <br/>
584 * "*" - request applies to server itself<br/>
585 * absoluteURI - required for proxy requests, e.g. http://localhost:8080/context<br/>
586 * (this form is generated automatically by HttpClient)<br/>
587 * abs_path - used for most methods, e.g. /context<br/>
588 * authority - used for CONNECT method only, e.g. localhost:8080<br/>
589 * <br/>
590 * For complete definition of URI components, see RFC 2396 sec3.<br/>
591 *
592 * @param uri
593 * new request URI
594 */
595 public void setRequestURI(String uri)
596 {
597 _uri = uri;
598 }
599
600 /* ------------------------------------------------------------ */
601 /**
602 * @param uri
603 * an absolute URI (for example 'http://localhost/foo/bar?a=1')
604 */
605 public void setURI(URI uri)
606 {
607 if (!uri.isAbsolute())
608 throw new IllegalArgumentException("!Absolute URI: " + uri);
609
610 if (uri.isOpaque())
611 throw new IllegalArgumentException("Opaque URI: " + uri);
612
613 if (LOG.isDebugEnabled())
614 LOG.debug("URI = {}",uri.toASCIIString());
615
616 String scheme = uri.getScheme();
617 int port = uri.getPort();
618 if (port <= 0)
619 port = "https".equalsIgnoreCase(scheme)?443:80;
620
621 setScheme(scheme);
622 setAddress(new Address(uri.getHost(),port));
623
624 HttpURI httpUri = new HttpURI(uri);
625 String completePath = httpUri.getCompletePath();
626 setRequestURI(completePath == null?"/":completePath);
627 }
628
629 /**
630 * Adds the specified request header
631 *
632 * @param name
633 * the header name
634 * @param value
635 * the header value
636 */
637 public void addRequestHeader(String name, String value)
638 {
639 getRequestFields().add(name,value);
640 }
641
642 /**
643 * Adds the specified request header
644 *
645 * @param name
646 * the header name
647 * @param value
648 * the header value
649 */
650 public void addRequestHeader(Buffer name, Buffer value)
651 {
652 getRequestFields().add(name,value);
653 }
654
655 /**
656 * Sets the specified request header
657 *
658 * @param name
659 * the header name
660 * @param value
661 * the header value
662 */
663 public void setRequestHeader(String name, String value)
664 {
665 getRequestFields().put(name,value);
666 }
667
668 /**
669 * Sets the specified request header
670 *
671 * @param name
672 * the header name
673 * @param value
674 * the header value
675 */
676 public void setRequestHeader(Buffer name, Buffer value)
677 {
678 getRequestFields().put(name,value);
679 }
680
681 /**
682 * @param value
683 * the content type of the request
684 */
685 public void setRequestContentType(String value)
686 {
687 getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,value);
688 }
689
690 /**
691 * @return the request headers
692 */
693 public HttpFields getRequestFields()
694 {
695 return _requestFields;
696 }
697
698 /**
699 * @param requestContent
700 * the request content
701 */
702 public void setRequestContent(Buffer requestContent)
703 {
704 _requestContent = requestContent;
705 }
706
707 /**
708 * @param stream
709 * the request content as a stream
710 */
711 public void setRequestContentSource(InputStream stream)
712 {
713 _requestContentSource = stream;
714 if (_requestContentSource != null && _requestContentSource.markSupported())
715 _requestContentSource.mark(Integer.MAX_VALUE);
716 }
717
718 /**
719 * @return the request content as a stream
720 */
721 public InputStream getRequestContentSource()
722 {
723 return _requestContentSource;
724 }
725
726 public Buffer getRequestContentChunk(Buffer buffer) throws IOException
727 {
728 synchronized (this)
729 {
730 if (_requestContentSource!=null)
731 {
732 if (buffer == null)
733 buffer = new ByteArrayBuffer(8192); // TODO configure
734
735 int space = buffer.space();
736 int length = _requestContentSource.read(buffer.array(),buffer.putIndex(),space);
737 if (length >= 0)
738 {
739 buffer.setPutIndex(buffer.putIndex()+length);
740 return buffer;
741 }
742 }
743 return null;
744 }
745 }
746
747 /**
748 * @return the request content
749 */
750 public Buffer getRequestContent()
751 {
752 return _requestContent;
753 }
754
755 /**
756 * @return whether a retry will be attempted or not
757 */
758 public boolean getRetryStatus()
759 {
760 return _retryStatus;
761 }
762
763 /**
764 * @param retryStatus
765 * whether a retry will be attempted or not
766 */
767 public void setRetryStatus(boolean retryStatus)
768 {
769 _retryStatus = retryStatus;
770 }
771
772 /**
773 * Initiates the cancelling of this exchange. The status of the exchange is set to {@link #STATUS_CANCELLING}. Cancelling the exchange is an asynchronous
774 * operation with respect to the request/response, and as such checking the request/response status of a cancelled exchange may return undefined results
775 * (for example it may have only some of the response headers being sent by the server). The cancelling of the exchange is completed when the exchange
776 * status (see {@link #getStatus()}) is {@link #STATUS_CANCELLED}, and this can be waited using {@link #waitForDone()}.
777 */
778 public void cancel()
779 {
780 setStatus(STATUS_CANCELLING);
781 abort();
782 }
783
784 private void done()
785 {
786 synchronized (this)
787 {
788 disassociate();
789 _onDone = true;
790 notifyAll();
791 }
792 }
793
794 private void abort()
795 {
796 AbstractHttpConnection httpConnection = _connection;
797 if (httpConnection != null)
798 {
799 try
800 {
801 // Closing the connection here will cause the connection
802 // to be returned in HttpConnection.handle()
803 httpConnection.close();
804 }
805 catch (IOException x)
806 {
807 LOG.debug(x);
808 }
809 finally
810 {
811 disassociate();
812 }
813 }
814 }
815
816 void associate(AbstractHttpConnection connection)
817 {
818 if (connection.getEndPoint().getLocalAddr() != null)
819 _localAddress = new Address(connection.getEndPoint().getLocalAddr(),connection.getEndPoint().getLocalPort());
820
821 _connection = connection;
822 if (getStatus() == STATUS_CANCELLING)
823 abort();
824 }
825
826 boolean isAssociated()
827 {
828 return this._connection != null;
829 }
830
831 AbstractHttpConnection disassociate()
832 {
833 AbstractHttpConnection result = _connection;
834 this._connection = null;
835 if (getStatus() == STATUS_CANCELLING)
836 setStatus(STATUS_CANCELLED);
837 return result;
838 }
839
840 public static String toState(int s)
841 {
842 String state;
843 switch (s)
844 {
845 case STATUS_START:
846 state = "START";
847 break;
848 case STATUS_WAITING_FOR_CONNECTION:
849 state = "CONNECTING";
850 break;
851 case STATUS_WAITING_FOR_COMMIT:
852 state = "CONNECTED";
853 break;
854 case STATUS_SENDING_REQUEST:
855 state = "SENDING";
856 break;
857 case STATUS_WAITING_FOR_RESPONSE:
858 state = "WAITING";
859 break;
860 case STATUS_PARSING_HEADERS:
861 state = "HEADERS";
862 break;
863 case STATUS_PARSING_CONTENT:
864 state = "CONTENT";
865 break;
866 case STATUS_COMPLETED:
867 state = "COMPLETED";
868 break;
869 case STATUS_EXPIRED:
870 state = "EXPIRED";
871 break;
872 case STATUS_EXCEPTED:
873 state = "EXCEPTED";
874 break;
875 case STATUS_CANCELLING:
876 state = "CANCELLING";
877 break;
878 case STATUS_CANCELLED:
879 state = "CANCELLED";
880 break;
881 default:
882 state = "UNKNOWN";
883 }
884 return state;
885 }
886
887 @Override
888 public String toString()
889 {
890 String state=toState(getStatus());
891 long now=System.currentTimeMillis();
892 long forMs = now -_lastStateChange;
893 String s= _lastState>=0
894 ?String.format("%s@%x=%s//%s%s#%s(%dms)->%s(%dms)",getClass().getSimpleName(),hashCode(),_method,_address,_uri,toState(_lastState),_lastStatePeriod,state,forMs)
895 :String.format("%s@%x=%s//%s%s#%s(%dms)",getClass().getSimpleName(),hashCode(),_method,_address,_uri,state,forMs);
896 if (getStatus()>=STATUS_SENDING_REQUEST && _sent>0)
897 s+="sent="+(now-_sent)+"ms";
898 return s;
899 }
900
901 /**
902 */
903 protected Connection onSwitchProtocol(EndPoint endp) throws IOException
904 {
905 return null;
906 }
907
908 /**
909 * Callback called when the request headers have been sent to the server. This implementation does nothing.
910 *
911 * @throws IOException
912 * allowed to be thrown by overriding code
913 */
914 protected void onRequestCommitted() throws IOException
915 {
916 }
917
918 /**
919 * Callback called when the request and its body have been sent to the server. This implementation does nothing.
920 *
921 * @throws IOException
922 * allowed to be thrown by overriding code
923 */
924 protected void onRequestComplete() throws IOException
925 {
926 }
927
928 /**
929 * Callback called when a response status line has been received from the server. This implementation does nothing.
930 *
931 * @param version
932 * the HTTP version
933 * @param status
934 * the HTTP status code
935 * @param reason
936 * the HTTP status reason string
937 * @throws IOException
938 * allowed to be thrown by overriding code
939 */
940 protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
941 {
942 }
943
944 /**
945 * Callback called for each response header received from the server. This implementation does nothing.
946 *
947 * @param name
948 * the header name
949 * @param value
950 * the header value
951 * @throws IOException
952 * allowed to be thrown by overriding code
953 */
954 protected void onResponseHeader(Buffer name, Buffer value) throws IOException
955 {
956 }
957
958 /**
959 * Callback called when the response headers have been completely received from the server. This implementation does nothing.
960 *
961 * @throws IOException
962 * allowed to be thrown by overriding code
963 */
964 protected void onResponseHeaderComplete() throws IOException
965 {
966 }
967
968 /**
969 * Callback called for each chunk of the response content received from the server. This implementation does nothing.
970 *
971 * @param content
972 * the buffer holding the content chunk
973 * @throws IOException
974 * allowed to be thrown by overriding code
975 */
976 protected void onResponseContent(Buffer content) throws IOException
977 {
978 }
979
980 /**
981 * Callback called when the entire response has been received from the server This implementation does nothing.
982 *
983 * @throws IOException
984 * allowed to be thrown by overriding code
985 */
986 protected void onResponseComplete() throws IOException
987 {
988 }
989
990 /**
991 * Callback called when an exception was thrown during an attempt to establish the connection with the server (for example the server is not listening).
992 * This implementation logs a warning.
993 *
994 * @param x
995 * the exception thrown attempting to establish the connection with the server
996 */
997 protected void onConnectionFailed(Throwable x)
998 {
999 LOG.warn("CONNECTION FAILED " + this,x);
1000 }
1001
1002 /**
1003 * Callback called when any other exception occurs during the handling of this exchange. This implementation logs a warning.
1004 *
1005 * @param x
1006 * the exception thrown during the handling of this exchange
1007 */
1008 protected void onException(Throwable x)
1009 {
1010 LOG.warn("EXCEPTION " + this,x);
1011 }
1012
1013 /**
1014 * Callback called when no response has been received within the timeout. This implementation logs a warning.
1015 */
1016 protected void onExpire()
1017 {
1018 LOG.warn("EXPIRED " + this);
1019 }
1020
1021 /**
1022 * Callback called when the request is retried (due to failures or authentication). Implementations must reset any consumable content that needs to be sent.
1023 *
1024 * @throws IOException
1025 * allowed to be thrown by overriding code
1026 */
1027 protected void onRetry() throws IOException
1028 {
1029 if (_requestContentSource != null)
1030 {
1031 if (_requestContentSource.markSupported())
1032 {
1033 _requestContent = null;
1034 _requestContentSource.reset();
1035 }
1036 else
1037 {
1038 throw new IOException("Unsupported retry attempt");
1039 }
1040 }
1041 }
1042
1043 /**
1044 * @return true if the exchange should have listeners configured for it by the destination, false if this is being managed elsewhere
1045 * @see #setConfigureListeners(boolean)
1046 */
1047 public boolean configureListeners()
1048 {
1049 return _configureListeners;
1050 }
1051
1052 /**
1053 * @param autoConfigure
1054 * whether the listeners are configured by the destination or elsewhere
1055 */
1056 public void setConfigureListeners(boolean autoConfigure)
1057 {
1058 this._configureListeners = autoConfigure;
1059 }
1060
1061 protected void scheduleTimeout(final HttpDestination destination)
1062 {
1063 assert _timeoutTask == null;
1064
1065 _timeoutTask = new Timeout.Task()
1066 {
1067 @Override
1068 public void expired()
1069 {
1070 HttpExchange.this.expire(destination);
1071 }
1072 };
1073
1074 HttpClient httpClient = destination.getHttpClient();
1075 long timeout = getTimeout();
1076 if (timeout > 0)
1077 httpClient.schedule(_timeoutTask,timeout);
1078 else
1079 httpClient.schedule(_timeoutTask);
1080 }
1081
1082 protected void cancelTimeout(HttpClient httpClient)
1083 {
1084 Timeout.Task task = _timeoutTask;
1085 if (task != null)
1086 httpClient.cancel(task);
1087 _timeoutTask = null;
1088 }
1089
1090 private class Listener implements HttpEventListener
1091 {
1092 public void onConnectionFailed(Throwable ex)
1093 {
1094 try
1095 {
1096 HttpExchange.this.onConnectionFailed(ex);
1097 }
1098 finally
1099 {
1100 done();
1101 }
1102 }
1103
1104 public void onException(Throwable ex)
1105 {
1106 try
1107 {
1108 HttpExchange.this.onException(ex);
1109 }
1110 finally
1111 {
1112 done();
1113 }
1114 }
1115
1116 public void onExpire()
1117 {
1118 try
1119 {
1120 HttpExchange.this.onExpire();
1121 }
1122 finally
1123 {
1124 done();
1125 }
1126 }
1127
1128 public void onRequestCommitted() throws IOException
1129 {
1130 HttpExchange.this.onRequestCommitted();
1131 }
1132
1133 public void onRequestComplete() throws IOException
1134 {
1135 try
1136 {
1137 HttpExchange.this.onRequestComplete();
1138 }
1139 finally
1140 {
1141 synchronized (HttpExchange.this)
1142 {
1143 _onRequestCompleteDone = true;
1144 // Member _onDone may already be true, for example
1145 // because the exchange expired or has been canceled
1146 _onDone |= _onResponseCompleteDone;
1147 if (_onDone)
1148 disassociate();
1149 HttpExchange.this.notifyAll();
1150 }
1151 }
1152 }
1153
1154 public void onResponseComplete() throws IOException
1155 {
1156 try
1157 {
1158 HttpExchange.this.onResponseComplete();
1159 }
1160 finally
1161 {
1162 synchronized (HttpExchange.this)
1163 {
1164 _onResponseCompleteDone = true;
1165 // Member _onDone may already be true, for example
1166 // because the exchange expired or has been canceled
1167 _onDone |= _onRequestCompleteDone;
1168 if (_onDone)
1169 disassociate();
1170 HttpExchange.this.notifyAll();
1171 }
1172 }
1173 }
1174
1175 public void onResponseContent(Buffer content) throws IOException
1176 {
1177 HttpExchange.this.onResponseContent(content);
1178 }
1179
1180 public void onResponseHeader(Buffer name, Buffer value) throws IOException
1181 {
1182 HttpExchange.this.onResponseHeader(name,value);
1183 }
1184
1185 public void onResponseHeaderComplete() throws IOException
1186 {
1187 HttpExchange.this.onResponseHeaderComplete();
1188 }
1189
1190 public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
1191 {
1192 HttpExchange.this.onResponseStatus(version,status,reason);
1193 }
1194
1195 public void onRetry()
1196 {
1197 HttpExchange.this.setRetryStatus(true);
1198 try
1199 {
1200 HttpExchange.this.onRetry();
1201 }
1202 catch (IOException e)
1203 {
1204 LOG.debug(e);
1205 }
1206 }
1207 }
1208
1209 /**
1210 * @deprecated use {@link org.eclipse.jetty.client.CachedExchange} instead
1211 */
1212 @Deprecated
1213 public static class CachedExchange extends org.eclipse.jetty.client.CachedExchange
1214 {
1215 public CachedExchange(boolean cacheFields)
1216 {
1217 super(cacheFields);
1218 }
1219 }
1220
1221 /**
1222 * @deprecated use {@link org.eclipse.jetty.client.ContentExchange} instead
1223 */
1224 @Deprecated
1225 public static class ContentExchange extends org.eclipse.jetty.client.ContentExchange
1226 {
1227 }
1228}