Skip to content

Adding middleware #85

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<Response>]. 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].
///
Expand Down
15 changes: 15 additions & 0 deletions lib/src/handler.dart
Original file line number Diff line number Diff line change
@@ -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<Response>].
///
/// A [Handler] may call an underlying HTTP implementation, or it may wrap
/// another [Handler] or a [Client].
typedef Future<Response> Handler(Request request);
31 changes: 31 additions & 0 deletions lib/src/handler_client.dart
Original file line number Diff line number Diff line change
@@ -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<Response> send(Request request) => _handler(request);

/// Closes the client and cleans up any resources associated with it.
void close() {
_close();
}
}
66 changes: 66 additions & 0 deletions lib/src/middleware.dart
Original file line number Diff line number Diff line change
@@ -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<Request>]. 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<Response]. The modified [Response] is then returned.
///
/// If provided, [onClose] will be invoked when the [Client.close] method is
/// called. Any cleanup of resources should happen at this point.
///
/// If provided, [errorHandler] receives errors thrown by the inner handler. It
/// does not receive errors thrown by [requestHandler] or [responseHandler].
/// It can either return a new response or throw an error.
Middleware createMiddleware({
Future<Request> requestHandler(Request request),
Future<Response> 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();
},
);
};
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"