-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathserver.py
More file actions
149 lines (128 loc) · 4.69 KB
/
Copy pathserver.py
File metadata and controls
149 lines (128 loc) · 4.69 KB
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
148
149
import http.server
import socketserver
import sys
import os
import threading
import hashlib
import urllib
import re
def copy_byte_range(infile, outfile, start=None, stop=None, bufsize=16*1024):
'''Like shutil.copyfileobj, but only copy a range of the streams.
Both start and stop are inclusive.
'''
if start is not None: infile.seek(start)
while 1:
to_read = min(bufsize, stop + 1 - infile.tell() if stop else bufsize)
buf = infile.read(to_read)
if not buf:
break
outfile.write(buf)
BYTE_RANGE_RE = re.compile(r'bytes=(\d+)-(\d+)?$')
def parse_byte_range(byte_range):
'''Returns the two numbers in 'bytes=123-456' or throws ValueError.
The last number or both numbers may be None.
'''
if byte_range.strip() == '':
return None, None
m = BYTE_RANGE_RE.match(byte_range)
if not m:
raise ValueError('Invalid byte range %s' % byte_range)
first, last = [x and int(x) for x in m.groups()]
if last and last < first:
raise ValueError('Invalid byte range %s' % byte_range)
return first, last
class request_handler(http.server.SimpleHTTPRequestHandler):
def translate_path (self, path):
pwd = os.getcwd()
os.chdir("wwwroot")
p = http.server.SimpleHTTPRequestHandler.translate_path (self, path)
os.chdir (pwd)
return p
def send_head(self):
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
parts = urllib.parse.urlsplit(self.path)
if not parts.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(http.HTTPStatus.MOVED_PERMANENTLY)
new_parts = (parts[0], parts[1], parts[2] + '/',
parts[3], parts[4])
new_url = urllib.parse.urlunsplit(new_parts)
self.send_header("Location", new_url)
self.end_headers()
return None
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)
try:
f = open(path, 'rb')
except OSError:
try:
fallback_url = '185.60.112.106:1119' if not "tpr" in self.path else 'blzddist1-a.akamaihd.net:80'
client = http.client.HTTPConnection(fallback_url)
print ("forwarding", self.path)
client.request("GET", self.path)
data = client.getresponse().read()
os.makedirs("wwwroot/" + os.path.dirname(self.path), exist_ok = True)
open(path, 'wb').write (data)
f = open(path, 'rb')
except Exception as ex:
self.send_error(http.HTTPStatus.NOT_FOUND, str(ex))
return None
ctype = self.guess_type(path)
fs = os.fstat(f.fileno())
file_len = fs[6]
if 'Range' in self.headers:
try:
self.range = parse_byte_range(self.headers['Range'])
except ValueError as e:
self.send_error(400, 'Invalid byte range')
return None
first, last = self.range
if first >= file_len:
self.send_error(416, 'Requested Range Not Satisfiable')
return None
if last is None or last >= file_len:
last = file_len - 1
response_length = last - first + 1
self.send_response(206)
self.send_header('Accept-Ranges', 'bytes')
self.send_header('Content-Range',
'bytes %s-%s/%s' % (first, last, file_len))
self.send_header('Content-Length', str(response_length))
else:
self.send_response(http.HTTPStatus.OK)
self.send_header("Content-Length", str(file_len))
self.range = None
self.send_header("ETag", '"{}:{}"'.format(hashlib.md5(f.read()).hexdigest(), int(fs.st_mtime)))
try:
self.send_header("Content-type", ctype)
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
f.seek(0, 0)
self.end_headers()
return f
except:
f.close()
raise
def copyfile(self, source, outputfile):
if not self.range:
return http.server.SimpleHTTPRequestHandler.copyfile(self, source, outputfile)
# SimpleHTTPRequestHandler uses shutil.copyfileobj, which doesn't let
# you stop the copying before the end of the file.
start, stop = self.range # set in send_head()
copy_byte_range(source, outputfile, start, stop)
configs = socketserver.TCPServer(("", 1119), request_handler)
cdn = socketserver.TCPServer(("", 80), request_handler)
threads = []
for server in [configs, cdn]:
thread = threading.Thread (target = server.serve_forever)
thread.daemon = True
thread.start()
import time
while True:
time.sleep(1)