13#include <zypp-core/zyppng/base/EventDispatcher>
14#include <zypp-core/zyppng/base/private/linuxhelpers_p.h>
15#include <zypp-core/zyppng/core/String>
16#include <zypp-core/fs/PathInfo.h>
18#include <zypp-curl/CurlConfig>
19#include <zypp-curl/auth/CurlAuthData>
20#include <zypp-media/MediaConfig>
21#include <zypp-core/base/String.h>
22#include <zypp-core/base/StringV.h>
23#include <zypp-core/base/IOTools.h>
24#include <zypp-core/Pathname.h>
31#include <boost/variant.hpp>
32#include <boost/variant/polymorphic_get.hpp>
38 static size_t nwr_headerCallback (
char *ptr,
size_t size,
size_t nmemb,
void *userdata ) {
42 NetworkRequestPrivate *that =
reinterpret_cast<NetworkRequestPrivate *
>( userdata );
43 return that->headerfunction( ptr, size * nmemb );
45 static size_t nwr_writeCallback (
char *ptr,
size_t size,
size_t nmemb,
void *userdata ) {
49 NetworkRequestPrivate *that =
reinterpret_cast<NetworkRequestPrivate *
>( userdata );
50 return that->writefunction( ptr, {}, size * nmemb );
54 template<
class T>
struct always_false : std::false_type {};
58 : _outFile(
std::move(prevState._outFile) )
59 , _downloaded( prevState._downloaded )
60 , _partialHelper(
std::move(prevState._partialHelper) )
64 : _partialHelper(
std::move(prevState._partialHelper) )
68 : _outFile(
std::move(prevState._outFile) )
69 , _partialHelper(
std::move(prevState._partialHelper) )
70 , _downloaded( prevState._downloaded )
78 ,
_headers(
std::unique_ptr< curl_slist, decltype (&curl_slist_free_all) >(
nullptr, &curl_slist_free_all ) )
108 const std::string urlScheme =
_url.getScheme();
109 if ( urlScheme ==
"http" || urlScheme ==
"https" )
121 std::string urlBuffer(
_url.asString() );
150 const auto &cHeaders =
_dispatcher->hostSpecificHeaders();
151 if (
auto i = cHeaders.find(
_url.getHost()); i != cHeaders.end() ) {
152 for (
const auto &[key, value] : i->second ) {
154 "%s: %s", key.c_str(), value.c_str() )
165 case 4:
setCurlOption( CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4 );
break;
166 case 6:
setCurlOption( CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6 );
break;
170 setCurlOption( CURLOPT_HEADERFUNCTION, &nwr_headerCallback );
185 if ( urlScheme ==
"https" )
189 bool allowHttp = (
_url.getHost() ==
"download.opensuse.org" );
209#ifdef CURLSSLOPT_ALLOW_BEAST
211 setCurlOption( CURLOPT_SSL_OPTIONS, CURLSSLOPT_ALLOW_BEAST );
238 if (use_auth.empty())
239 use_auth =
"digest,basic";
241 if( auth != CURLAUTH_NONE)
243 DBG <<
_easyHandle <<
" " <<
"Enabling HTTP authentication methods: " << use_auth
244 <<
" (CURLOPT_HTTPAUTH=" << auth <<
")" << std::endl;
253 setCurlOption(CURLOPT_PROXYAUTH, CURLAUTH_BASIC|CURLAUTH_DIGEST|CURLAUTH_NTLM );
264 if ( proxyuserpwd.empty() )
269 DBG <<
_easyHandle <<
" " <<
"Proxy: ~/.curlrc does not contain the proxy-user option" << std::endl;
273 DBG <<
_easyHandle <<
" " <<
"Proxy: using proxy-user from ~/.curlrc" << std::endl;
281 if ( ! proxyuserpwd.empty() )
286#if CURLVERSION_AT_LEAST(7,19,4)
291 DBG <<
_easyHandle <<
" " <<
"Proxy: explicitly NOPROXY" << std::endl;
306#if CURLVERSION_AT_LEAST(7,15,5)
318#if CURLVERSION_AT_LEAST(7,18,0)
325 for (
const auto &header : locSet.
headers() ) {
326 if ( !z_func()->addRequestHeader( header.c_str() ) )
338 return ( elem1.start < elem2.start );
342 if (
auto initState = std::get_if<pending_t>(&
_runningMode) ) {
345 initState->_partialHelper = std::make_unique<CurlMultiPartHandler>(
351 helper = initState->_partialHelper.get();
353 }
else if (
auto pendingState = std::get_if<prepareNextRangeBatch_t>(&
_runningMode) ) {
354 helper = pendingState->_partialHelper.get();
356 errBuf =
"Request is in invalid state to call setupHandle";
378 auto rmode = std::get_if<NetworkRequestPrivate::running_t>( &
_runningMode );
380 DBG <<
_easyHandle <<
"Can only create output file in running mode" << std::endl;
384 if ( !rmode->_outFile ) {
385 std::string openMode =
"w+b";
396 if ( !rmode->_outFile ) {
409 auto rmode = std::get_if<NetworkRequestPrivate::running_t>( &
_runningMode );
410 if ( rmode->_partialHelper )
return rmode->_partialHelper->canRecover();
416 auto rmode = std::get_if<NetworkRequestPrivate::running_t>( &
_runningMode );
418 errBuf =
"NetworkRequestPrivate::prepareToContinue called in invalid state";
422 if ( rmode->_partialHelper && rmode->_partialHelper->hasMoreWork() ) {
428 auto &prepMode = std::get<prepareNextRangeBatch_t>(
_runningMode);
429 if ( !prepMode._partialHelper->prepareToContinue() ) {
430 errBuf = prepMode._partialHelper->lastErrorMessage();
434 if ( hadRangeFail ) {
447 errBuf =
"Request has no more work";
455 return std::any_of(
_requestedRanges.begin(),
_requestedRanges.end(), [](
const auto &range ){ return range._rangeState == CurlMultiPartHandler::Pending; });
460 bool isRangeContinuation = std::holds_alternative<prepareNextRangeBatch_t>(
_runningMode );
461 if ( isRangeContinuation ) {
462 MIL <<
_easyHandle <<
" " <<
"Continuing a previously started range batch." << std::endl;
470 if ( m._activityTimer ) {
476 if ( !isRangeContinuation )
482 if ( std::holds_alternative<running_t>(
_runningMode) ) {
484 if ( rmode._partialHelper )
485 rmode._partialHelper->finalize();
492 resState.
_result = std::move(err);
494 if ( std::holds_alternative<running_t>(
_runningMode) ) {
503 if ( !rmode._partialHelper->verifyData() ){
510 if ( fseek( rmode._outFile, 0, SEEK_SET ) != 0 ) {
513 constexpr size_t bufSize = 4096;
515 while(
auto cnt = fread(buf, 1, bufSize, rmode._outFile ) > 0 ) {
526 const UByteArray &expSum = zypp::Digest::hexStringToUByteArray(
_fileVerification->_fileChecksum.checksum () );
527 if ( calcSum != expSum ) {
536 rmode._outFile.reset();
560 MIL_MEDIA <<
_easyHandle <<
" Request timeout interval: " << t.interval()<<
" remaining: " << t.remaining() << std::endl;
561 std::map<std::string, boost::any> extraInfo;
562 extraInfo.insert( {
"requestUrl",
_url } );
574 if ( std::holds_alternative<running_t>(
_runningMode ) ){
576 if ( rmode._activityTimer && rmode._activityTimer->isRunning() )
577 rmode._activityTimer->start();
587 if ( !std::holds_alternative<running_t>(that->
_runningMode) ){
588 DBG << that->
_easyHandle <<
" " <<
"Curl progress callback was called in invalid state "<< that->z_func()->state() << std::endl;
592 auto &rmode = std::get<running_t>( that->
_runningMode );
597 rmode._isInCallback =
true;
598 if ( rmode._lastProgressNow != dlnow ) {
599 rmode._lastProgressNow = dlnow;
600 that->
_sigProgress.emit( *that->z_func(), dltotal, dlnow, ultotal, ulnow );
602 rmode._isInCallback =
false;
604 return rmode._cachedResult ? CURLE_ABORTED_BY_CALLBACK : CURLE_OK;
617 std::string_view hdr( ptr, bytes );
619 hdr.remove_prefix( std::min( hdr.find_first_not_of(
" \t\r\n"), hdr.size() ) );
620 const auto lastNonWhitespace = hdr.find_last_not_of(
" \t\r\n");
621 if ( lastNonWhitespace != hdr.npos )
622 hdr.remove_suffix( hdr.size() - (lastNonWhitespace + 1) );
624 hdr = std::string_view();
631 const auto &repSize = rmode._partialHelper->reportedFileSize ();
637 if ( zypp::strv::hasPrefixCI( hdr,
"HTTP/" ) ) {
640 (void)curl_easy_getinfo(
_easyHandle, CURLINFO_RESPONSE_CODE, &statuscode);
646 }
else if ( zypp::strv::hasPrefixCI( hdr,
"Location:" ) ) {
650 }
else if ( zypp::strv::hasPrefixCI( hdr,
"Content-Length:") ) {
652 auto str = std::string ( lenStr.data(), lenStr.length() );
653 auto len = zypp::str::strtonum<typename zypp::ByteCount::SizeType>(
str.data() );
655 DBG <<
_easyHandle <<
" " <<
"Got Content-Length Header: " << len << std::endl;
685 if ( fseek( rmode._outFile, *offset, SEEK_SET ) != 0 ) {
687 "Unable to set output file pointer." );
690 rmode._currentFileOffset = *offset;
694 const auto &repSize = rmode._partialHelper->reportedFileSize ();
707 auto written = fwrite( data, 1, max, rmode._outFile );
716 rmode._currentFileOffset += written;
719 rmode._downloaded += written;
736 if ( d->_dispatcher )
737 d->_dispatcher->cancel( *
this,
"Request destroyed while still running" );
742 d_func()->_expectedFileSize = std::move( expectedFileSize );
749 if (
state() ==
Pending && triggerReschedule && d->_dispatcher )
750 d->_dispatcher->reschedule();
755 return d_func()->_priority;
760 d_func()->_options = opt;
765 return d_func()->_options;
774 d->_requestedRanges.push_back(
Range::make( start, len, std::move(digest), std::move( expectedChkSum ), std::move( userData ), digestCompareLen, chksumpad ) );
783 d->_requestedRanges.push_back( std::move(range) );
784 auto &rng = d->_requestedRanges.back();
786 rng.bytesWritten = 0;
788 rng._digest->reset();
803 ._fileChecksum = expected
813 d->_requestedRanges.clear();
818 const auto mystate =
state();
824 std::vector<Range> failed;
825 for (
auto &r : d->_requestedRanges ) {
827 failed.push_back( r.clone() );
835 return d_func()->_requestedRanges;
840 return d_func()->_lastRedirect;
845 return d_func()->_easyHandle;
850 const auto myerr =
error();
851 const auto mystate =
state();
857 auto getMeasurement = [ this ](
const CURLINFO info, std::chrono::microseconds &target ){
858 using FPSeconds = std::chrono::duration<double, std::chrono::seconds::period>;
860 const auto res = curl_easy_getinfo( d_func()->_easyHandle, info, &val );
861 if ( CURLE_OK == res ) {
862 target = std::chrono::duration_cast<std::chrono::microseconds>( FPSeconds(val) );
866 getMeasurement( CURLINFO_NAMELOOKUP_TIME, t.
namelookup );
867 getMeasurement( CURLINFO_CONNECT_TIME, t.
connect);
868 getMeasurement( CURLINFO_APPCONNECT_TIME, t.
appconnect);
869 getMeasurement( CURLINFO_PRETRANSFER_TIME , t.
pretransfer);
870 getMeasurement( CURLINFO_TOTAL_TIME, t.
total);
871 getMeasurement( CURLINFO_REDIRECT_TIME, t.
redirect);
880 if ( !std::holds_alternative<NetworkRequestPrivate::running_t>( d->_runningMode) )
883 const auto &rmode = std::get<NetworkRequestPrivate::running_t>( d->_runningMode );
889 return d_func()->_url;
903 return d_func()->_targetFile;
911 d->_targetFile = path;
916 return d_func()->_fMode;
924 d->_fMode = std::move( mode );
930 if ( curl_easy_getinfo( d_func()->_easyHandle, CURLINFO_CONTENT_TYPE, &ptr ) == CURLE_OK && ptr )
931 return std::string(ptr);
932 return std::string();
938 using T = std::decay_t<
decltype(arg)>;
939 if constexpr (std::is_same_v<T, NetworkRequestPrivate::pending_t> || std::is_same_v<T, NetworkRequestPrivate::prepareNextRangeBatch_t> )
941 else if constexpr (std::is_same_v<T, NetworkRequestPrivate::running_t>
942 || std::is_same_v<T, NetworkRequestPrivate::finished_t>)
943 return arg._contentLenght;
945 static_assert(always_false<T>::value,
"Unhandled state type");
946 }, d_func()->_runningMode);
952 using T = std::decay_t<
decltype(arg)>;
953 if constexpr (std::is_same_v<T, NetworkRequestPrivate::pending_t>)
955 else if constexpr (std::is_same_v<T, NetworkRequestPrivate::running_t>
956 || std::is_same_v<T, NetworkRequestPrivate::prepareNextRangeBatch_t>
957 || std::is_same_v<T, NetworkRequestPrivate::finished_t>)
958 return arg._downloaded;
960 static_assert(always_false<T>::value,
"Unhandled state type");
961 }, d_func()->_runningMode);
966 return d_func()->_settings;
971 return std::visit([
this](
auto& arg) {
972 using T = std::decay_t<
decltype(arg)>;
973 if constexpr (std::is_same_v<T, NetworkRequestPrivate::pending_t>)
975 else if constexpr (std::is_same_v<T, NetworkRequestPrivate::running_t> || std::is_same_v<T, NetworkRequestPrivate::prepareNextRangeBatch_t> )
977 else if constexpr (std::is_same_v<T, NetworkRequestPrivate::finished_t>) {
978 if ( std::get<NetworkRequestPrivate::finished_t>( d_func()->_runningMode ).
_result.isError() )
984 static_assert(always_false<T>::value,
"Unhandled state type");
985 }, d_func()->_runningMode);
990 const auto s =
state();
993 return std::get<NetworkRequestPrivate::finished_t>( d_func()->_runningMode)._result;
999 return std::string();
1013 curl_slist *res = curl_slist_append( d->_headers ? d->_headers.get() :
nullptr, header.c_str() );
1018 d->_headers = std::unique_ptr< curl_slist, decltype (&curl_slist_free_all) >( res, &curl_slist_free_all );
1025 return d_func()->_sigStarted;
1030 return d_func()->_sigBytesDownloaded;
1035 return d_func()->_sigProgress;
1040 return d_func()->_sigFinished;
ZYppCommitResult & _result
Store and operate with byte count.
static const Unit B
1 Byte
Compute Message Digests (MD5, SHA1 etc)
bool create(const std::string &name)
initialize creation of a new message digest
Base class for Exception.
std::string asString() const
Error message provided by dumpOn as string.
const char * c_str() const
String representation.
const std::string & asString() const
String representation.
bool empty() const
Test for an empty path.
The CurlMultiPartHandler class.
const std::string & lastErrorMessage() const
static zyppng::NetworkRequestError customError(NetworkRequestError::Type t, std::string &&errorMsg="", std::map< std::string, boost::any > &&extraInfo={})
The NetworkRequestError class Represents a error that occured in.
Type type() const
type Returns the type of the error
std::string nativeErrorString() const
bool isError() const
isError Will return true if this is a actual error
size_t headerfunction(char *ptr, size_t bytes) override
std::optional< FileVerifyInfo > _fileVerification
The digest for the full file.
enum zyppng::NetworkRequestPrivate::ProtocolMode _protocolMode
const std::string _currentCookieFile
zypp::Pathname _targetFile
void resetActivityTimer()
Signal< void(NetworkRequest &req, zypp::ByteCount count)> _sigBytesDownloaded
NetworkRequestDispatcher * _dispatcher
std::vector< NetworkRequest::Range > _requestedRanges
the requested ranges that need to be downloaded
size_t writefunction(char *ptr, std::optional< off_t > offset, size_t bytes) override
static int curlProgressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
std::string errorMessage() const
Signal< void(NetworkRequest &req)> _sigStarted
NetworkRequest::FileMode _fMode
std::variant< pending_t, running_t, prepareNextRangeBatch_t, finished_t > _runningMode
bool initialize(std::string &errBuf)
void onActivityTimeout(Timer &)
Signal< void(NetworkRequest &req, off_t dltotal, off_t dlnow, off_t ultotal, off_t ulnow)> _sigProgress
std::string _lastRedirect
to log/report redirections
NetworkRequest::Options _options
bool prepareToContinue(std::string &errBuf)
void setResult(NetworkRequestError &&err)
std::array< char, CURL_ERROR_SIZE+1 > _errorBuf
virtual ~NetworkRequestPrivate()
bool setupHandle(std::string &errBuf)
NetworkRequestPrivate(Url &&url, zypp::Pathname &&targetFile, NetworkRequest::FileMode fMode, NetworkRequest &p)
TransferSettings _settings
void setCurlOption(CURLoption opt, T data)
zypp::ByteCount _expectedFileSize
Signal< void(NetworkRequest &req, const NetworkRequestError &err)> _sigFinished
std::unique_ptr< curl_slist, decltype(&curl_slist_free_all) > _headers
bool setExpectedFileChecksum(const zypp::CheckSum &expected)
zypp::ByteCount reportedByteCount() const
Returns the number of bytes that are reported from the backend as the full download size,...
const zypp::Pathname & targetFilePath() const
Returns the target filename path.
zypp::ByteCount downloadedByteCount() const
Returns the number of already downloaded bytes as reported by the backend.
void resetRequestRanges()
void setUrl(const Url &url)
This will change the URL of the request.
void setExpectedFileSize(zypp::ByteCount expectedFileSize)
virtual ~NetworkRequest()
void setPriority(Priority prio, bool triggerReschedule=true)
std::vector< char > peekData(off_t offset, size_t count) const
std::string contentType() const
Returns the content type as reported from the server.
void setFileOpenMode(FileMode mode)
Sets the file open mode to mode.
bool addRequestHeader(const std::string &header)
void setOptions(Options opt)
FileMode fileOpenMode() const
Returns the currently configured file open mode.
bool hasError() const
Checks if there was a error with the request.
State state() const
Returns the current state the HttpDownloadRequest is in.
SignalProxy< void(NetworkRequest &req, const NetworkRequestError &err)> sigFinished()
Signals that the download finished.
SignalProxy< void(NetworkRequest &req, zypp::ByteCount count)> sigBytesDownloaded()
Signals that new data has been downloaded, this is only the payload and does not include control data...
std::optional< Timings > timings() const
After the request is finished query the timings that were collected during download.
std::string extendedErrorString() const
In some cases, curl can provide extended error information collected at runtime.
Priority priority() const
NetworkRequestError error() const
Returns the last set Error.
void setTargetFilePath(const zypp::Pathname &path)
Changes the target file path of the download.
void * nativeHandle() const
void addRequestRange(size_t start, size_t len=0, std::optional< zypp::Digest > &&digest={}, CheckSumBytes expectedChkSum=CheckSumBytes(), std::any userData=std::any(), std::optional< size_t > digestCompareLen={}, std::optional< size_t > chksumpad={})
std::vector< Range > failedRanges() const
const std::vector< Range > & requestedRanges() const
SignalProxy< void(NetworkRequest &req)> sigStarted()
Signals that the dispatcher dequeued the request and actually starts downloading data.
SignalProxy< void(NetworkRequest &req, off_t dltotal, off_t dlnow, off_t ultotal, off_t ulnow)> sigProgress()
Signals if there was data read from the download.
TransferSettings & transferSettings()
const std::string & lastRedirectInfo() const
#define EXPLICITLY_NO_PROXY
std::string curlUnEscape(std::string text_r)
void setupZYPP_MEDIA_CURL_DEBUG(CURL *curl)
Setup CURLOPT_VERBOSE and CURLOPT_DEBUGFUNCTION according to env::ZYPP_MEDIA_CURL_DEBUG.
CURLcode setCurlRedirProtocols(CURL *curl, bool enableHttp)
String related utilities and Regular expression matching.
int ZYPP_MEDIA_CURL_IPRESOLVE()
4/6 to force IPv4/v6
int assert_file_mode(const Pathname &path, unsigned mode)
Like assert_file but enforce mode even if the file already exists.
constexpr bool always_false
std::vector< char > peek_data_fd(FILE *fd, off_t offset, size_t count)
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
bool strToBool(const C_Str &str, bool default_r)
Parse str into a bool depending on the default value.
std::string trim(const std::string &s, const Trim trim_r)
Easy-to use interface to the ZYPP dependency resolver.
ZYPP_IMPL_PRIVATE(Provide)
static Range make(size_t start, size_t len=0, std::optional< zypp::Digest > &&digest={}, CheckSumBytes &&expectedChkSum=CheckSumBytes(), std::any &&userData=std::any(), std::optional< size_t > digestCompareLen={}, std::optional< size_t > _dataBlockPadding={})
NetworkRequestError _result
zypp::ByteCount _contentLenght
running_t(pending_t &&prevState)
std::chrono::microseconds appconnect
std::chrono::microseconds redirect
std::chrono::microseconds pretransfer
std::chrono::microseconds total
std::chrono::microseconds namelookup
std::chrono::microseconds connect
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.