mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Avoid http route linear search fallback when there are multiple paths (#95776)
This commit is contained in:
parent
17440c9608
commit
f1db497efe
@ -582,32 +582,37 @@ class FastUrlDispatcher(UrlDispatcher):
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""Initialize the dispatcher."""
|
"""Initialize the dispatcher."""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._resource_index: dict[str, AbstractResource] = {}
|
self._resource_index: dict[str, list[AbstractResource]] = {}
|
||||||
|
|
||||||
def register_resource(self, resource: AbstractResource) -> None:
|
def register_resource(self, resource: AbstractResource) -> None:
|
||||||
"""Register a resource."""
|
"""Register a resource."""
|
||||||
super().register_resource(resource)
|
super().register_resource(resource)
|
||||||
canonical = resource.canonical
|
canonical = resource.canonical
|
||||||
if "{" in canonical: # strip at the first { to allow for variables
|
if "{" in canonical: # strip at the first { to allow for variables
|
||||||
canonical = canonical.split("{")[0]
|
canonical = canonical.split("{")[0].rstrip("/")
|
||||||
canonical = canonical.rstrip("/")
|
# There may be multiple resources for a canonical path
|
||||||
self._resource_index[canonical] = resource
|
# so we use a list to avoid falling back to a full linear search
|
||||||
|
self._resource_index.setdefault(canonical, []).append(resource)
|
||||||
|
|
||||||
async def resolve(self, request: web.Request) -> UrlMappingMatchInfo:
|
async def resolve(self, request: web.Request) -> UrlMappingMatchInfo:
|
||||||
"""Resolve a request."""
|
"""Resolve a request."""
|
||||||
url_parts = request.rel_url.raw_parts
|
url_parts = request.rel_url.raw_parts
|
||||||
resource_index = self._resource_index
|
resource_index = self._resource_index
|
||||||
|
|
||||||
# Walk the url parts looking for candidates
|
# Walk the url parts looking for candidates
|
||||||
for i in range(len(url_parts), 1, -1):
|
for i in range(len(url_parts), 1, -1):
|
||||||
url_part = "/" + "/".join(url_parts[1:i])
|
url_part = "/" + "/".join(url_parts[1:i])
|
||||||
if (resource_candidate := resource_index.get(url_part)) is not None and (
|
if (resource_candidates := resource_index.get(url_part)) is not None:
|
||||||
match_dict := (await resource_candidate.resolve(request))[0]
|
for candidate in resource_candidates:
|
||||||
|
if (
|
||||||
|
match_dict := (await candidate.resolve(request))[0]
|
||||||
) is not None:
|
) is not None:
|
||||||
return match_dict
|
return match_dict
|
||||||
# Next try the index view if we don't have a match
|
# Next try the index view if we don't have a match
|
||||||
if (index_view_candidate := resource_index.get("/")) is not None and (
|
if (index_view_candidates := resource_index.get("/")) is not None:
|
||||||
match_dict := (await index_view_candidate.resolve(request))[0]
|
for candidate in index_view_candidates:
|
||||||
) is not None:
|
if (match_dict := (await candidate.resolve(request))[0]) is not None:
|
||||||
return match_dict
|
return match_dict
|
||||||
|
|
||||||
# Finally, fallback to the linear search
|
# Finally, fallback to the linear search
|
||||||
return await super().resolve(request)
|
return await super().resolve(request)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user