from io import BytesIO CHUNK_SIZE = 49600 HTTP_STATUS_CODES = { 100: "Continue", 101: "Switching Protocols", 102: "Processing", 200: "OK", 201: "Created", 202: "Accepted", 203: "Non Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi Status", 226: "IM Used", # see RFC 3229 300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect", 400: "Bad Request", 401: "Unauthorized", 402: "Payment Required", # unused 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Request Entity Too Large", 414: "Request URI Too Long", 415: "Unsupported Media Type", 416: "Requested Range Not Satisfiable", 417: "Expectation Failed", 418: "I'm a teapot", # see RFC 2324 421: "Misdirected Request", # see RFC 7540 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 426: "Upgrade Required", 428: "Precondition Required", # see RFC 6585 429: "Too Many Requests", 431: "Request Header Fields Too Large", 449: "Retry With", # proprietary MS extension 451: "Unavailable For Legal Reasons", 500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 507: "Insufficient Storage", 510: "Not Extended", } SUCCESS_CODE = 200 BAD_REQUEST_CODE = 400 NOT_FOUND_CODE = 404 METHOD_NOT_ALLOWED_CODE = 405 def render_template(path, **kwargs): with open(path, encoding='utf-8') as f: template = f.read() for key in kwargs: template = template.replace(f'%%{key}%%', kwargs[key]) return template def parse_multipart_form(data: bytes): b = BytesIO(data) separator = b.readline().strip() files = [] with open('request', 'wb') as f: f.write(data) while True: line = b.readline().strip().strip(b'-') if not line: break headers_raw = [] while not line.strip() == b'': headers_raw.append(line.strip().decode()) line = b.readline() headers = {'Content-Type': headers_raw[1].split(': ')[1]} for pair in headers_raw[0].split(': ')[1].split('; ')[1:]: key, value = pair.split('=') headers[key] = value[1:-1] data = b'' prev_chunk = b.read(CHUNK_SIZE) chunk = b.read(CHUNK_SIZE) while separator not in (prev_chunk + chunk): data += prev_chunk prev_chunk = chunk chunk = b.read(CHUNK_SIZE) if not chunk: break chunk = (prev_chunk + chunk) if chunk.startswith(separator) or chunk.endswith(separator): with_sep = chunk.strip(separator) else: if separator in chunk: with_sep, buffer = chunk.split(separator) b = BytesIO(buffer + b.read()) b.readline() else: with_sep = chunk data += with_sep headers['data'] = data files.append(headers) return files