120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied. See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import re
|
|
|
|
# Default allowed headers
|
|
|
|
ALLOWED_HEADERS = [
|
|
'origin',
|
|
'authorization',
|
|
'accept'
|
|
]
|
|
# Default allowed methods
|
|
ALLOWED_METHODS = [
|
|
'GET',
|
|
'POST',
|
|
'PUT',
|
|
'DELETE',
|
|
'OPTIONS'
|
|
]
|
|
|
|
|
|
class CORSMiddleware(object):
|
|
"""CORS Middleware.
|
|
|
|
By providing a list of allowed origins, methods, headers, and a max-age,
|
|
this middleware will detect and apply the appropriate CORS headers so
|
|
that your web application may elegantly overcome the browser's
|
|
same-origin sandbox.
|
|
|
|
For more information, see http://www.w3.org/TR/cors/
|
|
"""
|
|
|
|
def __init__(self, app, allowed_origins=None, allowed_methods=None,
|
|
allowed_headers=None, max_age=3600):
|
|
"""Create a new instance of the CORS middleware.
|
|
|
|
:param app: The application that is being wrapped.
|
|
:param allowed_origins: A list of allowed origins, as provided by the
|
|
'Origin:' Http header. Must include protocol, host, and port.
|
|
:param allowed_methods: A list of allowed HTTP methods.
|
|
:param allowed_headers: A list of allowed HTTP headers.
|
|
:param max_age: A maximum CORS cache age in seconds.
|
|
:return: A new middleware instance.
|
|
"""
|
|
|
|
# Wrapped app (or other middleware)
|
|
self.app = app
|
|
|
|
# Allowed origins
|
|
self.allowed_origins = allowed_origins or []
|
|
|
|
# List of allowed headers.
|
|
self.allowed_headers = ','.join(allowed_headers or ALLOWED_HEADERS)
|
|
|
|
# List of allowed methods.
|
|
self.allowed_methods = ','.join(allowed_methods or ALLOWED_METHODS)
|
|
|
|
# Cache age.
|
|
self.max_age = str(max_age)
|
|
|
|
def __call__(self, env, start_response):
|
|
"""Serve an application request.
|
|
|
|
:param env: Application environment parameters.
|
|
:param start_response: Wrapper method that starts the response.
|
|
:return:
|
|
"""
|
|
origin = env['HTTP_ORIGIN'] if 'HTTP_ORIGIN' in env else ''
|
|
method = env['REQUEST_METHOD'] if 'REQUEST_METHOD' in env else ''
|
|
|
|
def replacement_start_response(status, headers, exc_info=None):
|
|
"""Overrides the default response to attach CORS headers.
|
|
"""
|
|
|
|
# Decorate the headers
|
|
headers.append(('Access-Control-Allow-Origin',
|
|
origin))
|
|
headers.append(('Access-Control-Allow-Methods',
|
|
self.allowed_methods))
|
|
headers.append(('Access-Control-Expose-Headers',
|
|
self.allowed_headers))
|
|
headers.append(('Access-Control-Allow-Headers',
|
|
self.allowed_headers))
|
|
headers.append(('Access-Control-Max-Age',
|
|
self.max_age))
|
|
|
|
return start_response(status, headers, exc_info)
|
|
|
|
# Does this request match one of our origin domains?
|
|
for allowed_origin in self.allowed_origins:
|
|
if ((allowed_origin == origin)
|
|
or (allowed_origin.startswith('^')
|
|
and re.match(allowed_origin, origin))):
|
|
|
|
# Is this an OPTIONS request?
|
|
if method == 'OPTIONS':
|
|
options_headers = [('Content-Length', '0')]
|
|
replacement_start_response('204 No Content',
|
|
options_headers)
|
|
return ''
|
|
else:
|
|
# Handle the request.
|
|
return self.app(env, replacement_start_response)
|
|
# This is not a request for a permitted CORS domain. Return
|
|
# the response without the appropriate headers and let the browser
|
|
# figure out the details.
|
|
return self.app(env, start_response)
|