Repository: sanic-org/sanic

Issue:
Access log writing during request timeout causes exception
**Describe the bug**
On request timeout, the attempt from  Server write_error method to log to access log fails with the following error

```
Exception in callback <bound method WebSocketProtocol.request_timeout_callback of <sanic.websocket.WebSocketProtocol object at 0x7fe2485d1b40>> handle: <TimerHandle WebSocketProtocol.request_timeout_callback>

,stack_trace: Traceback (most recent call last):

File "uvloop/cbhandles.pyx", line 251, in uvloop.loop.TimerHandle._run
File "/venv/lib/python3.6/site-packages/sanic/websocket.py", line 31, in request_timeout_callback
  super().request_timeout_callback()
File "/venv/lib/python3.6/site-packages/sanic/server.py", line 197, in request_timeout_callback
  self.write_error(RequestTimeout("Request Timeout"))
File "/venv/lib/python3.6/site-packages/sanic/server.py", line 488, in write_error
  self.log_response(response)
File "/venv/lib/python3.6/site-packages/sanic/server.py", line 357, in log_response
  self.request.method, self.request.url
File "/venv/lib/python3.6/site-packages/sanic/request.py", line 301, in url
  (self.scheme, self.host, self.path, None, self.query_string, None)
File "/venv/lib/python3.6/site-packages/sanic/request.py", line 260, in scheme
  self.app.websocket_enabled
AttributeError: ‘NoneType’ object has no attribute ‘websocket_enabled’
```


**Code snippet**
I think this is a generic problem.. 

**Expected behavior**
A request timeout and a closing of the client connection.. 

**Environment (please complete the following information):**
 - OS: alpine 3.6
 - Version: 18.12.0 


**Additional context**
the request timeout was a very bad one, as in, the connection_made cb on the protocol was triggered but the handle_request was never triggered ( this is a guess ),  the eventloop was stuck for a different reason probably,  i am still debugging.




Discussion:
Yeah, it's basically because `handle_request ` has not been triggered, and it didn't reach here https://github.com/huge-success/sanic/blob/master/sanic/app.py#L884

And not sure why we need this check https://github.com/huge-success/sanic/blob/master/sanic/request.py#L378

Resolving patch:
diff --git a/sanic/app.py b/sanic/app.py
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -880,8 +880,6 @@ async def handle_request(self, request, write_callback, stream_callback):
             # -------------------------------------------- #
             # Request Middleware
             # -------------------------------------------- #
-
-            request.app = self
             response = await self._run_request_middleware(request)
             # No middleware results
             if not response:
@@ -1287,6 +1285,7 @@ def _helper(
             "port": port,
             "sock": sock,
             "ssl": ssl,
+            "app": self,
             "signal": Signal(),
             "debug": debug,
             "request_handler": self.handle_request,
diff --git a/sanic/request.py b/sanic/request.py
--- a/sanic/request.py
+++ b/sanic/request.py
@@ -95,11 +95,11 @@ class Request(dict):
         "version",
     )
 
-    def __init__(self, url_bytes, headers, version, method, transport):
+    def __init__(self, url_bytes, headers, version, method, transport, app):
         self.raw_url = url_bytes
         # TODO: Content-Encoding detection
         self._parsed_url = parse_url(url_bytes)
-        self.app = None
+        self.app = app
 
         self.headers = headers
         self.version = version
diff --git a/sanic/server.py b/sanic/server.py
--- a/sanic/server.py
+++ b/sanic/server.py
@@ -44,6 +44,8 @@ class HttpProtocol(asyncio.Protocol):
     """
 
     __slots__ = (
+        # app
+        "app",
         # event loop, connection
         "loop",
         "transport",
@@ -88,6 +90,7 @@ def __init__(
         self,
         *,
         loop,
+        app,
         request_handler,
         error_handler,
         signal=Signal(),
@@ -107,6 +110,7 @@ def __init__(
         **kwargs
     ):
         self.loop = loop
+        self.app = app
         self.transport = None
         self.request = None
         self.parser = None
@@ -303,6 +307,7 @@ def on_headers_complete(self):
             version=self.parser.get_http_version(),
             method=self.parser.get_method().decode(),
             transport=self.transport,
+            app=self.app,
         )
         # Remove any existing KeepAlive handler here,
         # It will be recreated if required on the new request.
@@ -607,6 +612,7 @@ def trigger_events(events, loop):
 def serve(
     host,
     port,
+    app,
     request_handler,
     error_handler,
     before_start=None,
@@ -704,6 +710,7 @@ def serve(
         loop=loop,
         connections=connections,
         signal=signal,
+        app=app,
         request_handler=request_handler,
         error_handler=error_handler,
         request_timeout=request_timeout,


Test patch: