This repository has been archived by the owner on Feb 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
G2D3Server.py
executable file
·227 lines (182 loc) · 6.55 KB
/
G2D3Server.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
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/usr/bin/env python3
import sys
import io
import os
import argparse
import json
import urllib
import http.server
in_memory_file = {}
class G2D3HTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
"""Custom server for G2D3
"""
def __init__(self, *args, **kwargs):
self.post_var = {}
super().__init__(*args, **kwargs)
def do_POST(self):
"""Serve a POST request."""
content_length = self.getContentLength()
if(content_length == None):
return None
# Look for data type
content_type = self.headers.get('Content-Type')
if content_type == None:
self.send_error(417, 'Content Type Required')
return None
supported_content_type = ['application/json', 'application/x-www-form-urlencoded']
if content_type not in supported_content_type:
self.send_error(415, 'Bad Content Type\nExpected: '+(', '.join(supported_content_type))+'\nGot: '+content_type)
return None
enc = sys.getfilesystemencoding()
data = str(self.rfile.read(content_length), enc)
# Parse posted data
self.post_var = {}
try:
if content_type == 'application/json':
self.post_var = json.loads(data)
elif content_type == 'application/x-www-form-urlencoded':
self.post_var = urllib.parse.parse_qs(data)
for k,v in self.post_var.items():
if len(v) == 1:
self.post_var[k] = v[0]
except Exception as e:
self.send_error(400, 'Request Syntax Incorrect')
return None
self.do_GET()
self.post_var = {}
def do_PUT(self):
"""Serve a PUT request."""
if self.path[-1] == '/':
self.send_error(400, 'Can PUT a directory')
return None
content_length = self.getContentLength()
if(content_length == None):
return None
data = self.rfile.read(content_length)
ind = self.path.rfind('/') + 1
path = self.path[0:ind]
filename = self.path[ind:]
if path not in in_memory_file:
in_memory_file[path] = {}
exist = filename in in_memory_file[path]
in_memory_file[path][filename] = io.BytesIO(data)
in_memory_file[path][filename].size = len(data)
if exist:
self.send_response(200, 'File Updated')
else:
self.send_response(201, 'File Created')
self.end_headers()
return None
def send_head(self):
"""Common code for GET, HEAD and POST commands.
This sends the response code and MIME headers.
Return value is either a file object (which has to be copied
to the outputfile by the caller unless the command was HEAD,
and must be closed by the caller under all circumstances), or
None, in which case the caller has nothing further to do.
"""
full_path = self.translate_path(self.path)
f = None
if os.path.isdir(full_path):
if not self.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
else:
path = self.post_var.get('path', './')
extensions = self.post_var.get('ext')
if extensions != None and not isinstance(extensions, list):
extensions = [extensions]
accept = self.headers.get('Accept')
if accept != None and 'application/json' in accept:
return self.list_directory_json(extensions)
else:
return self.list_directory(full_path);
ctype = self.guess_type(full_path)
ind = self.path.rfind('/') + 1
path = self.path[0:ind]
filename = self.path[ind:]
f = None
if path in in_memory_file and filename in in_memory_file[path]:
f = in_memory_file[path][filename]
content_length = f.size
else:
try:
f = open(full_path, 'rb')
content_length = os.fstat(f.fileno())[6]
except OSError:
self.send_error(404, 'File "' + self.path + '" not found')
return None
except:
f.close()
raise
self.send_response(200)
self.send_header('Content-type', ctype)
self.send_header('Content-Length', str(content_length))
self.end_headers()
return f
def list_directory_json(self, extensions=None):
"""Helper to produce a directory listing.
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
"""
r = []
directory_found = True
try:
list = os.listdir(self.translate_path(self.path))
except OSError:
list = []
directory_found = False
if self.path in in_memory_file:
for name in in_memory_file[self.path]:
if name not in list:
list.append(name)
elif not directory_found:
self.send_error(404, 'Directory "' + self.path + '" Not Found')
return None
if extensions != None:
list = [name for name in list for ext in extensions if name.lower().endswith('.'+ext)]
for name in list:
if name not in r:
r.append('"'+name+'"')
enc = sys.getfilesystemencoding()
encoded = ('['+(','.join(r))+']').encode(enc)
f = io.BytesIO(encoded)
self.send_response(200)
self.send_header('Content-Type', 'application/json; charset=%s' % enc)
self.send_header('Content-Length', str(len(encoded)))
self.end_headers()
return f
def getContentLength(self):
# Look for data length
content_length = self.headers.get('Content-Length')
if content_length == None:
self.send_error(411, 'Length Required')
return None
try:
content_length = int(content_length)
except ValueError:
self.send_error(417, 'Bad Length\nExpected a number, got: '+content_length)
return None
return content_length
def main():
parser = argparse.ArgumentParser(description='HTTP Server for G2D3')
parser.add_argument('--bind', '-b', default='', metavar='ADDRESS',
help='Specify alternate bind address '
'[default: all interfaces]')
parser.add_argument('port', action='store',
default=8000, type=int,
nargs='?',
help='Specify alternate port [default: 8000]')
args = parser.parse_args()
handler = G2D3HTTPRequestHandler
server = http.server.HTTPServer
server_address = (args.bind, args.port)
httpd = server(server_address, handler)
print('G2D3Server serving HTTP on',args.bind,'port',args.port,'...')
httpd.serve_forever()
if __name__ == '__main__':
main()