| Index: pkg/shelf/lib/shelf_io.dart
|
| diff --git a/pkg/shelf/lib/shelf_io.dart b/pkg/shelf/lib/shelf_io.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..98a3596931b05595576e387e824519fe857ccaa0
|
| --- /dev/null
|
| +++ b/pkg/shelf/lib/shelf_io.dart
|
| @@ -0,0 +1,103 @@
|
| +// Copyright (c) 2014, 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.
|
| +
|
| +/// A Shelf adapter for handling [HttpRequest] objects from `dart:io`.
|
| +///
|
| +/// One can provide an instance of [HttpServer] as the `requests` parameter in
|
| +/// [serveRequests].
|
| +library shelf.io;
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +
|
| +import 'package:stack_trace/stack_trace.dart';
|
| +
|
| +import 'shelf.dart';
|
| +import 'src/util.dart';
|
| +
|
| +/// Starts an [HttpServer] that listens on the specified [address] and
|
| +/// [port] and sends requests to [handler].
|
| +///
|
| +/// See the documentation for [HttpServer.bind] for more details on [address],
|
| +/// [port], and [backlog].
|
| +Future<HttpServer> serve(Handler handler, address, int port,
|
| + {int backlog}) {
|
| + if (backlog == null) backlog = 0;
|
| + return HttpServer.bind(address, port, backlog: backlog).then((server) {
|
| + serveRequests(server, handler);
|
| + return server;
|
| + });
|
| +}
|
| +
|
| +/// Serve a [Stream] of [HttpRequest]s.
|
| +///
|
| +/// [HttpServer] implements [Stream<HttpRequest>] so it can be passed directly
|
| +/// to [serveRequests].
|
| +void serveRequests(Stream<HttpRequest> requests, Handler handler) {
|
| + requests.listen((request) => handleRequest(request, handler));
|
| +}
|
| +
|
| +/// Uses [handler] to handle [request].
|
| +///
|
| +/// Returns a [Future] which completes when the request has been handled.
|
| +Future handleRequest(HttpRequest request, Handler handler) {
|
| + var shelfRequest = _fromHttpRequest(request);
|
| +
|
| + return syncFuture(() => handler(shelfRequest))
|
| + .catchError((error, stackTrace) {
|
| + var chain = new Chain.current();
|
| + if (stackTrace != null) {
|
| + chain = new Chain.forTrace(stackTrace)
|
| + .foldFrames((frame) => frame.isCore || frame.package == 'shelf')
|
| + .terse;
|
| + }
|
| +
|
| + return _logError('Error thrown by handler\n$error\n$chain');
|
| + }).then((response) {
|
| + if (response == null) {
|
| + response = _logError('null response from handler');
|
| + }
|
| +
|
| + return _writeResponse(response, request.response);
|
| + });
|
| +}
|
| +
|
| +/// Creates a new [Request] from the provided [HttpRequest].
|
| +Request _fromHttpRequest(HttpRequest request) {
|
| + //TODO(kevmoo): make headers case-insensitive
|
| + var headers = {};
|
| + request.headers.forEach((k, v) {
|
| + // Multiple header values are joined with commas.
|
| + // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22
|
| + headers[k] = v.join(',');
|
| + });
|
| +
|
| + return new Request(request.uri.path, request.uri.query, request.method,
|
| + '', request.protocolVersion, request.requestedUri,
|
| + headers, body: request);
|
| +}
|
| +
|
| +Future _writeResponse(Response response, HttpResponse httpResponse) {
|
| + httpResponse.statusCode = response.statusCode;
|
| +
|
| + response.headers.forEach((header, value) {
|
| + if (value == null) return;
|
| + httpResponse.headers.set(header, value);
|
| + });
|
| +
|
| + if (response.headers[HttpHeaders.SERVER] == null) {
|
| + var value = httpResponse.headers.value(HttpHeaders.SERVER);
|
| + httpResponse.headers.set(HttpHeaders.SERVER, '$value with Shelf');
|
| + }
|
| + return httpResponse.addStream(response.read())
|
| + .then((_) => httpResponse.close());
|
| +}
|
| +
|
| +// TODO(kevmoo) A developer mode is needed to include error info in response
|
| +// TODO(kevmoo) Make error output plugable. stderr, logging, etc
|
| +Response _logError(String message) {
|
| + stderr.writeln('ERROR - ${new DateTime.now()}');
|
| + stderr.writeln(message);
|
| + return new Response.internalServerError();
|
| +}
|
|
|