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     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 }