Repository: sanic-org/sanic

Issue:
Sanic 22.9.1 entered into the request middlewire twice when response timeout
### Is there an existing issue for this?

- [X] I have searched the existing issues

### Describe the bug

I'm trying to do perforamance test for our apps about new Sanic version v22.9.1 (old version is 22.6.0)
During performance testing ,my handler code is slow as expected, and my response time for sanic is  60 seconds

Normally, it will return a "response timeout error" to the client, and we test the 22.6.0, and everything works as designe.
But after upgrade to 22.9.1(The only change for the code), we see a new err which should be the failure inside one of our blueprint request middlewire. Normally the code will only run once. but in the new version, we see the function was called twice, and the interval between the two calls is longger than response timeout setting. 

It's very easy to repeat the situation. seems like sanic change involve this.





### Code snippet

```import asyncio
from sanic import Blueprint, Sanic
from sanic.request import Request
from sanic.response import HTTPResponse
from sanic.response import text


class AsyncNorthHandler():
    FUNCCOUNT = 0

async def http_handler(request):
    print("handling the request")
    await asyncio.sleep(100)
    return text('ok')

bp = Blueprint("async_north")
bp.add_route(http_handler, '/', methods=['GET'], name='handler')

@bp.middleware("request")
async def set_req_header(request):
    request: Request        
    AsyncNorthHandler.FUNCCOUNT = AsyncNorthHandler.FUNCCOUNT + 1
    print("enter the function {} time".format(AsyncNorthHandler.FUNCCOUNT))


app = Sanic("TEST")
app.blueprint(bp)
app.run(host='0.0.0.0', port=8888)
```

### Expected Behavior

entery the reqeust middlewire 1 time every request

### How do you run Sanic?

As a module

### Operating System

linux

### Sanic Version

22.9.1

### Additional context

_No response_


Discussion:
Confirmed this. I think the fix should be simple and is related specifically to the response timeout.

```py
            request_middleware = not isinstance(exception, ServiceUnavailable)
            try:
                await app.handle_exception(
                    self.request, exception, request_middleware
                )
```
https://github.com/sanic-org/sanic/blob/main/sanic/http/http1.py#L431

Will need to look closer at the logic to make sure there are not other use cases being impacted.

Resolving patch:
diff --git a/sanic/http/http1.py b/sanic/http/http1.py
--- a/sanic/http/http1.py
+++ b/sanic/http/http1.py
@@ -16,6 +16,7 @@
     PayloadTooLarge,
     RequestCancelled,
     ServerError,
+    ServiceUnavailable,
 )
 from sanic.headers import format_http1_response
 from sanic.helpers import has_message_body
@@ -428,8 +429,11 @@ async def error_response(self, exception: Exception) -> None:
             if self.request is None:
                 self.create_empty_request()
 
+            request_middleware = not isinstance(exception, ServiceUnavailable)
             try:
-                await app.handle_exception(self.request, exception)
+                await app.handle_exception(
+                    self.request, exception, request_middleware
+                )
             except Exception as e:
                 await app.handle_exception(self.request, e, False)
 
diff --git a/sanic/request.py b/sanic/request.py
--- a/sanic/request.py
+++ b/sanic/request.py
@@ -104,6 +104,7 @@ class Request:
         "_protocol",
         "_remote_addr",
         "_request_middleware_started",
+        "_response_middleware_started",
         "_scheme",
         "_socket",
         "_stream_id",
@@ -179,6 +180,7 @@ def __init__(
             Tuple[bool, bool, str, str], List[Tuple[str, str]]
         ] = defaultdict(list)
         self._request_middleware_started = False
+        self._response_middleware_started = False
         self.responded: bool = False
         self.route: Optional[Route] = None
         self.stream: Optional[Stream] = None
@@ -337,7 +339,8 @@ async def add_header(_, response: HTTPResponse):
             middleware = (
                 self.route and self.route.extra.response_middleware
             ) or self.app.response_middleware
-            if middleware:
+            if middleware and not self._response_middleware_started:
+                self._response_middleware_started = True
                 response = await self.app._run_response_middleware(
                     self, response, middleware
                 )


Test patch:
diff --git a/tests/test_middleware.py b/tests/test_middleware.py
--- a/tests/test_middleware.py
+++ b/tests/test_middleware.py
@@ -1,6 +1,6 @@
 import logging
 
-from asyncio import CancelledError
+from asyncio import CancelledError, sleep
 from itertools import count
 
 from sanic.exceptions import NotFound
@@ -318,6 +318,32 @@ async def handler(request):
         resp1 = await request.respond()
         return resp1
 
-    _, response = app.test_client.get("/")
+    app.test_client.get("/")
     assert response_middleware_run_count == 1
     assert request_middleware_run_count == 1
+
+
+def test_middleware_run_on_timeout(app):
+    app.config.RESPONSE_TIMEOUT = 0.1
+    response_middleware_run_count = 0
+    request_middleware_run_count = 0
+
+    @app.on_response
+    def response(_, response):
+        nonlocal response_middleware_run_count
+        response_middleware_run_count += 1
+
+    @app.on_request
+    def request(_):
+        nonlocal request_middleware_run_count
+        request_middleware_run_count += 1
+
+    @app.get("/")
+    async def handler(request):
+        resp1 = await request.respond()
+        await sleep(1)
+        return resp1
+
+    app.test_client.get("/")
+    assert request_middleware_run_count == 1
+    assert response_middleware_run_count == 1