Tutorial: Using HTTP/2¶
This tutorial will guide you through serving HTTP/2 and making use of
HTTP/2’s server push. The code for this tutorial is present in the
examples/http2
directory. The example itself is a very simple
webpage that does calculation on a server (it isn’t really practical).
Running the example¶
To run the example, in examples/http2
the following should start
the server, (see Installation first),
$ export QUART_APP=http2:app
$ quart run
this example is then available at https://localhost:5000/.
1: Structure¶
Quart by default expects the code to be structured in a certain way in order for templates and static file to be found. This means that you should structure the blog as follows,
http2/
http2/static/
http2/static/js/
http2/static/css/
http2/templates/
doing so will also make your project familiar to others, as you follow the same convention.
2: Installation¶
It is always best to run python projects within a pipenv, which should be created and activated as follows,
$ cd http2
$ pipenv install quart
for this we will only need Quart. Now pipenv can be activated,
$ pipenv shell
3: Creating the app¶
We can now create a basic hello world app, in a file called
http2.py
. This app has to be served over HTTPS in order for a
browser to use HTTP/2, this requires SSL certificates to be provided
and that the default quart run
command is overridden with one that
adds SSL. To create the certificates run and accept the defaults,
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
Warning
You shouldn’t use these certificates in production, see ssl for details.
The command itself and app code is then, see Using HTTP/2 full details,
from quart import Quart
app = Quart(__name__)
@app.route('/')
async def index():
return 'Hello World'
@app.cli.command('run')
def run():
app.run(port=5000, certfile='cert.pem', keyfile='key.pem')
and run it by the following,
$ export QUART_APP=blog:app
(venv) $ quart run
The hello world is then available at https://localhost:5000/ and should be served using the h2
protocol (see the developer toolbar in the browser).
Note
The QUART_APP
environment variable is assumed to be set for the
rest of this tutorial.
4: Using server push¶
Server push allows for the server to send responses to the client before the client sends the request. This is useful when the server can predict what the client will request, thereby saving time at the possible cost of bandwidth if the prediction is wrong.
In this example we will return html that references a css and a js file, and hence we can predict that the client will request both files. This allows us to push the files to the client before the client requests it.
To do so we simply change the index view-function to,
from quart import make_push_promise, render_template, url_for
@app.route('/')
async def index():
await make_push_promise(url_for('static', filename='http2.css'))
await make_push_promise(url_for('static', filename='http2.js'))
return await render_template('index.html')
5: Calculation¶
In addition to using HTTP/2 we actually want to do some calculation on
the server and return the result to the client. The calculation is
simple, accept JSON containing two values a
and b
and a
operator
, perform the operation and then return the result as
JSON,
from quart import abort, jsonify, request
@app.route('/', methods=['POST'])
async def calculate():
data = await request.get_json()
operator = data['operator']
try:
a = int(data['a'])
b = int(data['b'])
except ValueError:
abort(400)
if operator == '+':
return jsonify(a + b)
elif operator == '-':
return jsonify(a - b)
elif operator == '*':
return jsonify(a * b)
elif operator == '/':
return jsonify(a / b)
else:
abort(400)
The client side requires the following HTML elements, two inputs a
and b
and the various operations,
<body>
<p>
<input type="number" name="a" placeholder="a">
<input type="number" name="b" placeholder="b">
<label id="result">?</span>
</p>
<p>
<button id="add">Add a and b</button>
<button id="subtract">Subtract b from a</button>
<button id="multiply">Multiply a and b</button>
<button id="divide">Divide a by b</button>
</p>
</body>
and the following javascript to send the POST request and deal with the response,
document.addEventListener('DOMContentLoaded', function() {
var calculate = function(operator) {
fetch('/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify ({
a: document.getElementsByName("a")[0].value,
b: document.getElementsByName("b")[0].value,
operator: operator
}),
}).then(
function(response) {return response.json()
.then(
function(data) {document.getElementById('result').innerText = data;
}).catch(function() {});
};
document.getElementById('add').onclick = function(event) {calculate('+'); return false;};
document.getElementById('subtract').onclick = function(event) {calculate('-'); return false;};
document.getElementById('multiply').onclick = function(event) {calculate('*'); return false;};
document.getElementById('divide').onclick = function(event) {calculate('/'); return false;};
});
6: Conclusion¶
The example files contain this entire tutorial and a little more, so they are now worth a read. Hopefully you can now go ahead and create your own apps that are served over http/2.