blob: e7d08ab1910948e22d1d9be82adb69a6be791d83 [file] [log] [blame]
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
tornado-server.py
~~~~~~~~~~~~~~~~~
A fully-functional HTTP/2 server written for Tornado.
"""
import collections
import json
import ssl
import tornado.gen
import tornado.ioloop
import tornado.iostream
import tornado.tcpserver
from h2.config import H2Configuration
from h2.connection import H2Connection
from h2.events import RequestReceived, DataReceived
def create_ssl_context(certfile, keyfile):
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ssl_context.options |= (
ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_COMPRESSION
)
ssl_context.set_ciphers("ECDHE+AESGCM")
ssl_context.load_cert_chain(certfile=certfile, keyfile=keyfile)
ssl_context.set_alpn_protocols(["h2"])
return ssl_context
class H2Server(tornado.tcpserver.TCPServer):
@tornado.gen.coroutine
def handle_stream(self, stream, address):
handler = EchoHeadersHandler(stream)
yield handler.handle()
class EchoHeadersHandler(object):
def __init__(self, stream):
self.stream = stream
config = H2Configuration(client_side=False)
self.conn = H2Connection(config=config)
@tornado.gen.coroutine
def handle(self):
self.conn.initiate_connection()
yield self.stream.write(self.conn.data_to_send())
while True:
try:
data = yield self.stream.read_bytes(65535, partial=True)
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)
yield self.stream.write(self.conn.data_to_send())
except tornado.iostream.StreamClosedError:
break
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', str(len(data))),
('server', 'tornado-h2'),
)
self.conn.send_headers(stream_id, response_headers)
self.conn.send_data(stream_id, data, end_stream=True)
if __name__ == '__main__':
ssl_context = create_ssl_context('server.crt', 'server.key')
server = H2Server(ssl_options=ssl_context)
server.listen(8888)
io_loop = tornado.ioloop.IOLoop.current()
io_loop.start()