tdf#101094 (32): Use a partial GET as a fall-back for HEAD...

... partial GET means a GET with no data returned, to test for
resource 'real' availability in case HEAD fails or is disabled.
At  the same time disable use of HEAD method on the resource
for the whole access procedure.

This change is needed to manage web servers that don't permit the
use of either HEAD or OPTIONS methods.
To accomodate pure web sites that enable only GET to fetch contents.

Change-Id: If52316ddd6ce637fb7d2ef518423de246ee2dcfb
Reviewed-on: https://gerrit.libreoffice.org/28992
Tested-by: Giuseppe Castagno <giuseppe.castagno@acca-esse.eu>
Reviewed-by: Giuseppe Castagno <giuseppe.castagno@acca-esse.eu>
This commit is contained in:
Giuseppe Castagno
2016-08-27 17:31:06 +02:00
parent 914d72ee1e
commit 2a148e2b5e
4 changed files with 88 additions and 8 deletions

View File

@@ -24,6 +24,7 @@ DAVOptions::DAVOptions() :
m_isClass1( false ), m_isClass1( false ),
m_isClass2( false ), m_isClass2( false ),
m_isClass3( false ), m_isClass3( false ),
m_isHeadAllowed( true ),
m_isLocked( false ), m_isLocked( false ),
m_aAllowedMethods(), m_aAllowedMethods(),
m_nStaleTime( 0 ), m_nStaleTime( 0 ),
@@ -38,6 +39,7 @@ DAVOptions::DAVOptions( const DAVOptions & rOther ) :
m_isClass1( rOther.m_isClass1 ), m_isClass1( rOther.m_isClass1 ),
m_isClass2( rOther.m_isClass2 ), m_isClass2( rOther.m_isClass2 ),
m_isClass3( rOther.m_isClass3 ), m_isClass3( rOther.m_isClass3 ),
m_isHeadAllowed( rOther.m_isHeadAllowed ),
m_isLocked( rOther.m_isLocked ), m_isLocked( rOther.m_isLocked ),
m_aAllowedMethods( rOther.m_aAllowedMethods ), m_aAllowedMethods( rOther.m_aAllowedMethods ),
m_nStaleTime( rOther.m_nStaleTime ), m_nStaleTime( rOther.m_nStaleTime ),
@@ -173,5 +175,32 @@ bool DAVOptionsCache::isResourceFound( const OUString & rURL )
return true; return true;
} }
bool DAVOptionsCache::isHeadAllowed( const OUString & rURL )
{
osl::MutexGuard aGuard( m_aMutex );
OUString aEncodedUrl( ucb_impl::urihelper::encodeURI( NeonUri::unescape( rURL ) ) );
normalizeURLLastChar( aEncodedUrl );
DAVOptionsMap::iterator it;
it = m_aTheCache.find( aEncodedUrl );
if ( it != m_aTheCache.end() )
{
// first check for stale
TimeValue t1;
osl_getSystemTime( &t1 );
if( (*it).second.getStaleTime() < t1.Seconds )
{
m_aTheCache.erase( it );
return true; // to force again OPTIONS method
}
// check if the resource was present on server
return (*it).second.isHeadAllowed();
}
// this value is needed because some web server don't implement
// OPTIONS method, so the resource is considered found,
// until detected otherwise
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */

View File

@@ -81,6 +81,8 @@ namespace webdav_ucp
bool m_isClass1; bool m_isClass1;
bool m_isClass2; bool m_isClass2;
bool m_isClass3; bool m_isClass3;
// for server that do not implement it
bool m_isHeadAllowed;
// Internally used to maintain locked stated of the resource, only // Internally used to maintain locked stated of the resource, only
// if it's a Class 2 resource // if it's a Class 2 resource
bool m_isLocked; bool m_isLocked;
@@ -111,6 +113,9 @@ namespace webdav_ucp
bool isClass3() { return m_isClass3; }; bool isClass3() { return m_isClass3; };
void setClass3( bool Class3 = true ) { m_isClass3 = Class3; }; void setClass3( bool Class3 = true ) { m_isClass3 = Class3; };
bool isHeadAllowed() { return m_isHeadAllowed; };
void setHeadAllowed( bool HeadAllowed = true ) { m_isHeadAllowed = HeadAllowed; };
sal_uInt32 getStaleTime() const { return m_nStaleTime ; }; sal_uInt32 getStaleTime() const { return m_nStaleTime ; };
void setStaleTime( const sal_uInt32 nStaleTime ) { m_nStaleTime = nStaleTime; }; void setStaleTime( const sal_uInt32 nStaleTime ) { m_nStaleTime = nStaleTime; };
@@ -133,6 +138,7 @@ namespace webdav_ucp
m_isClass1 = false; m_isClass1 = false;
m_isClass2 = false; m_isClass2 = false;
m_isClass3 = false; m_isClass3 = false;
m_isHeadAllowed = true;
m_isLocked = false; m_isLocked = false;
m_aAllowedMethods.clear(); m_aAllowedMethods.clear();
m_nStaleTime = 0; m_nStaleTime = 0;
@@ -177,6 +183,8 @@ namespace webdav_ucp
*/ */
bool isResourceFound( const OUString & rURL ); bool isResourceFound( const OUString & rURL );
bool isHeadAllowed( const OUString & rURL );
private: private:
/// remove the last '/' in aUrl, if it exists /// remove the last '/' in aUrl, if it exists

View File

@@ -1589,6 +1589,10 @@ uno::Reference< sdbc::XRow > Content::getPropertyValues(
// clean cached value of PROPFIND property names // clean cached value of PROPFIND property names
// PROPPATCH can change them // PROPPATCH can change them
removeCachedPropertyNames( xResAccess->getURL() ); removeCachedPropertyNames( xResAccess->getURL() );
// test if HEAD allowed, if not, throw, will be catched immediately
if ( !aStaticDAVOptionsCache.isHeadAllowed( xResAccess->getURL() ) )
throw DAVException( DAVException::DAV_HTTP_ERROR, "405 Not Implemented" );
xResAccess->HEAD( aHeaderNames, resource, xEnv ); xResAccess->HEAD( aHeaderNames, resource, xEnv );
m_bDidGetOrHead = true; m_bDidGetOrHead = true;
@@ -4075,7 +4079,7 @@ void Content::getResourceOptions(
// instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED. // instead of SC_NOT_IMPLEMENTED or SC_METHOD_NOT_ALLOWED.
// So check if this is an available resource, or a real 'Not Found' event. // So check if this is an available resource, or a real 'Not Found' event.
sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound; sal_uInt32 nLifeTime = m_nOptsCacheLifeNotFound;
if( isResourceAvailable(xEnv, rResAccess ) ) if( isResourceAvailable( xEnv, rResAccess, rDAVOptions ) )
{ {
nLifeTime = m_nOptsCacheLifeNotImpl; nLifeTime = m_nOptsCacheLifeNotImpl;
rDAVOptions.setResourceFound(); // means it exists, but it's not DAV rDAVOptions.setResourceFound(); // means it exists, but it's not DAV
@@ -4134,17 +4138,54 @@ void Content::getResourceOptions(
//static //static
bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv, bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
const std::unique_ptr< DAVResourceAccess > & rResAccess ) const std::unique_ptr< DAVResourceAccess > & rResAccess,
DAVOptions& rDAVOptions )
{ {
std::vector< rtl::OUString > aHeaderNames;
DAVResource aResource;
try try
{ {
// To check for the physical URL resource availability, using a simple HEAD command // To check for the physical URL resource availability, first
// if HEAD is successfull, set element found. // try using a simple HEAD command
std::vector< OUString > aHeaderNames; // if HEAD is successfull, set element found,
DAVResource resource; rResAccess->HEAD( aHeaderNames, aResource, xEnv );
rResAccess->HEAD( aHeaderNames, resource, xEnv );
return true; return true;
} }
catch ( DAVException const & e )
{
if ( e.getStatus() == SC_NOT_IMPLEMENTED ||
e.getStatus() == SC_METHOD_NOT_ALLOWED ||
e.getStatus() == SC_NOT_FOUND )
{
SAL_WARN( "ucb.ucp.webdav", "HEAD not implemented: fall back to a partial GET" );
// set in cached OPTIONS "HEAD not implemented"
// so it won't be used again on this resource
rDAVOptions.setHeadAllowed( false );
try
{
// do a GET with a payload of 0, the server does not
// support HEAD (or has HEAD disabled)
DAVRequestHeaders aPartialGet;
aPartialGet.push_back(
DAVRequestHeader(
OUString( "Range" ),
OUString( "bytes=0-0" )));
rResAccess->GET( aPartialGet,
aHeaderNames,
aResource,
xEnv );
return true;
}
catch (...)
{
return false;
}
}
else
return false;
}
catch ( ... ) catch ( ... )
{ {
// some error... so set as not found // some error... so set as not found
@@ -4152,6 +4193,7 @@ bool Content::isResourceAvailable( const css::uno::Reference< css::ucb::XCommand
// in rResAccess function method. // in rResAccess function method.
return false; return false;
} }
return false;
} }

View File

@@ -329,7 +329,8 @@ public:
throw ( css::uno::Exception, std::exception ); throw ( css::uno::Exception, std::exception );
static bool isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv, static bool isResourceAvailable( const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
const std::unique_ptr< DAVResourceAccess > & rResAccess); const std::unique_ptr< DAVResourceAccess > & rResAccess,
DAVOptions& rDAVOptions );
static void removeCachedPropertyNames( const OUString & rURL ); static void removeCachedPropertyNames( const OUString & rURL );
}; };