View Javadoc

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     boolean _onRequestCompleteDone;
101     boolean _onResponseCompleteDone;
102     boolean _onDone; // == onConnectionFail || onException || onExpired || onCancelled || onResponseCompleted && onRequestCompleted
103 
104 
105     public int getStatus()
106     {
107         return _status.get();
108     }
109 
110     /**
111      * @param status the status to wait for
112      * @throws InterruptedException if the waiting thread is interrupted
113      * @deprecated Use {@link #waitForDone()} instead
114      */
115     @Deprecated
116     public void waitForStatus(int status) throws InterruptedException
117     {
118         throw new UnsupportedOperationException();
119     }
120 
121     /**
122      * Wait until the exchange is "done".
123      * Done is defined as when a final state has been passed to the
124      * HttpExchange via the associated onXxx call.  Note that an
125      * exchange can transit a final state when being used as part
126      * of a dialog (eg {@link SecurityListener}.   Done status
127      * is thus defined as:<pre>
128      *   done == onConnectionFailed
129      *        || onException
130      *        || onExpire
131      *        || onRequestComplete && onResponseComplete
132      * </pre>
133      * @return the done status
134      * @throws InterruptedException
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         // TODO - this should do a cancel and wakeup everybody that was waiting.
149         // might need a version number concept
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             // State machine: from which old status you can go into which new status
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                             // Don't change the status, it's too late
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                             // Ignore other statuses, we're cancelling
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                     // Here means I allowed to set a state that I don't recognize
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      * @param url Including protocol, host and port
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      * @param address the address of the server
370      */
371     public void setAddress(Address address)
372     {
373         _address = address;
374     }
375 
376     /**
377      * @return the address of the server
378      */
379     public Address getAddress()
380     {
381         return _address;
382     }
383 
384     /**
385      * @param scheme the scheme of the URL (for example 'http')
386      */
387     public void setScheme(Buffer scheme)
388     {
389         _scheme = scheme;
390     }
391 
392     /**
393      * @return the scheme of the URL
394      */
395     public Buffer getScheme()
396     {
397         return _scheme;
398     }
399 
400     /**
401      * @param version the HTTP protocol version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
402      */
403     public void setVersion(int version)
404     {
405         _version = version;
406     }
407 
408     /**
409      * @param version the HTTP protocol version as string
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      * @return the HTTP protocol version as integer
422      * @see #setVersion(int)
423      */
424     public int getVersion()
425     {
426         return _version;
427     }
428 
429     /**
430      * @param method the HTTP method (for example 'GET')
431      */
432     public void setMethod(String method)
433     {
434         _method = method;
435     }
436 
437     /**
438      * @return the HTTP method
439      */
440     public String getMethod()
441     {
442         return _method;
443     }
444 
445     /**
446      * @return the path of the URL
447      */
448     public String getURI()
449     {
450         return _uri;
451     }
452 
453     /**
454      * @param uri the path of the URL (for example '/foo/bar?a=1')
455      */
456     public void setURI(String uri)
457     {
458         _uri = uri;
459     }
460 
461     /**
462      * Adds the specified request header
463      * @param name the header name
464      * @param value the header value
465      */
466     public void addRequestHeader(String name, String value)
467     {
468         getRequestFields().add(name,value);
469     }
470 
471     /**
472      * Adds the specified request header
473      * @param name the header name
474      * @param value the header value
475      */
476     public void addRequestHeader(Buffer name, Buffer value)
477     {
478         getRequestFields().add(name,value);
479     }
480 
481     /**
482      * Sets the specified request header
483      * @param name the header name
484      * @param value the header value
485      */
486     public void setRequestHeader(String name, String value)
487     {
488         getRequestFields().put(name,value);
489     }
490 
491     /**
492      * Sets the specified request header
493      * @param name the header name
494      * @param value the header value
495      */
496     public void setRequestHeader(Buffer name, Buffer value)
497     {
498         getRequestFields().put(name,value);
499     }
500 
501     /**
502      * @param value the content type of the request
503      */
504     public void setRequestContentType(String value)
505     {
506         getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,value);
507     }
508 
509     /**
510      * @return the request headers
511      */
512     public HttpFields getRequestFields()
513     {
514         return _requestFields;
515     }
516 
517     /**
518      * @param requestContent the request content
519      */
520     public void setRequestContent(Buffer requestContent)
521     {
522         _requestContent = requestContent;
523     }
524 
525     /**
526      * @param stream the request content as a stream
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      * @return the request content as a stream
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); // TODO configure
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      * @return the request content
569      */
570     public Buffer getRequestContent()
571     {
572         return _requestContent;
573     }
574 
575     /**
576      * @return whether a retry will be attempted or not
577      */
578     public boolean getRetryStatus()
579     {
580         return _retryStatus;
581     }
582 
583     /**
584      * @param retryStatus whether a retry will be attempted or not
585      */
586     public void setRetryStatus(boolean retryStatus)
587     {
588         _retryStatus = retryStatus;
589     }
590 
591     /**
592      * Initiates the cancelling of this exchange.
593      * The status of the exchange is set to {@link #STATUS_CANCELLING}.
594      * Cancelling the exchange is an asynchronous operation with respect to the request/response,
595      * and as such checking the request/response status of a cancelled exchange may return undefined results
596      * (for example it may have only some of the response headers being sent by the server).
597      * The cancelling of the exchange is completed when the exchange status (see {@link #getStatus()}) is
598      * {@link #STATUS_CANCELLED}, and this can be waited using {@link #waitForDone()}.
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                 // Closing the connection here will cause the connection
624                 // to be returned in HttpConnection.handle()
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      * Callback called when the request headers have been sent to the server.
670      * This implementation does nothing.
671      * @throws IOException allowed to be thrown by overriding code
672      */
673     protected void onRequestCommitted() throws IOException
674     {
675     }
676 
677     /**
678      * Callback called when the request and its body have been sent to the server.
679      * This implementation does nothing.
680      * @throws IOException allowed to be thrown by overriding code
681      */
682     protected void onRequestComplete() throws IOException
683     {
684     }
685 
686     /**
687      * Callback called when a response status line has been received from the server.
688      * This implementation does nothing.
689      * @param version the HTTP version
690      * @param status the HTTP status code
691      * @param reason the HTTP status reason string
692      * @throws IOException allowed to be thrown by overriding code
693      */
694     protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
695     {
696     }
697 
698     /**
699      * Callback called for each response header received from the server.
700      * This implementation does nothing.
701      * @param name the header name
702      * @param value the header value
703      * @throws IOException allowed to be thrown by overriding code
704      */
705     protected void onResponseHeader(Buffer name, Buffer value) throws IOException
706     {
707     }
708 
709     /**
710      * Callback called when the response headers have been completely received from the server.
711      * This implementation does nothing.
712      * @throws IOException allowed to be thrown by overriding code
713      */
714     protected void onResponseHeaderComplete() throws IOException
715     {
716     }
717 
718     /**
719      * Callback called for each chunk of the response content received from the server.
720      * This implementation does nothing.
721      * @param content the buffer holding the content chunk
722      * @throws IOException allowed to be thrown by overriding code
723      */
724     protected void onResponseContent(Buffer content) throws IOException
725     {
726     }
727 
728     /**
729      * Callback called when the entire response has been received from the server
730      * This implementation does nothing.
731      * @throws IOException allowed to be thrown by overriding code
732      */
733     protected void onResponseComplete() throws IOException
734     {
735     }
736 
737     /**
738      * Callback called when an exception was thrown during an attempt to establish the connection
739      * with the server (for example the server is not listening).
740      * This implementation logs a warning.
741      * @param x the exception thrown attempting to establish the connection with the server
742      */
743     protected void onConnectionFailed(Throwable x)
744     {
745         Log.warn("CONNECTION FAILED " + this,x);
746     }
747 
748     /**
749      * Callback called when any other exception occurs during the handling of this exchange.
750      * This implementation logs a warning.
751      * @param x the exception thrown during the handling of this exchange
752      */
753     protected void onException(Throwable x)
754     {
755         Log.warn("EXCEPTION " + this,x);
756     }
757 
758     /**
759      * Callback called when no response has been received within the timeout.
760      * This implementation logs a warning.
761      */
762     protected void onExpire()
763     {
764         Log.warn("EXPIRED " + this);
765     }
766 
767     /**
768      * Callback called when the request is retried (due to failures or authentication).
769      * Implementations must reset any consumable content that needs to be sent.
770      * @throws IOException allowed to be thrown by overriding code
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      * @return true if the exchange should have listeners configured for it by the destination,
790      * false if this is being managed elsewhere
791      * @see #setConfigureListeners(boolean)
792      */
793     public boolean configureListeners()
794     {
795         return _configureListeners;
796     }
797 
798     /**
799      * @param autoConfigure whether the listeners are configured by the destination or elsewhere
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      * @deprecated use {@link org.eclipse.jetty.client.CachedExchange} instead
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      * @deprecated use {@link org.eclipse.jetty.client.ContentExchange} instead
936      */
937     @Deprecated
938     public static class ContentExchange extends org.eclipse.jetty.client.ContentExchange
939     {
940     }
941 }