forked from autotest/virt-test
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhttp_server.py
140 lines (114 loc) · 4.47 KB
/
http_server.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
import os, posixpath, urlparse, urllib, logging
import BaseHTTPServer, SimpleHTTPServer
class HTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
"""
Serve a GET request.
"""
rg = self.parse_header_byte_range()
if rg:
f = self.send_head_range(rg[0], rg[1])
if f:
self.copyfile_range(f, self.wfile, rg[0], rg[1])
f.close()
else:
f = self.send_head()
if f:
self.copyfile(f, self.wfile)
f.close()
def parse_header_byte_range(self):
range_param = 'Range'
range_discard = 'bytes='
if self.headers.has_key(range_param):
rg = self.headers.get(range_param)
if rg.startswith(range_discard):
rg = rg[len(range_discard):]
begin, end = rg.split('-')
return (int(begin), int(end))
return None
def copyfile_range(self, source_file, output_file, range_begin, range_end):
"""
Copies a range of a file to destination.
"""
range_size = range_end - range_begin + 1
source_file.seek(range_begin)
buf = source_file.read(range_size)
output_file.write(buf)
def send_head_range(self, range_begin, range_end):
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
for index in "index.html", "index.htm":
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
# Always read in binary mode. Opening files in text mode may cause
# newline translations, making the actual size of the content
# transmitted *less* than the content-length!
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(206, "Partial Content")
file_size = str(os.fstat(f.fileno())[6])
range_size = str(range_end - range_begin + 1)
self.send_header("Accept-Ranges", "bytes")
self.send_header("Content-Length", range_size)
self.send_header("Content-Range", "bytes %s-%s/%s" % (range_begin,
range_end,
file_size))
self.send_header("Content-type", ctype)
self.end_headers()
return f
def translate_path(self, path):
"""
Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = urlparse.urlparse(path)[2]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.server.cwd
for word in words:
_, word = os.path.splitdrive(word)
_, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def address_string(self):
'''
This HTTP server does not care about name resolution for the requests
The first reason is that most of the times our clients are going to be
virtual machines without a proper name resolution setup. Also, by not
resolving names, we should be a bit faster and be resilient about
misconfigured or resilient name servers.
'''
return self.client_address[0]
def log_message(self, fmt, *args):
logging.debug("builtin http server handling request from %s: %s" %
(self.address_string(), fmt%args))
def http_server(port=8000, cwd=None, terminate_callable=None):
http = BaseHTTPServer.HTTPServer(('', port), HTTPRequestHandler)
http.timeout = 1
if cwd is None:
cwd = os.getcwd()
http.cwd = cwd
while True:
if terminate_callable is not None:
terminate = terminate_callable()
else:
terminate = False
if terminate:
break
http.handle_request()
if __name__ == '__main__':
http_server()