Katkat

Implement an HTTP Router That Resolves Handlers for Paths with Named Parameters

GoogleSoftware Engineer · Technical Screen

Last 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/me and /users/:id for 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/settings and /users/:id/posts registered, what should /users/me/posts return?
  • What should registering the same pattern twice do, and what about two patterns that differ only in parameter names, such as /users/:id and /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 get return 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?