-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdhttpd.py
More file actions
executable file
·185 lines (157 loc) · 5.29 KB
/
dhttpd.py
File metadata and controls
executable file
·185 lines (157 loc) · 5.29 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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/python
# (c) Vincent Simonet, 2015.
import argparse
import httplib
import mimetypes
import os
import posixpath
import shutil
import threading
import urllib
import BaseHTTPServer
import SimpleHTTPServer
from SocketServer import ThreadingMixIn
class DelegatingHTTPServer(BaseHTTPServer.HTTPServer):
def __init__(self, server_address,
RequestHandlerClass,
dserver_address,
localdir):
BaseHTTPServer.HTTPServer.__init__(self,
server_address, RequestHandlerClass)
self.dserver_host = dserver_address[0]
self.dserver_port = dserver_address[1]
self.localdir = os.path.abspath(localdir)
class ThreadedDelegatingHTTPServer(ThreadingMixIn, DelegatingHTTPServer):
"""Handle requests in a separate thread."""
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
_FILTERED_HEADERS = set(['connection', 'host'])
def send_local(self):
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 False
ctype = self.guess_type(path)
if ctype.startswith('text/'):
mode = 'r'
else:
mode = 'rb'
try:
f = open(path, mode)
except IOError:
return False
self.send_response(200)
self.send_header("Content-type", ctype)
self.end_headers()
self.copyfile(f, self.wfile)
f.close()
return True
def translate_path(self, path):
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = self.server.localdir
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def copyfile(self, source, outputfile):
shutil.copyfileobj(source, outputfile)
def guess_type(self, path):
base, ext = posixpath.splitext(path)
if self.extensions_map.has_key(ext):
return self.extensions_map[ext]
ext = ext.lower()
if self.extensions_map.has_key(ext):
return self.extensions_map[ext]
else:
return self.extensions_map['']
extensions_map = mimetypes.types_map.copy()
extensions_map.update({
'': 'application/octet-stream', # Default
'.py': 'text/plain',
'.c': 'text/plain',
'.h': 'text/plain',
})
def do_LOCAL_or_PROXY(self):
if not self.send_local():
self.do_PROXY()
def do_PROXY(self):
conn = httplib.HTTPConnection(self.server.dserver_host,
self.server.dserver_port)
content_len = int(self.headers.get('content-length', 0))
body = self.rfile.read(content_len)
headers = dict((key, self.headers.get(key)) for key in self.headers
if not key in self._FILTERED_HEADERS)
conn.request(self.command, self.path, body, headers)
resp = conn.getresponse()
self.send_response(resp.status, resp.reason)
for name, value in resp.getheaders():
self.send_header(name, value)
self.end_headers()
self.wfile.write(resp.read())
conn.close()
do_GET = do_LOCAL_or_PROXY
do_POST = do_PROXY
do_HEAD = do_LOCAL_or_PROXY
do_PUT = do_PROXY
do_DELETE = do_PROXY
do_OPTIONS = do_PROXY
_DESCRIPTION = """
An HTTP server that serves files from a local directory and a delegate HTTP
server. For every request, the server first look for a local file to search.
If a file is found, it is returned directly. If no local file is found, the
request is forwarded to the delegate HTTP server.
"""
def _parse_host_port(s):
parts = s.split(':')
if len(parts) == 1:
return parts[0], 80
else:
return parts[0], int(parts[1])
def main():
parser = argparse.ArgumentParser(description=_DESCRIPTION)
parser.add_argument('--dserver',
action='store',
type=str,
dest='dserver',
help='Delegate HTTP server (default: localhost:8080)',
metavar='HOST:PORT',
default='localhost:8080')
parser.add_argument('-p', '--port',
action='store',
type=int,
dest='port',
help='Port to listen (default: 8081)',
metavar='PORT',
default='8081')
parser.add_argument('-l', '--localdir',
action='store',
type=str,
dest='localdir',
help='Local directory to serve',
metavar='DIR',
default='.')
args = parser.parse_args()
server_address = ('', args.port)
dserver_address = _parse_host_port(args.dserver)
httpd = ThreadedDelegatingHTTPServer(server_address,
RequestHandler,
dserver_address,
args.localdir)
httpd.daemon_threads = True
print 'Listening port ' + str(args.port)
try:
httpd.serve_forever()
except KeyboardInterrupt:
httpd.shutdown()
print '\nThat\'s all folks!'
if __name__ == '__main__':
main()