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