Repository: sanic-org/sanic

Issue:
"Named Response Middleware" executed in wrong order
**Describe the bug**
PR https://github.com/huge-success/sanic/pull/1690 Introduced "named response middlware" that is, middleware which is only executed in a given request context. For example a blueprint middleware is only executed on a route which is defined in _that_ blueprint.
There was a copy+paste error in the `register_named_middleware` function, here: https://github.com/huge-success/sanic/blob/e7001b00747b659f7042b0534802b936ee8a53e0/sanic/app.py#L656
When registering a "response" middleware, they are supposed to be added to the left in reverse. So `appendleft()` should be used instead of  `append()`. The correct behavior is seen in the normal `register_middleware` function.

**Code snippet**
See these two examples, the first using normal middleware, and the second using named middleware:
```
from sanic import Sanic
from sanic.response import text
app = Sanic(__name__)

@app.middleware('request')
def request_middleware_1(request):
    print('1')

@app.middleware('request')
def request_middleware_2(request):
    print('2')

@app.middleware('request')
def request_middleware_3(request):
    print('3')

@app.middleware('response')
def resp_middleware_4(request, response):
    print('4')

@app.middleware('response')
def resp_middleware_5(request, response):
    print('5')

@app.middleware('response')
def resp_middleware_6(request, response):
    print('6')

@app.route('/')
def pop_handler(request):
    return text('hello world')

app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)
```
vs:
```
from sanic import Sanic, Blueprint
from sanic.response import text

app = Sanic(__name__)
bp = Blueprint("bp_"+__name__)

@bp.middleware('request')
def request_middleware_1(request):
    print('1')

@bp.middleware('request')
def request_middleware_2(request):
    print('2')

@bp.middleware('request')
def request_middleware_3(request):
    print('3')

@bp.middleware('response')
def resp_middleware_4(request, response):
    print('4')

@bp.middleware('response')
def resp_middleware_5(request, response):
    print('5')

@bp.middleware('response')
def resp_middleware_6(request, response):
    print('6')

@bp.route('/')
def pop_handler(request):
    return text('hello world')

app.blueprint(bp, url_prefix='/bp')

app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)
```

**Expected behavior**
See the first snippet prints "1 2 3 6 5 4" (correct) but the second snippet prints "1 2 3 4 5 6". This should match the first.

**Additional Context**
This bug is _similar to_ but not the same as https://github.com/huge-success/sanic/issues/1845
This bug was uncovered while looking deeper into https://github.com/huge-success/sanic/issues/1845


Discussion:

Resolving patch:
diff --git a/examples/blueprint_middlware_execution_order.py b/examples/blueprint_middlware_execution_order.py
new file mode 100644
--- /dev/null
+++ b/examples/blueprint_middlware_execution_order.py
@@ -0,0 +1,43 @@
+from sanic import Sanic, Blueprint
+from sanic.response import text
+'''
+Demonstrates that blueprint request middleware are executed in the order they 
+are added. And blueprint response middleware are executed in _reverse_ order.
+On a valid request, it should print "1 2 3 6 5 4" to terminal
+'''
+
+app = Sanic(__name__)
+
+bp = Blueprint("bp_"+__name__)
+
+@bp.middleware('request')
+def request_middleware_1(request):
+    print('1')
+
+@bp.middleware('request')
+def request_middleware_2(request):
+    print('2')
+
+@bp.middleware('request')
+def request_middleware_3(request):
+    print('3')
+
+@bp.middleware('response')
+def resp_middleware_4(request, response):
+    print('4')
+
+@bp.middleware('response')
+def resp_middleware_5(request, response):
+    print('5')
+
+@bp.middleware('response')
+def resp_middleware_6(request, response):
+    print('6')
+
+@bp.route('/')
+def pop_handler(request):
+    return text('hello world')
+
+app.blueprint(bp, url_prefix='/bp')
+
+app.run(host="0.0.0.0", port=8000, debug=True, auto_reload=False)
diff --git a/sanic/app.py b/sanic/app.py
--- a/sanic/app.py
+++ b/sanic/app.py
@@ -653,7 +653,7 @@ def register_named_middleware(
                 if _rn not in self.named_response_middleware:
                     self.named_response_middleware[_rn] = deque()
                 if middleware not in self.named_response_middleware[_rn]:
-                    self.named_response_middleware[_rn].append(middleware)
+                    self.named_response_middleware[_rn].appendleft(middleware)
 
     # Decorator
     def middleware(self, middleware_or_request):


Test patch:
diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py
--- a/tests/test_blueprints.py
+++ b/tests/test_blueprints.py
@@ -253,7 +253,7 @@ def handler2(request):
 
 
 def test_bp_middleware(app):
-    blueprint = Blueprint("test_middleware")
+    blueprint = Blueprint("test_bp_middleware")
 
     @blueprint.middleware("response")
     async def process_response(request, response):
@@ -270,6 +270,38 @@ async def handler(request):
     assert response.status == 200
     assert response.text == "FAIL"
 
+def test_bp_middleware_order(app):
+    blueprint = Blueprint("test_bp_middleware_order")
+    order = list()
+    @blueprint.middleware("request")
+    def mw_1(request):
+        order.append(1)
+    @blueprint.middleware("request")
+    def mw_2(request):
+        order.append(2)
+    @blueprint.middleware("request")
+    def mw_3(request):
+        order.append(3)
+    @blueprint.middleware("response")
+    def mw_4(request, response):
+        order.append(6)
+    @blueprint.middleware("response")
+    def mw_5(request, response):
+        order.append(5)
+    @blueprint.middleware("response")
+    def mw_6(request, response):
+        order.append(4)
+
+    @blueprint.route("/")
+    def process_response(request):
+        return text("OK")
+
+    app.blueprint(blueprint)
+    order.clear()
+    request, response = app.test_client.get("/")
+
+    assert response.status == 200
+    assert order == [1, 2, 3, 4, 5, 6]
 
 def test_bp_exception_handler(app):
     blueprint = Blueprint("test_middleware")