97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
# 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)
|
|
|