def path_connection(path: Path) \ -> Callable[[aio.StreamReader, aio.StreamWriter], Coroutine[Any, Any, None]]: """ Creates a stream callback, and specifying the path for the server directory """ return (lambda reader, writer: server_connection(path, StreamPair(reader, writer)))
async def to_receiver(reader: aio.StreamReader, writer: aio.StreamWriter): """ Switch to determine which receiver to use """ conn = StreamPair(reader, writer) login = await Message[Login].read(reader) # TODO: add try except await self._peer_receiver(login, conn)
async def _connect_index(self, host: str) -> IndexerConnection: """ Establishes a connection with the indexer peer """ start = self._index_start logging.info(f'trying connection with indexer at {start}') in_pair = await aio.open_connection(start.host, start.port) in_pair = StreamPair(*in_pair) logging.info(f'connection with indexer') login = Login(self._user, host, self._port) await Message.write(in_pair.writer, login) return IndexerConnection(start, in_pair)
async def _peer_download(self, filename: str, target: tuple[str, int]): """ Client-side connection with any of the other peers """ logging.debug(f"attempting connection to {target}") fin, pend = await aio.wait({aio.open_connection(*target)}, timeout=8) for p in pend: p.cancel() if len(fin) == 0: logging.error('connection attempt failed') return pair = fin.pop().result() # will only be one result pair = StreamPair(*pair) reader, writer = pair log = logging.getLogger() try: await Message.write(writer, self.user) user = await Message.read(reader, str) log = logging.getLogger(user) log.debug("connected") await self._receive_file_retries(filename, pair, log) except Exception as e: logging.exception(e) finally: if not reader.at_eof(): writer.write_eof() writer.close() await writer.wait_closed() log.debug("disconnected")
async def _peer_download(self, filepath: Path, target: Location, *chunks: File.Chunk) -> Optional[bytes]: """ Client-side connection with any of the other peers """ log = logging.getLogger() try: self._check_chunks(target, *chunks) logging.debug(f"attempting connection to {target}") pair = await aio.open_connection(target.host, target.port) pair = StreamPair(*pair) reader, writer = pair try: await Message.write(writer, self._user) user = await Message[str].read(reader) log = logging.getLogger(user) log.debug("connected as client") return await self._download_requests(pair, filepath, *chunks, log=log) finally: if not reader.at_eof(): writer.write_eof() writer.close() await writer.wait_closed() log.debug("disconnected as client") except Exception as e: log.exception(e) raise e
async def open_connection( user: Optional[str], address: Optional[str], port: int, directory: str, workers: int, retries: int, timeout: int, log: str, verbosity: int, *args: Any, **kwargs: Any): """ Attempts to connect to a server with the given args """ try: user, host, num_sockets, path = process_args(user, address, workers, directory) init_log(log, verbosity) sockets: list[StreamPair] = [] for _ in range(num_sockets): pair = await aio.open_connection(host, port) pair = StreamPair(*pair) sockets.append(pair) await Message.write(pair.writer, user) # closes socket in session await client_session(path, sockets, retries, timeout) except Exception as e: logging.error(e) finally: logging.info('ending connection')
def to_upload(reader: aio.StreamReader, writer: aio.StreamWriter): """ Weak peer closure as a stream callback """ return self._peer_upload(StreamPair(reader, writer))
async def start_server(self): """ Connects the peer to the indexing node, and starts the peer server """ # async sync state needs to be init from async context self.dir_update = aio.Event() def to_upload(reader: aio.StreamReader, writer: aio.StreamWriter): """ Indexer closure as a stream callback """ return self._peer_upload(StreamPair(reader, writer)) try: host = socket.gethostname() server = await aio.start_server(to_upload, host, self.port, start_serving=False) logging.info(f'peer server on {host}, port {self.port}') async with server: start = self.index_start logging.info(f'trying connection with indexer at {start}') done, pend = await aio.wait( {aio.open_connection(start.host, start.port)}, timeout=8) for p in pend: p.cancel() if len(done) == 0: return in_pair = done.pop().result() in_pair = StreamPair(*in_pair) indexer = IndexState(start, in_pair) login = Login(self.user, host, self.port) await Message.write(in_pair.writer, login) # make sure that event is listening before it can be set and cleared first_update = aio.create_task(self.dir_update.wait()) sender = aio.create_task(self._send_dir(indexer)) checker = aio.create_task(self._check_dir()) # only accept connections and user input after a dir_update # should not deadlock, keep eye on await first_update await server.start_serving() session = aio.create_task(self._session(indexer)) conn = aio.create_task(self._watch_connection(indexer)) await aio.wait([session, conn], return_when=aio.FIRST_COMPLETED) checker.cancel() sender.cancel() if not session.done(): session.cancel() if not conn.done(): conn.cancel() await server.wait_closed() except Exception as e: logging.exception(e) for task in aio.all_tasks(): task.cancel() finally: logging.debug("disconnected from indexing server") logging.debug("ending peer")