Exemplo n.º 1
0
    async def handle_request(self, request: BlobRequest):
        addr = self.transport.get_extra_info('peername')
        peer_address, peer_port = addr

        responses = []
        address_request = request.get_address_request()
        if address_request:
            responses.append(BlobPaymentAddressResponse(lbrycrd_address=self.lbrycrd_address))
        availability_request = request.get_availability_request()
        if availability_request:
            responses.append(BlobAvailabilityResponse(available_blobs=list(set((
                filter(lambda blob_hash: blob_hash in self.blob_manager.completed_blob_hashes,
                       availability_request.requested_blobs)
            )))))
        price_request = request.get_price_request()
        if price_request:
            responses.append(BlobPriceResponse(blob_data_payment_rate='RATE_ACCEPTED'))
        download_request = request.get_blob_request()

        if download_request:
            blob = self.blob_manager.get_blob(download_request.requested_blob)
            if blob.get_is_verified():
                incoming_blob = {'blob_hash': blob.blob_hash, 'length': blob.length}
                responses.append(BlobDownloadResponse(incoming_blob=incoming_blob))
                self.send_response(responses)
                log.debug("send %s to %s:%i", blob.blob_hash[:8], peer_address, peer_port)
                try:
                    sent = await blob.sendfile(self)
                except (ConnectionResetError, BrokenPipeError, RuntimeError, OSError):
                    if self.transport:
                        self.transport.close()
                    return
                log.info("sent %s (%i bytes) to %s:%i", blob.blob_hash[:8], sent, peer_address, peer_port)
        if responses:
            self.send_response(responses)
Exemplo n.º 2
0
 def data_received(self, data):
     request = None
     if data:
         message, separator, remainder = data.rpartition(b'}')
         if not separator:
             self.buf += data
             return
         try:
             request = BlobRequest.deserialize(self.buf + data)
             self.buf = remainder
         except JSONDecodeError:
             addr = self.transport.get_extra_info('peername')
             peer_address, peer_port = addr
             log.error(
                 "failed to decode blob request from %s:%i (%i bytes): %s",
                 peer_address, peer_port, len(data),
                 '' if not data else binascii.hexlify(data).decode())
     if not request:
         addr = self.transport.get_extra_info('peername')
         peer_address, peer_port = addr
         log.warning("failed to decode blob request from %s:%i",
                     peer_address, peer_port)
         self.transport.close()
         return
     self.loop.create_task(self.handle_request(request))
Exemplo n.º 3
0
 async def _download_blob(self) -> typing.Tuple[bool, bool]:
     """
     :return: download success (bool), keep connection (bool)
     """
     request = BlobRequest.make_request_for_blob_hash(self.blob.blob_hash)
     try:
         msg = request.serialize()
         log.debug("send request to %s:%i -> %s", self.peer_address,
                   self.peer_port, msg.decode())
         self.transport.write(msg)
         response: BlobResponse = await asyncio.wait_for(self._response_fut,
                                                         self.peer_timeout,
                                                         loop=self.loop)
         availability_response = response.get_availability_response()
         price_response = response.get_price_response()
         blob_response = response.get_blob_response()
         if (not blob_response or blob_response.error) and\
                 (not availability_response or not availability_response.available_blobs):
             log.warning("blob not in availability response from %s:%i",
                         self.peer_address, self.peer_port)
             return False, True
         elif availability_response.available_blobs and \
                 availability_response.available_blobs != [self.blob.blob_hash]:
             log.warning(
                 "blob availability response doesn't match our request from %s:%i",
                 self.peer_address, self.peer_port)
             return False, False
         if not price_response or price_response.blob_data_payment_rate != 'RATE_ACCEPTED':
             log.warning("data rate rejected by %s:%i", self.peer_address,
                         self.peer_port)
             return False, False
         if not blob_response or blob_response.error:
             log.warning("blob cant be downloaded from %s:%i",
                         self.peer_address, self.peer_port)
             return False, True
         if not blob_response.error and blob_response.blob_hash != self.blob.blob_hash:
             log.warning("incoming blob hash mismatch from %s:%i",
                         self.peer_address, self.peer_port)
             return False, False
         if self.blob.length is not None and self.blob.length != blob_response.length:
             log.warning("incoming blob unexpected length from %s:%i",
                         self.peer_address, self.peer_port)
             return False, False
         msg = f"downloading {self.blob.blob_hash[:8]} from {self.peer_address}:{self.peer_port}," \
             f" timeout in {self.peer_timeout}"
         log.debug(msg)
         msg = f"downloaded {self.blob.blob_hash[:8]} from {self.peer_address}:{self.peer_port}"
         await asyncio.wait_for(self.writer.finished,
                                self.peer_timeout,
                                loop=self.loop)
         await self.blob.finished_writing.wait()
         log.info(msg)
         return True, True
     except asyncio.CancelledError:
         return False, True
     except asyncio.TimeoutError:
         return False, False
     finally:
         await self.close()
Exemplo n.º 4
0
 async def test_server_chunked_request(self):
     blob_hash = "7f5ab2def99f0ddd008da71db3a3772135f4002b19b7605840ed1034c8955431bd7079549e65e6b2a3b9c17c773073ed"
     server_protocol = BlobServerProtocol(self.loop, self.server_blob_manager, self.server.lbrycrd_address)
     transport = asyncio.Transport(extra={'peername': ('ip', 90)})
     received_data = BytesIO()
     transport.write = received_data.write
     server_protocol.connection_made(transport)
     blob_request = BlobRequest.make_request_for_blob_hash(blob_hash).serialize()
     for byte in blob_request:
         server_protocol.data_received(bytes([byte]))
     await asyncio.sleep(0.1)  # yield execution
     self.assertTrue(len(received_data.getvalue()) > 0)
Exemplo n.º 5
0
 async def _download_blob(self) -> typing.Tuple[int, typing.Optional[asyncio.Transport]]:
     """
     :return: download success (bool), keep connection (bool)
     """
     request = BlobRequest.make_request_for_blob_hash(self.blob.blob_hash)
     try:
         msg = request.serialize()
         log.debug("send request to %s:%i -> %s", self.peer_address, self.peer_port, msg.decode())
         self.transport.write(msg)
         response: BlobResponse = await asyncio.wait_for(self._response_fut, self.peer_timeout, loop=self.loop)
         availability_response = response.get_availability_response()
         price_response = response.get_price_response()
         blob_response = response.get_blob_response()
         if (not blob_response or blob_response.error) and\
                 (not availability_response or not availability_response.available_blobs):
             log.warning("%s not in availability response from %s:%i", self.blob.blob_hash, self.peer_address,
                         self.peer_port)
             log.warning(response.to_dict())
             return self._blob_bytes_received, self.close()
         elif availability_response.available_blobs and \
                 availability_response.available_blobs != [self.blob.blob_hash]:
             log.warning("blob availability response doesn't match our request from %s:%i",
                         self.peer_address, self.peer_port)
             return self._blob_bytes_received, self.close()
         if not price_response or price_response.blob_data_payment_rate != 'RATE_ACCEPTED':
             log.warning("data rate rejected by %s:%i", self.peer_address, self.peer_port)
             return self._blob_bytes_received, self.close()
         if not blob_response or blob_response.error:
             log.warning("blob cant be downloaded from %s:%i", self.peer_address, self.peer_port)
             return self._blob_bytes_received, self.transport
         if not blob_response.error and blob_response.blob_hash != self.blob.blob_hash:
             log.warning("incoming blob hash mismatch from %s:%i", self.peer_address, self.peer_port)
             return self._blob_bytes_received, self.close()
         if self.blob.length is not None and self.blob.length != blob_response.length:
             log.warning("incoming blob unexpected length from %s:%i", self.peer_address, self.peer_port)
             return self._blob_bytes_received, self.close()
         msg = f"downloading {self.blob.blob_hash[:8]} from {self.peer_address}:{self.peer_port}," \
             f" timeout in {self.peer_timeout}"
         log.debug(msg)
         msg = f"downloaded {self.blob.blob_hash[:8]} from {self.peer_address}:{self.peer_port}"
         await asyncio.wait_for(self.writer.finished, self.peer_timeout, loop=self.loop)
         log.info(msg)
         # await self.blob.finished_writing.wait()  not necessary, but a dangerous change. TODO: is it needed?
         return self._blob_bytes_received, self.transport
     except asyncio.TimeoutError:
         return self._blob_bytes_received, self.close()
     except (InvalidBlobHashError, InvalidDataError):
         log.warning("invalid blob from %s:%i", self.peer_address, self.peer_port)
         return self._blob_bytes_received, self.close()