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