1515"""AbstractRouterAdapter for aiohttp.web.UrlDispatcher.
1616"""
1717import collections
18- import re
1918
2019from typing import Union
2120
@@ -89,16 +88,19 @@ def __init__(self, default_config):
8988 self .method_config = {}
9089
9190
92- def _is_web_view (entity ):
91+ def _is_web_view (entity , strict = True ):
9392 webview = False
9493 if isinstance (entity , web .AbstractRoute ):
9594 handler = entity .handler
9695 if isinstance (handler , type ) and issubclass (handler , web .View ):
9796 webview = True
9897 if not issubclass (handler , CorsViewMixin ):
99- raise ValueError ("web view should be derivad from "
100- "aiohttp_cors.WebViewMixig for working "
101- "with the library" )
98+ if strict :
99+ raise ValueError ("web view should be derived from "
100+ "aiohttp_cors.WebViewMixig for working "
101+ "with the library" )
102+ else :
103+ return False
102104 return webview
103105
104106
@@ -143,10 +145,6 @@ def add_preflight_handler(
143145 Should fail if there are conflicting user-defined OPTIONS handlers.
144146 """
145147
146- webview = _is_web_view (routing_entity )
147- if webview :
148- raise ValueError ("WebView should use Route based dispatcher." )
149-
150148 if isinstance (routing_entity , web .Resource ):
151149 resource = routing_entity
152150
@@ -155,6 +153,22 @@ def add_preflight_handler(
155153 if resource in self ._resources_with_preflight_handlers :
156154 # Preflight handler already added for this resource.
157155 return
156+ for route_obj in resource :
157+ if route_obj .method == hdrs .METH_OPTIONS :
158+ if route_obj .handler is handler :
159+ return # already added
160+ else :
161+ raise ValueError (
162+ "{!r} already has OPTIONS handler {!r}"
163+ .format (resource , route_obj .handler ))
164+ elif route_obj .method == hdrs .METH_ANY :
165+ if _is_web_view (route_obj ):
166+ self ._preflight_routes .add (route_obj )
167+ self ._resources_with_preflight_handlers .add (resource )
168+ return
169+ else :
170+ raise ValueError ("{!r} already has a '*' handler "
171+ "for all methods" .format (resource ))
158172
159173 preflight_route = resource .add_route (hdrs .METH_OPTIONS , handler )
160174 self ._preflight_routes .add (preflight_route )
@@ -177,13 +191,8 @@ def add_preflight_handler(
177191 elif isinstance (routing_entity , web .ResourceRoute ):
178192 route = routing_entity
179193
180- # Preflight handler for Route's Resource already must be
181- # configured.
182194 if not self .is_cors_for_resource (route .resource ):
183- raise ValueError (
184- "Can't setup CORS for {!r} request, "
185- "CORS must be enabled for route's resource first." .format (
186- route ))
195+ self .add_preflight_handler (route .resource , handler )
187196
188197 else :
189198 raise ValueError (
@@ -204,8 +213,10 @@ def _request_resource(self, request: web.Request) -> web.Resource:
204213
205214 def is_preflight_request (self , request : web .Request ) -> bool :
206215 """Is `request` is a CORS preflight request."""
207-
208- return self ._request_route (request ) in self ._preflight_routes
216+ route = self ._request_route (request )
217+ if _is_web_view (route , strict = False ):
218+ return request .method == 'OPTIONS'
219+ return route in self ._preflight_routes
209220
210221 def is_cors_enabled_on_request (self , request : web .Request ) -> bool :
211222 """Is `request` is a request for CORS-enabled resource."""
@@ -219,8 +230,6 @@ def set_config_for_routing_entity(
219230 config ):
220231 """Record configuration for resource or it's route."""
221232
222- _is_web_view (routing_entity )
223-
224233 if isinstance (routing_entity , (web .Resource , web .StaticResource )):
225234 resource = routing_entity
226235
@@ -237,6 +246,9 @@ def set_config_for_routing_entity(
237246 route = routing_entity
238247
239248 # Add resource's route configuration or fail if it's already added.
249+ if route .resource not in self ._resource_config :
250+ self .set_config_for_routing_entity (route .resource , config )
251+
240252 if route .resource not in self ._resource_config :
241253 raise ValueError (
242254 "Can't setup CORS for {!r} request, "
@@ -297,168 +309,16 @@ def get_non_preflight_request_config(self, request: web.Request):
297309 resource_config = self ._resource_config [resource ]
298310 # Take Route config (if any) with defaults from Resource CORS
299311 # configuration and global defaults.
312+ route = request .match_info .route
313+ if _is_web_view (route , strict = False ):
314+ method_config = request .match_info .handler .get_request_config (
315+ request , request .method )
316+ else :
317+ method_config = resource_config .method_config .get (request .method ,
318+ {})
300319 defaulted_config = collections .ChainMap (
301- resource_config . method_config . get ( request . method , {}) ,
320+ method_config ,
302321 resource_config .default_config ,
303322 self ._default_config )
304323
305324 return defaulted_config
306-
307-
308- class OldRoutesUrlDispatcherRouterAdapter (AbstractRouterAdapter ):
309- """Adapter for `UrlDispatcher` for old-style routing only.
310-
311- In all use cases when Resource is not explicitly used,
312- Resource will automatically allocated for old route.
313- In this case all routes will have it's own resource, and to find
314- related routes (routes that shares same path) we need to iterate over
315- all routes with enabled CORS and check is they handle specific path.
316-
317- This whole class should go away when user will migrate to proper
318- Resource/Route usage scheme.
319- """
320-
321- def __init__ (self ,
322- router : web .UrlDispatcher ,
323- defaults ):
324- """
325- :param defaults:
326- Default CORS configuration.
327- """
328- self ._router = router
329-
330- # Default configuration for all routes.
331- self ._default_config = defaults
332-
333- # Mapping from route to config.
334- self ._route_config = collections .OrderedDict ()
335-
336- self ._preflight_routes = set ()
337-
338- def add_preflight_handler (
339- self ,
340- route : web .AbstractRoute ,
341- handler ):
342- """Add OPTIONS handler for same paths that `route` handles."""
343-
344- assert isinstance (route , web .AbstractRoute )
345- webview = _is_web_view (route )
346-
347- if webview :
348- self ._preflight_routes .add (route )
349- return
350-
351- if isinstance (route , web .ResourceRoute ):
352- # New-style route (which Resource is not used explicitly,
353- # otherwise it would be handled by other adapter).
354- preflight_route = route .resource .add_route (
355- hdrs .METH_OPTIONS , handler )
356-
357- elif isinstance (route , web .Route ):
358- # Old-style route.
359-
360- if isinstance (route , web .StaticRoute ):
361- # TODO: Use custom matches that uses `str.startswith()`
362- # if regexp performance is not enough.
363- pattern = re .compile ("^" + re .escape (route ._prefix ))
364- preflight_route = web .DynamicRoute (
365- hdrs .METH_OPTIONS , handler , None , pattern , "" )
366- self ._router .register_route (preflight_route )
367-
368- elif isinstance (route , web .PlainRoute ):
369- # May occur only if user manually creates PlainRoute.
370- preflight_route = self ._router .add_route (
371- hdrs .METH_OPTIONS , route ._path , handler )
372-
373- elif isinstance (route , web .DynamicRoute ):
374- # May occur only if user manually creates DynamicRoute.
375- preflight_route = web .DynamicRoute (
376- hdrs .METH_OPTIONS , handler , None ,
377- route ._pattern , route ._formatter )
378- self ._router .register_route (preflight_route )
379-
380- else :
381- raise RuntimeError (
382- "Unhandled deprecated route type {!r}" .format (route ))
383-
384- else :
385- raise RuntimeError ("Unhandled route type {!r}" .format (route ))
386-
387- self ._preflight_routes .add (preflight_route )
388-
389- def _request_route (self , request : web .Request ) -> web .ResourceRoute :
390- match_info = request .match_info
391- assert isinstance (match_info , web .UrlMappingMatchInfo )
392- return match_info .route
393-
394- def is_preflight_request (self , request : web .Request ) -> bool :
395- """Is `request` is a CORS preflight request."""
396-
397- return (self ._request_route (request ) in self ._preflight_routes and
398- request .method == hdrs .METH_OPTIONS )
399-
400- def is_cors_enabled_on_request (self , request : web .Request ) -> bool :
401- """Is `request` is a request for CORS-enabled resource."""
402-
403- return self ._request_route (request ) in self ._route_config
404-
405- def set_config_for_routing_entity (
406- self ,
407- route : web .AbstractRoute ,
408- config ):
409- """Record CORS configuration for route."""
410-
411- assert isinstance (route , web .AbstractRoute )
412- webview = _is_web_view (route )
413-
414- if any (options .allow_methods is not None
415- for options in config .values ()):
416- raise ValueError (
417- "'allow_methods' parameter is not supported on old-style "
418- "routes. You specified {!r} for {!r}. "
419- "Use Resources to configure CORS." .format (
420- config , route ))
421-
422- if route in self ._route_config :
423- raise ValueError (
424- "CORS is already configured for {!r} route." .format (
425- route ))
426-
427- if webview :
428- config = {'webview' : True }
429-
430- self ._route_config [route ] = config
431-
432- async def get_preflight_request_config (
433- self ,
434- preflight_request : web .Request ,
435- origin : str ,
436- requested_method : str ):
437- assert self .is_preflight_request (preflight_request )
438-
439- request = preflight_request .clone (method = requested_method )
440- for route , config in self ._route_config .items ():
441- match_info , allowed_methods = await route .resource .resolve (
442- request )
443- if match_info is not None :
444- return collections .ChainMap (config , self ._default_config )
445- else :
446- raise KeyError
447-
448- def get_non_preflight_request_config (self , request : web .Request ):
449- """Get stored CORS configuration for routing entity that handles
450- specified request."""
451-
452- assert self .is_cors_enabled_on_request (request )
453-
454- route = self ._request_route (request )
455- route_config = self ._route_config [route ]
456-
457- if route_config .get ('webview' , False ):
458- handler = self ._request_route (request ).handler
459- route_config = handler .get_request_config (request , request .method )
460-
461- defaulted_config = collections .ChainMap (
462- route_config , self ._default_config )
463-
464- return defaulted_config
0 commit comments