概要
イベントループのための asyncio モジュールおよび uvloop の練習のために簡易の HTTPS サーバーを立ててみました。uvloop は asyncio および libuv をもとにしたモジュールです。2016年以降、uvloop を採用した Sanic や japronto などの新しいマイクロフレームワークが台頭しました。
前提
Python 3.6 を前提とします。Python 3.6 で ssl モジュールの SSLContext のデフォルト値の変更や定数が整理統合されました。
uvloop について
uvloop は asyncio.AbstractEventLoop
を実装し、uvloop と互換性のある API を提供しています。libuv の制約から uvloop は Windows をサポートしないため、asyncio が uvloop フォールバックの役割を担っています。
自己署名証明書をつくる
次のワンライナーで自己署名証明書をつくることができます。
# https://stackoverflow.com/a/41366949/531320
openssl req -x509 -newkey rsa:4096 -sha256 \
-nodes -keyout server.key -out server.crt \
-subj "/CN=example.com" -days 3650
HTTPS サーバーを立てる
HTTP メソッドに関係なく文字列を返す echo サーバーを立ててみましょう。
import asyncio
import ssl
async def request_handler(reader, writer):
msg = (
'HTTP/1.1 200 OK\r\n'
'Content-Type: text/plain; charset=utf-8\r\n'
'\r\n'
'hello\r\n'
)
writer.write(msg.encode())
await writer.drain()
writer.close()
host = '127.0.0.1'
port = 8000
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('server.crt', keyfile='server.key')
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
loop = asyncio.get_event_loop()
coro = asyncio.start_server(
request_handler,
host, port, ssl=ctx, loop=loop
)
server = loop.run_until_complete(coro)
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
HTTPS クライアントをつくる
今度は HTTPS クライアントをつくってみましょう。
import asyncio
import ssl
async def http_client(host, port, msg, ctx, loop):
reader, writer = await asyncio.open_connection(
host, port, ssl=ctx, loop=loop
)
writer.write(msg.encode())
data = await reader.read()
print("Received: %r" % data.decode())
writer.close()
host = '127.0.0.1'
port = 8000
msg = (
'GET / HTTP/1.1\r\n'
'Host: localhost:8000\r\n'
'\r\n'
'\r\n'
)
ctx = ssl.create_default_context()
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
loop = asyncio.get_event_loop()
loop.run_until_complete(http_client(host, port, msg, ctx, loop))
loop.close()
aiohttp
aiohttp は asyncio をもとに開発された HTTP モジュールで、アプリケーションの開発機能も附属します。インストールは次のとおりです。
pip3 install aiohttp
HTML ファイルを表示するサーバーを立ててみましょう。
from aiohttp import web
from pathlib import Path
import ssl
host='localhost'
port=8000
ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
ctx.load_cert_chain('server.crt', keyfile='server.key')
ctx.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
app = web.Application()
app.router.add_static('/static', path=str(Path.cwd().joinpath('static')), show_index=True)
web.run_app(app, host=host, port=port, ssl_context=ctx)