libzypp 17.32.2
curlmultiparthandler.cc
Go to the documentation of this file.
2
4#include <zypp-core/base/StringV.h>
5
6#include <curl/curl.h>
7
8#include <utility>
9
10namespace zyppng {
11
12 namespace {
13
14 class CurlMultiPartSetoptError : public zypp::Exception
15 {
16 public:
17 CurlMultiPartSetoptError ( CURLcode code ) : _code(code){};
18 CURLcode code() const;
19 private:
20 CURLcode _code;
21 };
22
23 CURLcode CurlMultiPartSetoptError::code() const {
24 return _code;
25 }
26
27 class CurlMultInitRangeError : public zypp::Exception
28 {
29 public:
30 CurlMultInitRangeError ( std::string what ) : zypp::Exception( std::move(what) ){}
31 };
32
33 }
34
35 size_t CurlMultiPartHandler::curl_hdrcallback(char *ptr, size_t size, size_t nmemb, void *userdata)
36 {
37 if ( !userdata )
38 return 0;
39
40 CurlMultiPartHandler *that = reinterpret_cast<CurlMultiPartHandler *>( userdata );
41 return that->hdrcallback( ptr, size, nmemb );
42 }
43
44 size_t CurlMultiPartHandler::curl_wrtcallback(char *ptr, size_t size, size_t nmemb, void *userdata)
45 {
46 if ( !userdata )
47 return 0;
48
49 CurlMultiPartHandler *that = reinterpret_cast<CurlMultiPartHandler *>( userdata );
50 return that->wrtcallback( ptr, size, nmemb );
51 }
52
54 {
56 bytesWritten = 0;
57 if ( _digest ) _digest->reset();
58 }
59
61 {
62 return Range {
63 .start = start,
64 .len = len,
65 .bytesWritten = 0,
66 ._digest = _digest ? _digest->clone() : std::optional<zypp::Digest>{},
67 ._checksum = _checksum,
68 ._relevantDigestLen = _relevantDigestLen,
69 ._chksumPad = _chksumPad,
70 .userData = userData,
71 ._rangeState = _rangeState
72 };
73 }
74
75 CurlMultiPartHandler::Range CurlMultiPartHandler::Range::make(size_t start, size_t len, std::optional<zypp::Digest> &&digest, CheckSumBytes &&expectedChkSum, std::any &&userData, std::optional<size_t> digestCompareLen, std::optional<size_t> dataBlockPadding)
76 {
77 return Range {
78 .start = start,
79 .len = len,
80 .bytesWritten = 0,
81 ._digest = std::move( digest ),
82 ._checksum = std::move( expectedChkSum ),
83 ._relevantDigestLen = std::move( digestCompareLen ),
84 ._chksumPad = std::move( dataBlockPadding ),
85 .userData = std::move( userData ),
86 ._rangeState = State::Pending
87 };
88 }
89
90 CurlMultiPartHandler::CurlMultiPartHandler(ProtocolMode mode, void *easyHandle, std::vector<Range> &ranges, CurlMultiPartDataReceiver &receiver)
91 : _protocolMode( mode )
92 , _easyHandle( easyHandle )
93 , _receiver( receiver )
94 , _requestedRanges( ranges )
95 {
96 // non http can only do range by range
98 WAR << "!!!! Downloading ranges without HTTP might be slow !!!!" << std::endl;
100 }
101 }
102
104 {
105 if ( _easyHandle ) {
106 curl_easy_setopt( _easyHandle, CURLOPT_HEADERFUNCTION, nullptr );
107 curl_easy_setopt( _easyHandle, CURLOPT_HEADERDATA, nullptr );
108 curl_easy_setopt( _easyHandle, CURLOPT_WRITEFUNCTION, nullptr );
109 curl_easy_setopt( _easyHandle, CURLOPT_WRITEDATA, nullptr );
110 }
111 }
112
113 size_t CurlMultiPartHandler::hdrcallback(char *ptr, size_t size, size_t nmemb)
114 {
115 // it is valid to call this function with no data to read, just call the given handler
116 // or return ok
117 if ( size * nmemb == 0)
118 return _receiver.headerfunction( ptr, size * nmemb );
119
121
122 std::string_view hdr( ptr, size*nmemb );
123
124 hdr.remove_prefix( std::min( hdr.find_first_not_of(" \t\r\n"), hdr.size() ) );
125 const auto lastNonWhitespace = hdr.find_last_not_of(" \t\r\n");
126 if ( lastNonWhitespace != hdr.npos )
127 hdr.remove_suffix( hdr.size() - (lastNonWhitespace + 1) );
128 else
129 hdr = std::string_view();
130
131 // we just received whitespaces, wait for more
132 if ( !hdr.size() ) {
133 return ( size * nmemb );
134 }
135
136 if ( zypp::strv::hasPrefixCI( hdr, "HTTP/" ) ) {
137
138 long statuscode = 0;
139 (void)curl_easy_getinfo( _easyHandle, CURLINFO_RESPONSE_CODE, &statuscode);
140
141 const auto &doRangeFail = [&](){
142 WAR << _easyHandle << " " << "Range FAIL, trying with a smaller batch" << std::endl;
143
144 setCode ( Code::RangeFail, "Expected range status code 206, but got none." );
145
146 // reset all ranges we requested to pending, we never got the data for them
147 std::for_each( _requestedRanges.begin (), _requestedRanges.end(), []( auto &range ) {
148 if ( range._rangeState == Running )
149 range._rangeState = Pending;
150 });
151 return 0;
152 };
153
154 // ignore other status codes, maybe we are redirected etc.
155 if ( ( statuscode >= 200 && statuscode <= 299 && statuscode != 206 )
156 || statuscode == 416 ) {
157 return doRangeFail();
158 }
159
160 } else if ( zypp::strv::hasPrefixCI( hdr, "Content-Range:") ) {
161 Range r;
162
163 size_t fileLen = 0;
164 if ( !parseContentRangeHeader( hdr, r.start, r.len, fileLen ) ) {
165 //@TODO shouldn't we map this to a extra error? After all this is not our fault
166 setCode( Code::InternalError, "Invalid Content-Range header format." );
167 return 0;
168 }
169
170 if ( !_reportedFileSize ) {
171 WAR << "Setting request filesize to " << fileLen << std::endl;
172 _reportedFileSize = fileLen;
173 }
174
175 DBG << _easyHandle << " " << "Got content range :" << r.start << " len " << r.len << std::endl;
177 _currentSrvRange = std::move(r);
178
179 } else if ( zypp::strv::hasPrefixCI( hdr, "Content-Type:") ) {
180 std::string sep;
181 if ( parseContentTypeMultiRangeHeader( hdr, sep ) ) {
183 _isMuliPartResponse = true;
184 _seperatorString = "--"+sep;
185 }
186 }
187 }
188
189 return _receiver.headerfunction( ptr, size * nmemb );
190 }
191
192 size_t CurlMultiPartHandler::wrtcallback(char *ptr, size_t size, size_t nmemb)
193 {
194 const auto max = ( size * nmemb );
195
196 //it is valid to call this function with no data to write, just call the given handler
197 if ( max == 0)
198 return _receiver.writefunction( ptr, {}, max );
199
200 // try to consume all bytes that have been given to us
201 size_t bytesConsumedSoFar = 0;
202 while ( bytesConsumedSoFar != max ) {
203
204 std::optional<off_t> seekTo;
205
206 // this is called after all headers have been processed
207 if ( !_allHeadersReceived ) {
208 _allHeadersReceived = true;
209
210 // no ranges at all, this is a error
212 //we got a invalid response, the status code pointed to being partial ( 206 ) but we got no range definition
213 setCode( Code::ServerReturnedError, "Invalid data from server, range respone was announced but there was no range definiton." );
214 return 0;
215 }
216 }
217
219 /*
220 this branch is responsible to find a range that overlaps the current server range, so we know which of our ranges to fill
221 Entering here means we are in one of two situations:
222
223 1) we just have finished writing a requested range but
224 still have not completely consumed a range that we have received from the server.
225 Since HTTP std allows the server to coalesce requested ranges in order to optimize downloads
226 we need to find the best match ( because the current offset might not even be in our requested ranges )
227
228 2) we just parsed a Content-Length header and start a new block
229 */
230
231 std::optional<uint> foundRange;
232 const size_t beginSrvRange = _currentSrvRange->start + _currentSrvRange->bytesWritten;
233 const size_t endSrvRange = beginSrvRange + (_currentSrvRange->len - _currentSrvRange->bytesWritten);
234 auto currDist = ULONG_MAX;
235 for ( uint i = 0; i < _requestedRanges.size(); i++ ) {
236 const auto &currR = _requestedRanges[i];
237
238 // do not allow double ranges
239 if ( currR._rangeState == Finished || currR._rangeState == Error )
240 continue;
241
242 // check if the range was already written
243 if ( currR.len && currR.len == currR.bytesWritten )
244 continue;
245
246 const auto currRBegin = currR.start + currR.bytesWritten;
247 if ( !( beginSrvRange <= currRBegin && endSrvRange >= currRBegin ) )
248 continue;
249
250 // calculate the distance of the current ranges offset+data written to the range we got back from the server
251 const auto newDist = currRBegin - beginSrvRange;
252
253 // exact match
254 if ( currRBegin == beginSrvRange && currR.len == _currentSrvRange->len ) {
255 foundRange = i;
256 break;
257 }
258
259 if ( !foundRange ) {
260 foundRange = i;
261 currDist = newDist;
262 } else {
263 //pick the range with the closest distance
264 if ( newDist < currDist ) {
265 foundRange = i;
266 currDist = newDist;
267 }
268 }
269 }
270
271 if ( !foundRange ) {
272 // @TODO shouldn't we simply consume the rest of the range here and see if future data will contain a matchable range again?
273 setCode( Code::InternalError, "Unable to find a matching range for data returned by the server.");
274 return 0;
275 }
276
277 //set the found range as the current one
278 _currentRange = *foundRange;
279
280 //continue writing where we stopped
281 seekTo = _requestedRanges[*foundRange].start + _requestedRanges[*foundRange].bytesWritten;
282
283 //if we skip bytes we need to advance our written bytecount
284 const auto skipBytes = *seekTo - beginSrvRange;
285 bytesConsumedSoFar += skipBytes;
286 _currentSrvRange->bytesWritten += skipBytes;
287
288 std::string errBuf = "Receiver cancelled starting the current range.";
289 if ( !_receiver.beginRange (*_currentRange, errBuf) ) {
290 setCode( Code::InternalError, "Receiver cancelled starting the current range.");
291 return 0;
292 }
293
294 } else if ( _protocolMode != ProtocolMode::HTTP && !_currentRange ) {
295 // if we are not running in HTTP mode we can only request a single range, that means we get our data
296 // in one continous stream. Since our
297 // ranges are ordered by start, we just pick the first one that is marked as running
298 if ( !_currentRange ) {
299 const auto i = std::find_if( _requestedRanges.begin (), _requestedRanges.end(), []( const Range &r){ return r._rangeState == Running; });
300 if ( i == _requestedRanges.end() ) {
301 setCode( Code::InternalError, "Received data but no range was marked as requested." );
302 return 0;
303 }
304
305 _currentRange = std::distance( _requestedRanges.begin(), i );
306
307 //continue writing where we stopped
308 seekTo = _requestedRanges[*_currentRange].start + _requestedRanges[*_currentRange].bytesWritten;
309 }
310 }
311
312 if ( _currentRange ) {
313 /*
314 * We have data to write and know the target range
315 */
316
317 // make sure we do not read over the current server range
318 auto availableData = max - bytesConsumedSoFar;
319 if ( _currentSrvRange ) {
320 availableData = std::min( availableData, _currentSrvRange->len - _currentSrvRange->bytesWritten );
321 }
322
323 auto &rng = _requestedRanges[*_currentRange];
324
325 // do only write what we need until the range is full
326 const auto bytesToWrite = rng.len > 0 ? std::min( rng.len - rng.bytesWritten, availableData ) : availableData;
327
328 auto written = _receiver.writefunction( ptr + bytesConsumedSoFar, seekTo, bytesToWrite );
329 if ( written <= 0 )
330 return 0;
331
332 if ( rng._digest && rng._checksum.size() ) {
333 if ( !rng._digest->update( ptr + bytesConsumedSoFar, written ) )
334 return 0;
335 }
336
337 rng.bytesWritten += written;
338 if ( _currentSrvRange ) _currentSrvRange->bytesWritten += written;
339
340 // range is done
341 if ( rng.len > 0 && rng.bytesWritten >= rng.len ) {
342 std::string errBuf = "Receiver cancelled after finishing the current range.";
343 bool rngIsValid = validateRange( rng );
344 if ( !_receiver.finishedRange (*_currentRange, rngIsValid, errBuf) ) {
345 setCode( Code::InternalError, "Receiver cancelled starting the current range.");
346 return 0;
347 }
348 _currentRange.reset();
349 }
350
351 if ( _currentSrvRange && _currentSrvRange->len > 0 && _currentSrvRange->bytesWritten >= _currentSrvRange->len ) {
352 _currentSrvRange.reset();
353 // we ran out of data in the current chunk, reset the target range if it is not a open range as well, because next data will be
354 // a chunk header again
356 _currentRange.reset();
357 }
358
359 bytesConsumedSoFar += written;
360 }
361
362 if ( bytesConsumedSoFar == max )
363 return max;
364
366 /*
367 This branch is reponsible to parse the multibyte response we got from the
368 server and parse the next target range from it
369 */
370
371 std::string_view incoming( ptr + bytesConsumedSoFar, max - bytesConsumedSoFar );
372 auto hdrEnd = incoming.find("\r\n\r\n");
373 if ( hdrEnd == incoming.npos ) {
374 //no header end in the data yet, push to buffer and return
375 _rangePrefaceBuffer.insert( _rangePrefaceBuffer.end(), incoming.begin(), incoming.end() );
376 return max;
377 }
378
379 //append the data of the current header to the buffer and parse it
380 _rangePrefaceBuffer.insert( _rangePrefaceBuffer.end(), incoming.begin(), incoming.begin() + ( hdrEnd + 4 ) );
381 bytesConsumedSoFar += ( hdrEnd + 4 ); //header data plus header end
382
383 std::string_view data( _rangePrefaceBuffer.data(), _rangePrefaceBuffer.size() );
384 auto sepStrIndex = data.find( _seperatorString );
385 if ( sepStrIndex == data.npos ) {
386 setCode( Code::InternalError, "Invalid multirange header format, seperator string missing." );
387 return 0;
388 }
389
390 auto startOfHeader = sepStrIndex + _seperatorString.length();
391 std::vector<std::string_view> lines;
392 zypp::strv::split( data.substr( startOfHeader ), "\r\n", zypp::strv::Trim::trim, [&]( std::string_view strv ) { lines.push_back(strv); } );
393 for ( const auto &hdrLine : lines ) {
394 if ( zypp::strv::hasPrefixCI(hdrLine, "Content-Range:") ) {
395 size_t fileLen = 0;
396 Range r;
397 //if we can not parse the header the message must be broken
398 if(! parseContentRangeHeader( hdrLine, r.start, r.len, fileLen ) ) {
399 setCode( Code::InternalError, "Invalid Content-Range header format." );
400 return 0;
401 }
402 if ( !_reportedFileSize ) {
403 _reportedFileSize = fileLen;
404 }
405 _currentSrvRange = std::move(r);
406 break;
407 }
408 }
409
410 if ( !_currentSrvRange ){
411 setCode( Code::InternalError, "Could not read a server range from the response." );
412 return 0;
413 }
414
415 //clear the buffer again
416 _rangePrefaceBuffer.clear();
417 }
418 }
419
420 return bytesConsumedSoFar;
421 }
422
424 {
425 return _easyHandle;
426 }
427
429 {
430 // We can recover from RangeFail errors if we have more batch sizes to try
431 // we never auto downgrade to last range set ( which is 1 ) because in that case
432 // just downloading the full file is usually faster.
433 if ( _lastCode == Code::RangeFail )
434 return ( _rangeAttemptIdx + 1 < ( _rangeAttemptSize - 1 ) ) && hasMoreWork();
435 return false;
436 }
437
439 {
440 // check if we have ranges that have never been requested
441 return std::any_of( _requestedRanges.begin(), _requestedRanges.end(), []( const auto &range ){ return range._rangeState == Pending; });
442 }
443
445 {
446 return _lastCode != Code::NoError;
447 }
448
453
454 const std::string &CurlMultiPartHandler::lastErrorMessage() const
455 {
456 return _lastErrorMsg;
457 }
458
460 {
461 if ( hasMoreWork() ) {
462 // go to the next range batch level if we are restarted due to a failed range request
463 if ( _lastCode == Code::RangeFail ) {
464 if ( _rangeAttemptIdx + 1 >= _rangeAttemptSize ) {
465 setCode ( Code::RangeFail, "No more range batch sizes available", true );
466 return false;
467 }
469 }
470 return true;
471 }
472
473 setCode ( Code::NoError, "Request has no more work", true );
474 return false;
475
476 }
477
479 {
480 // if we still have a current range set it valid by checking the checksum
481 if ( _currentRange ) {
482 auto &currR = _requestedRanges[*_currentRange];
483 std::string errBuf;
484 bool rngIsValid = validateRange( currR );
485 _receiver.finishedRange (*_currentRange, rngIsValid, errBuf);
486 _currentRange.reset();
487 }
488 }
489
491 {
492 finalize();
493
494 for ( auto &r : _requestedRanges ) {
495 if ( r._rangeState != CurlMultiPartHandler::Finished ) {
496 if ( r.len > 0 && r.bytesWritten != r.len )
497 setCode( Code::MissingData, (zypp::str::Format("Did not receive all requested data from the server ( off: %1%, req: %2%, recv: %3% ).") % r.start % r.len % r.bytesWritten ) );
498 else if ( r._digest && r._checksum.size() && !checkIfRangeChkSumIsValid(r) ) {
499 setCode( Code::InvalidChecksum, (zypp::str::Format("Invalid checksum %1%, expected checksum %2%") % r._digest->digest() % zypp::Digest::digestVectorToString( r._checksum ) ) );
500 } else {
501 setCode( Code::InternalError, (zypp::str::Format("Download of block failed.") ) );
502 }
503 //we only report the first error
504 break;
505 }
506 }
507 return ( _lastCode == Code::NoError );
508 }
509
511 {
512 _lastCode = Code::NoError;
513 _lastErrorMsg.clear();
514 _seperatorString.clear();
515 _currentSrvRange.reset();
516 _reportedFileSize.reset();
517 _gotContentRangeInfo = false;
518 _allHeadersReceived = false;
519 _isMuliPartResponse = false;
520
521 if ( _requestedRanges.size() == 0 ) {
522 setCode( Code::InternalError, "Calling the CurlMultiPartHandler::prepare function without a range to download is not supported.");
523 return false;
524 }
525
526 const auto setCurlOption = [&]( CURLoption opt, auto &&data )
527 {
528 auto ret = curl_easy_setopt( _easyHandle, opt, data );
529 if ( ret != 0 ) {
530 throw CurlMultiPartSetoptError(ret);
531 }
532 };
533
534 try {
535 setCurlOption( CURLOPT_HEADERFUNCTION, CurlMultiPartHandler::curl_hdrcallback );
536 setCurlOption( CURLOPT_HEADERDATA, this );
537 setCurlOption( CURLOPT_WRITEFUNCTION, CurlMultiPartHandler::curl_wrtcallback );
538 setCurlOption( CURLOPT_WRITEDATA, this );
539
540 std::string rangeDesc;
541 uint rangesAdded = 0;
542 auto maxRanges = _rangeAttempt[_rangeAttemptIdx];
543
544 // helper function to build up the request string for the range
545 auto addRangeString = [ &rangeDesc, &rangesAdded ]( const std::pair<size_t, size_t> &range ) {
546 std::string rangeD = zypp::str::form("%llu-", static_cast<unsigned long long>( range.first ) );
547 if( range.second > 0 )
548 rangeD.append( zypp::str::form( "%llu", static_cast<unsigned long long>( range.second ) ) );
549
550 if ( rangeDesc.size() )
551 rangeDesc.append(",").append( rangeD );
552 else
553 rangeDesc = std::move( rangeD );
554
555 rangesAdded++;
556 };
557
558 std::optional<std::pair<size_t, size_t>> currentZippedRange;
559 bool closedRange = true;
560 for ( auto &range : _requestedRanges ) {
561
562 if ( range._rangeState != Pending )
563 continue;
564
565 //reset the download results
566 range.bytesWritten = 0;
567
568 //when we have a open range in the list of ranges we will get from start of range to end of file,
569 //all following ranges would never be marked as valid, so we have to fail early
570 if ( !closedRange )
571 throw CurlMultInitRangeError("It is not supported to request more ranges after a open range.");
572
573 const auto rangeEnd = range.len > 0 ? range.start + range.len - 1 : 0;
574 closedRange = (rangeEnd > 0);
575
576 bool added = false;
577
578 // we try to compress the requested ranges into as big chunks as possible for the request,
579 // when receiving we still track the original ranges so we can collect and test their checksums
580 if ( !currentZippedRange ) {
581 added = true;
582 currentZippedRange = std::make_pair( range.start, rangeEnd );
583 } else {
584 //range is directly consecutive to the previous range
585 if ( currentZippedRange->second + 1 == range.start ) {
586 added = true;
587 currentZippedRange->second = rangeEnd;
588 } else {
589 //this range does not directly follow the previous one, we build the string and start a new one
590 if ( rangesAdded +1 >= maxRanges ) break;
591 added = true;
592 addRangeString( *currentZippedRange );
593 currentZippedRange = std::make_pair( range.start, rangeEnd );
594 }
595 }
596
597 // remember range was already requested
598 if ( added ) {
599 setRangeState( range, Running );
600 range.bytesWritten = 0;
601 if ( range._digest )
602 range._digest->reset();
603 }
604
605 if ( rangesAdded >= maxRanges ) {
606 MIL << _easyHandle << " " << "Reached max nr of ranges (" << maxRanges << "), batching the request to not break the server" << std::endl;
607 break;
608 }
609 }
610
611 // add the last range too
612 if ( currentZippedRange )
613 addRangeString( *currentZippedRange );
614
615 MIL << _easyHandle << " " << "Requesting Ranges: " << rangeDesc << std::endl;
616
617 setCurlOption( CURLOPT_RANGE, rangeDesc.c_str() );
618
619 } catch( const CurlMultiPartSetoptError &err ) {
620 setCode( Code::InternalError, "" );
621 } catch( const CurlMultInitRangeError &err ) {
622 setCode( Code::InternalError, err.asUserString() );
623 } catch( const zypp::Exception &err ) {
624 setCode( Code::InternalError, err.asUserString() );
625 } catch( const std::exception &err ) {
626 setCode( Code::InternalError, zypp::str::asString(err.what()) );
627 }
628 return ( _lastCode == Code::NoError );
629 }
630
631 void CurlMultiPartHandler::setCode(Code c, std::string msg , bool force)
632 {
633 // never overwrite a error, this is reset when we restart
634 if ( _lastCode != Code::NoError && !force )
635 return;
636
637 _lastCode = c;
638 _lastErrorMsg = std::move(msg);
640 }
641
642 bool CurlMultiPartHandler::parseContentRangeHeader( const std::string_view &line, size_t &start, size_t &len, size_t &fileLen )
643 {
644 //content-range: bytes 10485760-19147879/19147880
645 static const zypp::str::regex regex("^Content-Range:[[:space:]]+bytes[[:space:]]+([0-9]+)-([0-9]+)\\/([0-9]+)$", zypp::str::regex::rxdefault | zypp::str::regex::icase );
646
648 if( !zypp::str::regex_match( std::string(line), what, regex ) || what.size() != 4 ) {
649 DBG << _easyHandle << " " << "Invalid Content-Range Header format: '" << std::string(line) << std::endl;
650 return false;
651 }
652
653 size_t s = zypp::str::strtonum<size_t>( what[1]);
654 size_t e = zypp::str::strtonum<size_t>( what[2]);
655 fileLen = zypp::str::strtonum<size_t>( what[3]);
656 start = s;
657 len = ( e - s ) + 1;
658 return true;
659 }
660
661 bool CurlMultiPartHandler::parseContentTypeMultiRangeHeader( const std::string_view &line, std::string &boundary )
662 {
663 static const zypp::str::regex regex("^Content-Type:[[:space:]]+multipart\\/byteranges;[[:space:]]+boundary=(.*)$", zypp::str::regex::rxdefault | zypp::str::regex::icase );
664
666 if( zypp::str::regex_match( std::string(line), what, regex ) ) {
667 if ( what.size() >= 2 ) {
668 boundary = what[1];
669 return true;
670 }
671 }
672 return false;
673 }
674
676 {
677 if ( rng._digest && rng._checksum.size() ) {
678 if ( ( rng.len == 0 || rng.bytesWritten == rng.len ) && checkIfRangeChkSumIsValid(rng) )
680 else
681 setRangeState(rng, Error);
682 } else {
683 if ( rng.len == 0 ? true : rng.bytesWritten == rng.len )
685 else
686 setRangeState(rng, Error);
687 }
688 return ( rng._rangeState == Finished );
689 }
690
692 {
693 if ( rng._digest && rng._checksum.size() ) {
694 auto bytesHashed = rng._digest->bytesHashed ();
695 if ( rng._chksumPad && *rng._chksumPad > bytesHashed ) {
696 MIL_MEDIA << _easyHandle << " " << "Padding the digest to required block size" << std::endl;
697 zypp::ByteArray padding( *rng._chksumPad - bytesHashed, '\0' );
698 rng._digest->update( padding.data(), padding.size() );
699 }
700 auto digVec = rng._digest->digestVector();
701 if ( rng._relevantDigestLen ) {
702 digVec.resize( *rng._relevantDigestLen );
703 }
704 return ( digVec == rng._checksum );
705 }
706
707 // no checksum required
708 return true;
709 }
710
712 {
713 if ( rng._rangeState != state ) {
714 rng._rangeState = state;
715 }
716 }
717
718 std::optional<off_t> CurlMultiPartHandler::currentRange() const
719 {
720 return _currentRange;
721 }
722
723 std::optional<size_t> CurlMultiPartHandler::reportedFileSize() const
724 {
725 return _reportedFileSize;
726 }
727
728} // namespace zyppng
static std::string digestVectorToString(const UByteArray &vec)
get hex string representation of the digest vector given as parameter
Definition Digest.cc:243
Base class for Exception.
Definition Exception.h:147
Regular expression.
Definition Regex.h:95
@ icase
Do not differentiate case.
Definition Regex.h:99
@ rxdefault
These are enforced even if you don't pass them as flag argument.
Definition Regex.h:103
Regular expression match result.
Definition Regex.h:168
unsigned size() const
Definition Regex.cc:106
virtual size_t headerfunction(char *ptr, size_t bytes)=0
virtual size_t writefunction(char *ptr, std::optional< off_t > offset, size_t bytes)=0
virtual bool finishedRange(off_t range, bool validated, std::string &cancelReason)
virtual bool beginRange(off_t range, std::string &cancelReason)
The CurlMultiPartHandler class.
size_t wrtcallback(char *ptr, size_t size, size_t nmemb)
static constexpr unsigned _rangeAttemptSize
CurlMultiPartHandler(ProtocolMode mode, void *easyHandle, std::vector< Range > &ranges, CurlMultiPartDataReceiver &receiver)
std::optional< Range > _currentSrvRange
std::string _seperatorString
The seperator string for multipart responses as defined in RFC 7233 Section 4.1.
static size_t curl_wrtcallback(char *ptr, size_t size, size_t nmemb, void *userdata)
static size_t curl_hdrcallback(char *ptr, size_t size, size_t nmemb, void *userdata)
const std::string & lastErrorMessage() const
static constexpr unsigned _rangeAttempt[]
bool parseContentRangeHeader(const std::string_view &line, size_t &start, size_t &len, size_t &fileLen)
void setCode(Code c, std::string msg, bool force=false)
std::vector< char > _rangePrefaceBuffer
Here we buffer.
std::optional< off_t > currentRange() const
std::optional< size_t > _reportedFileSize
Filesize as reported by the content range or byte range headers.
size_t hdrcallback(char *ptr, size_t size, size_t nmemb)
CurlMultiPartDataReceiver & _receiver
void setRangeState(Range &rng, State state)
std::optional< size_t > reportedFileSize() const
bool parseContentTypeMultiRangeHeader(const std::string_view &line, std::string &boundary)
std::vector< Range > & _requestedRanges
the requested ranges that need to be downloaded
std::optional< off_t > _currentRange
CURLcode _code
#define MIL_MEDIA
Definition Arch.h:364
const std::string & asString(const std::string &t)
Global asString() that works with std::string too.
Definition String.h:139
bool regex_match(const std::string &s, smatch &matches, const regex &regex)
\relates regex \ingroup ZYPP_STR_REGEX \relates regex \ingroup ZYPP_STR_REGEX
Definition Regex.h:70
std::string form(const char *format,...) __attribute__((format(printf
Printf style construction of std::string.
Definition String.cc:37
Easy-to use interface to the ZYPP dependency resolver.
Convenient building of std::string with boost::format.
Definition String.h:253
std::optional< zypp::Digest > _digest
CheckSumBytes _checksum
Enables automated checking of downloaded contents against a checksum.
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={})
#define DBG
Definition Logger.h:95
#define MIL
Definition Logger.h:96
#define WAR
Definition Logger.h:97