-
Notifications
You must be signed in to change notification settings - Fork 1
/
responses.py
147 lines (120 loc) · 5.11 KB
/
responses.py
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import html
import os
import urllib.parse
from mime_types import mime_types
_version = '1.0.0'
class Response(object):
def __init__(self, method, version, code, message):
self.method = method
self.version = version
self.code = code
self.message = message
self.headers = {'Server': 'GH-AutoIndex/' + _version,
'Connection': 'close'}
self.body = ''
def get_headers(self) -> bytes:
headers = str.format('HTTP/%s %s %s\r\n' % (self.version, self.code, self.message))
for key in self.headers:
headers += str.format('%s: %s\r\n' % (key, self.headers[key]))
headers += '\r\n'
return headers.encode()
def get_body(self) -> bytes:
return self.body.encode()
def get_response(self) -> bytes:
if self.method == 'HEAD':
return self.get_headers()
elif self.method == 'GET':
return self.get_headers() + self.get_body()
else:
return b''
class ErrorResponse(Response):
def __init__(self, method, code, message):
super().__init__(method, '1.0', code, message)
self.headers['Content-Type'] = 'text/html; charset=utf-8'
self.body = str.format('<html><head><title>%s %s</title></head>\r\n'
'<body bgcolor="white">\r\n'
'<center><h1>%s %s</h1></center>\r\n'
'<hr><center>GH-AutoIndex/%s</center>\r\n'
'</body></html>\r\n' %
(code, message, code, message, _version))
class AutoIndexResponse(Response):
def __init__(self, method, path, real_path):
super().__init__(method, '1.0', '200', 'OK')
self.headers['Content-Type'] = 'text/html; charset=utf-8'
self.path = path
self.real_path = real_path
title = html.escape(path)
self.contentStart = ('<html><head><title>Index of ' + title + '</title></head>\r\n'
'<body bgcolor="white">\r\n'
'<h1>Index of ' + title + '</h1><hr>\r\n'
'<pre>\r\n')
self.contentEnd = ('</pre>\r\n'
'<hr>\r\n'
'</body></html>\r\n')
self.folders = []
self.files = []
self.last_dir = None
def add_entry(self, name):
if not os.path.isfile(self.real_path + name):
name += '/'
link = urllib.parse.quote(name)
text = html.escape(name)
self.folders.append(str.format('<a href="%s">%s</a>\r\n' % (link, text)))
self.last_dir = urllib.parse.quote(self.path)
else:
link = urllib.parse.quote(name)
text = html.escape(name)
self.files.append(str.format('<a href="%s">%s</a>\r\n' % (link, text)))
def get_body(self) -> bytes:
content = self.contentStart
for entry in self.folders:
content += entry
for entry in self.files:
content += entry
content += self.contentEnd
return content.encode()
def get_headers(self) -> bytes:
self.headers['Set-Cookie'] = str.format('last=%s; Path=/' % self.last_dir)
self.headers['Content-Length'] = str.format('%d' % len(self.get_body()))
return super().get_headers()
class FileResponse(Response):
def __init__(self, method, path, part_range):
super().__init__(method, '1.1', '200', 'OK')
self.path = path
self.size = os.path.getsize(path)
self.part_range = part_range
self.headers['Content-Type'] = self.__file_type()
self.headers['Accept-Ranges'] = 'bytes'
if part_range is not None:
self.code = '206'
self.message = 'Partial Content'
self.start, self.end = part_range[0], part_range[1]
if self.end < 0:
self.end = self.size + self.end
self.headers['Content-Range'] = str.format('bytes %d-%d/%d' %
(self.start, self.end, self.size))
self.headers['Content-Length'] = str(self.end - self.start + 1)
else:
self.headers['Content-Length'] = str(self.size)
def __file_type(self) -> str:
f_type = mime_types.get(self.path.split('.')[-1])
if not f_type:
f_type = 'Application/octet-stream'
return f_type
def get_body(self) -> bytes:
with open(self.path, 'rb') as file:
if self.part_range is not None:
file.seek(self.start, 0)
return file.read(self.end - self.start + 1)
else:
return file.read()
class NonExistResponse(ErrorResponse):
def __init__(self, method):
super().__init__(method, '404', 'Not Found')
class InvalidMethodResponse(ErrorResponse):
def __init__(self, method):
super().__init__(method, '405', 'Method Not Allowed')
class RedirectResponse(ErrorResponse):
def __init__(self, method, path):
super().__init__(method, '302', 'Found')
self.headers['Location'] = path