import logging import socket from io import BufferedReader BUFSIZE = 4096 TIMEOUT = 3 FORMAT = "UTF-8" MAXLINE = 4096 class HTTPSocket: host: str conn: socket.socket file: BufferedReader def __init__(self, conn: socket.socket, host: str): self.host = host self.conn = conn self.conn.settimeout(TIMEOUT) self.conn.setblocking(True) self.conn.settimeout(60) self.file = self.conn.makefile("rb") def close(self): self.file.close() self.conn.close() def reset_request(self): self.file.close() self.file = self.conn.makefile("rb") def __do_receive(self): if self.conn.fileno() == -1: raise Exception("Connection closed") result = self.conn.recv(BUFSIZE) return result def receive(self): """Receive data from the client up to BUFSIZE """ count = 0 while True: count += 1 try: return self.__do_receive() except socket.timeout: logging.debug("Socket receive timed out after %s seconds", TIMEOUT) if count == 3: break logging.debug("Retrying %s", count) logging.debug("Timed out after waiting %s seconds for response", TIMEOUT * count) raise TimeoutError("Request timed out") def read(self, size=BUFSIZE, blocking=True) -> bytes: if blocking: return self.file.read(size) return self.file.read1(size) def read_line(self): return str(self.read_bytes_line(), FORMAT) def read_bytes_line(self) -> bytes: line = self.file.readline(MAXLINE + 1) if len(line) > MAXLINE: raise InvalidResponse("Line too long") return line class HTTPException(Exception): """ Base class for HTTP exceptions """ class InvalidResponse(HTTPException): """ Response message cannot be parsed """ def __init(self, message): self.message = message