"""
A fully-functional HTTP/2 server written for curio.
Requires Python 3.5+.
"""
import traceback
from curio import spawn
import h2.config
import h2.connection
import h2.events
from h2.exceptions import ProtocolError
# hyper2web imports
from . import sslsocket, abstract, http
create_listening_ssl_socket = sslsocket.create_listening_ssl_socket
HTTP = http.HTTP
# todo: move this to app.App
[docs]async def h2_server(address, certfile, keyfile, app: abstract.AbstractApp):
"""
Create an HTTP/2 server at the given address.
"""
sock = create_listening_ssl_socket(address, certfile, keyfile)
print("Now listening on %s:%d" % address)
async with sock:
while True:
client, _ = await sock.accept()
server = H2Server(client, app)
await spawn(server.run())
[docs]class H2Server:
"""
This class just connects to socket and that's about it.
Most heavy lifting is done by http.HTTP
"""
def __init__(self, sock, app: abstract.AbstractApp):
config = h2.config.H2Configuration(client_side=False, header_encoding='utf-8')
self.sock = sock
self.conn = h2.connection.H2Connection(config=config)
# the Application that needs this server
# this server runs this app
self.http = HTTP(app=app, sock=self.sock, connection=self.conn)
[docs] async def run(self):
"""
Loop over the connection, managing it appropriately.
"""
self.conn.initiate_connection()
await self.sock.sendall(self.conn.data_to_send())
while True:
# 65535 is basically arbitrary here: this amounts to "give me
# whatever data you have".
data = await self.sock.recv(65535)
if not data:
break
try:
events = self.conn.receive_data(data)
except ProtocolError as e:
# todo: add this to logger
traceback.print_exception(etype=e, value=e, tb=e.__traceback__)
continue
except Exception as e:
traceback.print_exception(etype=e, value=e, tb=e.__traceback__)
print("Does not expect other exceptions. Exit.")
exit()
for event in events:
await self.http.handle_event(event)
await self.sock.sendall(self.conn.data_to_send())