diff options
author | Giuseppe Castagno <giuseppe.castagno@acca-esse.eu> | 2016-08-26 19:40:33 +0200 |
---|---|---|
committer | Giuseppe Castagno <giuseppe.castagno@acca-esse.eu> | 2016-09-18 12:47:29 +0000 |
commit | 827cc09948c09d934dd32ca1867252618f761eab (patch) | |
tree | 7b1caa232639614899cca365f4f84e9c3dea30c0 /ucb | |
parent | e592877844573fa5ab934b7ab3b13aaa33ed621d (diff) |
Related: tdf#101094 (31): Add a simulated HEAD method: use GET
Some server have HEAD method disabled or not implemented.
So we can use GET with no data, retrieving only the header info.
The implementing code comes from LO WebDAV serf implementation.
Change-Id: I3b3d81f5a07232c4f1647888685e820939bc978c
Reviewed-on: https://gerrit.libreoffice.org/28982
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Giuseppe Castagno <giuseppe.castagno@acca-esse.eu>
Diffstat (limited to 'ucb')
-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 + } } } } |