View Javadoc

    1   // ========================================================================
    2   // Copyright 2006-2007 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14
    15  package org.mortbay.jetty.client;
    16
    17  import java.io.IOException;
    18  import java.io.InputStream;
    19  import java.net.InetSocketAddress;
    20
    21  import org.mortbay.io.Buffer;
    22  import org.mortbay.io.BufferCache.CachedBuffer;
    23  import org.mortbay.io.ByteArrayBuffer;
    24  import org.mortbay.jetty.HttpFields;
    25  import org.mortbay.jetty.HttpHeaders;
    26  import org.mortbay.jetty.HttpMethods;
    27  import org.mortbay.jetty.HttpSchemes;
    28  import org.mortbay.jetty.HttpURI;
    29  import org.mortbay.jetty.HttpVersions;
    30  import org.mortbay.log.Log;
    31
    32
    33  /**
    34   * An HTTP client API that encapsulates Exchange with a HTTP server.
    35   *
    36   * This object encapsulates:<ul>
    37   * <li>The HTTP server. (see {@link #setAddress(InetSocketAddress)} or {@link #setURL(String)})
    38   * <li>The HTTP request method, URI and HTTP version (see {@link #setMethod(String)}, {@link #setURI(String)}, and {@link #setVersion(int)}
    39   * <li>The Request headers (see {@link #addRequestHeader(String, String)} or {@link #setRequestHeader(String, String)})
    40   * <li>The Request content (see {@link #setRequestContent(Buffer)} or {@link #setRequestContentSource(InputStream)})
    41   * <li>The status of the exchange (see {@link #getStatus()})
    42   * <li>Callbacks to handle state changes (see the onXxx methods such as {@link #onRequestComplete()} or {@link #onResponseComplete()})
    43   * <li>The ability to intercept callbacks (see {@link #setEventListener(HttpEventListener)}
    44   * </ul>
    45   *
    46   * The HttpExchange class is intended to be used by a developer wishing to have close asynchronous
    47   * interaction with the the exchange.  Typically a developer will extend the HttpExchange class with a derived
    48   * class that implements some or all of the onXxx callbacks.  There are also some predefined HttpExchange subtypes
    49   * that can be used as a basis (see {@link ContentExchange} and {@link CachedExchange}.
    50   *
    51   * <p>Typically the HttpExchange is passed to a the {@link HttpClient#send(HttpExchange)} method, which in
    52   * turn selects a {@link HttpDestination} and calls it's {@link HttpDestination#send(HttpExchange), which
    53   * then creates or selects a {@link HttpConnection} and calls its {@link HttpConnection#send(HttpExchange).
    54   * A developer may wish to directly call send on the destination or connection if they wish to bypass
    55   * some handling provided (eg Cookie handling in the HttpDestination).
    56   *
    57   * <p>In some circumstances, the HttpClient or HttpDestination may wish to retry a HttpExchange (eg. failed
    58   * pipeline request, authentication retry or redirection).  In such cases, the HttpClient and/or HttpDestination
    59   * may insert their own HttpExchangeListener to intercept and filter the call backs intended for the
    60   * HttpExchange.
    61   *
    62   * @author gregw
    63   * @author Guillaume Nodet
    64   */
    65  public class HttpExchange
    66  {
    67      public static final int STATUS_START = 0;
    68      public static final int STATUS_WAITING_FOR_CONNECTION = 1;
    69      public static final int STATUS_WAITING_FOR_COMMIT = 2;
    70      public static final int STATUS_SENDING_REQUEST = 3;
    71      public static final int STATUS_WAITING_FOR_RESPONSE = 4;
    72      public static final int STATUS_PARSING_HEADERS = 5;
    73      public static final int STATUS_PARSING_CONTENT = 6;
    74      public static final int STATUS_COMPLETED = 7;
    75      public static final int STATUS_EXPIRED = 8;
    76      public static final int STATUS_EXCEPTED = 9;
    77
    78      Address _address;
    79      String _method = HttpMethods.GET;
    80      Buffer _scheme = HttpSchemes.HTTP_BUFFER;
    81      int _version = HttpVersions.HTTP_1_1_ORDINAL;
    82      String _uri;
    83      int _status = STATUS_START;
    84      HttpFields _requestFields = new HttpFields();
    85      Buffer _requestContent;
    86      InputStream _requestContentSource;
    87      Buffer _requestContentChunk;
    88      boolean _retryStatus = false;
    89
    90
    91      /**
    92       * boolean controlling if the exchange will have listeners autoconfigured by
    93       * the destination
    94       */
    95      boolean _configureListeners = true;
    96
    97
    98      private HttpEventListener _listener = new Listener();
    99
    100     /* ------------------------------------------------------------ */
    101     /* ------------------------------------------------------------ */
    102     /* ------------------------------------------------------------ */
    103     // methods to build request
    104
    105     /* ------------------------------------------------------------ */
    106     public int getStatus()
    107     {
    108         return _status;
    109     }
    110
    111     /* ------------------------------------------------------------ */
    112     /**
    113      * @deprecated
    114      */
    115     public void waitForStatus(int status) throws InterruptedException
    116     {
    117         synchronized (this)
    118         {
    119             while (_status < status)
    120             {
    121                 this.wait();
    122             }
    123         }
    124     }
    125
    126
    127     public int waitForDone () throws InterruptedException
    128     {
    129         synchronized (this)
    130         {
    131             while (!isDone(_status))
    132                 this.wait();
    133         }
    134         return _status;
    135     }
    136
    137
    138
    139
    140     /* ------------------------------------------------------------ */
    141     public void reset()
    142     {
    143         setStatus(STATUS_START);
    144     }
    145
    146     /* ------------------------------------------------------------ */
    147     void setStatus(int status)
    148     {
    149         synchronized (this)
    150         {
    151             _status = status;
    152             this.notifyAll();
    153
    154             try
    155             {
    156                 switch (status)
    157                 {
    158                     case STATUS_WAITING_FOR_CONNECTION:
    159                         break;
    160
    161                     case STATUS_WAITING_FOR_COMMIT:
    162                         break;
    163
    164                     case STATUS_SENDING_REQUEST:
    165                         break;
    166
    167                     case HttpExchange.STATUS_WAITING_FOR_RESPONSE:
    168                         getEventListener().onRequestCommitted();
    169                         break;
    170
    171                     case STATUS_PARSING_HEADERS:
    172                         break;
    173
    174                     case STATUS_PARSING_CONTENT:
    175                         getEventListener().onResponseHeaderComplete();
    176                         break;
    177
    178                     case STATUS_COMPLETED:
    179                         getEventListener().onResponseComplete();
    180                         break;
    181
    182                     case STATUS_EXPIRED:
    183                         getEventListener().onExpire();
    184                         break;
    185
    186                 }
    187             }
    188             catch (IOException e)
    189             {
    190                 Log.warn(e);
    191             }
    192         }
    193     }
    194
    195     /* ------------------------------------------------------------ */
    196     public boolean isDone (int status)
    197     {
    198         return ((status == STATUS_COMPLETED) || (status == STATUS_EXPIRED) || (status == STATUS_EXCEPTED));
    199     }
    200
    201     /* ------------------------------------------------------------ */
    202     public HttpEventListener getEventListener()
    203     {
    204         return _listener;
    205     }
    206
    207     /* ------------------------------------------------------------ */
    208     public void setEventListener(HttpEventListener listener)
    209     {
    210         _listener=listener;
    211     }
    212
    213     /* ------------------------------------------------------------ */
    214     /**
    215      * @param url Including protocol, host and port
    216      */
    217     public void setURL(String url)
    218     {
    219         HttpURI uri = new HttpURI(url);
    220         String scheme = uri.getScheme();
    221         if (scheme != null)
    222         {
    223             if (HttpSchemes.HTTP.equalsIgnoreCase(scheme))
    224                 setScheme(HttpSchemes.HTTP_BUFFER);
    225             else if (HttpSchemes.HTTPS.equalsIgnoreCase(scheme))
    226                 setScheme(HttpSchemes.HTTPS_BUFFER);
    227             else
    228                 setScheme(new ByteArrayBuffer(scheme));
    229         }
    230
    231         int port = uri.getPort();
    232         if (port <= 0)
    233             port = "https".equalsIgnoreCase(scheme)?443:80;
    234
    235         setAddress(new Address(uri.getHost(),port));
    236
    237         String completePath = uri.getCompletePath();
    238         if (completePath != null)
    239             setURI(completePath);
    240     }
    241
    242     /* ------------------------------------------------------------ */
    243     /**
    244      * @param address
    245      */
    246     public void setAddress(Address address)
    247     {
    248         _address = address;
    249     }
    250
    251     /* ------------------------------------------------------------ */
    252     /**
    253      * @return
    254      */
    255     public Address getAddress()
    256     {
    257         return _address;
    258     }
    259
    260     /* ------------------------------------------------------------ */
    261     /**
    262      * @param scheme
    263      */
    264     public void setScheme(Buffer scheme)
    265     {
    266         _scheme = scheme;
    267     }
    268
    269     /* ------------------------------------------------------------ */
    270     /**
    271      * @return
    272      */
    273     public Buffer getScheme()
    274     {
    275         return _scheme;
    276     }
    277
    278     /* ------------------------------------------------------------ */
    279     /**
    280      * @param version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
    281      */
    282     public void setVersion(int version)
    283     {
    284         _version = version;
    285     }
    286
    287     /* ------------------------------------------------------------ */
    288     public void setVersion(String version)
    289     {
    290         CachedBuffer v = HttpVersions.CACHE.get(version);
    291         if (v == null)
    292             _version = 10;
    293         else
    294             _version = v.getOrdinal();
    295     }
    296
    297     /* ------------------------------------------------------------ */
    298     /**
    299      * @return
    300      */
    301     public int getVersion()
    302     {
    303         return _version;
    304     }
    305
    306     /* ------------------------------------------------------------ */
    307     /**
    308      * @param method
    309      */
    310     public void setMethod(String method)
    311     {
    312         _method = method;
    313     }
    314
    315     /* ------------------------------------------------------------ */
    316     /**
    317      * @return
    318      */
    319     public String getMethod()
    320     {
    321         return _method;
    322     }
    323
    324     /* ------------------------------------------------------------ */
    325     /**
    326      * @return
    327      */
    328     public String getURI()
    329     {
    330         return _uri;
    331     }
    332
    333     /* ------------------------------------------------------------ */
    334     /**
    335      * @param uri
    336      */
    337     public void setURI(String uri)
    338     {
    339         _uri = uri;
    340     }
    341
    342     /* ------------------------------------------------------------ */
    343     /**
    344      * @param name
    345      * @param value
    346      */
    347     public void addRequestHeader(String name, String value)
    348     {
    349         getRequestFields().add(name,value);
    350     }
    351
    352     /* ------------------------------------------------------------ */
    353     /**
    354      * @param name
    355      * @param value
    356      */
    357     public void addRequestHeader(Buffer name, Buffer value)
    358     {
    359         getRequestFields().add(name,value);
    360     }
    361
    362     /* ------------------------------------------------------------ */
    363     /**
    364      * @param name
    365      * @param value
    366      */
    367     public void setRequestHeader(String name, String value)
    368     {
    369         getRequestFields().put(name,value);
    370     }
    371
    372     /* ------------------------------------------------------------ */
    373     /**
    374      * @param name
    375      * @param value
    376      */
    377     public void setRequestHeader(Buffer name, Buffer value)
    378     {
    379         getRequestFields().put(name,value);
    380     }
    381
    382     /* ------------------------------------------------------------ */
    383     /**
    384      * @param value
    385      */
    386     public void setRequestContentType(String value)
    387     {
    388         getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,value);
    389     }
    390
    391     /* ------------------------------------------------------------ */
    392     /**
    393      * @return
    394      */
    395     public HttpFields getRequestFields()
    396     {
    397         return _requestFields;
    398     }
    399
    400     /* ------------------------------------------------------------ */
    401     /* ------------------------------------------------------------ */
    402     /* ------------------------------------------------------------ */
    403     // methods to commit and/or send the request
    404
    405     /* ------------------------------------------------------------ */
    406     /**
    407      * @param requestContent
    408      */
    409     public void setRequestContent(Buffer requestContent)
    410     {
    411         _requestContent = requestContent;
    412     }
    413
    414     /* ------------------------------------------------------------ */
    415     /**
    416      * @param in
    417      */
    418     public void setRequestContentSource(InputStream in)
    419     {
    420         _requestContentSource = in;
    421     }
    422
    423     /* ------------------------------------------------------------ */
    424     public InputStream getRequestContentSource()
    425     {
    426         return _requestContentSource;
    427     }
    428
    429     /* ------------------------------------------------------------ */
    430     public Buffer getRequestContentChunk() throws IOException
    431     {
    432         synchronized (this)
    433         {
    434             if (_requestContentChunk == null)
    435                 _requestContentChunk = new ByteArrayBuffer(4096); // TODO configure
    436             else
    437             {
    438                 if (_requestContentChunk.hasContent())
    439                     throw new IllegalStateException();
    440                 _requestContentChunk.clear();
    441             }
    442
    443             int read = _requestContentChunk.capacity();
    444             int length = _requestContentSource.read(_requestContentChunk.array(),0,read);
    445             if (length >= 0)
    446             {
    447                 _requestContentChunk.setPutIndex(length);
    448                 return _requestContentChunk;
    449             }
    450             return null;
    451         }
    452     }
    453
    454     /* ------------------------------------------------------------ */
    455     public Buffer getRequestContent()
    456     {
    457         return _requestContent;
    458     }
    459
    460     public boolean getRetryStatus()
    461     {
    462         return _retryStatus;
    463     }
    464
    465     public void setRetryStatus( boolean retryStatus )
    466     {
    467         _retryStatus = retryStatus;
    468     }
    469
    470     /* ------------------------------------------------------------ */
    471     /** Cancel this exchange
    472      * Currently this implementation does nothing.
    473      */
    474     public void cancel()
    475     {
    476
    477     }
    478
    479     /* ------------------------------------------------------------ */
    480     public String toString()
    481     {
    482         return "HttpExchange@" + hashCode() + "=" + _method + "//" + _address.getHost() + ":" + _address.getPort() + _uri + "#" + _status;
    483     }
    484
    485
    486
    487     /* ------------------------------------------------------------ */
    488     /* ------------------------------------------------------------ */
    489     /* ------------------------------------------------------------ */
    490     // methods to handle response
    491     protected void onRequestCommitted() throws IOException
    492     {
    493     }
    494
    495     protected void onRequestComplete() throws IOException
    496     {
    497     }
    498
    499     protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
    500     {
    501     }
    502
    503     protected void onResponseHeader(Buffer name, Buffer value) throws IOException
    504     {
    505     }
    506
    507     protected void onResponseHeaderComplete() throws IOException
    508     {
    509     }
    510
    511     protected void onResponseContent(Buffer content) throws IOException
    512     {
    513     }
    514
    515     protected void onResponseComplete() throws IOException
    516     {
    517     }
    518
    519     protected void onConnectionFailed(Throwable ex)
    520     {
    521         Log.warn("CONNECTION FAILED on " + this,ex);
    522     }
    523
    524     protected void onException(Throwable ex)
    525     {
    526
    527         Log.warn("EXCEPTION on " + this,ex);
    528     }
    529
    530     protected void onExpire()
    531     {
    532         Log.debug("EXPIRED " + this);
    533     }
    534
    535     protected void onRetry() throws IOException
    536     {}
    537
    538     /**
    539      * true of the exchange should have listeners configured for it by the destination
    540      *
    541      * false if this is being managed elsewhere
    542      *
    543      * @return
    544      */
    545     public boolean configureListeners()
    546     {
    547         return _configureListeners;
    548     }
    549
    550     public void setConfigureListeners(boolean autoConfigure )
    551     {
    552         this._configureListeners = autoConfigure;
    553     }
    554
    555     private class Listener implements HttpEventListener
    556     {
    557         public void onConnectionFailed(Throwable ex)
    558         {
    559             HttpExchange.this.onConnectionFailed(ex);
    560         }
    561
    562         public void onException(Throwable ex)
    563         {
    564             HttpExchange.this.onException(ex);
    565         }
    566
    567         public void onExpire()
    568         {
    569             HttpExchange.this.onExpire();
    570         }
    571
    572         public void onRequestCommitted() throws IOException
    573         {
    574             HttpExchange.this.onRequestCommitted();
    575         }
    576
    577         public void onRequestComplete() throws IOException
    578         {
    579             HttpExchange.this.onRequestComplete();
    580         }
    581
    582         public void onResponseComplete() throws IOException
    583         {
    584             HttpExchange.this.onResponseComplete();
    585         }
    586
    587         public void onResponseContent(Buffer content) throws IOException
    588         {
    589             HttpExchange.this.onResponseContent(content);
    590         }
    591
    592         public void onResponseHeader(Buffer name, Buffer value) throws IOException
    593         {
    594             HttpExchange.this.onResponseHeader(name,value);
    595         }
    596
    597         public void onResponseHeaderComplete() throws IOException
    598         {
    599             HttpExchange.this.onResponseHeaderComplete();
    600         }
    601
    602         public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
    603         {
    604             HttpExchange.this.onResponseStatus(version,status,reason);
    605         }
    606
    607         public void onRetry()
    608         {
    609             HttpExchange.this.setRetryStatus( true );
    610             try
    611             {
    612                 HttpExchange.this.onRetry();
    613             }
    614             catch (IOException e)
    615             {
    616                 e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
    617             }
    618         }
    619     }
    620
    621     /**
    622      * @deprecated use {@link org.mortbay.jetty.client.CachedExchange}
    623      *
    624      */
    625     public static class CachedExchange extends org.mortbay.jetty.client.CachedExchange
    626     {
    627         public CachedExchange(boolean cacheFields)
    628         {
    629             super(cacheFields);
    630         }
    631     }
    632
    633     /**
    634      * @deprecated use {@link org.mortbay.jetty.client.ContentExchange}
    635      *
    636      */
    637     public static class ContentExchange extends org.mortbay.jetty.client.ContentExchange
    638     {
    639
    640     }
    641
    642
    643
    644 }