From dd89b114b2e19df1f5332367c196eed8f565a01c Mon Sep 17 00:00:00 2001 From: ulmentflam Date: Mon, 8 Feb 2021 12:56:20 -0500 Subject: [PATCH 1/4] Fixing the cors middleware to address the KeyError via issue #767 --- hug/middleware.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/hug/middleware.py b/hug/middleware.py index ea522794..5398df1a 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -24,7 +24,6 @@ import uuid from datetime import datetime - class SessionMiddleware(object): """Simple session middleware. @@ -166,15 +165,18 @@ def match_route(self, reqpath): """Match a request with parameter to it's corresponding route""" route_dicts = [routes for _, routes in self.api.http.routes.items()][0] routes = [route for route, _ in route_dicts.items()] - if reqpath not in routes: - for route in routes: # replace params in route with regex - reqpath = re.sub(r"^(/v\d*/?)", "/", reqpath) - base_url = getattr(self.api.http, "base_url", "") - reqpath = reqpath.replace(base_url, "", 1) if base_url else reqpath - if re.match(re.sub(r"/{[^{}]+}", ".+", route) + "$", reqpath, re.DOTALL): - return route - - return reqpath + # If the route is valid, it should return the valid route. + for route in routes: # replace params in route with regex + reqpath = re.sub(r"^(/v\d*/?)", "/", reqpath) + # This will match the path with our without the trailing slash + if reqpath in route: + return route + base_url = getattr(self.api.http, "base_url", "") + reqpath = reqpath.replace(base_url, "", 1) if base_url else reqpath + if re.match(re.sub(r"/{[^{}]+}", ".+", route) + "$", reqpath, re.DOTALL): + return route + # If match route does not find a valid http route, it should return None + return None def process_response(self, request, response, resource, req_succeeded): """Add CORS headers to the response""" @@ -185,10 +187,15 @@ def process_response(self, request, response, resource, req_succeeded): response.set_header("Access-Control-Allow-Origin", origin) if request.method == "OPTIONS": # check if we are handling a preflight request + route = self.match_route(request.path) + # If we cannot route to the preflight request, raise the appropriate error + if not route: + self.api.http.not_found(request, response) + return allowed_methods = set( method for _, routes in self.api.http.routes.items() - for method, _ in routes[self.match_route(request.path)].items() + for method, _ in routes[route].items() ) allowed_methods.add("OPTIONS") From 0fc1497d051fd1fdc89ed7d3e3223e808cc8a95a Mon Sep 17 00:00:00 2001 From: ulmentflam Date: Mon, 8 Feb 2021 15:50:39 -0500 Subject: [PATCH 2/4] Adding new testcases to help pass coveralls --- hug/middleware.py | 17 +++++++++-------- tests/test_middleware.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/hug/middleware.py b/hug/middleware.py index 5398df1a..f87ce6de 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -24,6 +24,7 @@ import uuid from datetime import datetime + class SessionMiddleware(object): """Simple session middleware. @@ -187,17 +188,17 @@ def process_response(self, request, response, resource, req_succeeded): response.set_header("Access-Control-Allow-Origin", origin) if request.method == "OPTIONS": # check if we are handling a preflight request + allowed_methods = {"OPTIONS"} + # If we cannot match the route of a preflight request, send not_found from the origin. route = self.match_route(request.path) - # If we cannot route to the preflight request, raise the appropriate error if not route: self.api.http.not_found(request, response) - return - allowed_methods = set( - method - for _, routes in self.api.http.routes.items() - for method, _ in routes[route].items() - ) - allowed_methods.add("OPTIONS") + else: + allowed_methods.update(set( + method + for _, routes in self.api.http.routes.items() + for method, _ in routes[route].items() + )) # return allowed methods response.set_header("Access-Control-Allow-Methods", ", ".join(allowed_methods)) diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 219e49fd..5ac6c5a6 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -153,3 +153,13 @@ def get_demo(param): assert set(methods.split(",")) == set(["OPTIONS", "GET", "DELETE", "PUT"]) assert set(allow.split(",")) == set(["OPTIONS", "GET", "DELETE", "PUT"]) assert response.headers_dict["access-control-max-age"] == "10" + + assert hug.test.get(hug_api, "/not_there").status_code == 404 + + response = hug.test.options(hug_api, "/not_there") + methods = response.headers_dict["access-control-allow-methods"].replace(" ", "") + allow = response.headers_dict["allow"].replace(" ", "") + assert set(methods.split(",")) == set(["OPTIONS", "GET"]) + assert set(allow.split(",")) == set(["OPTIONS", "GET"]) + assert response.headers_dict["access-control-max-age"] == "10" + assert response.status_code == 404 From 7e7094e35a5b47ff4399e83406552203d2fa5ad2 Mon Sep 17 00:00:00 2001 From: ulmentflam Date: Mon, 8 Feb 2021 15:56:49 -0500 Subject: [PATCH 3/4] Fixing typo of status_code and integer comparison --- tests/test_middleware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_middleware.py b/tests/test_middleware.py index 5ac6c5a6..f357584a 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -154,7 +154,7 @@ def get_demo(param): assert set(allow.split(",")) == set(["OPTIONS", "GET", "DELETE", "PUT"]) assert response.headers_dict["access-control-max-age"] == "10" - assert hug.test.get(hug_api, "/not_there").status_code == 404 + assert "404" in hug.test.get(hug_api, "/not_there").status response = hug.test.options(hug_api, "/not_there") methods = response.headers_dict["access-control-allow-methods"].replace(" ", "") @@ -162,4 +162,4 @@ def get_demo(param): assert set(methods.split(",")) == set(["OPTIONS", "GET"]) assert set(allow.split(",")) == set(["OPTIONS", "GET"]) assert response.headers_dict["access-control-max-age"] == "10" - assert response.status_code == 404 + assert "404" in response.status From 471dc0a6626f25e6b8d6f043b3271a9011868f3b Mon Sep 17 00:00:00 2001 From: ulmentflam Date: Mon, 8 Feb 2021 16:01:24 -0500 Subject: [PATCH 4/4] Fixing options set building --- hug/middleware.py | 2 +- tests/test_middleware.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hug/middleware.py b/hug/middleware.py index f87ce6de..19a3c964 100644 --- a/hug/middleware.py +++ b/hug/middleware.py @@ -188,7 +188,7 @@ def process_response(self, request, response, resource, req_succeeded): response.set_header("Access-Control-Allow-Origin", origin) if request.method == "OPTIONS": # check if we are handling a preflight request - allowed_methods = {"OPTIONS"} + allowed_methods = set(["OPTIONS"]) # If we cannot match the route of a preflight request, send not_found from the origin. route = self.match_route(request.path) if not route: diff --git a/tests/test_middleware.py b/tests/test_middleware.py index f357584a..b1c914fd 100644 --- a/tests/test_middleware.py +++ b/tests/test_middleware.py @@ -159,7 +159,7 @@ def get_demo(param): response = hug.test.options(hug_api, "/not_there") methods = response.headers_dict["access-control-allow-methods"].replace(" ", "") allow = response.headers_dict["allow"].replace(" ", "") - assert set(methods.split(",")) == set(["OPTIONS", "GET"]) - assert set(allow.split(",")) == set(["OPTIONS", "GET"]) + assert set(methods.split(",")) == set(["OPTIONS"]) + assert set(allow.split(",")) == set(["OPTIONS"]) assert response.headers_dict["access-control-max-age"] == "10" assert "404" in response.status