Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ASGI(非同期サーバゲートウェイインターフェース)の概要

 ASGI(非同期サーバゲートウェイインターフェース)の概要

PyCon JP 2020 カンファレンス 資料です。

Junya Fukuda

August 31, 2020
Tweet

More Decks by Junya Fukuda

Other Decks in Programming

Transcript

  1. 썡쎕썟썷쎣쎟 •෱ా ൏໵ʢJunya Fukudaʣʢ@JunyaFffʣ •גࣜձࣾ೔ຊγεςϜٕݚʢJSLʣॴଐ •PyCon JP 2020 Gold Sponser

    ʢΦϯϥΠϯϒʔεʹ΋༡ͼʹ͖ͯͶʣ •GEEKLAB.NAGANO •࠷ۙϚΠϯΫϥϑτͰখֶੜ޲͚Youtube഑৴࢝ΊΔ ΪʔΫϥϘ௕໺ϚΠΫϥ
  2. )551쎂썻썛썽쎅썡썬쎠썛 •HTTP/1.0 •HTTP/2 •HTTP/3 1990 2000 2010 2020 •HTTP/0.9 •HTTP/1.1

    1997 1999 2015 •HTTPͷඪ४ԽΛ࣌ܥྻʹ ύϑΥʔϚεͷ޲্ʹϑΥʔΧε
  3. )551쎂썻썛썽쎅썡썬쎠썛 •HTTP/1.0 •HTTP/2 •HTTP/3 1990 2000 2010 2020 •HTTP/0.9 •HTTP/1.1

    1997 1999 2015 2020೥ϒϥ΢βͰରԠ࢝·Γͩ͢ •HTTPͷඪ४ԽΛ࣌ܥྻʹ
  4. 84(* •WSGIʢWeb Server Gateway Interfaceʣͷུ •2003೥ʹPEP 333 ʢ 2010೥ PEP

    3333 Python3ରԠʣ IUUQTXXXQZUIPOPSHEFWQFQTQFQ
  5. 84(* •WSGIʢWeb Server Gateway Interfaceʣͷུ •2003೥ʹPEP 333 ʢ 2010೥ PEP

    3333 Python3ରԠʣ IUUQTXXXQZUIPOPSHEFWQFQTQFQ
  6. 84(* •WSGIʢWeb Server Gateway Interfaceʣͷུ •Type͸ʮInformationalʯ •2003೥ʹPEP 333 ʢ 2010೥

    PEP 3333 Python3ରԠʣ ΫϥΠΞϯτ 8FCαʔό ΞϓϦέʔγϣϯ ϑϨʔϜϫʔΫ
  7. 84(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ environ = { "REQUEST_METHOD": "GET", "SCRIPT_NAME": "", "PATH_INFO": "/",

    "QUERY_STRING":"search=red+blue&maximum_price=20", "SERVER_NAME": "www.example.org", "SERVER_PORT": 443, "REMOTE_HOST": "134.56.78.4", "REMOTE_PORT": 1453, "SERVER_PROTOCOL": "HTTP/1.1", "HTTP_HOST": "www.example.org", "HTTP_ACCEPT": "application/json", }
  8. 84(*쎿썗쏚 def application(environ, start_response): start_response( '200, OK', [('Content-Type', 'text/plain')] )

    return [b'Hello, World'] 84(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ •WSGI؀ڥม਺ͷ࡞੒
  9. 84(*쎿썗쏚 def application(environ, start_response): start_response( '200, OK', [('Content-Type', 'text/plain')] )

    return [b'Hello, World'] 84(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ •WSGI؀ڥม਺ͷ࡞੒ •WSGIΞϓϦέʔγϣϯ͔Βݺͼग़͞ΕΔίʔϧόοΫؔ਺ͷ࡞੒
  10. •WSGI؀ڥม਺ͷ࡞੒ env = {} env['wsgi.version'] = (1, 0) # WSGIͷόʔδϣϯɿܾΊଧͪ

    env['wsgi.url_scheme'] = 'http' # urlͷεΩʔϜ(http/https) # HTTP ϦΫΤετຊମͷόΠτྻΛಡΈग़͢͜ͱ͕Ͱ͖ΔೖྗετϦʔϜ env['wsgi.input'] = io.BytesIO(byte_request) env['wsgi.errors'] = sys.stderr env['wsgi.multithread'] = False env['wsgi.multiprocess'] = False env['wsgi.run_once'] = False env['REQUEST_METHOD'] = request_method # GET env['PATH_INFO'] = path # / env['SERVER_NAME'] = server_name # FQDN env['SERVER_PORT'] = str(port) # 8888 IUUQTXXXQZUIPOPSHEFWQFQTQFQFOWJSPOWBSJBCMFT 84(*쎿썗쏚
  11. "4(*쎅ొ৔ •ASGIʢAsynchronous Server Gateway Interfaceʣͷུ •2016೥ʹRead the Docs͕ొ৔ - 1,2Λܦͯݱࡏ

    3 Ͱ ࠷৽൛ͷ҆ఆ൛ •WSGIͷਫ਼ਆతʢεϐϦνϡΞϧʁʣͳޙܧऀʢa spiritual successorʣ
  12. "4(*쎅ొ৔ •ASGIʢAsynchronous Server Gateway Interfaceʣͷུ •HTTP/1.1ɺWebsocketɺHTTP/2ʹରԠ •2016೥ʹRead the Docs͕ొ৔ -

    1,2Λܦͯݱࡏ 3 Ͱ ࠷৽൛ͷ҆ఆ൛ •WSGIͷਫ਼ਆతʢεϐϦνϡΞϧʁʣͳޙܧऀʢa spiritual successorʣ
  13. "4(*쎅ొ৔ •ASGIʢAsynchronous Server Gateway Interfaceʣͷུ •HTTP/1.1ɺWebsocketɺHTTP/2ʹରԠ •2016೥ʹRead the Docs͕ొ৔ -

    1,2Λܦͯݱࡏ 3 Ͱ ࠷৽൛ͷ҆ఆ൛ •WSGIͷਫ਼ਆతʢεϐϦνϡΞϧʁʣͳޙܧऀʢa spiritual successorʣ •·ͩPEPʹ͸ͳ͍ͬͯͳ͍
  14. "4(*쎅ొ৔ •ASGIʢAsynchronous Server Gateway Interfaceʣͷུ •HTTP/1.1ɺWebsocketɺHTTP/2ʹରԠ •2016೥ʹRead the Docs͕ొ৔ -

    1,2Λܦͯݱࡏ 3 Ͱ ࠷৽൛ͷ҆ఆ൛ •WSGIͷਫ਼ਆతʢεϐϦνϡΞϧʁʣͳޙܧऀʢa spiritual successorʣ •·ͩPEPʹ͸ͳ͍ͬͯͳ͍
  15. "4(*쎂썻썛썽3FBEUIF%PDT •ͦͷ΄͔ͷASGIͷ࢓༷ •ASGI sub-specification - Life Span •ASGI Extention -

    HTTP/2 Server Push •ASGI Extention - Websocket Denial Response https://asgi.readthedocs.io/en/latest/
  16. "4(*쎂썻썛썽BTZODJPBTZODBXBJU •͜ͷޙͷίʔυͰ͸ asyncio async/await͕ొ৔͠·͢ɻ •asyncio •ίϧʔνϯ 処理を明示的に中断し、再開できるタスクを利用して複数の処理を実行すること コルーチンを定義するには async def

    を利用します •async/await 標準ライブラリに含まれ、コルーチンを活用して主にネットワーク関連の IO処理を非同期に行うためのモジュールです。
  17. "4(*쎂썻썛썽BTZODJPBTZODBXBJU •͜ͷޙͷίʔυͰ͸ asyncio async/await͕ొ৔͠·͢ɻ •asyncio •ίϧʔνϯ 処理を明示的に中断し、再開できるタスクを利用して複数の処理を実行すること コルーチンを定義するには async def

    を利用します •async/await 標準ライブラリに含まれ、コルーチンを活用して主にネットワーク関連の IO処理を非同期に行うためのモジュールです。
  18. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope['type'] == 'http'

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) IUUQTXXXVWJDPSOPSH
  19. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope['type'] == 'http'

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) IUUQTXXXVWJDPSOPSH
  20. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope['type'] == 'http'

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) scope
  21. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ = { "type": "http", "http_version": "2.0", "asgi": { "spec_version":

    "2.1", "version": “3.0", }, "method": "POST", "scheme": "https", "path": "/", "query_string": b"a=b", "headers": [(b"content-length", b"5")], "client": ("10.0.0.0", 1234), "server": ("127.0.0.1", 443), "root_path": ".", } scope IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMIUUQDPOOFDUJPOTDPQF HTTP Connection Scope
  22. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app( , , send): assert scope['type'] ==

    'http' await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) receive scope
  23. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ = { "type": "http.request.body", "body": b'{"example": "Some JSON data"}',

    "more_body": False } receive IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMSFRVFTUSFDFJWFFWFOU Request - receive event
  24. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ = { "type": "http.request.body", "body": b'{"example": "Some JSON data"}',

    "more_body": False } receive IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMSFRVFTUSFDFJWFFWFOU Request - receive event
  25. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ = { "type": "http.request.body", "body": b'{"example": "Some JSON data"}',

    "more_body": False } receive IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMSFRVFTUSFDFJWFFWFOU Request - receive event
  26. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ receive async def app(scope, receive, send): assert scope[“type”] ==

    “http” await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', })
  27. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope[“type”] == “http”

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMSFTQPOTFTUBSUTFOEFWFOU Response Start - send event
  28. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope[“type”] == “http”

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', }) IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMSFTQPOTFTUBSUTFOEFWFOU Response Start - send event
  29. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope[“type”] == “http”

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', ‘more_body’: })
  30. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope[“type”] == “http”

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', ‘more_body’: }) False
  31. "4(*४ڌ쎅쎬쏡쏴쎻썗쏁쏱쏽쎅ྫ async def app(scope, receive, send): assert scope[“type”] == “http”

    await send({ 'type': 'http.response.start', 'status': 200, 'headers': [ [b'content-type', b'text/plain'], ] }) await send({ 'type': 'http.response.body', 'body': b'Hello, world!', })
  32. •class HTTPParser: HTTPͷϦΫΤετΛύʔε͢Δ class HTTPParser: def __init__(self): self.part = "REQUEST"

    self.headers = [] self.body_length = 0 def feed_line(self, line: bytes): if self.part == "REQUEST": self.method, self.path, self.version = line.split(b" ", 2) self.part = "HEADERS" elif self.part == "HEADERS" and line.strip() == b"": self.part = "BODY" elif self.part == "HEADERS": name, value = line.split(b":", 1) self.headers.append((name.strip(), value.strip())) if name.lower() == b"content-length": self.body_length = int(value) "4(*FDIP8FC쎿썗쏚
  33. def create_scope(parser): return { "type": "http", "method": parser.method, "asgi": {

    "spec_version": "2.1", "version": "3.0", }, "scheme": "http", "raw_path": parser.path, "path": parser.path.decode(), "headers": parser.headers, } •create_scope ؔ਺ɿscopeΛ࡞ΔʢASGIΞϓϦͷୈҰҾ਺Λ࡞Δʣ "4(*FDIP8FC쎿썗쏚
  34. def create_message(body, more_body): return { "type": "http.request", "body": body, "more_body":

    more_body, } •create_message ؔ਺ɿreceive༻ͷϝοηʔδΛ࡞ΔʢASGIΞϓϦͷୈ̎Ҿ਺ʣ "4(*FDIP8FC쎿썗쏚
  35. if __name__ == "__main__": if len(sys.argv) < 2: sys.exit('"module:callable"ͷܗͰASGIΞϓϦέʔγϣϯΛࢦఆ͍ͯͩ͘͠͞') host,

    port = 'localhost', 8888 app_path = sys.argv[1] module, application = app_path.split(':') module = __import__(module) asgi_app = getattr(module, application) global app app = asgi_app asyncio.run(main(host, port)) •mainॲཧɾmainίϧʔνϯɿasyncio Ͱcreateserver "4(*FDIP8FC쎿썗쏚
  36. async def main(host, port): server = await asyncio.start_server(asgi_http_parser_server, host, port)

    await server.serve_forever() •mainॲཧɾmainίϧʔνϯɿasyncio Ͱcreateserver "4(*FDIP8FC쎿썗쏚
  37. async def main(host, port): server = await asyncio.start_server(asgi_http_parser_server, host, port)

    await server.serve_forever() •mainॲཧɾmainίϧʔνϯɿasyncio Ͱcreateserver "4(*FDIP8FC쎿썗쏚
  38. async def main(host, port): server = await asyncio.start_server(asgi_http_parser_server, host, port)

    await server.serve_forever() •mainॲཧɾmainίϧʔνϯɿasyncio Ͱcreateserver "4(*FDIP8FC쎿썗쏚
  39. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ async def asgi_http_parser_server(reader, writer): parser = HTTPParser() receive

    = asyncio.Queue() read = 0 while not reader.at_eof(): if parser.part != "BODY": parser.feed_line(await reader.readline()) elif parser.body_length == 0: await receive.put(create_message(b"", False)) break else: body = await reader.read(100) read += len(body) await receive.put(create_message(body, read < parser.body_length)) if read >= parser.body_length: break "4(*FDIP8FC쎿썗쏚
  40. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ async def asgi_http_parser_server(reader, writer): parser = HTTPParser() receive

    = asyncio.Queue() read = 0 while not reader.at_eof(): if parser.part != "BODY": parser.feed_line(await reader.readline()) elif parser.body_length == 0: await receive.put(create_message(b"", False)) break else: body = await reader.read(100) read += len(body) await receive.put(create_message(body, read < parser.body_length)) if read >= parser.body_length: break "4(*FDIP8FC쎿썗쏚
  41. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ async def asgi_http_parser_server(reader, writer): parser = HTTPParser() receive

    = asyncio.Queue() read = 0 while not reader.at_eof(): if parser.part != "BODY": parser.feed_line(await reader.readline()) elif parser.body_length == 0: await receive.put(create_message(b"", False)) break else: body = await reader.read(100) read += len(body) await receive.put(create_message(body, read < parser.body_length)) if read >= parser.body_length: break "4(*FDIP8FC쎿썗쏚
  42. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ # httpͷϦΫΤετͷparser͔ΒscopeΛ࡞Δ scope = create_scope(parser) { "type": "http",

    "http_version": "2.0", "asgi": { "spec_version": "2.1", "version": “3.0", }, "method": "POST", "scheme": "https", "path": "/", "query_string": b"a=b", "headers": [(b"content-length", b"5")], "client": ("10.0.0.0", 1234), "server": ("127.0.0.1", 443), "root_path": ".", } "4(*FDIP8FC쎿썗쏚
  43. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  44. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  45. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message[“type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  46. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message[“type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  47. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message[“type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  48. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  49. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  50. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  51. •asgi_http_parser_server ίϧʔνϯɿΞϓϦέʔγϣϯͱ΍ΓͱΓͷϝΠϯ while True: message = await send.get() if message["type"]

    == "http.response.start": writer.write(b"HTTP/1.1 %d\r\n" % message["status"]) for header in message["headers"]: writer.write(b"%s: %s\r\n" % (header)) writer.write(b"\r\n") elif message["type"] == "http.response.body": if message.get("body") is not None: writer.write(message["body"]) writer.write(b"\r\n") if not message.get("more_body", False): break "4(*FDIP8FC쎿썗쏚
  52. "4(*)551쏫쏍쏅썗쏂 Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'http' } { "type": "http.request", "body": b'', "more_body": False }
  53. "4(*)551쏫쏍쏅썗쏂 Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'http' } { "type": "http.request", "body": b'', "more_body": False } { "type": "http.response.start", "status": 200, "headers": [(b"name", b"value")], }
  54. "4(*)551쏫쏍쏅썗쏂 Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'http' } { "type": "http.request", "body": b'', "more_body": False } { "type": "http.response.start", "status": 200, "headers": [(b"name", b"value")], } { "type": "http.response.body", "body": b"Hello, World", "more_body": False, }
  55. "4(*)551쏫쏍쏅썗쏂 Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'http' } { "type": "http.request", "body": b'', "more_body": False } { "type": "http.response.start", "status": 200, "headers": [(b"name", b"value")], } { "type": "http.response.body", "body": b"Hello, World", "more_body": False, }
  56. "4(*)551쏫쏍쏅썗쏂 Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'http' } { "type": "http.request", "body": b'', "more_body": False } { "type": "http.response.start", "status": 200, "headers": [(b"name", b"value")], } { "type": "http.response.body", "body": b"Hello, World", "more_body": False, } {"type": "http.disconnect"}
  57. "4(*쎂썻썛썽3FBEUIF%PDT ASGI ΞϓϦέʔγϣϯ͸ async / await ޓ׵ͷίϧʔνϯ (͢ͳ Θͪ asyncio

    ޓ׵) ͱ࣮ͯ͠ߦ͞Εͳ͚Ε͹ͳΓ·ͤΜ ɻ https://asgi.readthedocs.io/en/latest/ ASGI applications must run as async / await compatible coroutines (i.e. asyncio-compatible)
  58. "4(*쎂썻썛썽3FBEUIF%PDT ASGI ΞϓϦέʔγϣϯ͸ async / await ޓ׵ͷίϧʔνϯ (͢ͳ Θͪ asyncio

    ޓ׵) ͱ࣮ͯ͠ߦ͞Εͳ͚Ε͹ͳΓ·ͤΜ ɻ https://asgi.readthedocs.io/en/latest/ ASGI applications must run as async / await compatible coroutines (i.e. asyncio-compatible)
  59. "4(*쎂썻썛썽3FBEUIF%PDT •ͦͷ΄͔ͷASGIͷ࢓༷ •ASGI sub-specification - Life Span •ASGI Extention -

    HTTP/2 Server Push •ASGI Extention - Websocket Denial Response https://asgi.readthedocs.io/en/latest/
  60. "4(*8FC4PDLFU Server Application { 'asgi': {'spec_version': ‘2.1', 'version': '3.0'}, ...

    'method': ‘GET', 'type': 'websocket' } { “type": "websocket.connect" } { “type": “websocket.accept" } { “type": “websocket.close” “code": 1000 } { “type": “websocket.send” “text": ”Hello, World” } { “type": “websocket.disconnect" } IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTXXXIUNMXFCTPDLFU
  61. "4(*-JGF4BQBO "scope" : { "type": "lifespan", "asgi": {"spec_version": "2.0", "version":

    "3.0"}, } --- SERVER {"type": "lifespan.startup"} {"type": "lifespan.shutdown"} --- APPLICATION {"type": "lifespan.startup.complete"} {"type": "lifespan.shutdown.complete"}
  62. "4(*-JGF4BQBO async def app(scope, receive, send): if scope['type'] == 'lifespan':

    while True: message = await receive() if message['type'] == 'lifespan.startup': ... # Do some startup here! await send({'type': 'lifespan.startup.complete'}) elif message['type'] == 'lifespan.shutdown': ... # Do some shutdown here! await send({'type': 'lifespan.shutdown.complete'}) return else: pass # Handle other types IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTMJGFTQBOIUNM
  63. "4(*TVCTQFDJpDBUJPO-JGF4BQBO async def app(scope, receive, send): if scope['type'] == 'lifespan':

    while True: message = await receive() if message['type'] == 'lifespan.startup': ... # Do some startup here! await send({'type': 'lifespan.startup.complete'}) elif message['type'] == 'lifespan.shutdown': ... # Do some shutdown here! await send({'type': 'lifespan.shutdown.complete'}) return else: pass # Handle other types IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUTQFDTMJGFTQBOIUNM
  64. "4(*&YUFOTJPOT)5514FSWFS1VTI "scope": { ..., "extensions": {"http.response.push": {}}, } --- {

    "type": "http.response.push", "path": "/path", "headers": [(b"name", b"value")], } Server Application IUUQTBTHJSFBEUIFEPDTJPFOMBUFTUFYUFOTJPOTIUNMIUUQTFSWFSQVTI
  65. "4(*'SBNFXPSLT$PNQBSFE  Framework HTTP WebSocket Server Push WebSocket HTTP response

    Quart ✓ ✓ ✓ ✓ FastAPI ✓ ✓ ❌ ❌ Responder ✓ ✓ ❌ ❌ Starlette ✓ ✓ ❌ ❌ Django ✓ ✓ ❌ ❌
  66. "4(*4FSWFST  $PNQBSFE  Framework HTTP WebSockets Server Push WebSocket

    HTTP response Hypercorn 1 ✓ 2 ✓ 1 ✓ 2 ✓ ✓ ✓ Uvicorn 1 ✓ 2 ❌ 1 ✓ 2 ❌ ❌ ❌ Daphne 1 ✓ 2 ✓ 1 ✓ 2 ❌ ❌ ❌ Mangum 1 ✓ 2 ❌ 1✓ 2 ❌ ❌ ❌
  67. "4(*1&1쎏쎅ಓ [email protected] ϝʔϦϯάϦετ 2020೥1݄28೔ Subject:Is the deployment standard for WSGI

    ready? *EPOUUIJOLUIJTTPSUPGUIJOHTIPVMECFB1&1 -PPLBU"4(*BOEIPXUIBUJTOUB1&1BOETUJMMNBOBHFTUPFYJTU 4JODFUIJTJTO`UBMBOHVBHFSFMBUFEPSQBDLBHFSFMBUFEUIJOH*EPOUUIJOLJUSFBMMZOFFETUPHPUISPVHIUIF1&1 QSPDFTT
  68. "4(*1&1쎏쎅ಓ [email protected] ϝʔϦϯάϦετ 2020೥1݄28೔ Subject:Is the deployment standard for WSGI

    ready? *EPOUUIJOLUIJTTPSUPGUIJOHTIPVMECFB1&1 -PPLBU"4(*BOEIPXUIBUJTOUB1&1BOETUJMMNBOBHFTUPFYJTU 4JODFUIJTJTO`UBMBOHVBHFSFMBUFEPSQBDLBHFSFMBUFEUIJOH*EPOUUIJOLJUSFBMMZOFFETUPHPUISPVHIUIF1&1 QSPDFTT 4JODF84(*BOE QSPCBCMZTPPO "4(*BSFWFSZDPNNPO"1*TUBOEBSETJO1ZUIPOMBOE UIFZEFTFSWFUP HPJOUPUIF1&1JOEFYBTXFMM *.0
  69. "4(*1&1쎏쎅ಓ It's worth noting that I was discouraged from making

    ASGI a PEP by several Python core developers, which is why I have not been pursuing that process any further. I'm not sure I share this view, so I may come back to it in the future, but there's a reason it's not in the process right now. ಛච͢΂͖͸ɺࢲ͕ASGIΛPEPʹ͢Δ͜ͱΛԿਓ͔ͷPythonίΞ։ൃऀ͔Βམ୾͞Εͯ ͍ͨ͜ͱͰɺͦΕҎ্ͦͷϓϩηεΛ௥ٻͯ͜͠ͳ͔ͬͨ͜ͱͰ͢ɻ ࢲ͸͜ͷݟղʹڞײ͍ͯ͠ΔΘ͚Ͱ͸ͳ͍ͷͰɺকདྷతʹ͸໭ͬͯ͘Δ͔΋͠Ε·ͤΜ ͕ɺࠓ͸ͦͷϓϩηεʹೖ͍ͬͯͳ͍ͷʹ͸ཧ༝͕͋Γ·͢ɻ [email protected] ϝʔϦϯάϦετ 2020೥1݄28೔
  70. ࢀߟࢿྉ •An ASGI Server from scratch / Philip Jones IUUQTXXXZPVUVCFDPNXBUDI

    W(QRY6*8KJX •An introduction to ASGI, Asynchronous Server Gateway Interface / Philip Jones IUUQTXXXZPVUVCFDPNXBUDI WUH$,2R986 •Hello, ASGI IUUQTXXXFODPEFJPBSUJDMFTIFMMPBTHJ •Introduction to ASGI: Emergence of an Async Python Web Ecosystem IUUQTqPSJNPOEEFWCMPHBSUJDMFTJOUSPEVDUJPOUPBTHJBTZODQZUIPOXFC