Metalink2RequestGroup.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /* <!-- copyright */
  2. /*
  3. * aria2 - The high speed download utility
  4. *
  5. * Copyright (C) 2006 Tatsuhiro Tsujikawa
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * In addition, as a special exception, the copyright holders give
  22. * permission to link the code of portions of this program with the
  23. * OpenSSL library under certain conditions as described in each
  24. * individual source file, and distribute linked combinations
  25. * including the two.
  26. * You must obey the GNU General Public License in all respects
  27. * for all of the code used other than OpenSSL. If you modify
  28. * file(s) with this exception, you may extend this exception to your
  29. * version of the file(s), but you are not obligated to do so. If you
  30. * do not wish to do so, delete this exception statement from your
  31. * version. If you delete this exception statement from all source
  32. * files in the program, then also delete it here.
  33. */
  34. /* copyright --> */
  35. #include "Metalink2RequestGroup.h"
  36. #include <algorithm>
  37. #include "RequestGroup.h"
  38. #include "Option.h"
  39. #include "LogFactory.h"
  40. #include "Logger.h"
  41. #include "prefs.h"
  42. #include "util.h"
  43. #include "message.h"
  44. #include "DownloadContext.h"
  45. #include "metalink_helper.h"
  46. #include "BinaryStream.h"
  47. #include "MemoryBufferPreDownloadHandler.h"
  48. #include "MetalinkEntry.h"
  49. #include "MetalinkResource.h"
  50. #include "MetalinkMetaurl.h"
  51. #include "FileEntry.h"
  52. #include "A2STR.h"
  53. #include "a2functional.h"
  54. #include "download_helper.h"
  55. #include "fmt.h"
  56. #include "SegList.h"
  57. #include "DownloadFailureException.h"
  58. #include "Signature.h"
  59. #include "download_handlers.h"
  60. #include "RequestGroupCriteria.h"
  61. #ifdef ENABLE_BITTORRENT
  62. # include "BtDependency.h"
  63. # include "download_helper.h"
  64. #endif // ENABLE_BITTORRENT
  65. #include "Checksum.h"
  66. #include "ChunkChecksum.h"
  67. namespace aria2 {
  68. Metalink2RequestGroup::Metalink2RequestGroup() {}
  69. namespace {
  70. class AccumulateNonP2PUri {
  71. private:
  72. std::vector<std::string>& urisPtr;
  73. public:
  74. AccumulateNonP2PUri(std::vector<std::string>& urisPtr)
  75. :urisPtr(urisPtr) {}
  76. void operator()(const std::unique_ptr<MetalinkResource>& resource) {
  77. switch(resource->type) {
  78. case MetalinkResource::TYPE_HTTP:
  79. case MetalinkResource::TYPE_HTTPS:
  80. case MetalinkResource::TYPE_FTP:
  81. urisPtr.push_back(resource->url);
  82. break;
  83. default:
  84. break;
  85. }
  86. }
  87. };
  88. } // namespace
  89. namespace {
  90. class FindBitTorrentUri {
  91. public:
  92. FindBitTorrentUri() {}
  93. bool operator()(const std::shared_ptr<MetalinkResource>& resource) {
  94. if(resource->type == MetalinkResource::TYPE_BITTORRENT) {
  95. return true;
  96. } else {
  97. return false;
  98. }
  99. }
  100. };
  101. } // namespace
  102. void
  103. Metalink2RequestGroup::generate
  104. (std::vector<std::shared_ptr<RequestGroup>>& groups,
  105. const std::string& metalinkFile,
  106. const std::shared_ptr<Option>& option,
  107. const std::string& baseUri)
  108. {
  109. std::vector<std::shared_ptr<RequestGroup>> tempgroups;
  110. createRequestGroup(tempgroups,
  111. metalink::parseAndQuery(metalinkFile, option.get(),
  112. baseUri),
  113. option);
  114. std::shared_ptr<MetadataInfo> mi;
  115. if(metalinkFile == DEV_STDIN) {
  116. mi = std::make_shared<MetadataInfo>();
  117. } else {
  118. // TODO Downloads from local metalink file does not save neither
  119. // its gid nor MetadataInfo's gid.
  120. mi = std::make_shared<MetadataInfo>(GroupId::create(), metalinkFile);
  121. }
  122. setMetadataInfo(std::begin(tempgroups), std::end(tempgroups), mi);
  123. groups.insert(std::end(groups),
  124. std::begin(tempgroups), std::end(tempgroups));
  125. }
  126. void
  127. Metalink2RequestGroup::generate
  128. (std::vector<std::shared_ptr<RequestGroup>>& groups,
  129. const std::shared_ptr<BinaryStream>& binaryStream,
  130. const std::shared_ptr<Option>& option,
  131. const std::string& baseUri)
  132. {
  133. std::vector<std::shared_ptr<RequestGroup>> tempgroups;
  134. createRequestGroup(tempgroups,
  135. metalink::parseAndQuery(binaryStream.get(), option.get(),
  136. baseUri),
  137. option);
  138. auto mi = std::make_shared<MetadataInfo>();
  139. setMetadataInfo(std::begin(tempgroups), std::end(tempgroups), mi);
  140. groups.insert(std::end(groups),
  141. std::begin(tempgroups), std::end(tempgroups));
  142. }
  143. void
  144. Metalink2RequestGroup::createRequestGroup
  145. (std::vector<std::shared_ptr<RequestGroup>>& groups,
  146. std::vector<std::unique_ptr<MetalinkEntry>> entries,
  147. const std::shared_ptr<Option>& optionTemplate)
  148. {
  149. if(entries.empty()) {
  150. A2_LOG_NOTICE(EX_NO_RESULT_WITH_YOUR_PREFS);
  151. return;
  152. }
  153. std::vector<std::string> locations;
  154. if(optionTemplate->defined(PREF_METALINK_LOCATION)) {
  155. auto& loc = optionTemplate->get(PREF_METALINK_LOCATION);
  156. util::split(std::begin(loc), std::end(loc),
  157. std::back_inserter(locations), ',', true);
  158. for(auto& s : locations) {
  159. util::lowercase(s);
  160. }
  161. }
  162. std::string preferredProtocol;
  163. if(optionTemplate->get(PREF_METALINK_PREFERRED_PROTOCOL) != V_NONE) {
  164. preferredProtocol = optionTemplate->get(PREF_METALINK_PREFERRED_PROTOCOL);
  165. }
  166. for(auto& entry : entries) {
  167. entry->dropUnsupportedResource();
  168. if(entry->resources.empty() && entry->metaurls.empty()) {
  169. continue;
  170. }
  171. entry->setLocationPriority
  172. (locations, -MetalinkResource::getLowestPriority());
  173. if(!preferredProtocol.empty()) {
  174. entry->setProtocolPriority
  175. (preferredProtocol, -MetalinkResource::getLowestPriority());
  176. }
  177. }
  178. auto sgl = util::parseIntSegments(optionTemplate->get(PREF_SELECT_FILE));
  179. sgl.normalize();
  180. if(sgl.hasNext()) {
  181. size_t inspoint = 0;
  182. for(size_t i = 0, len = entries.size(); i < len && sgl.hasNext(); ++i) {
  183. size_t j = sgl.peek() - 1;
  184. if(i == j) {
  185. if(inspoint != i) {
  186. entries[inspoint] = std::move(entries[i]);
  187. }
  188. ++inspoint;
  189. sgl.next();
  190. }
  191. }
  192. entries.resize(inspoint);
  193. }
  194. std::for_each(std::begin(entries), std::end(entries),
  195. std::mem_fn(&MetalinkEntry::reorderMetaurlsByPriority));
  196. auto entryGroups = metalink::groupEntryByMetaurlName(entries);
  197. for(auto& entryGroup : entryGroups) {
  198. auto& metaurl = entryGroup.first;
  199. auto& mes = entryGroup.second;
  200. A2_LOG_INFO(fmt("Processing metaurl group metaurl=%s", metaurl.c_str()));
  201. #ifdef ENABLE_BITTORRENT
  202. std::shared_ptr<RequestGroup> torrentRg;
  203. if(!metaurl.empty()) {
  204. std::vector<std::string> uris;
  205. uris.push_back(metaurl);
  206. {
  207. std::vector<std::shared_ptr<RequestGroup>> result;
  208. createRequestGroupForUri(result, optionTemplate, uris,
  209. /* ignoreForceSequential = */true,
  210. /* ignoreLocalPath = */true);
  211. if(!result.empty()) {
  212. torrentRg = result[0];
  213. }
  214. }
  215. if(torrentRg) {
  216. torrentRg->setNumConcurrentCommand(1);
  217. torrentRg->clearPreDownloadHandler();
  218. torrentRg->clearPostDownloadHandler();
  219. // remove "metalink" from Accept Type list to avoid loop in
  220. // transparent metalink
  221. torrentRg->getDownloadContext()->setAcceptMetalink(false);
  222. // make it in-memory download
  223. torrentRg->addPreDownloadHandler
  224. (download_handlers::getMemoryPreDownloadHandler());
  225. groups.push_back(torrentRg);
  226. }
  227. }
  228. #endif // ENABLE_BITTORRENT
  229. auto option = util::copy(optionTemplate);
  230. auto rg = std::make_shared<RequestGroup>(GroupId::create(), option);
  231. std::shared_ptr<DownloadContext> dctx;
  232. int numSplit = option->getAsInt(PREF_SPLIT);
  233. int maxConn = option->getAsInt(PREF_MAX_CONNECTION_PER_SERVER);
  234. if(mes.size() == 1) {
  235. auto entry = mes[0];
  236. A2_LOG_INFO(fmt(MSG_METALINK_QUEUEING, entry->getPath().c_str()));
  237. entry->reorderResourcesByPriority();
  238. std::vector<std::string> uris;
  239. std::for_each(std::begin(entry->resources), std::end(entry->resources),
  240. AccumulateNonP2PUri(uris));
  241. // If piece hash is specified in the metalink,
  242. // make segment size equal to piece hash size.
  243. int32_t pieceLength;
  244. if(!entry->chunkChecksum) {
  245. pieceLength = option->getAsInt(PREF_PIECE_LENGTH);
  246. } else {
  247. pieceLength = entry->chunkChecksum->getPieceLength();
  248. }
  249. dctx = std::make_shared<DownloadContext>
  250. (pieceLength,
  251. entry->getLength(),
  252. util::applyDir(option->get(PREF_DIR),
  253. entry->file->getPath()));
  254. dctx->getFirstFileEntry()->setUris(uris);
  255. dctx->getFirstFileEntry()->setMaxConnectionPerServer(maxConn);
  256. dctx->getFirstFileEntry()->setSuffixPath(entry->file->getPath());
  257. if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
  258. dctx->getFirstFileEntry()->setUniqueProtocol(true);
  259. }
  260. if(entry->checksum) {
  261. dctx->setDigest(entry->checksum->getHashType(),
  262. entry->checksum->getDigest());
  263. }
  264. if(entry->chunkChecksum) {
  265. dctx->setPieceHashes
  266. (entry->chunkChecksum->getHashType(),
  267. std::begin(entry->chunkChecksum->getPieceHashes()),
  268. std::end(entry->chunkChecksum->getPieceHashes()));
  269. }
  270. dctx->setSignature(entry->popSignature());
  271. rg->setNumConcurrentCommand
  272. (entry->maxConnections < 0 ?
  273. numSplit : std::min(numSplit, entry->maxConnections));
  274. } else {
  275. dctx = std::make_shared<DownloadContext>();
  276. // piece length is overridden by the one in torrent file.
  277. dctx->setPieceLength(option->getAsInt(PREF_PIECE_LENGTH));
  278. std::vector<std::shared_ptr<FileEntry>> fileEntries;
  279. int64_t offset = 0;
  280. for(auto entry : mes) {
  281. A2_LOG_INFO(fmt("Metalink: Queueing %s for download as a member.",
  282. entry->getPath().c_str()));
  283. A2_LOG_DEBUG(fmt("originalName = %s",
  284. entry->metaurls[0]->name.c_str()));
  285. entry->reorderResourcesByPriority();
  286. std::vector<std::string> uris;
  287. std::for_each(std::begin(entry->resources),
  288. std::end(entry->resources),
  289. AccumulateNonP2PUri(uris));
  290. auto fe = std::make_shared<FileEntry>
  291. (util::applyDir(option->get(PREF_DIR),
  292. entry->file->getPath()),
  293. entry->file->getLength(), offset, uris);
  294. fe->setMaxConnectionPerServer(maxConn);
  295. if(option->getAsBool(PREF_METALINK_ENABLE_UNIQUE_PROTOCOL)) {
  296. fe->setUniqueProtocol(true);
  297. }
  298. fe->setOriginalName(entry->metaurls[0]->name);
  299. fe->setSuffixPath(entry->file->getPath());
  300. fileEntries.push_back(fe);
  301. if(offset >
  302. std::numeric_limits<int64_t>::max() - entry->file->getLength()) {
  303. throw DOWNLOAD_FAILURE_EXCEPTION(fmt(EX_TOO_LARGE_FILE, offset));
  304. }
  305. offset += entry->file->getLength();
  306. }
  307. dctx->setFileEntries(std::begin(fileEntries), std::end(fileEntries));
  308. rg->setNumConcurrentCommand(numSplit);
  309. }
  310. rg->setDownloadContext(dctx);
  311. if(option->getAsBool(PREF_ENABLE_RPC)) {
  312. rg->setPauseRequested(option->getAsBool(PREF_PAUSE));
  313. }
  314. removeOneshotOption(option);
  315. // remove "metalink" from Accept Type list to avoid loop in
  316. // transparent metalink
  317. dctx->setAcceptMetalink(false);
  318. #ifdef ENABLE_BITTORRENT
  319. // Inject dependency between rg and torrentRg here if
  320. // torrentRg is true
  321. if(torrentRg) {
  322. auto dep = std::make_shared<BtDependency>(rg.get(), torrentRg);
  323. rg->dependsOn(dep);
  324. torrentRg->belongsTo(rg->getGID());
  325. // metadata download may take very long time. If URIs are
  326. // available, give up metadata download in at most 30 seconds.
  327. const time_t btStopTimeout = 30;
  328. time_t currentBtStopTimeout =
  329. torrentRg->getOption()->getAsInt(PREF_BT_STOP_TIMEOUT);
  330. if(currentBtStopTimeout == 0 || currentBtStopTimeout > btStopTimeout) {
  331. bool allHaveUri = true;
  332. for(auto& fe : dctx->getFileEntries()) {
  333. if(fe->getRemainingUris().empty()) {
  334. allHaveUri = false;
  335. break;
  336. }
  337. }
  338. if(allHaveUri) {
  339. torrentRg->getOption()->put
  340. (PREF_BT_STOP_TIMEOUT, util::itos(btStopTimeout));
  341. }
  342. }
  343. }
  344. #endif // ENABLE_BITTORRENT
  345. groups.push_back(rg);
  346. }
  347. }
  348. } // namespace aria2