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

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

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

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

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

Avatar for Junya Fukuda

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