This repository has been archived by the owner on Dec 23, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcommon_server.py
234 lines (181 loc) · 5.99 KB
/
common_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
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
228
229
230
231
232
233
234
import argparse
import json
import socketserver
import ssl
import time
import traceback
import urllib
import webbrowser
import os
from functools import wraps
from http import HTTPStatus, server
from http.server import HTTPServer
from urllib.error import URLError
from urllib.parse import unquote
from urllib.request import Request, urlopen
PATHS = {}
def path_optional(decorator):
def wrapped(func_or_path):
if callable(func_or_path):
return decorator("/" + func_or_path.__name__)(func_or_path)
else:
def actual_decorator(f):
return decorator(func_or_path)(f)
return actual_decorator
return wrapped
@path_optional
def route(path):
"""Register a route handler."""
def wrap(f):
PATHS[path] = f
return f
return wrap
@path_optional
def forward_to_server(path):
def wrap(f):
@wraps(f)
def wrapped(*args, **kwargs):
if IS_SERVER:
return f(*args, **kwargs)
else:
return multiplayer_post(path, kwargs)
return wrapped
return wrap
def server_only(f):
@wraps(f)
def wrapped(*args, **kwargs):
if IS_SERVER:
return f(*args, **kwargs)
else:
raise Exception("Method not available locally!")
return wrapped
def sendto(f):
def wrapped(data):
return f(**data)
return wrapped
class Handler(server.BaseHTTPRequestHandler):
"""HTTP handler."""
def do_GET(self):
self.send_response(HTTPStatus.OK)
path = GUI_FOLDER + unquote(self.path)[1:]
if "scripts" in path and not path.endswith(".js"):
path += ".js"
if path.endswith(".css"):
self.send_header("Content-type", "text/css")
elif path.endswith(".js"):
self.send_header("Content-type", "application/javascript")
self.end_headers()
if path == GUI_FOLDER:
path = GUI_FOLDER + "index.html"
try:
with open(path, "rb") as f:
self.wfile.write(f.read())
except Exception as e:
print(e)
# raise
def do_POST(self):
content_length = int(self.headers["Content-Length"])
raw_data = self.rfile.read(content_length).decode("utf-8")
data = json.loads(raw_data)
path = unquote(self.path)
self.send_response(HTTPStatus.OK)
self.send_header("Content-type", "application/json")
self.end_headers()
try:
result = PATHS[path](**snakify(data))
self.wfile.write(bytes(json.dumps(result), "utf-8"))
except Exception as e:
print(e)
raise
def log_message(self, *args, **kwargs):
pass
class Server:
def __getattr__(self, item):
def f(**kwargs):
if IS_SERVER:
return PATHS["/" + item](**kwargs)
else:
return multiplayer_post(item, kwargs)
return f
Server = Server()
def multiplayer_post(path, data, server_url=None):
"""Post DATA to a multiplayer server PATH and return the response."""
if not server_url:
server_url = DEFAULT_SERVER
data_bytes = bytes(json.dumps(data), encoding="utf-8")
request = Request(urllib.parse.urljoin(server_url, path), data_bytes, method="POST")
try:
response = urlopen(request, context=ssl._create_unverified_context())
text = response.read().decode("utf-8")
if text.strip():
return json.loads(text)
except Exception as e:
traceback.print_exc()
raise # print(e)
return None
def start_server():
global IS_SERVER
IS_SERVER = True
from flask import Flask, request, jsonify, send_from_directory
app = Flask(__name__, static_url_path="", static_folder="")
for route, handler in PATHS.items():
def wrapped_handler(handler=handler):
return jsonify(handler(**snakify(request.get_json(force=True))))
app.add_url_rule(route, handler.__name__, wrapped_handler, methods=["POST"])
@app.route("/")
def index():
return send_from_directory("", "index.html")
return app
def start_client(port, default_server, gui_folder, standalone):
"""Start web server."""
global DEFAULT_SERVER, GUI_FOLDER, IS_SERVER
DEFAULT_SERVER = default_server
GUI_FOLDER = gui_folder
IS_SERVER = False
socketserver.TCPServer.allow_reuse_address = True
httpd = HTTPServer(("127.0.0.1", port), Handler)
if not standalone:
webbrowser.open("http://127.0.0.1:" + str(port), new=0, autoraise=True)
httpd.serve_forever()
def snakify(data):
out = {}
for key, val in data.items():
snake_key = []
for x in key:
if x != x.lower():
snake_key += "_"
snake_key += x.lower()
out["".join(snake_key)] = val
return out
@route("/kill")
def kill():
if not IS_SERVER:
print("Exiting GUI")
exit(0)
def start(port, default_server, gui_folder, db_init=None):
global DEFAULT_SERVER
DEFAULT_SERVER = default_server
parser = argparse.ArgumentParser(description="Project GUI Server")
parser.add_argument(
"-s", help="Stand-alone: do not open browser", action="store_true"
)
parser.add_argument("-f", help="Force Flask app", action="store_true")
args, unknown = parser.parse_known_args()
import __main__
if "gunicorn" not in os.environ.get("SERVER_SOFTWARE", "") and not args.f:
request = Request("http://127.0.0.1:{}/kill".format(port), bytes(json.dumps({}), encoding="utf-8"), method='POST')
try:
urlopen(request)
print("Killing existing gui process...")
time.sleep(1)
except URLError:
pass
start_client(port, default_server, gui_folder, args.s)
else:
if db_init:
db_init()
app = start_server()
if args.f:
app.run(port=port, threaded=False, processes=1)
else:
return app