lbry-sdk/lbrynet/blob/writer.py

72 lines
2.5 KiB
Python
Raw Normal View History

2019-01-22 18:47:46 +01:00
import typing
2017-09-13 21:46:39 +02:00
import logging
2019-01-22 18:47:46 +01:00
import asyncio
2017-09-13 21:46:39 +02:00
from io import BytesIO
2019-01-22 18:47:46 +01:00
from lbrynet.error import InvalidBlobHashError, InvalidDataError
2018-11-07 21:15:05 +01:00
from lbrynet.cryptoutils import get_lbry_hash_obj
2017-09-13 21:46:39 +02:00
log = logging.getLogger(__name__)
class HashBlobWriter:
2019-01-22 18:47:46 +01:00
def __init__(self, expected_blob_hash: str, get_length: typing.Callable[[], int],
finished: asyncio.Future):
self.expected_blob_hash = expected_blob_hash
self.get_length = get_length
self.buffer = BytesIO()
self.finished = finished
self.finished.add_done_callback(lambda *_: self.close_handle())
2017-09-13 21:46:39 +02:00
self._hashsum = get_lbry_hash_obj()
self.len_so_far = 0
2019-01-22 18:47:46 +01:00
self.verified_bytes = b''
2017-09-13 21:46:39 +02:00
def __del__(self):
2019-01-22 18:47:46 +01:00
if self.buffer is not None:
2018-07-22 01:08:28 +02:00
log.warning("Garbage collection was called, but writer was not closed yet")
2019-01-22 18:47:46 +01:00
self.close_handle()
2019-01-22 18:47:46 +01:00
def calculate_blob_hash(self) -> str:
2017-09-13 21:46:39 +02:00
return self._hashsum.hexdigest()
2019-01-22 18:47:46 +01:00
def closed(self):
return self.buffer is None or self.buffer.closed
def write(self, data: bytes):
expected_length = self.get_length()
if not expected_length:
raise IOError("unknown blob length")
if self.buffer is None:
2018-07-27 03:49:35 +02:00
log.warning("writer has already been closed")
2019-01-22 18:47:46 +01:00
if not self.finished.done():
self.finished.cancel()
return
2017-09-15 19:46:38 +02:00
raise IOError('I/O operation on closed file')
2017-09-13 21:46:39 +02:00
self._hashsum.update(data)
self.len_so_far += len(data)
2019-01-22 18:47:46 +01:00
if self.len_so_far > expected_length:
self.close_handle()
self.finished.set_result(InvalidDataError(
f'Length so far is greater than the expected length. {self.len_so_far} to {expected_length}'
))
return
self.buffer.write(data)
if self.len_so_far == expected_length:
blob_hash = self.calculate_blob_hash()
if blob_hash != self.expected_blob_hash:
self.close_handle()
self.finished.set_result(InvalidBlobHashError(
f"blob hash is {blob_hash} vs expected {self.expected_blob_hash}"
))
return
self.buffer.seek(0)
self.verified_bytes = self.buffer.read()
self.close_handle()
if self.finished and not (self.finished.done() or self.finished.cancelled()):
self.finished.set_result(None)
2017-09-13 21:46:39 +02:00
def close_handle(self):
2019-01-22 18:47:46 +01:00
if self.buffer is not None:
self.buffer.close()
self.buffer = None