diff --git a/lib/src/client.dart b/lib/src/client.dart index fd4144326f..82b4dc4bed 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -7,6 +7,8 @@ import 'dart:convert'; import 'dart:typed_data'; import 'base_client.dart'; +import 'handler.dart'; +import 'handler_client.dart'; import 'io_client.dart'; import 'request.dart'; import 'response.dart'; @@ -28,6 +30,15 @@ abstract class Client { /// [BrowserClient] if `dart:html` is available. factory Client() => new IOClient(); + /// Creates a new [Client] from a [handler] callback. + /// + /// The [handler] is a function that receives a [Request] and returns a + /// [Future]. It will be called when [Client.send] is invoked. + /// + /// When [Client.close] is called the [onClose] function will be called. + factory Client.handler(Handler handler, {void onClose()}) + => new HandlerClient(handler, onClose ?? () {}); + /// Sends an HTTP HEAD request with the given headers to the given URL, which /// can be a [Uri] or a [String]. /// diff --git a/lib/src/handler.dart b/lib/src/handler.dart new file mode 100644 index 0000000000..f1d3a83f3b --- /dev/null +++ b/lib/src/handler.dart @@ -0,0 +1,15 @@ +// Copyright (c) 2017, 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 'request.dart'; +import 'response.dart'; + +/// The signature of a function which handles a [Request] and returns a +/// [Future]. +/// +/// A [Handler] may call an underlying HTTP implementation, or it may wrap +/// another [Handler] or a [Client]. +typedef Future Handler(Request request); diff --git a/lib/src/handler_client.dart b/lib/src/handler_client.dart new file mode 100644 index 0000000000..475a3067e5 --- /dev/null +++ b/lib/src/handler_client.dart @@ -0,0 +1,31 @@ +// Copyright (c) 2017, 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 'base_client.dart'; +import 'handler.dart'; +import 'request.dart'; +import 'response.dart'; + +/// A [Handler]-based HTTP client. +/// +/// The [HandlerClient] allows composition of a [Client] within a larger +/// application. +class HandlerClient extends BaseClient { + final Handler _handler; + final void Function() _close; + + /// Creates a new client using the [_handler] and [onClose] functions. + HandlerClient(this._handler, void onClose()) + : _close = onClose; + + /// Sends an HTTP request and asynchronously returns the response. + Future send(Request request) => _handler(request); + + /// Closes the client and cleans up any resources associated with it. + void close() { + _close(); + } +} diff --git a/lib/src/middleware.dart b/lib/src/middleware.dart new file mode 100644 index 0000000000..5635a5285c --- /dev/null +++ b/lib/src/middleware.dart @@ -0,0 +1,66 @@ +// Copyright (c) 2017, 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 'client.dart'; +import 'handler_client.dart'; +import 'request.dart'; +import 'response.dart'; + +/// A function which creates a new [Client] by wrapping a [Client]. +/// +/// You can extend the functions of a [Client] by wrapping it in [Middleware] +/// that can intercept and process a HTTP request before it it sent to a +/// client, a response after it is received by a client, or both. +/// +/// Because [Middleware] consumes a [Client] and returns a new [Client], +/// multiple [Middleware] instances can be composed together to offer rich +/// functionality. +/// +/// Common uses for middleware include caching, logging, and authentication. +/// +/// A simple [Middleware] can be created using [createMiddleware]. +typedef Client Middleware(Client inner); + +/// Creates a [Middleware] using the provided functions. +/// +/// If provided, [requestHandler] receives a [Request]. It replies to the +/// request by returning a [Future]. The modified [Request] is then +/// sent to the inner [Client]. +/// +/// If provided, [responseHandler] is called with the [Response] generated +/// by the inner [Client]. It replies to the response by returning a +/// [Future requestHandler(Request request), + Future responseHandler(Response response), + void onClose(), + void errorHandler(error, [StackTrace stackTrace]) +}) { + requestHandler ??= (request) async => request; + responseHandler ??= (response) async => response; + + return (inner) { + return new HandlerClient( + (request) => + requestHandler(request) + .then((req) => inner.send(req)) + .then((res) => responseHandler(res), onError: errorHandler), + onClose == null + ? inner.close + : () { + onClose(); + inner.close(); + }, + ); + }; +} diff --git a/pubspec.yaml b/pubspec.yaml index f059565133..10e970bec8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,4 +12,4 @@ dependencies: dev_dependencies: unittest: ">=0.9.0 <0.12.0" environment: - sdk: ">=1.23.0-dev.0.0 <2.0.0" + sdk: ">=1.24.0-dev.0.0 <2.0.0"