An implementation of a Tornado server for your Flask (or Bottle) application. I wrote this stub a long time ago when I was fairly new to Tornado and Flask, but the module also easily applies to the Marcel HellKamp's excellent Bottle Framework.
About Tornado, Flask, and Bottle I pay homage-- “An engineer has achieved perfection not when there is nothing left to add, but nothing left to take away” ~ Antoine de Saint-Exupery
dependencies: Tornado
This module allows the use of Tornado as the Flask or Bottle server
The flask project is here:
https://github.com/pallets/flask
The Bottle project is here:
https://github.com/bottlepy/bottle
The Tornado project is here:
https://github.com/tornadoweb/tornado
http://www.tornadoweb.org/en/stable/
As you will discover experimenting with flask, the app.run() flask development server is a single-threaded "blocking server" -- this was an intentional choice to allow better debugging. Hence, it was never, ever, intended to be used in production. It was included as a convenience to get users developing/testing quickly without lots of extra resources.
If you need evidence, you can test this behavior by running an app on your development environment, and then trying to access it on several different browsers simultaneously. One browser "wins" and grabs the connection while the other is forced to wait... and wait... and wait.
Important Note: as of Flask release of 1.x version, the development server is multi-threaded so the previous discussion is no longer valid. Tornado still is a better choice because it is fast, fast, fast!
Enter Tornado.
Tornado is another web framework, worthy of study. We're not going to ditch Flask, we are interested in using Tornado's server with a Flask app. Tornado's included WSGI server can be used quite well with Flask. Tornado's server as a very fast and can function well as a stand-alone server or, better yet, as a proxy worker behind Apache or Nginx.
If you are looking for alternatives, you could also employ Gunicorn. This works very well for production environments too. But Tornado can be "baked-in" to your app from the beginning to bring simple apps up quickly without much mucking around with your deployment
Disclaimer - this is not necessarily the most secure or thread-safe way to do things but Tornado can scale, so you could theoretically use the app in a small to medium scale production environment as a stand-alone app.
However, to really do things correctly, you would want to use Apache or Nginx front end and set this up as a worker. So test, test, test before you go in to production!!
keep it simple
The reason why we love Flask and Tornado is their surface API is simple and approachable.
For now... it is easy to just copy the file "flask_tornado.py" into the same directory as your app! Thus adhering to the KISS principle-- or "Keep it Simple Stupid" paradigm.
from flask import Flask
from flask_tornado import TornadoServer
import datetime
app = Flask(__name__)
server = TornadoServer(app) # instantiate a server object
@app.route('/')
def index():
return "Hello World the local server time is {}".format(datetime.datetime.now())
if __name__ == '__main__':
server.run(port=12345)
from flask import Flask
import flask_tornado
import datetime
app = Flask(__name__)
@app.route('/')
def index():
return "Hello World the local server time is {}".format(datetime.datetime.now())
if __name__ == '__main__':
# it is required to reference the app (default port is 5000)
flask_tornado.run(app)
from flask import Flask
from flask_tornado import TornadoServer
import datetime
app = Flask(__name__)
server = TornadoServer(app)
@app.route('/')
def index():
return "Hello World the local server time is {}".format(datetime.datetime.now())
if __name__ == '__main__':
server.run(port=8443, ssl_cert='path_to_your_ssl_cert_file', ssl_key='path_to_your_ssl_key')
todo
At some point Armin Ronacher (creator of Flask) or Ben Darnell (creator of Tornado) will write a simple way to redirect HTTP to HTTPS equivalents. Sure, you can do this with Apache or NGINX servers too, but since this article is about simple deployment, I offer several simple strategies that can be employed to accomplish this scenario:
- create a no_redirect_app, that presents a message, in my example, "These are not the droids you are looking for", and gives a link to the root URL on the 443 app.
reference Flask docs http://flask.pocoo.org/docs/1.0/patterns/errorpages/
from flask import Flask
from flask_tornado import TornadoServer
app = Flask(__name__)
server = TornadoServer(app)
@app.errorhandler(404)
def page_not_found(e):
"""returns a explicit redirect to your site"""
return 'These are not the droids you are looking for... <a href="https://yoursite.com">try checking here</a>'
if __name__ == '__main__':
server.run(port=80)
-
A slightly fancier approach, create a redirect_app that listens to requests on port 80 and then 301 redirects to the actual app listening on port 443. If you've read this far in the docs, I assume you know what you are doing here.
-
run a second instance of the app on port 80... but rewrite the LOGIN method to drive them to the 443 url. This way users can consume unencrypted (public) data as they require but when they login or use a form that requires encryption, we redirect. This unfortunately adds complexity to your project, but achieves the desired effect of SSL.
-
The simplest approach is to write an app which drives all requests to the SSL port.
from flask import Flask, redirect
# You could do this slightly more elegantly
# with environmental vars
domain = "your domain or IP address"
app = Flask(__name__)
@app.route('/<path:path>')
def generic_redirect(path):
return redirect(f'https://{domain}/{path}')
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)