View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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  
20  package org.eclipse.jetty.client.webdav;
21  
22  import java.io.IOException;
23  
24  import org.eclipse.jetty.client.HttpDestination;
25  import org.eclipse.jetty.client.HttpEventListenerWrapper;
26  import org.eclipse.jetty.client.HttpExchange;
27  import org.eclipse.jetty.client.security.SecurityListener;
28  import org.eclipse.jetty.http.HttpMethods;
29  import org.eclipse.jetty.http.HttpStatus;
30  import org.eclipse.jetty.io.Buffer;
31  import org.eclipse.jetty.util.URIUtil;
32  import org.eclipse.jetty.util.log.Log;
33  import org.eclipse.jetty.util.log.Logger;
34  
35  /**
36   * WebdavListener
37   * 
38   * 
39   * 
40   * 
41   */
42  public class WebdavListener extends HttpEventListenerWrapper
43  {
44      private static final Logger LOG = Log.getLogger(WebdavListener.class);
45  
46      private HttpDestination _destination;
47      private HttpExchange _exchange;
48      private boolean _requestComplete;
49      private boolean _responseComplete; 
50      private boolean _webdavEnabled;
51      private boolean _needIntercept;
52  
53      public WebdavListener(HttpDestination destination, HttpExchange ex)
54      {
55          // Start of sending events through to the wrapped listener
56          // Next decision point is the onResponseStatus
57          super(ex.getEventListener(),true);
58          _destination=destination;
59          _exchange=ex;
60  
61          // We'll only enable webdav if this is a PUT request
62          if ( HttpMethods.PUT.equalsIgnoreCase( _exchange.getMethod() ) )
63          {
64              _webdavEnabled = true;
65          }
66      }
67  
68      @Override
69      public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
70      {
71          if ( !_webdavEnabled )
72          {
73              _needIntercept = false;
74              super.onResponseStatus(version, status, reason);
75              return;
76          }
77          
78          if (LOG.isDebugEnabled())
79              LOG.debug("WebdavListener:Response Status: " + status );
80  
81          // The dav spec says that CONFLICT should be returned when the parent collection doesn't exist but I am seeing
82          // FORBIDDEN returned instead so running with that.
83          if ( status == HttpStatus.FORBIDDEN_403 || status == HttpStatus.CONFLICT_409 )
84          {
85              if ( _webdavEnabled )
86              {
87                  if (LOG.isDebugEnabled())
88                      LOG.debug("WebdavListener:Response Status: dav enabled, taking a stab at resolving put issue" );
89                  setDelegatingResponses( false ); // stop delegating, we can try and fix this request
90                  _needIntercept = true;
91              }
92              else
93              {
94                  if (LOG.isDebugEnabled())
95                      LOG.debug("WebdavListener:Response Status: Webdav Disabled" );
96                  setDelegatingResponses( true ); // just make sure we delegate
97                  setDelegatingRequests( true );
98                  _needIntercept = false;
99              }
100         }
101         else
102         {
103             _needIntercept = false;
104             setDelegatingResponses( true );
105             setDelegatingRequests( true );
106         }
107 
108         super.onResponseStatus(version, status, reason);
109     }
110 
111     @Override
112     public void onResponseComplete() throws IOException
113     {
114         _responseComplete = true;
115         if (_needIntercept)
116         {
117             if ( _requestComplete && _responseComplete)
118             {
119                 try
120                 {
121                     // we have some work to do before retrying this
122                     if ( resolveCollectionIssues() )
123                     {
124                         setDelegatingRequests( true );
125                         setDelegatingResponses(true);
126                         _requestComplete = false;
127                         _responseComplete = false;
128                         _destination.resend(_exchange);
129                     }
130                     else
131                     {
132                         // admit defeat but retry because someone else might have 
133                         setDelegationResult(false);
134                         setDelegatingRequests( true );
135                         setDelegatingResponses(true);
136                         super.onResponseComplete();
137                     }
138                 }
139                 catch ( IOException ioe )
140                 {
141                     LOG.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
142                     super.onResponseComplete();
143                 }
144             }
145             else
146             {
147                 if (LOG.isDebugEnabled())
148                     LOG.debug("WebdavListener:Not ready, calling super");
149                 super.onResponseComplete();
150             }
151         }
152         else
153         {
154             super.onResponseComplete();
155         }
156     }
157 
158     
159     
160     @Override
161     public void onRequestComplete () throws IOException
162     {
163         _requestComplete = true;
164         if (_needIntercept)
165         {
166             if ( _requestComplete && _responseComplete)
167             {
168                 try
169                 {
170                     // we have some work to do before retrying this
171                     if ( resolveCollectionIssues() )
172                     {
173                         setDelegatingRequests( true );
174                         setDelegatingResponses(true);
175                         _requestComplete = false;
176                         _responseComplete = false;
177                         _destination.resend(_exchange);
178                     }
179                     else
180                     {
181                         // admit defeat but retry because someone else might have 
182                         setDelegatingRequests( true );
183                         setDelegatingResponses(true);
184                         super.onRequestComplete();
185                     }
186                 }
187                 catch ( IOException ioe )
188                 {
189                     LOG.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
190                     super.onRequestComplete();
191                 }
192             }
193             else
194             {
195                 if (LOG.isDebugEnabled())
196                     LOG.debug("WebdavListener:Not ready, calling super");
197                 super.onRequestComplete();
198             }
199         }
200         else
201         {
202             super.onRequestComplete();
203         } 
204     }
205 
206    
207     
208     
209     /**
210      * walk through the steps to try and resolve missing parent collection issues via webdav
211      *
212      * TODO this really ought to use URI itself for this resolution
213      *
214      * @return
215      * @throws IOException
216      */
217     private boolean resolveCollectionIssues() throws IOException
218     {
219 
220         String uri = _exchange.getURI();
221         String[] uriCollection = _exchange.getURI().split("/");
222         int checkNum = uriCollection.length;
223         int rewind = 0;
224 
225         String parentUri = URIUtil.parentPath( uri );
226         while ( parentUri != null && !checkExists(parentUri) )
227         {
228             ++rewind;
229             parentUri = URIUtil.parentPath( parentUri );
230         }
231 
232         // confirm webdav is supported for this collection
233         if ( checkWebdavSupported() )
234         {
235             for (int i = 0; i < rewind;)
236             {
237                 makeCollection(parentUri + "/" + uriCollection[checkNum - rewind - 1]);
238                 parentUri = parentUri + "/" + uriCollection[checkNum - rewind - 1];
239                 --rewind;
240             }
241         }
242         else
243         {
244             return false;
245         }
246 
247         return true;
248     }
249 
250     private boolean checkExists( String uri ) throws IOException
251     {
252         if (uri == null)
253         {
254             System.out.println("have failed miserably");
255             return false;
256         }
257         
258         PropfindExchange propfindExchange = new PropfindExchange();
259         propfindExchange.setAddress( _exchange.getAddress() );
260         propfindExchange.setMethod( HttpMethods.GET ); // PROPFIND acts wonky, just use get
261         propfindExchange.setScheme( _exchange.getScheme() );
262         propfindExchange.setEventListener( new SecurityListener( _destination, propfindExchange ) );
263         propfindExchange.setConfigureListeners( false );
264         propfindExchange.setRequestURI( uri );
265 
266         _destination.send( propfindExchange );
267 
268         try
269         {
270             propfindExchange.waitForDone();
271 
272             return propfindExchange.exists();
273         }
274         catch ( InterruptedException ie )
275         {
276             LOG.ignore( ie );                  
277             return false;
278         }
279     }
280 
281     private boolean makeCollection( String uri ) throws IOException
282     {
283         MkcolExchange mkcolExchange = new MkcolExchange();
284         mkcolExchange.setAddress( _exchange.getAddress() );
285         mkcolExchange.setMethod( "MKCOL " + uri + " HTTP/1.1" );
286         mkcolExchange.setScheme( _exchange.getScheme() );
287         mkcolExchange.setEventListener( new SecurityListener( _destination, mkcolExchange ) );
288         mkcolExchange.setConfigureListeners( false );
289         mkcolExchange.setRequestURI( uri );
290 
291         _destination.send( mkcolExchange );
292 
293         try
294         {
295             mkcolExchange.waitForDone();
296 
297             return mkcolExchange.exists();
298         }
299         catch ( InterruptedException ie )
300         {
301             LOG.ignore( ie );
302             return false;
303         }
304     }
305 
306     
307     private boolean checkWebdavSupported() throws IOException
308     {
309         WebdavSupportedExchange supportedExchange = new WebdavSupportedExchange();
310         supportedExchange.setAddress( _exchange.getAddress() );
311         supportedExchange.setMethod( HttpMethods.OPTIONS );
312         supportedExchange.setScheme( _exchange.getScheme() );
313         supportedExchange.setEventListener( new SecurityListener( _destination, supportedExchange ) );
314         supportedExchange.setConfigureListeners( false );
315         supportedExchange.setRequestURI( _exchange.getURI() );
316 
317         _destination.send( supportedExchange );
318 
319         try
320         {
321             supportedExchange.waitTilCompletion();
322             return supportedExchange.isWebdavSupported();
323         }
324         catch (InterruptedException ie )
325         {            
326             LOG.ignore( ie );
327             return false;
328         }
329 
330     }
331 
332 }