Eventlet Example ServerΒΆ
This example is a basic HTTP/2 server written using the eventlet concurrent networking framework. This example is notable for demonstrating how to configure PyOpenSSL, which eventlet uses for its TLS layer.
In terms of HTTP/2 functionality, this example is very simple: it returns the request headers as a JSON document to the caller. It does not obey HTTP/2 flow control, which is a flaw, but it is otherwise functional.
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | # -*- coding: utf-8 -*-
"""
eventlet-server.py
~~~~~~~~~~~~~~~~~~
A fully-functional HTTP/2 server written for Eventlet.
"""
import collections
import json
import eventlet
from eventlet.green.OpenSSL import SSL, crypto
from h2.connection import H2Connection
from h2.events import RequestReceived, DataReceived
class ConnectionManager(object):
"""
An object that manages a single HTTP/2 connection.
"""
def __init__(self, sock):
self.sock = sock
self.conn = H2Connection(client_side=False)
def run_forever(self):
self.conn.initiate_connection()
self.sock.sendall(self.conn.data_to_send())
while True:
data = self.sock.recv(65535)
if not data:
break
events = self.conn.receive_data(data)
for event in events:
if isinstance(event, RequestReceived):
self.request_received(event.headers, event.stream_id)
elif isinstance(event, DataReceived):
self.conn.reset_stream(event.stream_id)
self.sock.sendall(self.conn.data_to_send())
def request_received(self, headers, stream_id):
headers = collections.OrderedDict(headers)
data = json.dumps({'headers': headers}, indent=4).encode('utf-8')
response_headers = (
(':status', '200'),
('content-type', 'application/json'),
('content-length', len(data)),
('server', 'eventlet-h2'),
)
self.conn.send_headers(stream_id, response_headers)
self.conn.send_data(stream_id, data, end_stream=True)
def alpn_callback(conn, protos):
if b'h2' in protos:
return b'h2'
raise RuntimeError("No acceptable protocol offered!")
def npn_advertise_cb(conn):
return [b'h2']
# Let's set up SSL. This is a lot of work in PyOpenSSL.
options = (
SSL.OP_NO_COMPRESSION |
SSL.OP_NO_SSLv2 |
SSL.OP_NO_SSLv3 |
SSL.OP_NO_TLSv1 |
SSL.OP_NO_TLSv1_1
)
context = SSL.Context(SSL.SSLv23_METHOD)
context.set_options(options)
context.set_verify(SSL.VERIFY_NONE, lambda *args: True)
context.use_privatekey_file('server.key')
context.use_certificate_file('server.crt')
context.set_npn_advertise_callback(npn_advertise_cb)
context.set_alpn_select_callback(alpn_callback)
context.set_cipher_list(
"ECDHE+AESGCM"
)
context.set_tmp_ecdh(crypto.get_elliptic_curve(u'prime256v1'))
server = eventlet.listen(('0.0.0.0', 443))
server = SSL.Connection(context, server)
pool = eventlet.GreenPool()
while True:
try:
new_sock, _ = server.accept()
manager = ConnectionManager(new_sock)
pool.spawn_n(manager.run_forever)
except (SystemExit, KeyboardInterrupt):
break
|