Skip to content

Instantly share code, notes, and snippets.

@yxlwfds
Created May 2, 2021 12:28
Show Gist options
  • Save yxlwfds/aec22a1f85898d14723687f47531d58b to your computer and use it in GitHub Desktop.
Save yxlwfds/aec22a1f85898d14723687f47531d58b to your computer and use it in GitHub Desktop.
trio h2
import sys
import ssl
import json
import collections
import trio
import h2.config
import h2.connection
import h2.events
ReceivedData = collections.namedtuple("ReceivedData", ("headers", "data"))
class H2EchoServer:
def __init__(self):
config = h2.config.H2Configuration(client_side=False, header_encoding="utf-8")
self.connection = h2.connection.H2Connection(config=config)
self.received_data = {}
self.flow_control_events = {}
self.write_lock = trio.Lock()
self.stream = None
async def write_all_pending_data(self):
async with self.write_lock:
await self.stream.send_all(self.connection.data_to_send())
def request_received(self, event):
self.received_data[event.stream_id] = ReceivedData(event.headers, bytearray())
def data_received(self, event):
try:
self.received_data[event.stream_id].data.extend(event.data)
except KeyError:
self.connection.reset_stream(event.stream_id,
h2.errors.ErrorCodes.PROTOCOL_ERROR)
else:
self.connection.acknowledge_received_data(event.flow_controlled_length,
event.stream_id)
async def reply_echo(self, stream_id):
self.flow_control_events[stream_id] = trio.Event()
response_body = json.dumps({
"headers": collections.OrderedDict(self.received_data[stream_id].headers),
"body": self.received_data[stream_id].data.decode("utf-8")
}, indent=4).encode("utf-8")
response_headers = (
(":status", "200"),
("content-type", "application/json"),
("content-length", str(len(response_body))),
("server", "python-trio-h2"),
)
self.connection.send_headers(stream_id, response_headers)
ptr = 0
while ptr < len(response_body):
while self.connection.local_flow_control_window(stream_id) == 0:
await self.flow_control_events[stream_id]
chunk_size = min(self.connection.max_outbound_frame_size,
self.connection.local_flow_control_window(stream_id))
self.connection.send_data(stream_id, response_body[ptr:ptr+chunk_size])
await self.write_all_pending_data()
ptr += chunk_size
self.connection.end_stream(stream_id)
await self.write_all_pending_data()
def window_updated(self, event):
if event.stream_id == 0:
for event in self.flow_control_events.values():
event.set()
else:
try:
self.flow_control_events[event.stream_id].set()
except KeyError:
self.connection.reset_stream(event.stream_id,
h2.errors.ErrorCodes.PROTOCOL_ERROR)
async def __call__(self, server_stream):
self.stream = server_stream
try:
self.connection.initiate_connection()
await self.write_all_pending_data()
try:
async with trio.open_nursery() as nursery:
while True:
data = await server_stream.receive_some(65536)
if not data:
return
events = self.connection.receive_data(data)
for event in events:
if isinstance(event, h2.events.RequestReceived):
self.request_received(event)
elif isinstance(event, h2.events.DataReceived):
self.data_received(event)
elif isinstance(event, h2.events.StreamEnded):
nursery.start_soon(self.reply_echo, event.stream_id)
elif isinstance(event, h2.events.WindowUpdated):
self.window_updated(event)
elif isinstance(event, h2.events.ConnectionTerminated):
return
await self.write_all_pending_data()
finally:
await self.write_all_pending_data()
except:
print("Got exception: {!r}".format(sys.exc_info()))
async def main(port):
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="cert.crt", keyfile="cert.key")
ssl_context.set_alpn_protocols(["h2"])
await trio.serve_ssl_over_tcp(lambda stream: H2EchoServer()(stream),
port, ssl_context)
if __name__ == "__main__":
port = int(sys.argv[1])
print("Try: $ curl --tlsv1.2 --http2 -k https://localhost:{}/path -d'data'"
.format(port))
print("Or open a browser to https://localhost:{}/ and accept all the warnings"
.format(port))
trio.run(main, port)
@yxlwfds
Copy link
Author

yxlwfds commented May 2, 2021

cert.crt

-----BEGIN CERTIFICATE-----
MIIDhTCCAm2gAwIBAgIJAOrxh0dOYJLdMA0GCSqGSIb3DQEBCwUAMFkxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xNTA5MTkxNDE2
NDRaFw0xNTEwMTkxNDE2NDRaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l
LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV
BAMMCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMqt
A1iu8EN00FU0eBcBGlLVmNEgV7Jkbukra+kwS8j/U2y50QPGJc/FiIVDfuBqk5dL
ACTNc6A/FQcXvWmOc5ixmC3QKKasMpuofqKz0V9C6irZdYXZ9rcsW0gHQIr989yd
R+N1VbIlEVW/T9FJL3B2UD9GVIkUELzm47CSOWZvAxQUlsx8CUNuUCWqyZJoqTFN
j0LeJDOWGCsug1Pkj0Q1x+jMVL6l6Zf6vMkLNOMsOsWsxUk+0L3tl/OzcTgUOCsw
UzY59RIi6Rudrp0oaU8NuHr91yiSqPbKFlX10M9KwEEdnIpcxhND3dacrDycj3ux
eWlqKync2vOFUkhwiaMCAwEAAaNQME4wHQYDVR0OBBYEFA0PN+PGoofZ+QIys2Jy
1Zz94vBOMB8GA1UdIwQYMBaAFA0PN+PGoofZ+QIys2Jy1Zz94vBOMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAEplethBoPpcP3EbR5Rz6snDDIcbtAJu
Ngd0YZppGT+P0DYnPJva4vRG3bb84ZMSuppz5j67qD6DdWte8UXhK8BzWiHzwmQE
QmbKyzzTMKQgTNFntpx5cgsSvTtrHpNYoMHzHOmyAOboNeM0DWiRXsYLkWTitLTN
qbOpstwPubExbT9lPjLclntShT/lCupt+zsbnrR9YiqlYFY/fDzfAybZhrD5GMBY
XdMPItwAc/sWvH31yztarjkLmld76AGCcO5r8cSR/cX98SicyfjOBbSco8GkjYNY
582gTPkKGYpStuN7GNT5tZmxvMq935HRa2XZvlAIe8ufp8EHVoYiF3c=
-----END CERTIFICATE-----

@yxlwfds
Copy link
Author

yxlwfds commented May 2, 2021

cert.key

-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyq0DWK7wQ3TQVTR4FwEaUtWY0SBXsmRu6Str6TBLyP9TbLnR
A8Ylz8WIhUN+4GqTl0sAJM1zoD8VBxe9aY5zmLGYLdAopqwym6h+orPRX0LqKtl1
hdn2tyxbSAdAiv3z3J1H43VVsiURVb9P0UkvcHZQP0ZUiRQQvObjsJI5Zm8DFBSW
zHwJQ25QJarJkmipMU2PQt4kM5YYKy6DU+SPRDXH6MxUvqXpl/q8yQs04yw6xazF
ST7Qve2X87NxOBQ4KzBTNjn1EiLpG52unShpTw24ev3XKJKo9soWVfXQz0rAQR2c
ilzGE0Pd1pysPJyPe7F5aWorKdza84VSSHCJowIDAQABAoIBACp+nh4BB/VMz8Wd
q7Q/EfLeQB1Q57JKpoqTBRwueSVai3ZXe4CMEi9/HkG6xiZtkiZ9njkZLq4hq9oB
2z//kzMnwV2RsIRJxI6ohGy+wR51HD4BvEdlTPpY/Yabpqe92VyfSYxidKZWaU0O
QMED1EODOw4ZQ+4928iPrJu//PMB4e7TFao0b9Fk/XLWtu5/tQZz9jsrlTi1zthh
7n+oaGNhfTeIJJL4jrhTrKW1CLHXATtr9SJlfZ3wbMxQVeyj2wUlP1V0M6kBuhNj
tbGbMpixD5iCNJ49Cm2PHg+wBOfS3ADGIpi3PcGw5mb8nB3N9eGBRPhLShAlq5Hi
Lv4tyykCgYEA8u3b3xJ04pxWYN25ou/Sc8xzgDCK4XvDNdHVTuZDjLVA+VTVPzql
lw7VvJArsx47MSPvsaX/+4hQXYtfnR7yJpx6QagvQ+z4ludnIZYrQwdUmb9pFL1s
8UNj+3j9QFRPenIiIQ8qxxNIQ9w2HsVQ8scvc9CjYop/YYAPaQyHaL8CgYEA1ZSz
CR4NcpfgRSILdhb1dLcyw5Qus1VOSAx3DYkhDkMiB8XZwgMdJjwehJo9yaqRCLE8
Sw5znMnkfoZpu7+skrjK0FqmMpXMH9gIszHvFG8wSw/6+2HIWS19/wOu8dh95LuC
0zurMk8rFqxgWMWF20afhgYrUz42cvUTo10FVB0CgYEAt7mW6W3PArfUSCxIwmb4
VmXREKkl0ATHDYQl/Cb//YHzot467TgQll883QB4XF5HzBFurX9rSzO7/BN1e6I0
52i+ubtWC9xD4fUetXMaQvZfUGxIL8xXgVxDWKQXfLiG54c8Mp6C7s6xf8kjEUCP
yR1F0SSA/Pzb+8RbY0p7eocCgYA+1rs+SXtHZev0KyoYGnUpW+Uxqd17ofOgOxqj
/t6c5Z+TjeCdtnDTGQkZlo/rT6XQWuUUaDIXxUbW+xEMzj4mBPyXBLS1WWFvVQ5q
OpzO9E/PJeqAH6rkof/aEelc+oc/zvOU1o9uA+D3kMvgEm1psIOq2RHSMhGvDPA0
NmAk+QKBgQCwd1681GagdIYSZUCBecnLtevXmIsJyDW2yR1NNcIe/ukcVQREMDvy
5DDkhnGDgnV1D5gYcXb34g9vYvbfTnBMl/JXmMAAG1kIS+3pvHyN6f1poVe3yJV1
yHVuvymnJxKnyaV0L3ntepVvV0vVNIkA3oauoUTLto6txBI+b/ImDA==
-----END RSA PRIVATE KEY-----

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment