1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
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 = []
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
|