| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #pragma once
- #include "data/data_file_origin.h"
- #include "base/timer.h"
- #include "base/weak_ptr.h"
- class ApiWrap;
- namespace MTP {
- class Error;
- } // namespace MTP
- namespace Storage {
- // Different part sizes are not supported for now :(
- // Because we start downloading with some part size
- // and then we get a CDN-redirect where we support only
- // fixed part size download for hash checking.
- constexpr auto kDownloadPartSize = 128 * 1024;
- class DownloadMtprotoTask;
- class DownloadManagerMtproto final : public base::has_weak_ptr {
- public:
- using Task = DownloadMtprotoTask;
- explicit DownloadManagerMtproto(not_null<ApiWrap*> api);
- ~DownloadManagerMtproto();
- [[nodiscard]] ApiWrap &api() const {
- return *_api;
- }
- void enqueue(not_null<Task*> task, int priority);
- void remove(not_null<Task*> task);
- void notifyTaskFinished() {
- _taskFinished.fire({});
- }
- [[nodiscard]] rpl::producer<> taskFinished() const {
- return _taskFinished.events();
- }
- int changeRequestedAmount(MTP::DcId dcId, int index, int delta);
- void requestSucceeded(
- MTP::DcId dcId,
- int index,
- int amountAtRequestStart,
- crl::time timeAtRequestStart);
- void checkSendNextAfterSuccess(MTP::DcId dcId);
- [[nodiscard]] int chooseSessionIndex(MTP::DcId dcId) const;
- void notifyNonPremiumDelay(DocumentId id) {
- _nonPremiumDelays.fire_copy(id);
- }
- [[nodiscard]] rpl::producer<DocumentId> nonPremiumDelays() const {
- return _nonPremiumDelays.events();
- }
- private:
- class Queue final {
- public:
- void enqueue(not_null<Task*> task, int priority);
- void remove(not_null<Task*> task);
- void resetGeneration();
- [[nodiscard]] bool empty() const;
- [[nodiscard]] Task *nextTask(bool onlyHighestPriority) const;
- void removeSession(int index);
- private:
- struct Enqueued {
- not_null<Task*> task;
- int priority = 0;
- };
- std::vector<Enqueued> _tasks;
- };
- struct DcSessionBalanceData {
- DcSessionBalanceData();
- int requested = 0;
- int successes = 0; // Since last timeout in this dc in any session.
- int maxWaitedAmount = 0;
- };
- struct DcBalanceData {
- DcBalanceData();
- std::vector<DcSessionBalanceData> sessions;
- crl::time lastSessionRemove = 0;
- int sessionRemoveIndex = 0;
- int sessionRemoveTimes = 0;
- int timeouts = 0; // Since all sessions had successes >= required.
- int totalRequested = 0;
- };
- void checkSendNext();
- void checkSendNext(MTP::DcId dcId, Queue &queue);
- bool trySendNextPart(MTP::DcId dcId, Queue &queue);
- void killSessionsSchedule(MTP::DcId dcId);
- void killSessionsCancel(MTP::DcId dcId);
- void killSessions();
- void killSessions(MTP::DcId dcId);
- void resetGeneration();
- void sessionTimedOut(MTP::DcId dcId, int index);
- void removeSession(MTP::DcId dcId);
- const not_null<ApiWrap*> _api;
- rpl::event_stream<> _taskFinished;
- rpl::event_stream<DocumentId> _nonPremiumDelays;
- base::flat_map<MTP::DcId, DcBalanceData> _balanceData;
- base::Timer _resetGenerationTimer;
- base::flat_map<MTP::DcId, crl::time> _killSessionsWhen;
- base::Timer _killSessionsTimer;
- base::flat_map<MTP::DcId, Queue> _queues;
- rpl::lifetime _lifetime;
- };
- class DownloadMtprotoTask : public base::has_weak_ptr {
- public:
- struct Location {
- std::variant<
- StorageFileLocation,
- WebFileLocation,
- GeoPointLocation,
- AudioAlbumThumbLocation> data;
- };
- DownloadMtprotoTask(
- not_null<DownloadManagerMtproto*> owner,
- const StorageFileLocation &location,
- Data::FileOrigin origin);
- DownloadMtprotoTask(
- not_null<DownloadManagerMtproto*> owner,
- MTP::DcId dcId,
- const Location &location);
- virtual ~DownloadMtprotoTask();
- [[nodiscard]] MTP::DcId dcId() const;
- [[nodiscard]] Data::FileOrigin fileOrigin() const;
- [[nodiscard]] uint64 objectId() const;
- [[nodiscard]] const Location &location() const;
- [[nodiscard]] virtual bool readyToRequest() const = 0;
- void loadPart(int sessionIndex);
- void removeSession(int sessionIndex);
- void refreshFileReferenceFrom(
- const Data::UpdatedFileReferences &updates,
- int requestId,
- const QByteArray ¤t);
- protected:
- [[nodiscard]] bool haveSentRequests() const;
- [[nodiscard]] bool haveSentRequestForOffset(int64 offset) const;
- void cancelAllRequests();
- void cancelRequestForOffset(int64 offset);
- void addToQueue(int priority = 0);
- void removeFromQueue();
- [[nodiscard]] ApiWrap &api() const {
- return _owner->api();
- }
- private:
- struct RequestData {
- int64 offset = 0;
- mutable int sessionIndex = 0;
- int requestedInSession = 0;
- crl::time sent = 0;
- inline bool operator<(const RequestData &other) const {
- return offset < other.offset;
- }
- };
- struct CdnFileHash {
- CdnFileHash(int limit, QByteArray hash) : limit(limit), hash(hash) {
- }
- int limit = 0;
- QByteArray hash;
- };
- enum class CheckCdnHashResult {
- NoHash,
- Invalid,
- Good,
- };
- enum class FinishRequestReason {
- Success,
- Redirect,
- Cancel,
- };
- // Called only if readyToRequest() == true.
- [[nodiscard]] virtual int64 takeNextRequestOffset() = 0;
- virtual bool feedPart(int64 offset, const QByteArray &bytes) = 0;
- virtual bool setWebFileSizeHook(int64 size);
- virtual void cancelOnFail() = 0;
- void cancelRequest(mtpRequestId requestId);
- void makeRequest(const RequestData &requestData);
- void normalPartLoaded(
- const MTPupload_File &result,
- mtpRequestId requestId);
- void webPartLoaded(
- const MTPupload_WebFile &result,
- mtpRequestId requestId);
- void cdnPartLoaded(
- const MTPupload_CdnFile &result,
- mtpRequestId requestId);
- void reuploadDone(
- const MTPVector<MTPFileHash> &result,
- mtpRequestId requestId);
- void requestMoreCdnFileHashes();
- void getCdnFileHashesDone(
- const MTPVector<MTPFileHash> &result,
- mtpRequestId requestId);
- void partLoaded(int64 offset, const QByteArray &bytes);
- bool partFailed(const MTP::Error &error, mtpRequestId requestId);
- bool normalPartFailed(
- QByteArray fileReference,
- const MTP::Error &error,
- mtpRequestId requestId);
- bool cdnPartFailed(const MTP::Error &error, mtpRequestId requestId);
- [[nodiscard]] mtpRequestId sendRequest(const RequestData &requestData);
- void placeSentRequest(
- mtpRequestId requestId,
- const RequestData &requestData);
- [[nodiscard]] RequestData finishSentRequest(
- mtpRequestId requestId,
- FinishRequestReason reason);
- void switchToCDN(
- const RequestData &requestData,
- const MTPDupload_fileCdnRedirect &redirect);
- void addCdnHashes(const QVector<MTPFileHash> &hashes);
- void changeCDNParams(
- const RequestData &requestData,
- MTP::DcId dcId,
- const QByteArray &token,
- const QByteArray &encryptionKey,
- const QByteArray &encryptionIV,
- const QVector<MTPFileHash> &hashes);
- [[nodiscard]] CheckCdnHashResult checkCdnFileHash(
- int64 offset,
- bytes::const_span buffer);
- void subscribeToNonPremiumLimit();
- const not_null<DownloadManagerMtproto*> _owner;
- const MTP::DcId _dcId = 0;
- // _location can be changed with an updated file_reference.
- Location _location;
- const Data::FileOrigin _origin;
- base::flat_map<mtpRequestId, RequestData> _sentRequests;
- base::flat_map<int64, mtpRequestId> _requestByOffset;
- MTP::DcId _cdnDcId = 0;
- QByteArray _cdnToken;
- QByteArray _cdnEncryptionKey;
- QByteArray _cdnEncryptionIV;
- base::flat_map<int64, CdnFileHash> _cdnFileHashes;
- base::flat_map<RequestData, QByteArray> _cdnUncheckedParts;
- mtpRequestId _cdnHashesRequestId = 0;
- rpl::lifetime _nonPremiumLimitSubscription;
- };
- } // namespace Storage
|