打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Serving a WSGI app, WebSockets and static files with Twisted | saghul, on code

Serving a WSGI app, WebSockets and static files with Twisted

Posted on 07/09/2013

Long time no post! Lets solve that now shall we?

A few days ago I started playing a bit with Flask, since I’m considering it as the framework to build some API server. I have no web development experience, and Flask looks like a great project so I went with that.

I started with a tiny little hello world, and then I wanted to add some websockets and some CSS. Oh the trouble. When I started looking for how to combine a Flask app with WebSockets I found references to gevent-socketio for the most part, but I somewhat wanted to use Twisted this time, so I kept looking. Soon enough I found AutoBahn, a great WebSocket implementation for Twisted, which can be combined with a WSGI app, brilliant! After seeing how AutoBahn manages to add the websocket route to the WSGI app, adding support for static files was kind of trivial.

Here is the result of my experiments, a really simple web app which consists of a Flask WSGI app, a WebSocket server and some static files, all served by the same process running Twisted. You may not want to do this in a production environment, but hey, I’m just playing here

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
import os
from autobahn.resource import WebSocketResource, WSGIRootResource
from autobahn.websocket import WebSocketServerFactory, WebSocketServerProtocol
from flask import Flask, render_template
from twisted.application import internet, service
from twisted.internet import reactor
from twisted.python.threadpool import ThreadPool
from twisted.web.server import Site
from twisted.web.static import File
from twisted.web.wsgi import WSGIResource
import settings
class EchoServerProtocol(WebSocketServerProtocol):
def onMessage(self, msg, binary):
self.sendMessage(msg, binary)
app = Flask('Echo Test')
@app.route('/')
def index():
return render_template('index.html', ws_port=settings.PORT)
# create a Twisted Web resource for our WebSocket server
ws_factory = WebSocketServerFactory("ws://%s:%d" % (settings.INTERFACE, settings.PORT))
ws_factory.protocol = EchoServerProtocol
ws_resource = WebSocketResource(ws_factory)
# create thread pool used to serve WSGI requests
thread_pool = ThreadPool(maxthreads=settings.THREAD_POOL_SIZE)
thread_pool.start()
reactor.addSystemEventTrigger('before', 'shutdown', thread_pool.stop)
# create a Twisted Web WSGI resource for our Flask server
wsgi_resource = WSGIResource(reactor, thread_pool, app)
# create resource for static assets
static_resource = File(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates', 'assets'))
# create a root resource serving everything via WSGI/Flask, but
# the path "/assets" served by our File stuff and
# the path "/ws" served by our WebSocket stuff
root_resource = WSGIRootResource(wsgi_resource, {'assets': static_resource, 'ws': ws_resource})
# create a Twisted Web Site
site = Site(root_resource)
# setup an application for serving the site
web_service = internet.TCPServer(settings.PORT, site, interface=settings.INTERFACE)
application = service.Application("Echo Test")
web_service.setServiceParent(application)
view raw app.py hosted with
by GitHub
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
<html>
<head>
<link rel="stylesheet" type="text/css" href="/assets/style.css">
<script type="text/javascript">
var sock = null;
var ellog = null;
window.onload = function() {
ellog = document.getElementById('log');
var wsuri = "ws://" + window.location.hostname + ":{{ ws_port }}/ws";
if ("WebSocket" in window) {
sock = new WebSocket(wsuri);
} else {
log("Browser does not support WebSocket!");
window.location = "http://autobahn.ws/unsupportedbrowser";
}
if (sock) {
sock.onopen = function() {
log("Connected to " + wsuri);
}
sock.onclose = function(e) {
log("Connection closed (wasClean = " + e.wasClean + ", code = " + e.code + ", reason = '" + e.reason + "')");
sock = null;
}
sock.onmessage = function(e) {
log("Got echo: " + e.data);
}
}
};
function send() {
var msg = document.getElementById('message').value;
if (sock) {
sock.send(msg);
log("Sent: " + msg);
} else {
log("Not connected.");
}
};
function log(m) {
ellog.innerHTML += m + '\n';
ellog.scrollTop = ellog.scrollHeight;
};
</script>
</head>
<body>
<h1>Echo Test</h1>
<noscript>You must enable JavaScript</noscript>
<form>
<p>Message: <input id="message" type="text" size="50" maxlength="50" value="Hello, world!"></p>
</form>
<button onclick='send();'>Send Message</button>
<pre id="log" style="height: 20em; overflow-y: scroll;"></pre>
</body>
</html>
view raw index.html hosted with
by GitHub
1 2 3 4 5 6 7
# Make sure this file is valid Python code
PORT = 8081
INTERFACE = "0.0.0.0"
THREAD_POOL_SIZE = 10
view raw settings.py hosted with
by GitHub
1 2 3 4 5 6
body {
font-family: Helvetica,Arial,sans-serif;
background-color:#b0c4de;
}
view raw style.css hosted with
by GitHub

Since Gist does not currently allow folders, make sure you keep this layout after downloading the files:

1
2
3
4
5
6
├── app.py
├── settings.py
└── templates
    ├── assets
    │   └── style.css
    └── index.html

We’ll use the twistd command line tool to launch out application, since it can take care of logging, running as a daemon, etc. To run it in the foreground:

twistd -n -l - -y app.py

This will launch the application in non-daemon mode and log to standard output.

Hope this helps someone, all feedback is more than welcome

:wq

Serving a WSGI app, WebSockets and static files with Twisted

Posted on 07/09/2013

Long time no post! Lets solve that now shall we?

A few days ago I started playing a bit with Flask, since I’m considering it as the framework to build some API server. I have no web development experience, and Flask looks like a great project so I went with that.

I started with a tiny little hello world, and then I wanted to add some websockets and some CSS. Oh the trouble. When I started looking for how to combine a Flask app with WebSockets I found references to gevent-socketio for the most part, but I somewhat wanted to use Twisted this time, so I kept looking. Soon enough I found AutoBahn, a great WebSocket implementation for Twisted, which can be combined with a WSGI app, brilliant! After seeing how AutoBahn manages to add the websocket route to the WSGI app, adding support for static files was kind of trivial.

Here is the result of my experiments, a really simple web app which consists of a Flask WSGI app, a WebSocket server and some static files, all served by the same process running Twisted. You may not want to do this in a production environment, but hey, I’m just playing here

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
import os
from autobahn.resource import WebSocketResource, WSGIRootResource
from autobahn.websocket import WebSocketServerFactory, WebSocketServerProtocol
from flask import Flask, render_template
from twisted.application import internet, service
from twisted.internet import reactor
from twisted.python.threadpool import ThreadPool
from twisted.web.server import Site
from twisted.web.static import File
from twisted.web.wsgi import WSGIResource
import settings
class EchoServerProtocol(WebSocketServerProtocol):
def onMessage(self, msg, binary):
self.sendMessage(msg, binary)
app = Flask('Echo Test')
@app.route('/')
def index():
return render_template('index.html', ws_port=settings.PORT)
# create a Twisted Web resource for our WebSocket server
ws_factory = WebSocketServerFactory("ws://%s:%d" % (settings.INTERFACE, settings.PORT))
ws_factory.protocol = EchoServerProtocol
ws_resource = WebSocketResource(ws_factory)
# create thread pool used to serve WSGI requests
thread_pool = ThreadPool(maxthreads=settings.THREAD_POOL_SIZE)
thread_pool.start()
reactor.addSystemEventTrigger('before', 'shutdown', thread_pool.stop)
# create a Twisted Web WSGI resource for our Flask server
wsgi_resource = WSGIResource(reactor, thread_pool, app)
# create resource for static assets
static_resource = File(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'templates', 'assets'))
# create a root resource serving everything via WSGI/Flask, but
# the path "/assets" served by our File stuff and
# the path "/ws" served by our WebSocket stuff
root_resource = WSGIRootResource(wsgi_resource, {'assets': static_resource, 'ws': ws_resource})
# create a Twisted Web Site
site = Site(root_resource)
# setup an application for serving the site
web_service = internet.TCPServer(settings.PORT, site, interface=settings.INTERFACE)
application = service.Application("Echo Test")
web_service.setServiceParent(application)
view raw app.py hosted with
by GitHub
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
<html>
<head>
<link rel="stylesheet" type="text/css" href="/assets/style.css">
<script type="text/javascript">
var sock = null;
var ellog = null;
window.onload = function() {
ellog = document.getElementById('log');
var wsuri = "ws://" + window.location.hostname + ":{{ ws_port }}/ws";
if ("WebSocket" in window) {
sock = new WebSocket(wsuri);
} else {
log("Browser does not support WebSocket!");
window.location = "http://autobahn.ws/unsupportedbrowser";
}
if (sock) {
sock.onopen = function() {
log("Connected to " + wsuri);
}
sock.onclose = function(e) {
log("Connection closed (wasClean = " + e.wasClean + ", code = " + e.code + ", reason = '" + e.reason + "')");
sock = null;
}
sock.onmessage = function(e) {
log("Got echo: " + e.data);
}
}
};
function send() {
var msg = document.getElementById('message').value;
if (sock) {
sock.send(msg);
log("Sent: " + msg);
} else {
log("Not connected.");
}
};
function log(m) {
ellog.innerHTML += m + '\n';
ellog.scrollTop = ellog.scrollHeight;
};
</script>
</head>
<body>
<h1>Echo Test</h1>
<noscript>You must enable JavaScript</noscript>
<form>
<p>Message: <input id="message" type="text" size="50" maxlength="50" value="Hello, world!"></p>
</form>
<button onclick='send();'>Send Message</button>
<pre id="log" style="height: 20em; overflow-y: scroll;"></pre>
</body>
</html>
view raw index.html hosted with
by GitHub
1 2 3 4 5 6 7
# Make sure this file is valid Python code
PORT = 8081
INTERFACE = "0.0.0.0"
THREAD_POOL_SIZE = 10
view raw settings.py hosted with
by GitHub
1 2 3 4 5 6
body {
font-family: Helvetica,Arial,sans-serif;
background-color:#b0c4de;
}
view raw style.css hosted with
by GitHub

Since Gist does not currently allow folders, make sure you keep this layout after downloading the files:

1
2
3
4
5
6
├── app.py
├── settings.py
└── templates
    ├── assets
    │   └── style.css
    └── index.html

We’ll use the twistd command line tool to launch out application, since it can take care of logging, running as a daemon, etc. To run it in the foreground:

twistd -n -l - -y app.py

This will launch the application in non-daemon mode and log to standard output.

Hope this helps someone, all feedback is more than welcome

:wq

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
中华偏方 中药外敷治老年便秘验方 贵康网
大功率逆变电源制作
看過這個煎牛排知識圖片後,你也可以在家也可以吃到 5 星級廚師等級的牛排了! |
白启哲山水画艺术
囧知:爱因斯坦到底错哪儿了?
国外变态Seo超强优化器(PR Storm v2.1汉化版)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服