import logging import socket from io import BufferedReader from typing import Tuple from httplib.exceptions import BadRequest BUFSIZE = 4096 TIMEOUT = 3 FORMAT = "UTF-8" MAXLINE = 4096 class HTTPSocket: host: str conn: socket.socket file: Tuple[BufferedReader, None] 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): """ Close this socket """ self.file.close() self.conn.close() def is_closed(self): return self.file is None def reset_request(self): """ Close the file handle of this socket and create a new one. """ self.file.close() self.file = self.conn.makefile("rb") def read(self, size=BUFSIZE, blocking=True) -> bytes: """ Read bytes up to the specified buffer size. This method will block when `blocking` is set to True (Default). """ if blocking: buffer = self.file.read(size) else: buffer = self.file.read1(size) if len(buffer) == 0: raise ConnectionAbortedError return buffer def read_line(self): """ Read a line decoded as `httpsocket.FORMAT`. @return: the decoded line @raise: UnicodeDecodeError """ return str(self.read_bytes_line(), FORMAT) def read_bytes_line(self) -> bytes: """ Read a line as bytes. """ line = self.file.readline(MAXLINE + 1) if len(line) > MAXLINE: raise InvalidResponse("Line too long") elif len(line) == 0: raise ConnectionAbortedError 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