Skip to content

Commit

Permalink
Quick dirty fix for new middleware-registry behaviour on sanic v22.9.0,
Browse files Browse the repository at this point in the history
fixes #64

Vary 'Origin' header will be added to any existing Vary string on response, fixes #62
  • Loading branch information
ashleysommer committed Oct 7, 2022
1 parent 1418fb3 commit e0a7ab1
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Change Log

## 2.2.0
- Quick dirty fix for new middleware-registry behaviour on sanic v22.9.0, fixes #64
- Vary 'Origin' header will be added to any existing Vary string on response, fixes #62

## 2.2.0b1
- Quick dirty fix for new middleware-registry behaviour on sanic v22.9.0

Expand Down
7 changes: 3 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@ credential'ed requests, and please make sure you add some sort of
`CSRF <http://en.wikipedia.org/wiki/Cross-site_request_forgery>`__
protection before doing so!

**Sept 2022 Notice:**
If you are having unexpected results in Sanic v22.9+, upgrade to Sanic-CORS v2.2.0

**December 2021 Notice:**
If you need compatibility with Sanic v21.12+, upgrade to Sanic-CORS v2.0

**Sept 2021 Notice:**
Please upgrade to Sanic-CORS v1.0.1 if you need compatibility with Sanic v21.9,<21.12

**Older Notice:**
Please upgrade to Sanic-CORS v1.0.0 if you need compatibility with Sanic v21.3+ (and don't forget to replace SPF with SPTK)
Please upgrade to Sanic-CORS v0.10.0 if you need compatibility with Sanic v19.12+. See `here <https://github.com/huge-success/sanic/issues/1749#issuecomment-571881532>`_ for more details.

Installation
------------

Expand Down
38 changes: 27 additions & 11 deletions sanic_cors/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import logging
import collections
from datetime import timedelta
from typing import Dict
try:
# Sanic compat Header from Sanic v19.9.0 and above
from sanic.compat import Header as CIMultiDict
Expand Down Expand Up @@ -173,12 +174,16 @@ def get_allow_headers(options, acl_request_headers):
return None


def get_cors_headers(options, request_headers, request_method):
origins_to_set = get_cors_origins(options, request_headers.get('Origin'))
headers = CIMultiDict()
def get_cors_headers(options: Dict, request_headers: CIMultiDict, request_method):
found_origins_list = request_headers.getall('Origin', None)
found_origins = ", ".join(found_origins_list) if found_origins_list else None
origins_to_set = get_cors_origins(options, found_origins)

if not origins_to_set: # CORS is not enabled for this route
return headers
return CIMultiDict()

# This is a regular dict here, it gets converted to a CIMultiDict at the bottom of this function.
headers = {}

for origin in origins_to_set:
# TODO, with CIDict, with will only allow one origin
Expand All @@ -202,7 +207,9 @@ def get_cors_headers(options, request_headers, request_method):
# If method is not a case-sensitive match for any of the values in
# list of methods do not set any additional headers and terminate
# this set of steps.
headers[ACL_ALLOW_HEADERS] = get_allow_headers(options, request_headers.get(ACL_REQUEST_HEADERS))
acl_request_headers_list = request_headers.getall(ACL_REQUEST_HEADERS, None)
acl_request_headers = ", ".join(acl_request_headers_list) if acl_request_headers_list else None
headers[ACL_ALLOW_HEADERS] = get_allow_headers(options, acl_request_headers)
headers[ACL_MAX_AGE] = str(options.get('max_age')) # sanic cannot handle integers in header values.
headers[ACL_METHODS] = options.get('methods')
else:
Expand All @@ -219,7 +226,7 @@ def get_cors_headers(options, request_headers, request_method):
elif (len(options.get('origins')) > 1 or
len(origins_to_set) > 1 or
any(map(probably_regex, options.get('origins')))):
headers['Vary'] = 'Origin'
headers['Vary'] = "Origin"

return CIMultiDict((k, v) for k, v in headers.items() if v)

Expand Down Expand Up @@ -251,14 +258,23 @@ def set_cors_headers(req, resp, req_context, options):
resp.headers = CIMultiDict()

headers_to_set = get_cors_headers(options, req.headers, req.method)

LOG.debug('Settings CORS headers: %s', str(headers_to_set))

for k, v in headers_to_set.items():
try:
resp.headers.add(k, v)
except Exception as e2:
resp.headers[k] = v
# Special case for "Vary" header, we should append it to a comma separated list
if (k == "vary" or k == "Vary") and "vary" in resp.headers:
vary_list = resp.headers.popall("vary")
vary_list.append(v)
new_vary = ", ".join(vary_list)
try:
resp.headers.add('Vary', new_vary)
except Exception:
resp.headers['Vary'] = new_vary
else:
try:
resp.headers.add(k, v)
except Exception:
resp.headers[k] = v
return resp


Expand Down
26 changes: 26 additions & 0 deletions sanic_cors/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,20 @@ async def unapplied_cors_response_middleware(req, resp, context=None):
debug('No CORS rule matches')

def _make_cors_request_middleware_function(app, context=None):
"""If app is a blueprint, this function is executed when the CORS extension is initialized, it can insert
the middleware into the correct location in the blueprint's future_middleware at any time.
If app is a Sanic server, this function is executed by the before_server_start callback, it inserts the middleware
at the correct location at that point in time.
The exception is with Sanic v22.9+, middlwares are finalized _before_ the before_server_start event, so we must
run this function at plugin initialization time, but v22.9+ has priorities, so it can be inserted with priority.
"""
mw = update_wrapper(partial(unapplied_cors_request_middleware, context=context), unapplied_cors_request_middleware)
_old_name = getattr(mw, "__name__", None)
if _old_name:
setattr(mw, "__name__", str(_old_name).replace("unapplied_", ""))
_old_qname = getattr(mw, "__qualname__", None)
if _old_qname:
setattr(mw, "__qualname__", str(_old_qname).replace("unapplied_", ""))
if SANIC_22_9_0 <= SANIC_VERSION:
new_mw = Middleware(mw, MiddlewareLocation.REQUEST, priority=99)
else:
Expand All @@ -378,7 +391,20 @@ def _make_cors_request_middleware_function(app, context=None):
app.request_middleware.appendleft(new_mw)

def _make_cors_response_middleware_function(app, context=None):
"""If app is a blueprint, this function is executed when the CORS extension is initialized, it can insert
the middleware into the correct location in the blueprint's future_middleware at any time.
If app is a Sanic server, this function is executed by the before_server_start callback, it inserts the middleware
at the correct location at that point in time.
The exception is with Sanic v22.9+, middlwares are finalized _before_ the before_server_start event, so we must
run this function at plugin initialization time, but v22.9+ has priorities, so it can be inserted with priority.
"""
mw = update_wrapper(partial(unapplied_cors_response_middleware, context=context), unapplied_cors_request_middleware)
_old_name = getattr(mw, "__name__", None)
if _old_name:
setattr(mw, "__name__", str(_old_name).replace("unapplied_", ""))
_old_qname = getattr(mw, "__qualname__", None)
if _old_qname:
setattr(mw, "__qualname__", str(_old_qname).replace("unapplied_", ""))
if SANIC_22_9_0 <= SANIC_VERSION:
new_mw = Middleware(mw, MiddlewareLocation.RESPONSE, priority=999)
else:
Expand Down
2 changes: 1 addition & 1 deletion sanic_cors/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.2.0b1'
__version__ = '2.2.0'

0 comments on commit e0a7ab1

Please sign in to comment.