Implement an HTTP Router That Resolves Handlers for Paths with Named Parameters
GoogleSoftware Engineer · Technical ScreenLast updated
Implement an HTTP router that registers handlers for path patterns, some with named parameters and some without, and looks up the handler and parameter values for a request path. It tests data structure choice, precedence between fixed and parameterized segments, conflict handling and lookup complexity.
Implement an HTTP router: the component that maps the path of an incoming request to the handler registered for it. It supports two operations:
register(pattern, handler)stores a handler for a path pattern.get(path)finds the handler whose pattern matches a concrete request path and returns it together with the values of the pattern's parameters.
Some patterns contain parameters and some do not. Assume a parameter is written as :name and stands for exactly one path segment: /users/:id/profile matches /users/42/profile with id = "42", while /about matches only /about.
You do not need to write the code that splits a path into segments; assume this helper already exists:
def split_path(path: str) -> list[str]:
""""/users/42/profile" -> ["users", "42", "profile"]; "/" -> []"""
A possible interface (adjust it as you clarify):
from typing import Callable
Handler = Callable[..., object]
class Router:
def register(self, pattern: str, handler: Handler) -> None: ...
def get(self, path: str) -> tuple[Handler, dict[str, str]] | None: ...
Constraints and Clarifications
- The parameter syntax (
:name, occupying one whole segment) is an assumption; confirm it. - Splitting is handled by
split_path, and the query string has already been removed from the request path.
Clarifying Questions
- If both a fixed pattern and a parameterized pattern match a request, such as
/users/meand/users/:idfor the path/users/me, which one wins? - If a fixed branch matches the first segments but has no route for the rest, should the router fall back to a parameterized pattern? For example, with
/users/me/settingsand/users/:id/postsregistered, what should/users/me/postsreturn? - What should registering the same pattern twice do, and what about two patterns that differ only in parameter names, such as
/users/:idand/users/:user_id? - Is the HTTP method part of the route? Are there wildcards that match the rest of the path?
- Are trailing slashes significant, and are paths case-sensitive?
- What should
getreturn when no pattern matches?
Hints
Hint 1
Do not compare against every route: Many patterns begin with the same segments. Look for a structure in which a lookup walks the request path once, segment by segment, instead of testing it against each registered pattern.
Hint 2
Where the names live: Decide where a parameter's name is stored so that a lookup can report the value it matched, and what a lookup should do when a fixed segment and a parameter could both match at the same position.
Follow-up questions
- The same path can now have different handlers for GET and POST, and a path that exists under another method must be distinguishable from one that does not exist. What changes?
- Add a catch-all parameter that matches the rest of the path, such as `/static/*filepath`. Where does it rank against the other kinds of segment?
- What is the worst-case cost of one `get` call in your design, and which sets of registered patterns trigger it?
- Routes are registered at startup and then read by many request threads. What, if anything, needs locking, and what changes if routes can be added while traffic is being served?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.