We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support `shutdown` as a synonym for `cleanup`
* Support either `cleanup` or `shutdown` as the ASGI lifespan message name.
* Update uvicorn to move to shutdown - https://github.com/encode/uvicorn/issues/233
* Finally, after a small period of time, drop `cleanup`

Easy PR for a contributor to jump on would be addressing the first part of this, and supporting either name.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `starlette/lifespan.py`
Content:
```
1 import asyncio
2 import logging
3 import traceback
4 import typing
5 from types import TracebackType
6 from starlette.types import ASGIApp, ASGIInstance, Receive, Message, Send
7 
8 
9 STATE_TRANSITION_ERROR = "Got invalid state transition on lifespan protocol."
10 
11 
12 class LifespanHandler:
13     def __init__(self) -> None:
14         self.startup_handlers = []  # type: typing.List[typing.Callable]
15         self.cleanup_handlers = []  # type: typing.List[typing.Callable]
16 
17     def on_event(self, event_type: str) -> typing.Callable:
18         def decorator(func: typing.Callable) -> typing.Callable:
19             self.add_event_handler(event_type, func)
20             return func
21 
22         return decorator
23 
24     def add_event_handler(self, event_type: str, func: typing.Callable) -> None:
25         assert event_type in ("startup", "cleanup")
26 
27         if event_type == "startup":
28             self.startup_handlers.append(func)
29         else:
30             self.cleanup_handlers.append(func)
31 
32     async def run_startup(self) -> None:
33         for handler in self.startup_handlers:
34             if asyncio.iscoroutinefunction(handler):
35                 await handler()
36             else:
37                 handler()
38 
39     async def run_cleanup(self) -> None:
40         for handler in self.cleanup_handlers:
41             if asyncio.iscoroutinefunction(handler):
42                 await handler()
43             else:
44                 handler()
45 
46     def __call__(self, scope: Message) -> ASGIInstance:
47         assert scope["type"] == "lifespan"
48         return self.run_lifespan
49 
50     async def run_lifespan(self, receive: Receive, send: Send) -> None:
51         message = await receive()
52         assert message["type"] == "lifespan.startup"
53         await self.run_startup()
54         await send({"type": "lifespan.startup.complete"})
55         message = await receive()
56         assert message["type"] == "lifespan.cleanup"
57         await self.run_cleanup()
58         await send({"type": "lifespan.cleanup.complete"})
59 
60 
61 class LifespanContext:
62     def __init__(
63         self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10
64     ) -> None:
65         self.startup_timeout = startup_timeout
66         self.cleanup_timeout = cleanup_timeout
67         self.startup_event = asyncio.Event()
68         self.cleanup_event = asyncio.Event()
69         self.receive_queue = asyncio.Queue()  # type: asyncio.Queue
70         self.asgi = app({"type": "lifespan"})  # type: ASGIInstance
71 
72     def __enter__(self) -> None:
73         loop = asyncio.get_event_loop()
74         loop.create_task(self.run_lifespan())
75         loop.run_until_complete(self.wait_startup())
76 
77     def __exit__(
78         self,
79         exc_type: typing.Type[BaseException],
80         exc: BaseException,
81         tb: TracebackType,
82     ) -> None:
83         loop = asyncio.get_event_loop()
84         loop.run_until_complete(self.wait_cleanup())
85 
86     async def run_lifespan(self) -> None:
87         try:
88             await self.asgi(self.receive, self.send)
89         finally:
90             self.startup_event.set()
91             self.cleanup_event.set()
92 
93     async def send(self, message: Message) -> None:
94         if message["type"] == "lifespan.startup.complete":
95             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
96             assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
97             self.startup_event.set()
98         else:
99             assert message["type"] == "lifespan.cleanup.complete"
100             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
101             assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
102             self.cleanup_event.set()
103 
104     async def receive(self) -> Message:
105         return await self.receive_queue.get()
106 
107     async def wait_startup(self) -> None:
108         await self.receive_queue.put({"type": "lifespan.startup"})
109         await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)
110 
111     async def wait_cleanup(self) -> None:
112         await self.receive_queue.put({"type": "lifespan.cleanup"})
113         await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)
114
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/starlette/lifespan.py b/starlette/lifespan.py
--- a/starlette/lifespan.py
+++ b/starlette/lifespan.py
@@ -22,7 +22,7 @@
         return decorator
 
     def add_event_handler(self, event_type: str, func: typing.Callable) -> None:
-        assert event_type in ("startup", "cleanup")
+        assert event_type in ("startup", "shutdown", "cleanup")
 
         if event_type == "startup":
             self.startup_handlers.append(func)
@@ -53,19 +53,26 @@
         await self.run_startup()
         await send({"type": "lifespan.startup.complete"})
         message = await receive()
-        assert message["type"] == "lifespan.cleanup"
+        assert (
+            message["type"] == "lifespan.shutdown"
+            or message["type"] == "lifespan.cleanup"
+        )
         await self.run_cleanup()
-        await send({"type": "lifespan.cleanup.complete"})
+        if message["type"] == "lifespan.shutdown":
+            await send({"type": "lifespan.shutdown.complete"})
+
+        if message["type"] == "lifespan.cleanup":
+            await send({"type": "lifespan.cleanup.complete"})  # pragma: no cover
 
 
 class LifespanContext:
     def __init__(
-        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10
+        self, app: ASGIApp, startup_timeout: int = 10, shutdown_timeout: int = 10
     ) -> None:
         self.startup_timeout = startup_timeout
-        self.cleanup_timeout = cleanup_timeout
+        self.shutdown_timeout = shutdown_timeout
         self.startup_event = asyncio.Event()
-        self.cleanup_event = asyncio.Event()
+        self.shutdown_event = asyncio.Event()
         self.receive_queue = asyncio.Queue()  # type: asyncio.Queue
         self.asgi = app({"type": "lifespan"})  # type: ASGIInstance
 
@@ -81,25 +88,25 @@
         tb: TracebackType,
     ) -> None:
         loop = asyncio.get_event_loop()
-        loop.run_until_complete(self.wait_cleanup())
+        loop.run_until_complete(self.wait_shutdown())
 
     async def run_lifespan(self) -> None:
         try:
             await self.asgi(self.receive, self.send)
         finally:
             self.startup_event.set()
-            self.cleanup_event.set()
+            self.shutdown_event.set()
 
     async def send(self, message: Message) -> None:
         if message["type"] == "lifespan.startup.complete":
             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR
-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
             self.startup_event.set()
         else:
-            assert message["type"] == "lifespan.cleanup.complete"
+            assert message["type"] == "lifespan.shutdown.complete"
             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR
-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR
-            self.cleanup_event.set()
+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR
+            self.shutdown_event.set()
 
     async def receive(self) -> Message:
         return await self.receive_queue.get()
@@ -108,6 +115,8 @@
         await self.receive_queue.put({"type": "lifespan.startup"})
         await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)
 
-    async def wait_cleanup(self) -> None:
-        await self.receive_queue.put({"type": "lifespan.cleanup"})
-        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)
+    async def wait_shutdown(self) -> None:
+        await self.receive_queue.put({"type": "lifespan.shutdown"})
+        await asyncio.wait_for(
+            self.shutdown_event.wait(), timeout=self.shutdown_timeout
+        )

{"golden_diff": "diff --git a/starlette/lifespan.py b/starlette/lifespan.py\n--- a/starlette/lifespan.py\n+++ b/starlette/lifespan.py\n@@ -22,7 +22,7 @@\n         return decorator\n \n     def add_event_handler(self, event_type: str, func: typing.Callable) -> None:\n-        assert event_type in (\"startup\", \"cleanup\")\n+        assert event_type in (\"startup\", \"shutdown\", \"cleanup\")\n \n         if event_type == \"startup\":\n             self.startup_handlers.append(func)\n@@ -53,19 +53,26 @@\n         await self.run_startup()\n         await send({\"type\": \"lifespan.startup.complete\"})\n         message = await receive()\n-        assert message[\"type\"] == \"lifespan.cleanup\"\n+        assert (\n+            message[\"type\"] == \"lifespan.shutdown\"\n+            or message[\"type\"] == \"lifespan.cleanup\"\n+        )\n         await self.run_cleanup()\n-        await send({\"type\": \"lifespan.cleanup.complete\"})\n+        if message[\"type\"] == \"lifespan.shutdown\":\n+            await send({\"type\": \"lifespan.shutdown.complete\"})\n+\n+        if message[\"type\"] == \"lifespan.cleanup\":\n+            await send({\"type\": \"lifespan.cleanup.complete\"})  # pragma: no cover\n \n \n class LifespanContext:\n     def __init__(\n-        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10\n+        self, app: ASGIApp, startup_timeout: int = 10, shutdown_timeout: int = 10\n     ) -> None:\n         self.startup_timeout = startup_timeout\n-        self.cleanup_timeout = cleanup_timeout\n+        self.shutdown_timeout = shutdown_timeout\n         self.startup_event = asyncio.Event()\n-        self.cleanup_event = asyncio.Event()\n+        self.shutdown_event = asyncio.Event()\n         self.receive_queue = asyncio.Queue()  # type: asyncio.Queue\n         self.asgi = app({\"type\": \"lifespan\"})  # type: ASGIInstance\n \n@@ -81,25 +88,25 @@\n         tb: TracebackType,\n     ) -> None:\n         loop = asyncio.get_event_loop()\n-        loop.run_until_complete(self.wait_cleanup())\n+        loop.run_until_complete(self.wait_shutdown())\n \n     async def run_lifespan(self) -> None:\n         try:\n             await self.asgi(self.receive, self.send)\n         finally:\n             self.startup_event.set()\n-            self.cleanup_event.set()\n+            self.shutdown_event.set()\n \n     async def send(self, message: Message) -> None:\n         if message[\"type\"] == \"lifespan.startup.complete\":\n             assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n             self.startup_event.set()\n         else:\n-            assert message[\"type\"] == \"lifespan.cleanup.complete\"\n+            assert message[\"type\"] == \"lifespan.shutdown.complete\"\n             assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n-            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n-            self.cleanup_event.set()\n+            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n+            self.shutdown_event.set()\n \n     async def receive(self) -> Message:\n         return await self.receive_queue.get()\n@@ -108,6 +115,8 @@\n         await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n         await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)\n \n-    async def wait_cleanup(self) -> None:\n-        await self.receive_queue.put({\"type\": \"lifespan.cleanup\"})\n-        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)\n+    async def wait_shutdown(self) -> None:\n+        await self.receive_queue.put({\"type\": \"lifespan.shutdown\"})\n+        await asyncio.wait_for(\n+            self.shutdown_event.wait(), timeout=self.shutdown_timeout\n+        )\n", "issue": "Support `shutdown` as a synonym for `cleanup`\n* Support either `cleanup` or `shutdown` as the ASGI lifespan message name.\r\n* Update uvicorn to move to shutdown - https://github.com/encode/uvicorn/issues/233\r\n* Finally, after a small period of time, drop `cleanup`\r\n\r\nEasy PR for a contributor to jump on would be addressing the first part of this, and supporting either name.\n", "before_files": [{"content": "import asyncio\nimport logging\nimport traceback\nimport typing\nfrom types import TracebackType\nfrom starlette.types import ASGIApp, ASGIInstance, Receive, Message, Send\n\n\nSTATE_TRANSITION_ERROR = \"Got invalid state transition on lifespan protocol.\"\n\n\nclass LifespanHandler:\n    def __init__(self) -> None:\n        self.startup_handlers = []  # type: typing.List[typing.Callable]\n        self.cleanup_handlers = []  # type: typing.List[typing.Callable]\n\n    def on_event(self, event_type: str) -> typing.Callable:\n        def decorator(func: typing.Callable) -> typing.Callable:\n            self.add_event_handler(event_type, func)\n            return func\n\n        return decorator\n\n    def add_event_handler(self, event_type: str, func: typing.Callable) -> None:\n        assert event_type in (\"startup\", \"cleanup\")\n\n        if event_type == \"startup\":\n            self.startup_handlers.append(func)\n        else:\n            self.cleanup_handlers.append(func)\n\n    async def run_startup(self) -> None:\n        for handler in self.startup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    async def run_cleanup(self) -> None:\n        for handler in self.cleanup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    def __call__(self, scope: Message) -> ASGIInstance:\n        assert scope[\"type\"] == \"lifespan\"\n        return self.run_lifespan\n\n    async def run_lifespan(self, receive: Receive, send: Send) -> None:\n        message = await receive()\n        assert message[\"type\"] == \"lifespan.startup\"\n        await self.run_startup()\n        await send({\"type\": \"lifespan.startup.complete\"})\n        message = await receive()\n        assert message[\"type\"] == \"lifespan.cleanup\"\n        await self.run_cleanup()\n        await send({\"type\": \"lifespan.cleanup.complete\"})\n\n\nclass LifespanContext:\n    def __init__(\n        self, app: ASGIApp, startup_timeout: int = 10, cleanup_timeout: int = 10\n    ) -> None:\n        self.startup_timeout = startup_timeout\n        self.cleanup_timeout = cleanup_timeout\n        self.startup_event = asyncio.Event()\n        self.cleanup_event = asyncio.Event()\n        self.receive_queue = asyncio.Queue()  # type: asyncio.Queue\n        self.asgi = app({\"type\": \"lifespan\"})  # type: ASGIInstance\n\n    def __enter__(self) -> None:\n        loop = asyncio.get_event_loop()\n        loop.create_task(self.run_lifespan())\n        loop.run_until_complete(self.wait_startup())\n\n    def __exit__(\n        self,\n        exc_type: typing.Type[BaseException],\n        exc: BaseException,\n        tb: TracebackType,\n    ) -> None:\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(self.wait_cleanup())\n\n    async def run_lifespan(self) -> None:\n        try:\n            await self.asgi(self.receive, self.send)\n        finally:\n            self.startup_event.set()\n            self.cleanup_event.set()\n\n    async def send(self, message: Message) -> None:\n        if message[\"type\"] == \"lifespan.startup.complete\":\n            assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n            self.startup_event.set()\n        else:\n            assert message[\"type\"] == \"lifespan.cleanup.complete\"\n            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.cleanup_event.is_set(), STATE_TRANSITION_ERROR\n            self.cleanup_event.set()\n\n    async def receive(self) -> Message:\n        return await self.receive_queue.get()\n\n    async def wait_startup(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n        await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)\n\n    async def wait_cleanup(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.cleanup\"})\n        await asyncio.wait_for(self.cleanup_event.wait(), timeout=self.cleanup_timeout)\n", "path": "starlette/lifespan.py"}], "after_files": [{"content": "import asyncio\nimport logging\nimport traceback\nimport typing\nfrom types import TracebackType\nfrom starlette.types import ASGIApp, ASGIInstance, Receive, Message, Send\n\n\nSTATE_TRANSITION_ERROR = \"Got invalid state transition on lifespan protocol.\"\n\n\nclass LifespanHandler:\n    def __init__(self) -> None:\n        self.startup_handlers = []  # type: typing.List[typing.Callable]\n        self.cleanup_handlers = []  # type: typing.List[typing.Callable]\n\n    def on_event(self, event_type: str) -> typing.Callable:\n        def decorator(func: typing.Callable) -> typing.Callable:\n            self.add_event_handler(event_type, func)\n            return func\n\n        return decorator\n\n    def add_event_handler(self, event_type: str, func: typing.Callable) -> None:\n        assert event_type in (\"startup\", \"shutdown\", \"cleanup\")\n\n        if event_type == \"startup\":\n            self.startup_handlers.append(func)\n        else:\n            self.cleanup_handlers.append(func)\n\n    async def run_startup(self) -> None:\n        for handler in self.startup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    async def run_cleanup(self) -> None:\n        for handler in self.cleanup_handlers:\n            if asyncio.iscoroutinefunction(handler):\n                await handler()\n            else:\n                handler()\n\n    def __call__(self, scope: Message) -> ASGIInstance:\n        assert scope[\"type\"] == \"lifespan\"\n        return self.run_lifespan\n\n    async def run_lifespan(self, receive: Receive, send: Send) -> None:\n        message = await receive()\n        assert message[\"type\"] == \"lifespan.startup\"\n        await self.run_startup()\n        await send({\"type\": \"lifespan.startup.complete\"})\n        message = await receive()\n        assert (\n            message[\"type\"] == \"lifespan.shutdown\"\n            or message[\"type\"] == \"lifespan.cleanup\"\n        )\n        await self.run_cleanup()\n        if message[\"type\"] == \"lifespan.shutdown\":\n            await send({\"type\": \"lifespan.shutdown.complete\"})\n\n        if message[\"type\"] == \"lifespan.cleanup\":\n            await send({\"type\": \"lifespan.cleanup.complete\"})  # pragma: no cover\n\n\nclass LifespanContext:\n    def __init__(\n        self, app: ASGIApp, startup_timeout: int = 10, shutdown_timeout: int = 10\n    ) -> None:\n        self.startup_timeout = startup_timeout\n        self.shutdown_timeout = shutdown_timeout\n        self.startup_event = asyncio.Event()\n        self.shutdown_event = asyncio.Event()\n        self.receive_queue = asyncio.Queue()  # type: asyncio.Queue\n        self.asgi = app({\"type\": \"lifespan\"})  # type: ASGIInstance\n\n    def __enter__(self) -> None:\n        loop = asyncio.get_event_loop()\n        loop.create_task(self.run_lifespan())\n        loop.run_until_complete(self.wait_startup())\n\n    def __exit__(\n        self,\n        exc_type: typing.Type[BaseException],\n        exc: BaseException,\n        tb: TracebackType,\n    ) -> None:\n        loop = asyncio.get_event_loop()\n        loop.run_until_complete(self.wait_shutdown())\n\n    async def run_lifespan(self) -> None:\n        try:\n            await self.asgi(self.receive, self.send)\n        finally:\n            self.startup_event.set()\n            self.shutdown_event.set()\n\n    async def send(self, message: Message) -> None:\n        if message[\"type\"] == \"lifespan.startup.complete\":\n            assert not self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n            self.startup_event.set()\n        else:\n            assert message[\"type\"] == \"lifespan.shutdown.complete\"\n            assert self.startup_event.is_set(), STATE_TRANSITION_ERROR\n            assert not self.shutdown_event.is_set(), STATE_TRANSITION_ERROR\n            self.shutdown_event.set()\n\n    async def receive(self) -> Message:\n        return await self.receive_queue.get()\n\n    async def wait_startup(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.startup\"})\n        await asyncio.wait_for(self.startup_event.wait(), timeout=self.startup_timeout)\n\n    async def wait_shutdown(self) -> None:\n        await self.receive_queue.put({\"type\": \"lifespan.shutdown\"})\n        await asyncio.wait_for(\n            self.shutdown_event.wait(), timeout=self.shutdown_timeout\n        )\n", "path": "starlette/lifespan.py"}]}