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 }