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:

Patch:
diff --git a/sanic/http/http1.py b/sanic/http/http1.py
index ccfae75d..0cf2fd11 100644
--- a/sanic/http/http1.py
+++ b/sanic/http/http1.py
@@ -16,6 +16,7 @@ from sanic.exceptions import (
     PayloadTooLarge,
     RequestCancelled,
     ServerError,
+    ServiceUnavailable,
 )
 from sanic.headers import format_http1_response
 from sanic.helpers import has_message_body
@@ -428,8 +429,11 @@ class Http(Stream, metaclass=TouchUpMeta):
             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
index f7b3b999..8b8d9530 100644
--- 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 @@ class Request:
             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 @@ class Request:
             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
index a2034450..6589f4a4 100644
--- 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 @@ def test_middleware_return_response(app):
         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