# (c) 2009-2024 Martin Wendt and contributors; see WsgiDAV https://github.com/mar10/wsgidav# Licensed under the MIT license:# http://www.opensource.org/licenses/mit-license.php"""WSGI middleware used for CORS support (optional).Respond to CORS preflight OPTIONS request and inject CORS headers."""fromwsgidavimportutilfromwsgidav.mw.base_mwimportBaseMiddleware__docformat__="reStructuredText"_logger=util.get_module_logger(__name__)
[docs]classCors(BaseMiddleware):def__init__(self,wsgidav_app,next_app,config):super().__init__(wsgidav_app,next_app,config)opts=config.get("cors",None)ifoptsisNone:opts={}allow_origins=opts.get("allow_origin")iftype(allow_origins)isstr:allow_origins=allow_origins.strip()ifallow_origins!="*":allow_origins=[allow_origins]elifallow_origins:allow_origins=[ao.strip()foraoinallow_origins]allow_headers=",".join(util.to_set(opts.get("allow_headers")))allow_methods=",".join(util.to_set(opts.get("allow_methods")))expose_headers=",".join(util.to_set(opts.get("expose_headers")))allow_credentials=opts.get("allow_credentials",False)max_age=opts.get("max_age")always_headers=opts.get("add_always")add_always=[]ifallow_credentials:add_always.append(("Access-Control-Allow-Credentials","true"))ifalways_headers:iftype(always_headers)isnotdict:raiseValueError(f"cors.add_always must be a list a dict: {always_headers}")forn,vinalways_headers.items():add_always.append((n,v))add_non_preflight=add_always[:]ifexpose_headers:add_always.append(("Access-Control-Expose-Headers",expose_headers))add_preflight=add_always[:]ifallow_headers:add_preflight.append(("Access-Control-Allow-Headers",allow_headers))ifallow_methods:add_preflight.append(("Access-Control-Allow-Methods",allow_methods))ifmax_age:add_preflight.append(("Access-Control-Max-Age",str(int(max_age))))self.non_preflight_headers=add_non_preflightself.preflight_headers=add_preflight#: Either '*' or al list of originsself.allow_origins=allow_originsdef__repr__(self):allow_origin=self.get_config("cors.allow_origin",None)returnf"{self.__module__}.{self.__class__.__name__}({allow_origin})"
[docs]defis_disabled(self):"""Optionally return True to skip this module on startup."""returnnotself.get_config("cors.allow_origin",False)
[docs]def__call__(self,environ,start_response):method=environ["REQUEST_METHOD"].upper()origin=environ.get("HTTP_ORIGIN")ac_req_meth=environ.get("HTTP_ACCESS_CONTROL_REQUEST_METHOD")ac_req_headers=environ.get("HTTP_ACCESS_CONTROL_REQUEST_HEADERS")acao_headers=Noneifself.allow_origins=="*":acao_headers=[("Access-Control-Allow-Origin","*")]eliforigininself.allow_origins:acao_headers=[("Access-Control-Allow-Origin",origin),("Vary","Origin"),]ifacao_headers:_logger.debug(f"Granted CORS {method}{environ['PATH_INFO']!r} "f"{ac_req_meth!r}, headers: {ac_req_headers}, origin: {origin!r}")else:# Deny (still return 200 on preflight)_logger.warning(f"Denied CORS {method}{environ['PATH_INFO']!r} "f"{ac_req_meth!r}, headers: {ac_req_headers}, origin: {origin!r}")is_preflight=method=="OPTIONS"andac_req_methisnotNone# Handle preflight requestifis_preflight:# Always return 2xx, but only add Access-Control-Allow-Origin etc.# if Origin is allowedresp_headers=[("Content-Length","0"),("Date",util.get_rfc1123_time()),]ifacao_headers:resp_headers+=acao_headers+self.preflight_headersstart_response("204 No Content",resp_headers)return[b""]# non_preflight CORS requestdefwrapped_start_response(status,headers,exc_info=None):ifacao_headers:util.update_headers_in_place(headers,acao_headers+self.non_preflight_headers,)start_response(status,headers,exc_info)returnself.next_app(environ,wrapped_start_response)