1 // ========================================================================
2 // Copyright (c) 2006-2009 Mort Bay Consulting Pty. Ltd.
3 // ------------------------------------------------------------------------
4 // All rights reserved. This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v1.0
6 // and Apache License v2.0 which accompanies this distribution.
7 // The Eclipse Public License is available at
8 // http://www.eclipse.org/legal/epl-v10.html
9 // The Apache License v2.0 is available at
10 // http://www.opensource.org/licenses/apache2.0.php
11 // You may elect to redistribute this code under either of these licenses.
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 * <p>An HTTP client API that encapsulates an exchange (a request and its response) with a HTTP server.</p>
37 *
38 * This object encapsulates:
39 * <ul>
40 * <li>The HTTP server address, see {@link #setAddress(Address)} or {@link #setURL(String)})
41 * <li>The HTTP request method, URI and HTTP version (see {@link #setMethod(String)}, {@link #setURI(String)}, and {@link #setVersion(int)}
42 * <li>The request headers (see {@link #addRequestHeader(String, String)} or {@link #setRequestHeader(String, String)})
43 * <li>The request content (see {@link #setRequestContent(Buffer)} or {@link #setRequestContentSource(InputStream)})
44 * <li>The status of the exchange (see {@link #getStatus()})
45 * <li>Callbacks to handle state changes (see the onXxx methods such as {@link #onRequestComplete()} or {@link #onResponseComplete()})
46 * <li>The ability to intercept callbacks (see {@link #setEventListener(HttpEventListener)}
47 * </ul>
48 *
49 * <p>The HttpExchange class is intended to be used by a developer wishing to have close asynchronous
50 * interaction with the the exchange.<br />
51 * Typically a developer will extend the HttpExchange class with a derived
52 * class that overrides some or all of the onXxx callbacks. <br />
53 * There are also some predefined HttpExchange subtypes that can be used as a basis,
54 * see {@link org.eclipse.jetty.client.ContentExchange} and {@link org.eclipse.jetty.client.CachedExchange}.</p>
55 *
56 * <p>Typically the HttpExchange is passed to the {@link HttpClient#send(HttpExchange)} method, which in
57 * turn selects a {@link HttpDestination} and calls its {@link HttpDestination#send(HttpExchange)}, which
58 * then creates or selects a {@link HttpConnection} and calls its {@link HttpConnection#send(HttpExchange)}.
59 * A developer may wish to directly call send on the destination or connection if they wish to bypass
60 * some handling provided (eg Cookie handling in the HttpDestination).</p>
61 *
62 * <p>In some circumstances, the HttpClient or HttpDestination may wish to retry a HttpExchange (eg. failed
63 * pipeline request, authentication retry or redirection). In such cases, the HttpClient and/or HttpDestination
64 * may insert their own HttpExchangeListener to intercept and filter the call backs intended for the
65 * HttpExchange.</p>
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 // HTTP protocol fields
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 // controls if the exchange will have listeners autoconfigured by the destination
96 private boolean _configureListeners = true;
97 private HttpEventListener _listener = new Listener();
98 private volatile HttpConnection _connection;
99
100 private Address _localAddress = null;
101
102 // a timeout for this exchange
103 private long _timeout = -1;
104
105 boolean _onRequestCompleteDone;
106 boolean _onResponseCompleteDone;
107 boolean _onDone; // == onConnectionFail || onException || onExpired || onCancelled || onResponseCompleted && onRequestCompleted
108
109
110 public int getStatus()
111 {
112 return _status.get();
113 }
114
115 /**
116 * @param status the status to wait for
117 * @throws InterruptedException if the waiting thread is interrupted
118 * @deprecated Use {@link #waitForDone()} instead
119 */
120 @Deprecated
121 public void waitForStatus(int status) throws InterruptedException
122 {
123 throw new UnsupportedOperationException();
124 }
125
126 /**
127 * Wait until the exchange is "done".
128 * Done is defined as when a final state has been passed to the
129 * HttpExchange via the associated onXxx call. Note that an
130 * exchange can transit a final state when being used as part
131 * of a dialog (eg {@link SecurityListener}. Done status
132 * is thus defined as:<pre>
133 * done == onConnectionFailed
134 * || onException
135 * || onExpire
136 * || onRequestComplete && onResponseComplete
137 * </pre>
138 * @return the done status
139 * @throws InterruptedException
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 // TODO - this should do a cancel and wakeup everybody that was waiting.
154 // might need a version number concept
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 // State machine: from which old status you can go into which new status
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 // Don't change the status, it's too late
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 // Ignore other statuses, we're cancelling
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 // Here means I allowed to set a state that I don't recognize
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 * @deprecated
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 * @param url Including protocol, host and port
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 * @param address the address of the server
399 */
400 public void setAddress(Address address)
401 {
402 _address = address;
403 }
404
405 /**
406 * @return the address of the server
407 */
408 public Address getAddress()
409 {
410 return _address;
411 }
412
413 /**
414 * the local address used by the connection
415 *
416 * Note: this method will not be populated unless the exchange
417 * has been executed by the HttpClient
418 *
419 * @return the local address used for the running of the exchange if available, null otherwise.
420 */
421 public Address getLocalAddress()
422 {
423 return _localAddress;
424 }
425
426 /**
427 * @param scheme the scheme of the URL (for example 'http')
428 */
429 public void setScheme(Buffer scheme)
430 {
431 _scheme = scheme;
432 }
433
434 /**
435 * @return the scheme of the URL
436 */
437 public Buffer getScheme()
438 {
439 return _scheme;
440 }
441
442 /**
443 * @param version the HTTP protocol version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
444 */
445 public void setVersion(int version)
446 {
447 _version = version;
448 }
449
450 /**
451 * @param version the HTTP protocol version as string
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 * @return the HTTP protocol version as integer
464 * @see #setVersion(int)
465 */
466 public int getVersion()
467 {
468 return _version;
469 }
470
471 /**
472 * @param method the HTTP method (for example 'GET')
473 */
474 public void setMethod(String method)
475 {
476 _method = method;
477 }
478
479 /**
480 * @return the HTTP method
481 */
482 public String getMethod()
483 {
484 return _method;
485 }
486
487 /**
488 * @return the path of the URL
489 */
490 public String getURI()
491 {
492 return _uri;
493 }
494
495 /**
496 * @param uri the path of the URL (for example '/foo/bar?a=1')
497 */
498 public void setURI(String uri)
499 {
500 _uri = uri;
501 }
502
503 /**
504 * Adds the specified request header
505 * @param name the header name
506 * @param value the header value
507 */
508 public void addRequestHeader(String name, String value)
509 {
510 getRequestFields().add(name,value);
511 }
512
513 /**
514 * Adds the specified request header
515 * @param name the header name
516 * @param value the header value
517 */
518 public void addRequestHeader(Buffer name, Buffer value)
519 {
520 getRequestFields().add(name,value);
521 }
522
523 /**
524 * Sets the specified request header
525 * @param name the header name
526 * @param value the header value
527 */
528 public void setRequestHeader(String name, String value)
529 {
530 getRequestFields().put(name,value);
531 }
532
533 /**
534 * Sets the specified request header
535 * @param name the header name
536 * @param value the header value
537 */
538 public void setRequestHeader(Buffer name, Buffer value)
539 {
540 getRequestFields().put(name,value);
541 }
542
543 /**
544 * @param value the content type of the request
545 */
546 public void setRequestContentType(String value)
547 {
548 getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,value);
549 }
550
551 /**
552 * @return the request headers
553 */
554 public HttpFields getRequestFields()
555 {
556 return _requestFields;
557 }
558
559 /**
560 * @param requestContent the request content
561 */
562 public void setRequestContent(Buffer requestContent)
563 {
564 _requestContent = requestContent;
565 }
566
567 /**
568 * @param stream the request content as a stream
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 * @return the request content as a stream
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); // TODO configure
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 * @return the request content
611 */
612 public Buffer getRequestContent()
613 {
614 return _requestContent;
615 }
616
617 /**
618 * @return whether a retry will be attempted or not
619 */
620 public boolean getRetryStatus()
621 {
622 return _retryStatus;
623 }
624
625 /**
626 * @param retryStatus whether a retry will be attempted or not
627 */
628 public void setRetryStatus(boolean retryStatus)
629 {
630 _retryStatus = retryStatus;
631 }
632
633 /**
634 * Initiates the cancelling of this exchange.
635 * The status of the exchange is set to {@link #STATUS_CANCELLING}.
636 * Cancelling the exchange is an asynchronous operation with respect to the request/response,
637 * and as such checking the request/response status of a cancelled exchange may return undefined results
638 * (for example it may have only some of the response headers being sent by the server).
639 * The cancelling of the exchange is completed when the exchange status (see {@link #getStatus()}) is
640 * {@link #STATUS_CANCELLED}, and this can be waited using {@link #waitForDone()}.
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 // Closing the connection here will cause the connection
666 // to be returned in HttpConnection.handle()
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 HttpConnection disassociate()
694 {
695 HttpConnection 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 * Callback called when the request headers have been sent to the server.
717 * This implementation does nothing.
718 * @throws IOException allowed to be thrown by overriding code
719 */
720 protected void onRequestCommitted() throws IOException
721 {
722 _connection.getEndPoint();
723
724 }
725
726 /**
727 * Callback called when the request and its body have been sent to the server.
728 * This implementation does nothing.
729 * @throws IOException allowed to be thrown by overriding code
730 */
731 protected void onRequestComplete() throws IOException
732 {
733 }
734
735 /**
736 * Callback called when a response status line has been received from the server.
737 * This implementation does nothing.
738 * @param version the HTTP version
739 * @param status the HTTP status code
740 * @param reason the HTTP status reason string
741 * @throws IOException allowed to be thrown by overriding code
742 */
743 protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
744 {
745 }
746
747 /**
748 * Callback called for each response header received from the server.
749 * This implementation does nothing.
750 * @param name the header name
751 * @param value the header value
752 * @throws IOException allowed to be thrown by overriding code
753 */
754 protected void onResponseHeader(Buffer name, Buffer value) throws IOException
755 {
756 }
757
758 /**
759 * Callback called when the response headers have been completely received from the server.
760 * This implementation does nothing.
761 * @throws IOException allowed to be thrown by overriding code
762 */
763 protected void onResponseHeaderComplete() throws IOException
764 {
765 }
766
767 /**
768 * Callback called for each chunk of the response content received from the server.
769 * This implementation does nothing.
770 * @param content the buffer holding the content chunk
771 * @throws IOException allowed to be thrown by overriding code
772 */
773 protected void onResponseContent(Buffer content) throws IOException
774 {
775 }
776
777 /**
778 * Callback called when the entire response has been received from the server
779 * This implementation does nothing.
780 * @throws IOException allowed to be thrown by overriding code
781 */
782 protected void onResponseComplete() throws IOException
783 {
784 }
785
786 /**
787 * Callback called when an exception was thrown during an attempt to establish the connection
788 * with the server (for example the server is not listening).
789 * This implementation logs a warning.
790 * @param x the exception thrown attempting to establish the connection with the server
791 */
792 protected void onConnectionFailed(Throwable x)
793 {
794 Log.warn("CONNECTION FAILED " + this,x);
795 }
796
797 /**
798 * Callback called when any other exception occurs during the handling of this exchange.
799 * This implementation logs a warning.
800 * @param x the exception thrown during the handling of this exchange
801 */
802 protected void onException(Throwable x)
803 {
804 Log.warn("EXCEPTION " + this,x);
805 }
806
807 /**
808 * Callback called when no response has been received within the timeout.
809 * This implementation logs a warning.
810 */
811 protected void onExpire()
812 {
813 Log.warn("EXPIRED " + this);
814 }
815
816 /**
817 * Callback called when the request is retried (due to failures or authentication).
818 * Implementations must reset any consumable content that needs to be sent.
819 * @throws IOException allowed to be thrown by overriding code
820 */
821 protected void onRetry() throws IOException
822 {
823 if (_requestContentSource != null)
824 {
825 if (_requestContentSource.markSupported())
826 {
827 _requestContent = null;
828 _requestContentSource.reset();
829 }
830 else
831 {
832 throw new IOException("Unsupported retry attempt");
833 }
834 }
835 }
836
837 /**
838 * @return true if the exchange should have listeners configured for it by the destination,
839 * false if this is being managed elsewhere
840 * @see #setConfigureListeners(boolean)
841 */
842 public boolean configureListeners()
843 {
844 return _configureListeners;
845 }
846
847 /**
848 * @param autoConfigure whether the listeners are configured by the destination or elsewhere
849 */
850 public void setConfigureListeners(boolean autoConfigure)
851 {
852 this._configureListeners = autoConfigure;
853 }
854
855 private class Listener implements HttpEventListener
856 {
857
858 public void onConnectionFailed(Throwable ex)
859 {
860 try
861 {
862 HttpExchange.this.onConnectionFailed(ex);
863 }
864 finally
865 {
866 done();
867 }
868 }
869
870 public void onException(Throwable ex)
871 {
872 try
873 {
874 HttpExchange.this.onException(ex);
875 }
876 finally
877 {
878 done();
879 }
880 }
881
882 public void onExpire()
883 {
884 try
885 {
886 HttpExchange.this.onExpire();
887 }
888 finally
889 {
890 done();
891 }
892 }
893
894 public void onRequestCommitted() throws IOException
895 {
896 HttpExchange.this.onRequestCommitted();
897 }
898
899 public void onRequestComplete() throws IOException
900 {
901 try
902 {
903 HttpExchange.this.onRequestComplete();
904 }
905 finally
906 {
907 synchronized(HttpExchange.this)
908 {
909 _onRequestCompleteDone=true;
910 _onDone=_onResponseCompleteDone;
911 if (_onDone)
912 disassociate();
913 HttpExchange.this.notifyAll();
914 }
915 }
916 }
917
918 public void onResponseComplete() throws IOException
919 {
920 try
921 {
922 HttpExchange.this.onResponseComplete();
923 }
924 finally
925 {
926 synchronized(HttpExchange.this)
927 {
928 _onResponseCompleteDone=true;
929 _onDone=_onRequestCompleteDone;
930 if (_onDone)
931 disassociate();
932 HttpExchange.this.notifyAll();
933 }
934 }
935 }
936
937 public void onResponseContent(Buffer content) throws IOException
938 {
939 HttpExchange.this.onResponseContent(content);
940 }
941
942 public void onResponseHeader(Buffer name, Buffer value) throws IOException
943 {
944 HttpExchange.this.onResponseHeader(name,value);
945 }
946
947 public void onResponseHeaderComplete() throws IOException
948 {
949 HttpExchange.this.onResponseHeaderComplete();
950 }
951
952 public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
953 {
954 HttpExchange.this.onResponseStatus(version,status,reason);
955 }
956
957 public void onRetry()
958 {
959 HttpExchange.this.setRetryStatus( true );
960 try
961 {
962 HttpExchange.this.onRetry();
963 }
964 catch (IOException e)
965 {
966 Log.debug(e);
967 }
968 }
969 }
970
971 /**
972 * @deprecated use {@link org.eclipse.jetty.client.CachedExchange} instead
973 */
974 @Deprecated
975 public static class CachedExchange extends org.eclipse.jetty.client.CachedExchange
976 {
977 public CachedExchange(boolean cacheFields)
978 {
979 super(cacheFields);
980 }
981 }
982
983 /**
984 * @deprecated use {@link org.eclipse.jetty.client.ContentExchange} instead
985 */
986 @Deprecated
987 public static class ContentExchange extends org.eclipse.jetty.client.ContentExchange
988 {
989 }
990 }