Skip to content

Commit 2d0cf4d

Browse files
committed
fix to support gevent v1.0+ which doesn't provide a direct access to its wfile in its WSGI interface
1 parent a4b732a commit 2d0cf4d

2 files changed

Lines changed: 190 additions & 8 deletions

File tree

example/echo_gevent_server.py

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# -*- coding: utf-8 -*-
2+
import argparse
3+
import random
4+
import os
5+
6+
from gevent import monkey; monkey.patch_all()
7+
import gevent.pywsgi
8+
9+
from ws4py.server.geventserver import UpgradableWSGIHandler
10+
from ws4py.server.wsgi.middleware import WebSocketUpgradeMiddleware
11+
12+
class EchoWebSocketServer(gevent.pywsgi.WSGIServer):
13+
handler_class = UpgradableWSGIHandler
14+
15+
def __init__(self, host, port):
16+
gevent.pywsgi.WSGIServer.__init__(self, (host, port))
17+
18+
self.host = host
19+
self.port = port
20+
21+
self.application = self
22+
23+
# let's use wrap the websocket handler with
24+
# a middleware that'll perform the websocket
25+
# handshake
26+
self.ws = WebSocketUpgradeMiddleware(self.websocket)
27+
28+
# keep track of connected websocket clients
29+
# so that we can brodcasts messages sent by one
30+
# to all of them. Aren't we cool?
31+
self.clients = []
32+
33+
def __call__(self, environ, start_response):
34+
"""
35+
Good ol' WSGI application. This is a simple demo
36+
so I tried to stay away from dependencies.
37+
"""
38+
if environ['PATH_INFO'] == '/favicon.ico':
39+
return self.favicon(environ, start_response)
40+
41+
if environ['PATH_INFO'] == '/ws':
42+
return self.ws(environ, start_response)
43+
44+
if environ['PATH_INFO'].startswith('/js'):
45+
return self.static(environ, start_response)
46+
47+
return self.webapp(environ, start_response)
48+
49+
def favicon(self, environ, start_response):
50+
"""
51+
Don't care about favicon, let's send nothing.
52+
"""
53+
status = '200 OK'
54+
headers = [('Content-type', 'text/plain')]
55+
start_response(status, headers)
56+
return ""
57+
58+
def static(self, environ, start_response):
59+
"""
60+
Not the sexiest static handler but does the job
61+
for the demo
62+
"""
63+
path = os.path.normpath(os.path.join(os.path.dirname(__file__),
64+
'./static/%s' % environ['PATH_INFO']))
65+
if not os.path.exists(path):
66+
status = '404 Not Found'
67+
headers = [('Content-type', 'text/plain')]
68+
return ""
69+
70+
status = '200 OK'
71+
headers = [('Content-type', 'text/javascript')]
72+
73+
start_response(status, headers)
74+
75+
return file(path).read()
76+
77+
def websocket(self, websocket, environ):
78+
"""
79+
Can it be simpler to run websocket with gevent?
80+
"""
81+
self.clients.append(websocket)
82+
83+
try:
84+
while True:
85+
msg = websocket.receive(msg_obj=True)
86+
if msg is not None:
87+
# broadcast the received message to all
88+
# connected clients
89+
for client in self.clients:
90+
client.send(msg.data, msg.is_binary)
91+
else:
92+
break
93+
finally:
94+
websocket.close()
95+
self.clients.remove(websocket)
96+
97+
def webapp(self, environ, start_response):
98+
"""
99+
Our main webapp that'll display the chat form
100+
"""
101+
status = '200 OK'
102+
headers = [('Content-type', 'text/html')]
103+
104+
start_response(status, headers)
105+
106+
return """<html>
107+
<head>
108+
<script type='application/javascript' src='/js/jquery-1.6.2.min.js'></script>
109+
<script type='application/javascript'>
110+
$(document).ready(function() {
111+
112+
websocket = 'ws://%(host)s:%(port)s/ws';
113+
if (window.WebSocket) {
114+
ws = new WebSocket(websocket);
115+
}
116+
else if (window.MozWebSocket) {
117+
ws = MozWebSocket(websocket);
118+
}
119+
else {
120+
console.log('WebSocket Not Supported');
121+
return;
122+
}
123+
124+
$(window).unload(function() {
125+
ws.close();
126+
});
127+
ws.onmessage = function (evt) {
128+
$('#chat').val($('#chat').val() + evt.data + '\\n');
129+
};
130+
ws.onopen = function() {
131+
ws.send("%(username)s entered the room");
132+
};
133+
134+
$('#send').click(function() {
135+
console.log($('#message').val());
136+
ws.send('%(username)s: ' + $('#message').val());
137+
$('#message').val("");
138+
return false;
139+
});
140+
});
141+
</script>
142+
</head>
143+
<body>
144+
<form action='#' id='chatform' method='get'>
145+
<textarea id='chat' cols='35' rows='10'></textarea>
146+
<br />
147+
<label for='message'>%(username)s: </label><input type='text' id='message' />
148+
<input id='send' type='submit' value='Send' />
149+
</form>
150+
</body>
151+
</html>
152+
""" % {'username': "User%d" % random.randint(0, 100),
153+
'host': self.host,
154+
'port': self.port}
155+
156+
if __name__ == '__main__':
157+
parser = argparse.ArgumentParser(description='Echo gevent Server')
158+
parser.add_argument('--host', default='127.0.0.1')
159+
parser.add_argument('-p', '--port', default=9000, type=int)
160+
args = parser.parse_args()
161+
162+
server = EchoWebSocketServer(args.host, args.port)
163+
server.serve_forever()
164+

ws4py/server/geventserver.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import gevent.pywsgi
2+
from gevent import version_info
3+
IS_GEVENT_V10 = version_info[0] == 1
4+
del version_info
25

36
from ws4py.server.wsgi.middleware import WebSocketUpgradeMiddleware
47

@@ -47,12 +50,27 @@ def start_response_for_upgrade(status, headers, exc_info=None):
4750
write = self.start_response(status, headers, exc_info)
4851
if self.code == 101:
4952
# flushes headers now
50-
towrite = ['%s %s\r\n' % (self.request_version, self.status)]
51-
for header in headers:
52-
towrite.append('%s: %s\r\n' % header)
53-
towrite.append('\r\n')
54-
self.wfile.writelines(towrite)
55-
self.response_length += sum(len(x) for x in towrite)
53+
if IS_GEVENT_V10:
54+
self.headers_sent = True
55+
56+
sline = '%s %s\r\n' % (self.request_version, self.status)
57+
write(sline)
58+
self.response_length += len(sline)
59+
60+
for header in headers:
61+
hline = '%s: %s\r\n' % header
62+
write(hline)
63+
self.response_length += len(hline)
64+
65+
write('\r\n')
66+
self.response_length += 2
67+
else:
68+
towrite = ['%s %s\r\n' % (self.request_version, self.status)]
69+
for header in headers:
70+
towrite.append('%s: %s\r\n' % header)
71+
towrite.append('\r\n')
72+
self.wfile.writelines(towrite)
73+
self.response_length += sum(len(x) for x in towrite)
5674
return write
5775
try:
5876
self.result = self.application(self.environ, start_response_for_upgrade)
@@ -72,8 +90,8 @@ def __init__(self, *args, **kwargs):
7290
protocols = kwargs.pop('websocket_protocols', [])
7391
extensions = kwargs.pop('websocket_extensions', [])
7492
self.application = WebSocketUpgradeMiddleware(self.application,
75-
protocols=protocols,
76-
extensions=extensions)
93+
protocols=protocols,
94+
extensions=extensions)
7795

7896
if __name__ == '__main__':
7997
def echo_handler(websocket, environ):

0 commit comments

Comments
 (0)