libzypp  17.31.31
preparemulti_p.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 ----------------------------------------------------------------------*/
9 
14 #include <zypp-curl/parser/ZsyncParser>
15 #include <zypp-core/fs/PathInfo.h>
16 
17 #include "preparemulti_p.h"
18 
19 #if ENABLE_ZCHUNK_COMPRESSION
20 #include "zck_p.h"
21 #endif
22 
23 namespace zyppng {
24 
25  PrepareMultiState::PrepareMultiState( std::shared_ptr<Request> oldReq, Mode m, DownloadPrivate &parent )
26  : SimpleState( parent )
27  , _mode(m)
28  , _oldRequest( oldReq )
29  {
30  MIL << "About to enter PrepareMultiState for URL: " << parent._spec.url() << std::endl;
31  }
32 
34  {
35  auto &sm = stateMachine();
36  const auto &spec = sm._spec;
37  const auto &url = spec.url();
38  const auto &targetPath = spec.targetPath();
39 #if ENABLE_ZCHUNK_COMPRESSION
40  _haveZckData = (isZchunkFile( spec.deltaFile() ) && spec.headerSize() > 0);
41  MIL << " Upgrading request for URL: "<< url << " to multipart download , which zckunk=" << _haveZckData << std::endl;
42 #else
43  MIL << " Upgrading request for URL: "<< url << " to multipart download , which zckunk=false" << std::endl;
44 #endif
45 
46 
47  //we have a metalink download, lets parse it and see what we got
48  _mirrors.clear();
49 
50  std::vector<zypp::media::MetalinkMirror> mirrs;
51 
52  try {
53 
54  const auto &parseMetadata = [&]( auto &&parser ) {
55  using T = std::decay_t<decltype (parser)>;
56  constexpr auto metalinkMode = std::is_same< T, zypp::media::MetaLinkParser>();
57 
58  parser.parse( targetPath );
59 
60  // we only care about the metalink chunks if we have no zchunk data
61  #if ENABLE_ZCHUNK_COMPRESSION
62  if ( !_haveZckData ) {
63  #else
64  if ( true ) {
65  #endif
66  auto bl = parser.getBlockList();
67  if ( !bl.haveBlocks() )
68  MIL << "Got no blocks for URL " << spec.url() << " but got filesize? " << bl.getFilesize() << std::endl;
69  if ( bl.haveBlocks() || bl.haveFilesize() )
70  _blockList = std::move(bl);
71  }
72 
73  //migrate some settings from the base url to the mirror
74  if constexpr ( !metalinkMode ) {
75  const auto &urlList = parser.getUrls();
76  std::for_each( urlList.begin(), urlList.end(), [&]( const auto &url ) {
77  mirrs.push_back( { 0, -1, url } );
78  });
79  } else {
80  mirrs = parser.getMirrors();
81  }
82 
83  for ( auto urliter = mirrs.begin(); urliter != mirrs.end(); ++urliter ) {
84  try {
85  const std::string scheme = urliter->url.getScheme();
86  if (scheme == "http" || scheme == "https" || scheme == "ftp" || scheme == "tftp") {
87  if ( !sm._requestDispatcher->supportsProtocol( urliter->url )) {
88  urliter = mirrs.erase( urliter );
89  continue;
90  }
91  urliter->url = ::internal::propagateQueryParams( urliter->url, url );
92  _mirrors.push_back( urliter->url );
93  }
94  }
95  catch (...) { }
96  }
97 
98  if ( mirrs.empty() ) {
99  mirrs.push_back( { 0, -1, url } );
100  _mirrors.push_back( url );
101  }
102  };
103 
104  switch( _mode ) {
105  case Zsync: {
106  parseMetadata( zypp::media::ZsyncParser() );
107  break;
108  }
109  case Metalink: {
110  parseMetadata( zypp::media::MetaLinkParser() );
111  break;
112  }
113  }
114  } catch ( const zypp::Exception &ex ) {
115  std::string err = zypp::str::Format("Failed to parse metalink information.(%1%)" ) % ex.asUserString();
116  WAR << err << std::endl;
118  _sigFailed.emit();
119  return;
120  }
121 
122  if ( mirrs.size() == 0 ) {
123  std::string err = zypp::str::Format("Invalid metalink information.( No mirrors in metalink file)" );
124  WAR << err << std::endl;
126  _sigFailed.emit();
127  return;
128  }
129 
130  //remove the metalink file
131  zypp::filesystem::unlink( targetPath );
133 
134  // this will emit a mirrorsReady signal once some connection tests have been done
135  sm._mirrorControl->registerMirrors( mirrs );
136  }
137 
139  {
140  // if we did not pass on the existing request to the next state we destroy it here
141  if ( _oldRequest )
142  _oldRequest.reset();
143  }
144 
146  {
147  auto &sm = stateMachine();
148  const auto &spec = sm._spec;
149  const auto &url = spec.url();
150  _mirrorControlReadyConn.disconnect();
151 
152 #if ENABLE_ZCHUNK_COMPRESSION
153  if ( _haveZckData ) {
154  _sigFinished.emit();
155  return;
156  }
157 #endif
158 
159  // we have no zchunk data, so for a multi download we need a blocklist
160  if ( !_blockList.haveBlocks() ) {
161  //if we have no filesize we can not generate a blocklist, we need to fall back to normal download
162  if ( !_blockList.haveFilesize() ) {
163 
164  //fall back to normal download but use a mirror from the mirror list
165  //otherwise we get HTTPS to HTTP redirect errors
166  _sigFallback.emit();
167  return;
168  } else {
169  //we generate a blocklist on the fly based on the filesize
170 
171  MIL << "Generate blocklist, since there was none in the metalink file." << url << std::endl;
172 
173  off_t currOff = 0;
174  off_t filesize = _blockList.getFilesize();
175  const auto prefSize = std::max<zypp::ByteCount>( sm._spec.preferredChunkSize(), zypp::ByteCount(4, zypp::ByteCount::K) );
176 
177  while ( currOff < filesize ) {
178 
179  auto blksize = filesize - currOff ;
180  if ( blksize > prefSize )
181  blksize = prefSize;
182 
183  _blockList.addBlock( currOff, blksize );
184  currOff += blksize;
185  }
186 
187  MIL_MEDIA << "Generated blocklist: " << std::endl << _blockList << std::endl << " End blocklist " << std::endl;
188  }
189  }
190 
191  _sigFinished.emit();
192  }
193 
194  std::shared_ptr<DlNormalFileState> PrepareMultiState::fallbackToNormalTransition()
195  {
196  MIL << "No blocklist and no filesize, falling back to normal download for URL " << stateMachine()._spec.url() << std::endl;
197  std::shared_ptr<DlNormalFileState> ptr;
198  if ( _oldRequest ) {
199  ptr = std::make_shared<DlNormalFileState>( std::move(_oldRequest), stateMachine() );
200  } else {
201  ptr = std::make_shared<DlNormalFileState>( stateMachine() );
202  }
203 
204  ptr->_fileMirrors = std::move(_mirrors);
205  if ( _blockList.haveFileChecksum() ) {
206  ptr->_chksumtype = _blockList.fileChecksumType();
207  ptr->_chksumVec = _blockList.getFileChecksum();
208  }
209 
210  return ptr;
211  }
212 
213  std::shared_ptr<DlMetalinkState> PrepareMultiState::transitionToMetalinkDl()
214  {
215  return std::make_shared<DlMetalinkState>( std::move(_blockList), std::move(_mirrors), stateMachine() );
216  }
217 
218  std::shared_ptr<FinishedState> PrepareMultiState::transitionToFinished()
219  {
220  return std::make_shared<FinishedState>( std::move(_error), stateMachine() );
221  }
222 
223 #if ENABLE_ZCHUNK_COMPRESSION
224  std::shared_ptr<DLZckHeadState> PrepareMultiState::transitionToZckHeadDl()
225  {
226  if ( _oldRequest )
227  return std::make_shared<DLZckHeadState>( std::move(_mirrors), std::move(_oldRequest), stateMachine() );
228  return std::make_shared<DLZckHeadState>( std::move(_mirrors), stateMachine() );
229  }
230 
231  bool PrepareMultiState::toZckHeadDownloadGuard() const
232  {
233  return ( stateMachine().hasZckInfo() );
234  }
235 #endif
236 
238  {
239 #if ENABLE_ZCHUNK_COMPRESSION
240  return (!toZckHeadDownloadGuard());
241 #else
242  return true;
243 #endif
244  }
245 
246 }
size_t addBlock(off_t off, size_t size)
add a block with offset off and size size to the block list.
#define MIL
Definition: Logger.h:96
SignalProxy< void()> sigNewMirrorsReady()
std::shared_ptr< DlMetalinkState > transitionToMetalinkDl()
Signal< void() > _sigFailed
zypp::Url propagateQueryParams(zypp::Url url_r, const zypp::Url &template_r)
Definition: curlhelper.cc:400
DownloadSpec _spec
Definition: base_p.h:98
Store and operate with byte count.
Definition: ByteCount.h:30
sigc::connection _mirrorControlReadyConn
zypp::media::MediaBlockList _blockList
Convenient building of std::string with boost::format.
Definition: String.h:252
const UByteArray & getFileChecksum()
NetworkRequestError _error
std::shared_ptr< Request > _oldRequest
bool haveBlocks() const
do we have a blocklist describing the file? set to true when addBlock() is called ...
int unlink(const Pathname &path)
Like &#39;unlink&#39;.
Definition: PathInfo.cc:700
std::string asUserString() const
Translated error message as string suitable for the user.
Definition: Exception.cc:82
PrepareMultiState(std::shared_ptr< Request > oldReq, Mode m, DownloadPrivate &parent)
#define WAR
Definition: Logger.h:97
Signal< void() > _sigFallback
#define MIL_MEDIA
Definition: mediadebug_p.h:29
std::vector< Url > _mirrors
Signal< void() > _sigFinished
Base class for Exception.
Definition: Exception.h:145
static const Unit K
1024 Byte
Definition: ByteCount.h:45
std::shared_ptr< DlNormalFileState > fallbackToNormalTransition()
static zyppng::NetworkRequestError customError(NetworkRequestError::Type t, std::string &&errorMsg="", std::map< std::string, boost::any > &&extraInfo={})
const Url & url() const
Definition: downloadspec.cc:50
bool toMetalinkDownloadGuard() const
std::string fileChecksumType() const
std::shared_ptr< FinishedState > transitionToFinished()