LoginSignup
10
10

More than 5 years have passed since last update.

asyncio を使って簡易の HTTPS サーバーを立てる

Last updated at Posted at 2017-09-06

概要

イベントループのための 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 サーバーを立ててみましょう。

server.py
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 クライアントをつくってみましょう。

client.py
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 ファイルを表示するサーバーを立ててみましょう。

server.py
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)
10
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
10