ucb/source/ucp/webdav-curl/CurlSession.cxx | 73 ++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 16 deletions(-)
New commits: commit 2fae5ab2a8cefa758c631d6ae23847089b68800d Author: Michael Stahl <michael.st...@allotropia.de> AuthorDate: Fri Nov 26 19:20:37 2021 +0100 Commit: Michael Stahl <michael.st...@allotropia.de> CommitDate: Mon Nov 29 15:23:42 2021 +0100 ucb: webdav-curl: don't read from XInputStream during upload Nextcloud will reply to a PROPFIND request with "100 Continue" and then after the data is uploaded it will send a "401 Unauthorized" if the auth header is missing in the headers to which it replied with "100 Continue". In the next call to ProcessRequestImpl(), reading from the stream returns no data because it's at EOF, and because of the Content-Length header the server will hang forever waiting for data. So copy the stream to a temporary buffer and use that for multiple calls to ProcessRequestImpl(). Change-Id: If5943a32c4cf50259fe1f84013141765cb5bd891 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/125923 Tested-by: Jenkins Reviewed-by: Michael Stahl <michael.st...@allotropia.de> diff --git a/ucb/source/ucp/webdav-curl/CurlSession.cxx b/ucb/source/ucp/webdav-curl/CurlSession.cxx index 43bfa6cb85f8..d63d3566bfaa 100644 --- a/ucb/source/ucp/webdav-curl/CurlSession.cxx +++ b/ucb/source/ucp/webdav-curl/CurlSession.cxx @@ -18,6 +18,8 @@ #include <comphelper/scopeguard.hxx> #include <comphelper/string.hxx> +#include <o3tl/safeint.hxx> + #include <officecfg/Inet.hxx> #include <com/sun/star/beans/NamedValue.hpp> @@ -89,12 +91,13 @@ struct DownloadTarget struct UploadSource { - uno::Reference<io::XInputStream> xInStream; + uno::Sequence<sal_Int8> const& rInData; ResponseHeaders const& rHeaders; - UploadSource(uno::Reference<io::XInputStream> const& i_xInStream, - ResponseHeaders const& i_rHeaders) - : xInStream(i_xInStream) + size_t nPosition; + UploadSource(uno::Sequence<sal_Int8> const& i_rInData, ResponseHeaders const& i_rHeaders) + : rInData(i_rInData) , rHeaders(i_rHeaders) + , nPosition(0) { } }; @@ -348,15 +351,14 @@ static size_t read_callback(char* const buffer, size_t const size, size_t const { auto* const pSource(static_cast<UploadSource*>(userdata)); assert(pSource); - assert(pSource->xInStream.is()); size_t const nBytes(size * nitems); size_t nRet(0); try { - uno::Sequence<sal_Int8> data; - data.realloc(nBytes); - nRet = pSource->xInStream->readSomeBytes(data, nBytes); - ::std::memcpy(buffer, data.getConstArray(), nRet); + assert(pSource->nPosition <= o3tl::make_unsigned(pSource->rInData.getLength())); + nRet = ::std::min<size_t>(pSource->rInData.getLength() - pSource->nPosition, nBytes); + ::std::memcpy(buffer, pSource->rInData.getConstArray() + pSource->nPosition, nRet); + pSource->nPosition += nRet; } catch (...) { @@ -737,7 +739,7 @@ struct CurlProcessor static auto ProcessRequestImpl( CurlSession& rSession, CurlUri const& rURI, curl_slist* pRequestHeaderList, uno::Reference<io::XOutputStream> const* pxOutStream, - uno::Reference<io::XInputStream> const* pxInStream, + uno::Sequence<sal_Int8> const* pInData, ::std::pair<::std::vector<OUString> const&, DAVResource&> const* pRequestedHeaders, ResponseHeaders& rHeaders) -> void; @@ -792,7 +794,7 @@ auto CurlProcessor::URIReferenceToURI(CurlSession& rSession, OUString const& rUR auto CurlProcessor::ProcessRequestImpl( CurlSession& rSession, CurlUri const& rURI, curl_slist* const pRequestHeaderList, uno::Reference<io::XOutputStream> const* const pxOutStream, - uno::Reference<io::XInputStream> const* const pxInStream, + uno::Sequence<sal_Int8> const* const pInData, ::std::pair<::std::vector<OUString> const&, DAVResource&> const* const pRequestedHeaders, ResponseHeaders& rHeaders) -> void { @@ -805,7 +807,7 @@ auto CurlProcessor::ProcessRequestImpl( rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_WRITEDATA, nullptr); assert(rc == CURLE_OK); } - if (pxInStream) + if (pInData) { rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, nullptr); assert(rc == CURLE_OK); @@ -839,9 +841,9 @@ auto CurlProcessor::ProcessRequestImpl( assert(rc == CURLE_OK); } ::std::optional<UploadSource> oUploadSource; - if (pxInStream) + if (pInData) { - oUploadSource.emplace(*pxInStream, rHeaders); + oUploadSource.emplace(*pInData, rHeaders); rc = curl_easy_setopt(rSession.m_pCurl.get(), CURLOPT_READDATA, &*oUploadSource); assert(rc == CURLE_OK); // libcurl won't upload without setting this @@ -1103,6 +1105,45 @@ auto CurlProcessor::ProcessRequest( } } + uno::Sequence<sal_Int8> data; + if (pxInStream) + { + uno::Reference<io::XSeekable> const xSeekable(*pxInStream, uno::UNO_QUERY); + if (xSeekable.is()) + { + auto const len(xSeekable->getLength() - xSeekable->getPosition()); + if ((**pxInStream).readBytes(data, len) != len) + { + throw uno::RuntimeException("short readBytes"); + } + } + else + { + ::std::vector<uno::Sequence<sal_Int8>> bufs; + bool isDone(false); + do + { + bufs.emplace_back(); + isDone = (**pxInStream).readSomeBytes(bufs.back(), 65536) == 0; + } while (!isDone); + sal_Int32 nSize(0); + for (auto const& rBuf : bufs) + { + if (o3tl::checked_add(nSize, rBuf.getLength(), nSize)) + { + throw std::bad_alloc(); // too large for Sequence + } + } + data.realloc(nSize); + size_t nCopied(0); + for (auto const& rBuf : bufs) + { + ::std::memcpy(data.getArray() + nCopied, rBuf.getConstArray(), rBuf.getLength()); + nCopied += rBuf.getLength(); // can't overflow + } + } + } + // Clear flag before transfer starts; only a transfer started before // calling abort() will be aborted, not one started later. rSession.m_AbortFlag.store(false); @@ -1227,8 +1268,8 @@ auto CurlProcessor::ProcessRequest( try { - ProcessRequestImpl(rSession, rURI, pRequestHeaderList.get(), pxOutStream, pxInStream, - pRequestedHeaders, headers); + ProcessRequestImpl(rSession, rURI, pRequestHeaderList.get(), pxOutStream, + pxInStream ? &data : nullptr, pRequestedHeaders, headers); } catch (DAVException const& rException) {