blob: 2fe31c4d050c922758fd62ebe9499f1302b025ae [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:async';
import 'package:path/path.dart' as p;
import 'package:shelf/shelf.dart' as shelf;
/// A handler that routes to sub-handlers based on exact path prefixes.
class PathHandler {
/// A trie of path components to handlers.
final _paths = _Node();
/// The shelf handler.
shelf.Handler get handler => _onRequest;
/// Returns middleware that nests all requests beneath the URL prefix
/// [beneath].
static shelf.Middleware nestedIn(String beneath) {
return (handler) {
var pathHandler = PathHandler()..add(beneath, handler);
return pathHandler.handler;
};
}
/// Routes requests at or under [path] to [handler].
///
/// If [path] is a parent or child directory of another path in this handler,
/// the longest matching prefix wins.
void add(String path, shelf.Handler handler) {
var node = _paths;
for (var component in p.url.split(path)) {
node = node.children.putIfAbsent(component, () => _Node());
}
node.handler = handler;
}
FutureOr<shelf.Response> _onRequest(shelf.Request request) {
shelf.Handler handler;
int handlerIndex;
var node = _paths;
var components = p.url.split(request.url.path);
for (var i = 0; i < components.length; i++) {
node = node.children[components[i]];
if (node == null) break;
if (node.handler == null) continue;
handler = node.handler;
handlerIndex = i;
}
if (handler == null) return shelf.Response.notFound('Not found.');
return handler(
request.change(path: p.url.joinAll(components.take(handlerIndex + 1))));
}
}
/// A trie node.
class _Node {
shelf.Handler handler;
final children = <String, _Node>{};
}