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             completePath = "/";
240         
241         setURI(completePath);
242     }
243 
244     /* ------------------------------------------------------------ */
245     /**
246      * @param address
247      */
248     public void setAddress(Address address)
249     {
250         _address = address;
251     }
252 
253     /* ------------------------------------------------------------ */
254     /**
255      * @return
256      */
257     public Address getAddress()
258     {
259         return _address;
260     }
261 
262     /* ------------------------------------------------------------ */
263     /**
264      * @param scheme
265      */
266     public void setScheme(Buffer scheme)
267     {
268         _scheme = scheme;
269     }
270 
271     /* ------------------------------------------------------------ */
272     /**
273      * @return
274      */
275     public Buffer getScheme()
276     {
277         return _scheme;
278     }
279 
280     /* ------------------------------------------------------------ */
281     /**
282      * @param version as integer, 9, 10 or 11 for 0.9, 1.0 or 1.1
283      */
284     public void setVersion(int version)
285     {
286         _version = version;
287     }
288 
289     /* ------------------------------------------------------------ */
290     public void setVersion(String version)
291     {
292         CachedBuffer v = HttpVersions.CACHE.get(version);
293         if (v == null)
294             _version = 10;
295         else
296             _version = v.getOrdinal();
297     }
298 
299     /* ------------------------------------------------------------ */
300     /**
301      * @return
302      */
303     public int getVersion()
304     {
305         return _version;
306     }
307 
308     /* ------------------------------------------------------------ */
309     /**
310      * @param method
311      */
312     public void setMethod(String method)
313     {
314         _method = method;
315     }
316 
317     /* ------------------------------------------------------------ */
318     /**
319      * @return
320      */
321     public String getMethod()
322     {
323         return _method;
324     }
325 
326     /* ------------------------------------------------------------ */
327     /**
328      * @return
329      */
330     public String getURI()
331     {
332         return _uri;
333     }
334 
335     /* ------------------------------------------------------------ */
336     /**
337      * @param uri
338      */
339     public void setURI(String uri)
340     {
341         _uri = uri;
342     }
343 
344     /* ------------------------------------------------------------ */
345     /**
346      * @param name
347      * @param value
348      */
349     public void addRequestHeader(String name, String value)
350     {
351         getRequestFields().add(name,value);
352     }
353 
354     /* ------------------------------------------------------------ */
355     /**
356      * @param name
357      * @param value
358      */
359     public void addRequestHeader(Buffer name, Buffer value)
360     {
361         getRequestFields().add(name,value);
362     }
363 
364     /* ------------------------------------------------------------ */
365     /**
366      * @param name
367      * @param value
368      */
369     public void setRequestHeader(String name, String value)
370     {
371         getRequestFields().put(name,value);
372     }
373 
374     /* ------------------------------------------------------------ */
375     /**
376      * @param name
377      * @param value
378      */
379     public void setRequestHeader(Buffer name, Buffer value)
380     {
381         getRequestFields().put(name,value);
382     }
383 
384     /* ------------------------------------------------------------ */
385     /**
386      * @param value
387      */
388     public void setRequestContentType(String value)
389     {
390         getRequestFields().put(HttpHeaders.CONTENT_TYPE_BUFFER,value);
391     }
392 
393     /* ------------------------------------------------------------ */
394     /**
395      * @return
396      */
397     public HttpFields getRequestFields()
398     {
399         return _requestFields;
400     }
401 
402     /* ------------------------------------------------------------ */
403     /* ------------------------------------------------------------ */
404     /* ------------------------------------------------------------ */
405     // methods to commit and/or send the request
406 
407     /* ------------------------------------------------------------ */
408     /**
409      * @param requestContent
410      */
411     public void setRequestContent(Buffer requestContent)
412     {
413         _requestContent = requestContent;
414     }
415 
416     /* ------------------------------------------------------------ */
417     /**
418      * @param in
419      */
420     public void setRequestContentSource(InputStream in)
421     {
422         _requestContentSource = in;
423     }
424 
425     /* ------------------------------------------------------------ */
426     public InputStream getRequestContentSource()
427     {
428         return _requestContentSource;
429     }
430 
431     /* ------------------------------------------------------------ */
432     public Buffer getRequestContentChunk() throws IOException
433     {
434         synchronized (this)
435         {
436             if (_requestContentChunk == null)
437                 _requestContentChunk = new ByteArrayBuffer(4096); // TODO configure
438             else
439             {
440                 if (_requestContentChunk.hasContent())
441                     throw new IllegalStateException();
442                 _requestContentChunk.clear();
443             }
444 
445             int read = _requestContentChunk.capacity();
446             int length = _requestContentSource.read(_requestContentChunk.array(),0,read);
447             if (length >= 0)
448             {
449                 _requestContentChunk.setPutIndex(length);
450                 return _requestContentChunk;
451             }
452             return null;
453         }
454     }
455 
456     /* ------------------------------------------------------------ */
457     public Buffer getRequestContent()
458     {
459         return _requestContent;
460     }
461 
462     public boolean getRetryStatus()
463     {
464         return _retryStatus;
465     }
466 
467     public void setRetryStatus( boolean retryStatus )
468     {
469         _retryStatus = retryStatus;
470     }
471 
472     /* ------------------------------------------------------------ */
473     /** Cancel this exchange
474      * Currently this implementation does nothing.
475      */
476     public void cancel()
477     {
478 
479     }
480 
481     /* ------------------------------------------------------------ */
482     public String toString()
483     {
484         return "HttpExchange@" + hashCode() + "=" + _method + "//" + _address.getHost() + ":" + _address.getPort() + _uri + "#" + _status;
485     }
486 
487 
488 
489     /* ------------------------------------------------------------ */
490     /* ------------------------------------------------------------ */
491     /* ------------------------------------------------------------ */
492     // methods to handle response
493     protected void onRequestCommitted() throws IOException
494     {
495     }
496 
497     protected void onRequestComplete() throws IOException
498     {
499     }
500 
501     protected void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
502     {
503     }
504 
505     protected void onResponseHeader(Buffer name, Buffer value) throws IOException
506     {
507     }
508 
509     protected void onResponseHeaderComplete() throws IOException
510     {
511     }
512 
513     protected void onResponseContent(Buffer content) throws IOException
514     {
515     }
516 
517     protected void onResponseComplete() throws IOException
518     {
519     }
520 
521     protected void onConnectionFailed(Throwable ex)
522     {
523         Log.warn("CONNECTION FAILED on " + this,ex);
524     }
525 
526     protected void onException(Throwable ex)
527     {
528         Log.warn("EXCEPTION on " + this,ex);
529     }
530 
531     protected void onExpire()
532     {
533         Log.warn("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.mortbay.jetty.client.CachedExchange}
624      *
625      */
626     public static class CachedExchange extends org.mortbay.jetty.client.CachedExchange
627     {
628         public CachedExchange(boolean cacheFields)
629         {
630             super(cacheFields);
631         }
632     }
633 
634     /**
635      * @deprecated use {@link org.mortbay.jetty.client.ContentExchange}
636      *
637      */
638     public static class ContentExchange extends org.mortbay.jetty.client.ContentExchange
639     {
640 
641     }
642 
643 
644 
645 }