| 43 | |
| 44 | |
| 45 | class ASGIHTTPConnection: |
| 46 | def __init__(self, app: Quart, scope: HTTPScope) -> None: |
| 47 | self.app = app |
| 48 | self.scope = scope |
| 49 | |
| 50 | async def __call__( |
| 51 | self, receive: ASGIReceiveCallable, send: ASGISendCallable |
| 52 | ) -> None: |
| 53 | request = self._create_request_from_scope(send) |
| 54 | receiver_task = asyncio.ensure_future(self.handle_messages(request, receive)) |
| 55 | handler_task = asyncio.ensure_future(self.handle_request(request, send)) |
| 56 | done, pending = await asyncio.wait( |
| 57 | [handler_task, receiver_task], return_when=asyncio.FIRST_COMPLETED |
| 58 | ) |
| 59 | await cancel_tasks(pending) |
| 60 | raise_task_exceptions(done) |
| 61 | |
| 62 | async def handle_messages( |
| 63 | self, request: Request, receive: ASGIReceiveCallable |
| 64 | ) -> None: |
| 65 | while True: |
| 66 | message = await receive() |
| 67 | if message["type"] == "http.request": |
| 68 | request.body.append(message.get("body", b"")) |
| 69 | if not message.get("more_body", False): |
| 70 | request.body.set_complete() |
| 71 | elif message["type"] == "http.disconnect": |
| 72 | return |
| 73 | |
| 74 | def _create_request_from_scope(self, send: ASGISendCallable) -> Request: |
| 75 | headers = Headers() |
| 76 | headers["Remote-Addr"] = (self.scope.get("client") or ["<local>"])[0] |
| 77 | for name, value in self.scope["headers"]: |
| 78 | headers.add(name.decode("latin1").title(), value.decode("latin1")) |
| 79 | if self.scope["http_version"] < "1.1": |
| 80 | headers.setdefault("Host", self.app.config["SERVER_NAME"] or "") |
| 81 | |
| 82 | path = self.scope["path"] |
| 83 | path = path if path[0] == "/" else urlparse(path).path |
| 84 | root_path = self.scope.get("root_path", "") |
| 85 | if root_path != "": |
| 86 | try: |
| 87 | path = path.split(root_path, 1)[1] |
| 88 | path = " " if path == "" else path |
| 89 | except IndexError: |
| 90 | path = " " # Invalid in paths, hence will result in 404 |
| 91 | |
| 92 | return self.app.request_class( |
| 93 | self.scope["method"], |
| 94 | self.scope["scheme"], |
| 95 | path, |
| 96 | self.scope["query_string"], |
| 97 | headers, |
| 98 | self.scope.get("root_path", ""), |
| 99 | self.scope["http_version"], |
| 100 | max_content_length=self.app.config["MAX_CONTENT_LENGTH"], |
| 101 | body_timeout=self.app.config["BODY_TIMEOUT"], |
| 102 | send_push_promise=partial(self._send_push_promise, send), |
no outgoing calls
searching dependent graphs…