The Chain Builder

Step 5: The Chain Builder

Wiring handlers by hand with repeated set_next calls gets tedious. A small builder takes a list of handlers and links them into a chain, returning the head so callers can dispatch through it.

Implement build_chain(handlers):

  • For each adjacent pair, call handlers[i].set_next(handlers[i + 1]) so every handler points at the next one.
  • Return handlers[0], the head of the chain. Dispatching a request means calling head.handle(request).

Now assembling a pipeline is one call: build_chain([AuthHandler(), AppHandler()]) returns the auth handler already linked to the app.

Hints
Starter code
class Handler:
    def __init__(self):
        self._next = None

    def set_next(self, handler):
        self._next = handler
        return handler

    def handle(self, request):
        if self._next is not None:
            return self._next.handle(request)
        return None


class AuthHandler(Handler):
    def handle(self, request):
        if request.get("user") is None:
            return {"status": 401, "body": "unauthorized"}
        return super().handle(request)


class AppHandler(Handler):
    def handle(self, request):
        return {"status": 200, "body": "ok:" + request["path"]}


class RateLimitHandler(Handler):
    def __init__(self, limit):
        super().__init__()
        self.limit = limit
        self.counts = {}

    def handle(self, request):
        user = request.get("user")
        self.counts[user] = self.counts.get(user, 0) + 1
        if self.counts[user] > self.limit:
            return {"status": 429, "body": "rate limited"}
        return super().handle(request)


def build_chain(handlers):
    """Link each handler to the next and return the head of the chain."""
    # TODO: for i in range(len(handlers) - 1): handlers[i].set_next(handlers[i + 1])
    # TODO: return handlers[0]
    pass
Write it on a laptop

The editor and the test runner need a wide screen. Open this page on a desktop browser and your progress will be waiting.