2525from .urldispatcher_router_adapter import ResourcesUrlDispatcherRouterAdapter
2626from .abc import AbstractRouterAdapter
2727from .resource_options import ResourceOptions
28+ from .preflight_handler import _PreflightHandler
2829
2930__all__ = (
3031 "CorsConfig" ,
3132)
3233
33-
3434# Positive response to Access-Control-Allow-Credentials
3535_TRUE = "true"
3636# CORS simple response headers:
@@ -103,7 +103,7 @@ def _parse_config_options(
103103_ConfigType = Mapping [str , Union [ResourceOptions , Mapping [str , Any ]]]
104104
105105
106- class _CorsConfigImpl :
106+ class _CorsConfigImpl ( _PreflightHandler ) :
107107
108108 def __init__ (self ,
109109 app : web .Application ,
@@ -118,7 +118,8 @@ def __init__(self,
118118
119119 def add (self ,
120120 routing_entity ,
121- config : _ConfigType = None ):
121+ config : _ConfigType = None ,
122+ webview : bool = False ):
122123 """Enable CORS for specific route or resource.
123124
124125 If route is passed CORS is enabled for route's resource.
@@ -133,9 +134,9 @@ def add(self,
133134 parsed_config = _parse_config_options (config )
134135
135136 self ._router_adapter .add_preflight_handler (
136- routing_entity , self ._preflight_handler )
137+ routing_entity , self ._preflight_handler , webview = webview )
137138 self ._router_adapter .set_config_for_routing_entity (
138- routing_entity , parsed_config )
139+ routing_entity , parsed_config , webview = webview )
139140
140141 return routing_entity
141142
@@ -196,127 +197,12 @@ def _on_response_prepare(self,
196197 # Set allowed credentials.
197198 response .headers [hdrs .ACCESS_CONTROL_ALLOW_CREDENTIALS ] = _TRUE
198199
199- @staticmethod
200- def _parse_request_method (request : web .Request ):
201- """Parse Access-Control-Request-Method header of the preflight request
202- """
203- method = request .headers .get (hdrs .ACCESS_CONTROL_REQUEST_METHOD )
204- if method is None :
205- raise web .HTTPForbidden (
206- text = "CORS preflight request failed: "
207- "'Access-Control-Request-Method' header is not specified" )
208-
209- # FIXME: validate method string (ABNF: method = token), if parsing
210- # fails, raise HTTPForbidden.
211-
212- return method
213-
214- @staticmethod
215- def _parse_request_headers (request : web .Request ):
216- """Parse Access-Control-Request-Headers header or the preflight request
217-
218- Returns set of headers in upper case.
219- """
220- headers = request .headers .get (hdrs .ACCESS_CONTROL_REQUEST_HEADERS )
221- if headers is None :
222- return frozenset ()
223-
224- # FIXME: validate each header string, if parsing fails, raise
225- # HTTPForbidden.
226- # FIXME: check, that headers split and stripped correctly (according
227- # to ABNF).
228- headers = (h .strip (" \t " ).upper () for h in headers .split ("," ))
229- # pylint: disable=bad-builtin
230- return frozenset (filter (None , headers ))
231-
232200 @asyncio .coroutine
233- def _preflight_handler (self , request : web .Request ):
234- """CORS preflight request handler"""
235-
236- # Handle according to part 6.2 of the CORS specification.
237-
238- origin = request .headers .get (hdrs .ORIGIN )
239- if origin is None :
240- # Terminate CORS according to CORS 6.2.1.
241- raise web .HTTPForbidden (
242- text = "CORS preflight request failed: "
243- "origin header is not specified in the request" )
244-
245- # CORS 6.2.3. Doing it out of order is not an error.
246- request_method = self ._parse_request_method (request )
247-
248- # CORS 6.2.5. Doing it out of order is not an error.
249-
250- try :
251- config = \
252- yield from self ._router_adapter .get_preflight_request_config (
253- request , origin , request_method )
254- except KeyError :
255- raise web .HTTPForbidden (
256- text = "CORS preflight request failed: "
257- "request method {!r} is not allowed "
258- "for {!r} origin" .format (request_method , origin ))
259-
260- if not config :
261- # No allowed origins for the route.
262- # Terminate CORS according to CORS 6.2.1.
263- raise web .HTTPForbidden (
264- text = "CORS preflight request failed: "
265- "no origins are allowed" )
266-
267- options = config .get (origin , config .get ("*" ))
268- if options is None :
269- # No configuration for the origin - deny.
270- # Terminate CORS according to CORS 6.2.2.
271- raise web .HTTPForbidden (
272- text = "CORS preflight request failed: "
273- "origin '{}' is not allowed" .format (origin ))
274-
275- # CORS 6.2.4
276- request_headers = self ._parse_request_headers (request )
277-
278- # CORS 6.2.6
279- if options .allow_headers == "*" :
280- pass
281- else :
282- disallowed_headers = request_headers - options .allow_headers
283- if disallowed_headers :
284- raise web .HTTPForbidden (
285- text = "CORS preflight request failed: "
286- "headers are not allowed: {}" .format (
287- ", " .join (disallowed_headers )))
288-
289- # Ok, CORS actual request with specified in the preflight request
290- # parameters is allowed.
291- # Set appropriate headers and return 200 response.
292-
293- response = web .Response ()
294-
295- # CORS 6.2.7
296- response .headers [hdrs .ACCESS_CONTROL_ALLOW_ORIGIN ] = origin
297- if options .allow_credentials :
298- # Set allowed credentials.
299- response .headers [hdrs .ACCESS_CONTROL_ALLOW_CREDENTIALS ] = _TRUE
300-
301- # CORS 6.2.8
302- if options .max_age is not None :
303- response .headers [hdrs .ACCESS_CONTROL_MAX_AGE ] = \
304- str (options .max_age )
305-
306- # CORS 6.2.9
307- # TODO: more optimal for client preflight request cache would be to
308- # respond with ALL allowed methods.
309- response .headers [hdrs .ACCESS_CONTROL_ALLOW_METHODS ] = request_method
310-
311- # CORS 6.2.10
312- if request_headers :
313- # Note: case of the headers in the request is changed, but this
314- # shouldn't be a problem, since the headers should be compared in
315- # the case-insensitive way.
316- response .headers [hdrs .ACCESS_CONTROL_ALLOW_HEADERS ] = \
317- "," .join (request_headers )
318-
319- return response
201+ def _get_config (self , request , origin , request_method ):
202+ config = \
203+ yield from self ._router_adapter .get_preflight_request_config (
204+ request , origin , request_method )
205+ return config
320206
321207
322208class CorsConfig :
@@ -341,7 +227,7 @@ def __init__(self, app: web.Application, *,
341227 Router adapter. Required if application uses non-default router.
342228 """
343229
344- defaults = _parse_config_options (defaults )
230+ self . defaults = _parse_config_options (defaults )
345231
346232 self ._cors_impl = None
347233
@@ -355,13 +241,13 @@ def __init__(self, app: web.Application, *,
355241
356242 elif isinstance (app .router , web .UrlDispatcher ):
357243 self ._resources_router_adapter = \
358- ResourcesUrlDispatcherRouterAdapter (app .router , defaults )
244+ ResourcesUrlDispatcherRouterAdapter (app .router , self . defaults )
359245 self ._resources_cors_impl = _CorsConfigImpl (
360246 app ,
361247 self ._resources_router_adapter )
362248 self ._old_routes_cors_impl = _CorsConfigImpl (
363249 app ,
364- OldRoutesUrlDispatcherRouterAdapter (app .router , defaults ))
250+ OldRoutesUrlDispatcherRouterAdapter (app .router , self . defaults ))
365251 else :
366252 raise RuntimeError (
367253 "Router adapter is not specified. "
@@ -370,7 +256,8 @@ def __init__(self, app: web.Application, *,
370256
371257 def add (self ,
372258 routing_entity ,
373- config : _ConfigType = None ):
259+ config : _ConfigType = None ,
260+ webview : bool = False ):
374261 """Enable CORS for specific route or resource.
375262
376263 If route is passed CORS is enabled for route's resource.
@@ -404,7 +291,7 @@ def add(self,
404291 # Route which resource has no CORS configuration, i.e.
405292 # old-style route.
406293 return self ._old_routes_cors_impl .add (
407- routing_entity , config )
294+ routing_entity , config , webview = webview )
408295
409296 else :
410297 raise ValueError (
0 commit comments