Fix package:shelf_router to ensure GET handlers also respond to HEAD requests (#36)
diff --git a/pkgs/shelf_router/CHANGELOG.md b/pkgs/shelf_router/CHANGELOG.md
index edcd170..08a1d6c 100644
--- a/pkgs/shelf_router/CHANGELOG.md
+++ b/pkgs/shelf_router/CHANGELOG.md
@@ -1,3 +1,8 @@
+## v0.7.2
+
+ * Always register a `HEAD` handler whenever a `GET` handler is registered.
+ Defaulting to calling the `GET` handler and throwing away the body.
+
## v0.7.1
* Use `Function` instead of `dynamic` in `RouterEntry` to improve typing.
diff --git a/pkgs/shelf_router/lib/src/router.dart b/pkgs/shelf_router/lib/src/router.dart
index a298b52..6e58f5e 100644
--- a/pkgs/shelf_router/lib/src/router.dart
+++ b/pkgs/shelf_router/lib/src/router.dart
@@ -32,6 +32,17 @@
return value;
}
+/// Middleware to remove body from request.
+final _removeBody = createMiddleware(responseHandler: (r) {
+ if (r == null) {
+ return null;
+ }
+ if (r.headers.containsKey('content-length')) {
+ r = r.change(headers: {'content-length': '0'});
+ }
+ return r.change(body: <int>[]);
+});
+
/// A shelf [Router] routes requests to handlers based on HTTP verb and route
/// pattern.
///
@@ -77,6 +88,11 @@
final List<RouterEntry> _routes = [];
/// Add [handler] for [verb] requests to [route].
+ ///
+ /// If [verb] is `GET` the [handler] will also be called for `HEAD` requests
+ /// matching [route]. This is because handling `GET` requests without handling
+ /// `HEAD` is always wrong. To explicitely implement a `HEAD` handler it must
+ /// be registered before the `GET` handler.
void add(String verb, String route, Function handler) {
ArgumentError.checkNotNull(verb, 'verb');
if (!isHttpMethod(verb)) {
@@ -84,6 +100,11 @@
}
verb = verb.toUpperCase();
+ if (verb == 'GET') {
+ // Handling in a 'GET' request without handling a 'HEAD' request is always
+ // wrong, thus, we add a default implementation that discards the body.
+ _routes.add(RouterEntry('HEAD', route, handler, middleware: _removeBody));
+ }
_routes.add(RouterEntry(verb, route, handler));
}
@@ -135,6 +156,9 @@
// Handlers for all methods
/// Handle `GET` request to [route] using [handler].
+ ///
+ /// If no matching handler for `HEAD` requests is registered, such requests
+ /// will also be routed to the [handler] registered here.
void get(String route, Function handler) => add('GET', route, handler);
/// Handle `HEAD` request to [route] using [handler].
diff --git a/pkgs/shelf_router/lib/src/router_entry.dart b/pkgs/shelf_router/lib/src/router_entry.dart
index 05d4b8c..251a91a 100644
--- a/pkgs/shelf_router/lib/src/router_entry.dart
+++ b/pkgs/shelf_router/lib/src/router_entry.dart
@@ -33,6 +33,7 @@
final String verb, route;
final Function _handler;
+ final Middleware _middleware;
/// Expression that the request path must match.
///
@@ -45,7 +46,12 @@
/// List of parameter names in the route pattern.
List<String> get params => _params.toList(); // exposed for using generator.
- RouterEntry(this.verb, this.route, this._handler) {
+ RouterEntry(
+ this.verb,
+ this.route,
+ this._handler, {
+ Middleware middleware,
+ }) : _middleware = middleware ?? ((Handler fn) => fn) {
ArgumentError.checkNotNull(verb, 'verb');
ArgumentError.checkNotNull(route, 'route');
ArgumentError.checkNotNull(_handler, 'handler');
@@ -92,10 +98,13 @@
// invoke handler with given request and params
Future<Response> invoke(Request request, Map<String, String> params) async {
request = request.change(context: {'shelf_router/params': params});
- if (_handler is Handler || _params.isEmpty) {
- return await _handler(request);
- }
- return await Function.apply(
- _handler, [request]..addAll(_params.map((n) => params[n])));
+
+ return await _middleware((request) async {
+ if (_handler is Handler || _params.isEmpty) {
+ return await _handler(request);
+ }
+ return await Function.apply(
+ _handler, [request]..addAll(_params.map((n) => params[n])));
+ })(request);
}
}
diff --git a/pkgs/shelf_router/pubspec.yaml b/pkgs/shelf_router/pubspec.yaml
index f10cf88..5cf23ce 100644
--- a/pkgs/shelf_router/pubspec.yaml
+++ b/pkgs/shelf_router/pubspec.yaml
@@ -1,5 +1,5 @@
name: shelf_router
-version: 0.7.1
+version: 0.7.2
authors:
- Jonas Finnemann Jensen <jonasfj@google.com>
description: |
@@ -16,4 +16,4 @@
pedantic: ^1.4.0
http: ^0.12.0+1
environment:
- sdk: '>=2.0.0 <3.0.0'
\ No newline at end of file
+ sdk: '>=2.0.0 <3.0.0'
diff --git a/pkgs/shelf_router/test/router_test.dart b/pkgs/shelf_router/test/router_test.dart
index 6c09d5c..5eeae1b 100644
--- a/pkgs/shelf_router/test/router_test.dart
+++ b/pkgs/shelf_router/test/router_test.dart
@@ -39,6 +39,8 @@
tearDown(() => server.close());
Future<String> get(String path) => http.read(server.url.toString() + path);
+ Future<int> head(String path) async =>
+ (await http.head(server.url.toString() + path)).statusCode;
test('get sync/async handler', () async {
var app = Router();
@@ -63,6 +65,10 @@
expect(await get('/sync-hello'), 'hello-world');
expect(await get('/async-hello'), 'hello-world');
expect(await get('/wrong-path'), 'not-found');
+
+ expect(await head('/sync-hello'), 200);
+ expect(await head('/async-hello'), 200);
+ expect(await head('/wrong-path'), 200);
});
test('params', () async {