Skip to content

Commit edbdbf7

Browse files
author
yorickvanpelt
committed
- Codechange: try to use cPickle on the webserver if available
- Codechange: use the SimpleHTTPServer content_types instead of our own - Codechange: use builtin error messages - Add: zlib compression to webserver - Codechange: make the webserver multithreaded and merge some classes
1 parent 592121f commit edbdbf7

File tree

1 file changed

+70
-65
lines changed

1 file changed

+70
-65
lines changed

webserver.py

+70-65
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,24 @@
11
import string,cgi,time,traceback, threading, SocketServer, BaseHTTPServer, os.path, urllib
2+
import SimpleHTTPServer
23
import ottd_config
34
from ottd_lib import DataStorageClass
45
from log import LOG
56
try:
67
import json
78
except ImportError:
89
import simplejson as json
9-
import pickle
10+
try:
11+
import cPickle as pickle
12+
except ImportError:
13+
import pickle
1014

11-
ext2conttype = {"jpg": "image/jpeg",
12-
"jpeg": "image/jpeg",
13-
"png": "image/png",
14-
"gif": "image/gif",
15-
"html": "text/html",
16-
"htm": "text/html",
17-
"swf": "application/x-shockwave-flash",
18-
"xml": "text/xml",
19-
}
2015
def content_type(filename):
21-
ext = filename[filename.rfind(".")+1:].lower()
22-
if ext in ext2conttype.keys():
23-
return ext2conttype[ext]
16+
ext = filename[filename.rfind("."):].lower()
17+
map = SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map
18+
if ext in map:
19+
return map[ext]
2420
else:
25-
return "text/html"
21+
return map['']
2622

2723
class DataStorageJSONEncoder(json.JSONEncoder):
2824
def default(self, obj):
@@ -43,18 +39,26 @@ def loadTemplate(self, name):
4339
return fc
4440
else:
4541
return None
46-
47-
def sendError(self, num=404):
48-
txt = """\
49-
<html>
50-
<head><title>404 file not found</title></head>
51-
<body>404 file not found</body>
52-
</html>\n"""
53-
self.send_response(num)
54-
self.send_header("Content-type", "text/html")
55-
self.send_header("Content-Length", str (len (txt)))
56-
self.end_headers()
57-
self.wfile.write(txt)
42+
def compress(self, data):
43+
try:
44+
enc = self.headers.getheader("Accept-Encoding").split(',')
45+
fmt = []
46+
for e in enc: fmt.append(e.strip())
47+
except:
48+
fmt = []
49+
def encoder(data):
50+
return data
51+
format = None
52+
try:
53+
if "deflate" in fmt:
54+
import zlib
55+
encoder = zlib.compress
56+
format = "deflate"
57+
except:
58+
pass
59+
print fmt, format
60+
return format, encoder(data)
61+
5862

5963
def do_GET(self):
6064
#print self.path
@@ -84,13 +88,16 @@ def do_GET(self):
8488
'server_port': cls.port,
8589
}
8690
else:
87-
self.sendError()
91+
self.send_error(404)
8892
return
8993

90-
self.send_response(200)
94+
encoding, data = self.compress(output)
95+
self.send_response(200, "OK")
9196
self.send_header('Content-type', 'text/html')
97+
if not encoding is None:
98+
self.send_header('Content-Encoding', encoding)
9299
self.end_headers()
93-
self.wfile.write(output)
100+
self.wfile.write(data)
94101
elif self.path == "/data/companies":
95102
try:
96103
f = open(ottd_config.config.get("stats", "cachefilename"), 'rb')
@@ -100,19 +107,28 @@ def do_GET(self):
100107
obj = pickle.load(f)
101108
f.close()
102109
cls = self.server._callbackclass
103-
self.send_response(200)
104-
self.send_header('Content-type:', 'application/json')
105-
self.end_headers()
106110
jsonoutput = json.dumps(obj, sort_keys=True, indent=4, cls=DataStorageJSONEncoder)
107-
self.wfile.write(jsonoutput)
111+
encoding, data = self.compress(jsonoutput)
112+
113+
self.send_response(200, "OK")
114+
self.send_header('Content-type', 'application/json')
115+
if not encoding is None:
116+
self.send_header('Content-Encoding', encoding)
117+
self.send_header ("Content-Length", len(data))
118+
self.end_headers()
119+
self.wfile.write(data)
108120
elif self.path == "/data/clients":
109121
cls = self.server._callbackclass
110122

111123
response = json.dumps(cls.playerlist, indent=4, sort_keys=True)
112-
self.send_response(200)
113-
self.send_header('Content-type:', 'application/json')
124+
encoding, data = self.compress(response)
125+
self.send_response(200, "OK")
126+
self.send_header('Content-type', 'application/json')
127+
if not encoding is None:
128+
self.send_header('Content-Encoding', encoding)
129+
self.send_header ("Content-Length", len(data))
114130
self.end_headers()
115-
self.wfile.write(response)
131+
self.wfile.write(data)
116132
else:
117133
path = urllib.unquote(self.path)
118134
if path.find("?") >= 0:
@@ -124,61 +140,50 @@ def do_GET(self):
124140
if os.path.isfile(newindex):
125141
fn = newindex
126142
else:
127-
self.sendError()
143+
self.send_error(500, "couldn't find template")
128144
return
129145

130146
if os.path.exists(fn):
147+
f = open(fn, "rb")
148+
content = f.read()
149+
f.close()
150+
encoding, data = self.compress(content)
131151
self.send_response (200)
132152
self.send_header ("Content-type", content_type(fn))
133-
self.send_header ("Content-Length", os.path.getsize(fn))
153+
if not encoding is None:
154+
self.send_header("Content-Encoding", encoding)
155+
self.send_header ("Content-Length", len(data))
134156
self.end_headers()
135-
f = open(fn, "rb")
136-
self.wfile.write(f.read())
137-
f.close()
157+
self.wfile.write(data)
138158
else:
139-
self.sendError()
159+
self.send_error(404)
140160
return
141161

142162

143163

144-
class myHTTPServer(BaseHTTPServer.HTTPServer):
145-
def __init__(self, addr, handlerClass, cls):
146-
"""
147-
constructor
148-
@type cls: SpectatorClient
149-
@param cls: class to get the data from
150-
"""
151-
BaseHTTPServer.HTTPServer.__init__(self, addr, handlerClass)
152-
self._callbackclass = cls
153-
154-
155-
class myWebServer(threading.Thread):
164+
class myWebServer(threading.Thread, SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
156165
"""
157-
webserver that display the currently connected clients
166+
multithreaded webserver class for openttd-python
158167
"""
159168
def __init__(self, cls, port):
160169
"""
161170
constructor
162171
@type cls: SpectatorClient
163172
@param cls: class to get the data from
164-
@type port: number
165-
@param port: the port on which to start the webserver
173+
@type port: int
174+
@param port: port to run with
166175
"""
167-
self.cls = cls
168176
self.port = port
177+
self._callbackclass = cls
178+
BaseHTTPServer.HTTPServer.__init__(self, ('', self.port), MyHandler)
169179
threading.Thread.__init__(self)
170-
171180
def run(self):
172181
"""
173182
thread entry point for the webserver
174183
"""
175-
LOG.debug('started httpserver...')
176-
self.server = myHTTPServer(('', self.port), MyHandler, self.cls)
177-
self.server.serve_forever()
178-
184+
self.serve_forever()
179185
def stop(self):
180186
"""
181187
this stops the webserver
182188
"""
183-
self.server.server_close()
184-
189+
self.server_close()

0 commit comments

Comments
 (0)