import logging from urllib.parse import urlparse from client.ResponseHandler import ResponseHandler from client.httpclient import FORMAT, HTTPClient, InvalidResponse, InvalidStatusLine, UnsupportedEncoding class Command: command: str def __init__(self, url: str, port: str): self.url = url self.port = port @staticmethod def create(command: str, url: str, port: str): if command == "GET": return GetCommand(url, port) elif command == "HEAD": return HeadCommand(url, port) elif command == "POST": return PostCommand(url, port) elif command == "PUT": return PutCommand(url, port) else: raise ValueError() def execute(self): (host, path) = self.parse_uri() client = HTTPClient(host) client.connect((host, int(self.port))) message = f"{self.command} {path} HTTP/1.1\r\n" message += f"Host: {host}\r\n" message += "Accept: */*\r\nAccept-Encoding: identity\r\n" encoded_msg = self._build_message(message) logging.info("---request begin---\r\n%s---request end---", encoded_msg.decode(FORMAT)) logging.debug("Sending HTTP message: %r", encoded_msg) client.sendall(encoded_msg) logging.info("HTTP request sent, awaiting response...") try: self._await_response(client) except InvalidResponse as e: logging.debug("Internal error: Response could not be parsed", exc_info=e) return except InvalidStatusLine as e: logging.debug("Internal error: Invalid status-line in response", exc_info=e) return except UnsupportedEncoding as e: logging.debug("Internal error: Unsupported encoding in response", exc_info=e) finally: client.close() def _await_response(self, client: HTTPClient): pass def _build_message(self, message: str) -> bytes: return (message + "\r\n").encode(FORMAT) def parse_uri(self): parsed = urlparse(self.url) # If there is no netloc, the url is invalid, so prepend `//` and try again if parsed.netloc == "": parsed = urlparse("//" + self.url) host = parsed.netloc path = parsed.path if len(path) == 0 or path[0] != '/': path = "/" + path port_pos = host.find(":") if port_pos >= 0: host = host[:port_pos] return host, path class HeadCommand(Command): command = "HEAD" def _await_response(self, client): while True: line = client.read_line() print(line, end="") if line in ("\r\n", "\n", ""): break class GetCommand(Command): command = "GET" def _await_response(self, client): (version, status, msg) = ResponseHandler.get_status_line(client) logging.debug("Parsed status-line: version: %s, status: %s", version, status) headers = ResponseHandler.get_headers(client) logging.debug("Parsed headers: %r", headers) handler = ResponseHandler.create(client, headers, status, self.url) handler.handle() class PostCommand(HeadCommand): command = "POST" def _build_message(self, message: str) -> bytes: body = input("Enter POST data: ").encode(FORMAT) print() message += "Content-Type: text/plain\r\n" message += f"Content-Length: {len(body)}\r\n" message += "\r\n" message = message.encode(FORMAT) message += body message += b"\r\n" return message class PutCommand(PostCommand): command = "PUT"