# Components of HTTP/1.1 responses # # Use when manually composing an HTTP response # Expand as required for your use # # For HTTP/1.1 specification see: https://www.ietf.org/rfc/rfc2616.txt # For MIME types see: https://www.iana.org/assignments/media-types/media-types.xhtml # # Copyright 2021 (c) Erik de Lange # Released under MIT license from lib.httpserver.sendfile import sendfile reason = { 200: "OK", 301: "Moved Permanently", 302: "Found", 400: "Bad Request", 404: "Not Found", 418: "I'm a teapot" } mimetype = { "HTML": b"Content-Type: text/html\r\n", "EVENT_STREAM": b"Content-Type: text/event-stream\r\n", "X_ICON": b"Content-Type: image/x-icon\r\n", "JSON": b"Content-Type: application/json\r\n" } response_header = { "CLOSE": b"Connection: close\r\n", "KEEP_ALIVE": b"Connection: keep-alive\r\n" } class HTTPResponse: def __init__(self, con, status, mimetype=None, close=True, header=None): """ Create a response object :param int status: HTTP status code :param socket con: socket :param str mimetype: HTTP mime type :param bool close: if true close connection else keep alive :param dict header: key,value pairs for HTTP response header fields """ self.status = status self.con = con self.mimetype = mimetype self.close = close if header is None: self.header = {} else: self.header = header def redirect(self, location): """ Redirect client """ self.con.write(f"HTTP/1.1 {self.status} {reason.get(self.status, 'NA')}\nLocation: {location}\n") self.con.write(response_header.get("CLOSE")) def send_file(self, filepath: str = ""): """ Send response to stream writer """ self.con.write(f"HTTP/1.1 {self.status} {reason.get(self.status, 'NA')}\n") if self.mimetype: self.con.write(mimetype.get(self.mimetype)) if self.close: self.con.write(response_header.get("CLOSE")) else: self.con.write(response_header.get("KEEP_ALIVE")) if len(self.header) > 0: for key, value in self.header.items(): self.con.write(f"{key}: {value}\n") sendfile(self.con, filepath) def send_raw(self, raw: bytes): """ Send response to stream writer """ self.con.write(f"HTTP/1.1 {self.status} {reason.get(self.status, 'NA')}\n") if self.mimetype: self.con.write(mimetype.get(self.mimetype)) if self.close: self.con.write(response_header.get("CLOSE")) else: self.con.write(response_header.get("KEEP_ALIVE")) if len(self.header) > 0: for key, value in self.header.items(): self.con.write(f"{key}: {value};\n") self.con.write(b'\r\n\r\n') self.con.write(raw)