示例#1
0
 def setUp(self):
     self.db_dir, self.blob_dir = mk_db_and_blob_dir()
     self.blob_manager = MagicMock()
     self.client = MagicMock()
     self.blob_hash = ('d17272b17a1ad61c4316ac13a651c2b0952063214a81333e'
                       '838364b01b2f07edbd165bb7ec60d2fb2f337a2c02923852')
     self.blob = BlobFile(self.blob_dir, self.blob_hash)
     self.blob_manager.get_blob.side_effect = lambda _: self.blob
     self.response = MagicMock(code=200, length=400)
     self.client.get.side_effect = lambda uri: defer.succeed(self.response)
     self.downloader = HTTPBlobDownloader(self.blob_manager,
                                          [self.blob_hash], ['server1'],
                                          self.client,
                                          retry=False)
     self.downloader.interval = 0
 def __init__(self,
              rowid,
              stream_hash,
              peer_finder,
              rate_limiter,
              blob_manager,
              storage,
              lbry_file_manager,
              payment_rate_manager,
              wallet,
              download_directory,
              file_name,
              stream_name,
              sd_hash,
              key,
              suggested_file_name,
              download_mirrors=None):
     super().__init__(stream_hash, peer_finder, rate_limiter, blob_manager,
                      storage, payment_rate_manager, wallet,
                      download_directory, key, stream_name, file_name)
     self.sd_hash = sd_hash
     self.rowid = rowid
     self.suggested_file_name = unhexlify(suggested_file_name).decode()
     self.lbry_file_manager = lbry_file_manager
     self._saving_status = False
     self.claim_id = None
     self.outpoint = None
     self.claim_name = None
     self.txid = None
     self.nout = None
     self.channel_claim_id = None
     self.channel_name = None
     self.metadata = None
     self.mirror = None
     if download_mirrors or conf.settings['download_mirrors']:
         self.mirror = HTTPBlobDownloader(
             self.blob_manager,
             servers=download_mirrors or conf.settings['download_mirrors'])
示例#3
0
def download_sd_blob(blob_hash,
                     blob_manager,
                     peer_finder,
                     rate_limiter,
                     payment_rate_manager,
                     wallet,
                     timeout=None,
                     download_mirrors=None):
    """
    Downloads a single blob from the network

    @param session:

    @param blob_hash:

    @param payment_rate_manager:

    @return: An object of type HashBlob
    """

    downloader = StandaloneBlobDownloader(blob_hash, blob_manager, peer_finder,
                                          rate_limiter, payment_rate_manager,
                                          wallet, timeout)
    mirror = HTTPBlobDownloader(blob_manager, [blob_hash],
                                download_mirrors or [],
                                sd_hashes=[blob_hash],
                                retry=False)
    mirror.start()
    sd_blob = yield downloader.download()
    mirror.stop()
    sd_reader = BlobStreamDescriptorReader(sd_blob)
    sd_info = yield sd_reader.get_info()
    try:
        validate_descriptor(sd_info)
    except InvalidStreamDescriptorError as err:
        yield blob_manager.delete_blobs([blob_hash])
        raise err
    raw_sd = yield sd_reader._get_raw_data()
    yield blob_manager.storage.add_known_blob(blob_hash, len(raw_sd))
    yield save_sd_info(blob_manager, sd_blob.blob_hash, sd_info)
    defer.returnValue(sd_blob)
class ManagedEncryptedFileDownloader(EncryptedFileSaver):
    STATUS_RUNNING = "running"
    STATUS_STOPPED = "stopped"
    STATUS_FINISHED = "finished"

    def __init__(self,
                 rowid,
                 stream_hash,
                 peer_finder,
                 rate_limiter,
                 blob_manager,
                 storage,
                 lbry_file_manager,
                 payment_rate_manager,
                 wallet,
                 download_directory,
                 file_name,
                 stream_name,
                 sd_hash,
                 key,
                 suggested_file_name,
                 download_mirrors=None):
        super().__init__(stream_hash, peer_finder, rate_limiter, blob_manager,
                         storage, payment_rate_manager, wallet,
                         download_directory, key, stream_name, file_name)
        self.sd_hash = sd_hash
        self.rowid = rowid
        self.suggested_file_name = unhexlify(suggested_file_name).decode()
        self.lbry_file_manager = lbry_file_manager
        self._saving_status = False
        self.claim_id = None
        self.outpoint = None
        self.claim_name = None
        self.txid = None
        self.nout = None
        self.channel_claim_id = None
        self.channel_name = None
        self.metadata = None
        self.mirror = None
        if download_mirrors or conf.settings['download_mirrors']:
            self.mirror = HTTPBlobDownloader(
                self.blob_manager,
                servers=download_mirrors or conf.settings['download_mirrors'])

    def set_claim_info(self, claim_info):
        self.claim_id = claim_info['claim_id']
        self.txid = claim_info['txid']
        self.nout = claim_info['nout']
        self.channel_claim_id = claim_info['channel_claim_id']
        self.outpoint = "%s:%i" % (self.txid, self.nout)
        self.claim_name = claim_info['name']
        self.channel_name = claim_info['channel_name']
        self.metadata = claim_info['value']['stream']['metadata']

    @defer.inlineCallbacks
    def get_claim_info(self, include_supports=True):
        claim_info = yield self.storage.get_content_claim(
            self.stream_hash, include_supports)
        if claim_info:
            self.set_claim_info(claim_info)

        defer.returnValue(claim_info)

    @property
    def saving_status(self):
        return self._saving_status

    def restore(self, status):
        if status == ManagedEncryptedFileDownloader.STATUS_RUNNING:
            # start returns self.finished_deferred
            # which fires when we've finished downloading the file
            # and we don't want to wait for the entire download
            self.start()
        elif status == ManagedEncryptedFileDownloader.STATUS_STOPPED:
            pass
        elif status == ManagedEncryptedFileDownloader.STATUS_FINISHED:
            self.completed = True
        else:
            raise Exception(
                f"Unknown status for stream {self.stream_hash}: {status}")

    @defer.inlineCallbacks
    def stop(self, err=None, change_status=True):
        log.debug('Stopping download for stream %s',
                  short_hash(self.stream_hash))
        if self.mirror:
            self.mirror.stop()
        # EncryptedFileSaver deletes metadata when it's stopped. We don't want that here.
        yield EncryptedFileDownloader.stop(self, err=err)
        if change_status is True:
            status = yield self._save_status()
            defer.returnValue(status)

    @defer.inlineCallbacks
    def status(self):
        blobs = yield self.storage.get_blobs_for_stream(self.stream_hash)
        blob_hashes = [b.blob_hash for b in blobs if b.blob_hash is not None]
        completed_blobs = yield self.blob_manager.completed_blobs(blob_hashes)
        num_blobs_completed = len(completed_blobs)
        num_blobs_known = len(blob_hashes)

        if self.completed:
            status = "completed"
        elif self.stopped:
            status = "stopped"
        else:
            status = "running"
        defer.returnValue(
            EncryptedFileStatusReport(self.file_name, num_blobs_completed,
                                      num_blobs_known, status))

    @defer.inlineCallbacks
    def _start(self):
        yield EncryptedFileSaver._start(self)
        status = yield self._save_status()
        log_status(self.sd_hash, status)
        if self.mirror:
            self.mirror.download_stream(self.stream_hash, self.sd_hash)
        defer.returnValue(status)

    def _get_finished_deferred_callback_value(self):
        if self.completed is True:
            return "Download successful"
        else:
            return "Download stopped"

    @defer.inlineCallbacks
    def _save_status(self):
        self._saving_status = True
        if self.completed is True:
            status = ManagedEncryptedFileDownloader.STATUS_FINISHED
        elif self.stopped is True:
            status = ManagedEncryptedFileDownloader.STATUS_STOPPED
        else:
            status = ManagedEncryptedFileDownloader.STATUS_RUNNING
        status = yield self.lbry_file_manager.change_lbry_file_status(
            self, status)
        self._saving_status = False
        defer.returnValue(status)

    def save_status(self):
        return self._save_status()

    def _get_progress_manager(self, download_manager):
        return FullStreamProgressManager(self._finished_downloading,
                                         self.blob_manager, download_manager)
示例#5
0
class HTTPBlobDownloaderTest(unittest.TestCase):
    def setUp(self):
        self.db_dir, self.blob_dir = mk_db_and_blob_dir()
        self.blob_manager = MagicMock()
        self.client = MagicMock()
        self.blob_hash = ('d17272b17a1ad61c4316ac13a651c2b0952063214a81333e'
                          '838364b01b2f07edbd165bb7ec60d2fb2f337a2c02923852')
        self.blob = BlobFile(self.blob_dir, self.blob_hash)
        self.blob_manager.get_blob.side_effect = lambda _: defer.succeed(self.
                                                                         blob)
        self.response = MagicMock(code=200, length=400)
        self.client.get.side_effect = lambda uri: defer.succeed(self.response)
        self.downloader = HTTPBlobDownloader(self.blob_manager,
                                             [self.blob_hash], ['server1'],
                                             self.client,
                                             retry=False)
        self.downloader.interval = 0

    def tearDown(self):
        self.downloader.stop()
        rm_db_and_blob_dir(self.db_dir, self.blob_dir)

    @defer.inlineCallbacks
    def test_download_successful(self):
        self.client.collect.side_effect = collect
        yield self.downloader.start()
        self.blob_manager.get_blob.assert_called_with(self.blob_hash)
        self.client.get.assert_called_with('http://{}/{}'.format(
            'server1', self.blob_hash))
        self.client.collect.assert_called()
        self.assertEqual(self.blob.get_length(), self.response.length)
        self.assertTrue(self.blob.get_is_verified())
        self.assertEqual(self.blob.writers, {})

    @defer.inlineCallbacks
    def test_download_invalid_content(self):
        self.client.collect.side_effect = bad_collect
        yield self.downloader.start()
        self.assertEqual(self.blob.get_length(), self.response.length)
        self.assertFalse(self.blob.get_is_verified())
        self.assertEqual(self.blob.writers, {})

    @defer.inlineCallbacks
    def test_peer_finished_first_causing_a_write_on_closed_handle(self):
        self.client.collect.side_effect = lambda response, write: defer.fail(
            IOError('I/O operation on closed file'))
        yield self.downloader.start()
        self.blob_manager.get_blob.assert_called_with(self.blob_hash)
        self.client.get.assert_called_with('http://{}/{}'.format(
            'server1', self.blob_hash))
        self.client.collect.assert_called()
        self.assertEqual(self.blob.get_length(), self.response.length)
        self.assertEqual(self.blob.writers, {})

    @defer.inlineCallbacks
    def test_download_transfer_failed(self):
        self.client.collect.side_effect = lambda response, write: defer.fail(
            Exception())
        yield self.downloader.start()
        self.assertEqual(len(self.client.collect.mock_calls),
                         self.downloader.max_failures)
        self.blob_manager.get_blob.assert_called_with(self.blob_hash)
        self.assertEqual(self.blob.get_length(), self.response.length)
        self.assertFalse(self.blob.get_is_verified())
        self.assertEqual(self.blob.writers, {})

    @defer.inlineCallbacks
    def test_blob_not_found(self):
        self.response.code = 404
        yield self.downloader.start()
        self.blob_manager.get_blob.assert_called_with(self.blob_hash)
        self.client.get.assert_called_with('http://{}/{}'.format(
            'server1', self.blob_hash))
        self.client.collect.assert_not_called()
        self.assertFalse(self.blob.get_is_verified())
        self.assertEqual(self.blob.writers, {})

    def test_stop(self):
        self.client.collect.side_effect = lambda response, write: defer.Deferred(
        )
        self.downloader.start(
        )  # hangs if yielded, as intended, to simulate a long ongoing write while we call stop
        self.downloader.stop()
        self.blob_manager.get_blob.assert_called_with(self.blob_hash)
        self.client.get.assert_called_with('http://{}/{}'.format(
            'server1', self.blob_hash))
        self.client.collect.assert_called()
        self.assertEqual(self.blob.get_length(), self.response.length)
        self.assertFalse(self.blob.get_is_verified())
        self.assertEqual(self.blob.writers, {})