blob: 8797f31fd68bd9c62d334c2a5e162da9b1be63ea [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.client.webdav;
import java.io.IOException;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpEventListenerWrapper;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.security.SecurityListener;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.Buffer;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* WebdavListener
*
*
*
*
*/
public class WebdavListener extends HttpEventListenerWrapper
{
private static final Logger LOG = Log.getLogger(WebdavListener.class);
private HttpDestination _destination;
private HttpExchange _exchange;
private boolean _requestComplete;
private boolean _responseComplete;
private boolean _webdavEnabled;
private boolean _needIntercept;
public WebdavListener(HttpDestination destination, HttpExchange ex)
{
// Start of sending events through to the wrapped listener
// Next decision point is the onResponseStatus
super(ex.getEventListener(),true);
_destination=destination;
_exchange=ex;
// We'll only enable webdav if this is a PUT request
if ( HttpMethods.PUT.equalsIgnoreCase( _exchange.getMethod() ) )
{
_webdavEnabled = true;
}
}
@Override
public void onResponseStatus(Buffer version, int status, Buffer reason) throws IOException
{
if ( !_webdavEnabled )
{
_needIntercept = false;
super.onResponseStatus(version, status, reason);
return;
}
if (LOG.isDebugEnabled())
LOG.debug("WebdavListener:Response Status: " + status );
// The dav spec says that CONFLICT should be returned when the parent collection doesn't exist but I am seeing
// FORBIDDEN returned instead so running with that.
if ( status == HttpStatus.FORBIDDEN_403 || status == HttpStatus.CONFLICT_409 )
{
if ( _webdavEnabled )
{
if (LOG.isDebugEnabled())
LOG.debug("WebdavListener:Response Status: dav enabled, taking a stab at resolving put issue" );
setDelegatingResponses( false ); // stop delegating, we can try and fix this request
_needIntercept = true;
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("WebdavListener:Response Status: Webdav Disabled" );
setDelegatingResponses( true ); // just make sure we delegate
setDelegatingRequests( true );
_needIntercept = false;
}
}
else
{
_needIntercept = false;
setDelegatingResponses( true );
setDelegatingRequests( true );
}
super.onResponseStatus(version, status, reason);
}
@Override
public void onResponseComplete() throws IOException
{
_responseComplete = true;
if (_needIntercept)
{
if ( _requestComplete && _responseComplete)
{
try
{
// we have some work to do before retrying this
if ( resolveCollectionIssues() )
{
setDelegatingRequests( true );
setDelegatingResponses(true);
_requestComplete = false;
_responseComplete = false;
_destination.resend(_exchange);
}
else
{
// admit defeat but retry because someone else might have
setDelegationResult(false);
setDelegatingRequests( true );
setDelegatingResponses(true);
super.onResponseComplete();
}
}
catch ( IOException ioe )
{
LOG.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
super.onResponseComplete();
}
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("WebdavListener:Not ready, calling super");
super.onResponseComplete();
}
}
else
{
super.onResponseComplete();
}
}
@Override
public void onRequestComplete () throws IOException
{
_requestComplete = true;
if (_needIntercept)
{
if ( _requestComplete && _responseComplete)
{
try
{
// we have some work to do before retrying this
if ( resolveCollectionIssues() )
{
setDelegatingRequests( true );
setDelegatingResponses(true);
_requestComplete = false;
_responseComplete = false;
_destination.resend(_exchange);
}
else
{
// admit defeat but retry because someone else might have
setDelegatingRequests( true );
setDelegatingResponses(true);
super.onRequestComplete();
}
}
catch ( IOException ioe )
{
LOG.debug("WebdavListener:Complete:IOException: might not be dealing with dav server, delegate");
super.onRequestComplete();
}
}
else
{
if (LOG.isDebugEnabled())
LOG.debug("WebdavListener:Not ready, calling super");
super.onRequestComplete();
}
}
else
{
super.onRequestComplete();
}
}
/**
* walk through the steps to try and resolve missing parent collection issues via webdav
*
* TODO this really ought to use URI itself for this resolution
*
* @return
* @throws IOException
*/
private boolean resolveCollectionIssues() throws IOException
{
String uri = _exchange.getURI();
String[] uriCollection = _exchange.getURI().split("/");
int checkNum = uriCollection.length;
int rewind = 0;
String parentUri = URIUtil.parentPath( uri );
while ( parentUri != null && !checkExists(parentUri) )
{
++rewind;
parentUri = URIUtil.parentPath( parentUri );
}
// confirm webdav is supported for this collection
if ( checkWebdavSupported() )
{
for (int i = 0; i < rewind;)
{
makeCollection(parentUri + "/" + uriCollection[checkNum - rewind - 1]);
parentUri = parentUri + "/" + uriCollection[checkNum - rewind - 1];
--rewind;
}
}
else
{
return false;
}
return true;
}
private boolean checkExists( String uri ) throws IOException
{
if (uri == null)
{
System.out.println("have failed miserably");
return false;
}
PropfindExchange propfindExchange = new PropfindExchange();
propfindExchange.setAddress( _exchange.getAddress() );
propfindExchange.setMethod( HttpMethods.GET ); // PROPFIND acts wonky, just use get
propfindExchange.setScheme( _exchange.getScheme() );
propfindExchange.setEventListener( new SecurityListener( _destination, propfindExchange ) );
propfindExchange.setConfigureListeners( false );
propfindExchange.setRequestURI( uri );
_destination.send( propfindExchange );
try
{
propfindExchange.waitForDone();
return propfindExchange.exists();
}
catch ( InterruptedException ie )
{
LOG.ignore( ie );
return false;
}
}
private boolean makeCollection( String uri ) throws IOException
{
MkcolExchange mkcolExchange = new MkcolExchange();
mkcolExchange.setAddress( _exchange.getAddress() );
mkcolExchange.setMethod( "MKCOL " + uri + " HTTP/1.1" );
mkcolExchange.setScheme( _exchange.getScheme() );
mkcolExchange.setEventListener( new SecurityListener( _destination, mkcolExchange ) );
mkcolExchange.setConfigureListeners( false );
mkcolExchange.setRequestURI( uri );
_destination.send( mkcolExchange );
try
{
mkcolExchange.waitForDone();
return mkcolExchange.exists();
}
catch ( InterruptedException ie )
{
LOG.ignore( ie );
return false;
}
}
private boolean checkWebdavSupported() throws IOException
{
WebdavSupportedExchange supportedExchange = new WebdavSupportedExchange();
supportedExchange.setAddress( _exchange.getAddress() );
supportedExchange.setMethod( HttpMethods.OPTIONS );
supportedExchange.setScheme( _exchange.getScheme() );
supportedExchange.setEventListener( new SecurityListener( _destination, supportedExchange ) );
supportedExchange.setConfigureListeners( false );
supportedExchange.setRequestURI( _exchange.getURI() );
_destination.send( supportedExchange );
try
{
supportedExchange.waitTilCompletion();
return supportedExchange.isWebdavSupported();
}
catch (InterruptedException ie )
{
LOG.ignore( ie );
return false;
}
}
}