diff options
-rw-r--r-- | ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx | 45 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx | 7 | ||||
-rw-r--r-- | ucb/source/ucp/webdav-neon/webdavcontent.cxx | 155 |
3 files changed, 202 insertions, 5 deletions
diff --git a/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx b/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx index 026186db347e..0cf396d8a717 100644 --- a/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx +++ b/ucb/source/ucp/webdav-neon/DAVResourceAccess.cxx @@ -497,6 +497,51 @@ uno::Reference< io::XInputStream > DAVResourceAccess::GET( } +uno::Reference< io::XInputStream > DAVResourceAccess::GET( + DAVRequestHeaders &rRequestHeaders, + const std::vector< OUString > & rHeaderNames, + DAVResource & rResource, + const uno::Reference< ucb::XCommandEnvironment > & xEnv ) + throw( DAVException ) +{ + initialize(); + + uno::Reference< io::XInputStream > xStream; + int errorCount = 0; + bool bRetry; + do + { + bRetry = false; + try + { + getUserRequestHeaders( xEnv, + getRequestURI(), + ucb::WebDAVHTTPMethod_GET, + rRequestHeaders ); + + xStream = m_xSession->GET( getRequestURI(), + rHeaderNames, + rResource, + DAVRequestEnvironment( + getRequestURI(), + new DAVAuthListener_Impl( + xEnv, m_aURL ), + rRequestHeaders, xEnv ) ); + } + catch ( const DAVException & e ) + { + errorCount++; + bRetry = handleException( e, errorCount ); + if ( !bRetry ) + throw; + } + } + while ( bRetry ); + + return xStream; +} + + void DAVResourceAccess::GET( uno::Reference< io::XOutputStream > & rStream, const std::vector< OUString > & rHeaderNames, diff --git a/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx b/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx index 96b308d3c487..f2fa411dd050 100644 --- a/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx +++ b/ucb/source/ucp/webdav-neon/DAVResourceAccess.hxx @@ -134,6 +134,13 @@ public: const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) throw ( DAVException ); + css::uno::Reference< css::io::XInputStream > + GET( DAVRequestHeaders & rRequestHeaders, + const std::vector< rtl::OUString > & rHeaderNames, // empty == 'all' + DAVResource & rResource, + const css::uno::Reference< css::ucb::XCommandEnvironment > & xEnv ) + throw ( DAVException ); + void GET( css::uno::Reference< css::io::XOutputStream > & rStream, const std::vector< OUString > & rHeaderNames, // empty == 'all' diff --git a/ucb/source/ucp/webdav-neon/webdavcontent.cxx b/ucb/source/ucp/webdav-neon/webdavcontent.cxx index 099b7e2800b2..be3258294a51 100644 --- a/ucb/source/ucp/webdav-neon/webdavcontent.cxx +++ b/ucb/source/ucp/webdav-neon/webdavcontent.cxx @@ -93,6 +93,121 @@ using namespace com::sun::star; using namespace webdav_ucp; +namespace +{ + // implement a GET to substitute HEAD, when HEAD not available + void lcl_sendPartialGETRequest( bool &bError, + DAVException &aLastException, + const std::vector< rtl::OUString >& rProps, + std::vector< rtl::OUString > &aHeaderNames, + const std::unique_ptr< DAVResourceAccess > &xResAccess, + std::unique_ptr< ContentProperties > &xProps, + const uno::Reference< ucb::XCommandEnvironment >& xEnv ) + { + bool bIsRequestSize = false; + DAVResource aResource; + DAVRequestHeaders aPartialGet; + aPartialGet.push_back( DAVRequestHeader( OUString( "Range" ), // see <https://tools.ietf.org/html/rfc7233#section-3.1> + OUString( "bytes=0-0" ) ) ); + + for ( std::vector< rtl::OUString >::const_iterator it = aHeaderNames.begin(); + it != aHeaderNames.end(); ++it ) + { + if ( *it == "Content-Length" ) + { + bIsRequestSize = true; + break; + } + } + + if ( bIsRequestSize ) + { + // we need to know if the server accepts range requests for a resource + // and the range unit it uses + aHeaderNames.push_back( OUString( "Accept-Ranges" ) ); // see <https://tools.ietf.org/html/rfc7233#section-2.3> + aHeaderNames.push_back( OUString( "Content-Range" ) ); // see <https://tools.ietf.org/html/rfc7233#section-4.2> + } + try + { + uno::Reference< io::XInputStream > xIn = xResAccess->GET( aPartialGet, + aHeaderNames, + aResource, + xEnv ); + bError = false; + + if ( bIsRequestSize ) + { + // the ContentProperties maps "Content-Length" to the UCB "Size" property + // This would have an unrealistic value of 1 byte because we did only a partial GET + // Solution: if "Content-Range" is present, map it with UCB "Size" property + rtl::OUString aAcceptRanges, aContentRange, aContentLength; + std::vector< DAVPropertyValue > &aResponseProps = aResource.properties; + for ( std::vector< DAVPropertyValue >::const_iterator it = aResponseProps.begin(); + it != aResponseProps.end(); ++it ) + { + if ( it->Name == "Accept-Ranges" ) + it->Value >>= aAcceptRanges; + else if ( it->Name == "Content-Range" ) + it->Value >>= aContentRange; + else if ( it->Name == "Content-Length" ) + it->Value >>= aContentLength; + } + + sal_Int64 nSize = 1; + if ( aContentLength.getLength() ) + { + nSize = aContentLength.toInt64(); + } + + // according to <> http://tools.ietf.org/html/rfc2616#section-3.12 + // <https://tools.ietf.org/html/rfc7233#section-2> + // needs some explanation for this + // probably some changes? + // the only range unit defined is "bytes" and implementations + // MAY ignore ranges specified using other units. + if ( nSize == 1 && + aContentRange.getLength() && + aAcceptRanges == "bytes" ) + { + // Parse the Content-Range to get the size + // vid. http://tools.ietf.org/html/rfc2616#section-14.16 + // Content-Range: <range unit> <bytes range>/<size> + sal_Int32 nSlash = aContentRange.lastIndexOf( '/' ); + if ( nSlash != -1 ) + { + rtl::OUString aSize = aContentRange.copy( nSlash + 1 ); + // "*" means that the instance-length is unknown at the time when the response was generated + if ( aSize != "*" ) + { + for ( std::vector< DAVPropertyValue >::iterator it = aResponseProps.begin(); + it != aResponseProps.end(); ++it ) + { + if (it->Name == "Content-Length") + { + it->Value <<= aSize; + break; + } + } + } + } + } + } + + if ( xProps.get() ) + xProps->addProperties( + rProps, + ContentProperties( aResource ) ); + else + xProps.reset ( new ContentProperties( aResource ) ); + } + catch ( DAVException const & ex ) + { + aLastException = ex; + } + } +} + + // Static value, to manage a simple OPTIONS cache // Key is the URL, element is the DAVOptions resulting from an OPTIONS call. // Cached DAVOptions have a lifetime that depends on the errors received or not received @@ -1492,13 +1607,43 @@ uno::Reference< sdbc::XRow > Content::getPropertyValues( } catch ( DAVException const & e ) { - bNetworkAccessAllowed - = shouldAccessNetworkAfterException( e ); + // non "general-purpose servers" may not support HEAD requests + // see http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.1 + // In this case, perform a partial GET only to get the header info + // vid. http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 + // WARNING if the server does not support partial GETs, + // the GET will transfer the whole content + bool bError = true; + DAVException aLastException = e; + + // According to the spec. the origin server SHOULD return + // * 405 (Method Not Allowed): + // the method is known but not allowed for the requested resource + // * 501 (Not Implemented): + // the method is unrecognized or not implemented + // TODO SC_NOT_FOUND is only for google-code server + if ( aLastException.getStatus() == SC_NOT_IMPLEMENTED || + aLastException.getStatus() == SC_METHOD_NOT_ALLOWED || + aLastException.getStatus() == SC_NOT_FOUND ) + { + SAL_WARN( "ucb.ucp.webdav", "HEAD not implemented: fall back to a partial GET" ); + lcl_sendPartialGETRequest( bError, + aLastException, + aMissingProps, + aHeaderNames, + xResAccess, + xProps, + xEnv ); + m_bDidGetOrHead = !bError; + } - if ( !bNetworkAccessAllowed ) + if ( bError ) { - cancelCommandExecution( e, xEnv ); - // unreachable + if ( !shouldAccessNetworkAfterException( e ) ) + { + cancelCommandExecution( e, xEnv ); + // unreachable + } } } } |