OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 /// A Shelf adapter for handling [HttpRequest] objects from `dart:io`. |
| 6 /// |
| 7 /// One can provide an instance of [HttpServer] as the `requests` parameter in |
| 8 /// [serveRequests]. |
| 9 library shelf.io; |
| 10 |
| 11 import 'dart:async'; |
| 12 import 'dart:io'; |
| 13 |
| 14 import 'package:stack_trace/stack_trace.dart'; |
| 15 |
| 16 import 'shelf.dart'; |
| 17 import 'src/util.dart'; |
| 18 |
| 19 /// Starts an [HttpServer] that listens on the specified [address] and |
| 20 /// [port] and sends requests to [handler]. |
| 21 /// |
| 22 /// See the documentation for [HttpServer.bind] for more details on [address], |
| 23 /// [port], and [backlog]. |
| 24 Future<HttpServer> serve(Handler handler, address, int port, |
| 25 {int backlog}) { |
| 26 if (backlog == null) backlog = 0; |
| 27 return HttpServer.bind(address, port, backlog: backlog).then((server) { |
| 28 serveRequests(server, handler); |
| 29 return server; |
| 30 }); |
| 31 } |
| 32 |
| 33 /// Serve a [Stream] of [HttpRequest]s. |
| 34 /// |
| 35 /// [HttpServer] implements [Stream<HttpRequest>] so it can be passed directly |
| 36 /// to [serveRequests]. |
| 37 void serveRequests(Stream<HttpRequest> requests, Handler handler) { |
| 38 requests.listen((request) => handleRequest(request, handler)); |
| 39 } |
| 40 |
| 41 /// Uses [handler] to handle [request]. |
| 42 /// |
| 43 /// Returns a [Future] which completes when the request has been handled. |
| 44 Future handleRequest(HttpRequest request, Handler handler) { |
| 45 var shelfRequest = _fromHttpRequest(request); |
| 46 |
| 47 return syncFuture(() => handler(shelfRequest)) |
| 48 .catchError((error, stackTrace) { |
| 49 var chain = new Chain.current(); |
| 50 if (stackTrace != null) { |
| 51 chain = new Chain.forTrace(stackTrace) |
| 52 .foldFrames((frame) => frame.isCore || frame.package == 'shelf') |
| 53 .terse; |
| 54 } |
| 55 |
| 56 return _logError('Error thrown by handler\n$error\n$chain'); |
| 57 }).then((response) { |
| 58 if (response == null) { |
| 59 response = _logError('null response from handler'); |
| 60 } |
| 61 |
| 62 return _writeResponse(response, request.response); |
| 63 }); |
| 64 } |
| 65 |
| 66 /// Creates a new [Request] from the provided [HttpRequest]. |
| 67 Request _fromHttpRequest(HttpRequest request) { |
| 68 //TODO(kevmoo): make headers case-insensitive |
| 69 var headers = {}; |
| 70 request.headers.forEach((k, v) { |
| 71 // Multiple header values are joined with commas. |
| 72 // See http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-21#page-22 |
| 73 headers[k] = v.join(','); |
| 74 }); |
| 75 |
| 76 return new Request(request.uri.path, request.uri.query, request.method, |
| 77 '', request.protocolVersion, request.requestedUri, |
| 78 headers, body: request); |
| 79 } |
| 80 |
| 81 Future _writeResponse(Response response, HttpResponse httpResponse) { |
| 82 httpResponse.statusCode = response.statusCode; |
| 83 |
| 84 response.headers.forEach((header, value) { |
| 85 if (value == null) return; |
| 86 httpResponse.headers.set(header, value); |
| 87 }); |
| 88 |
| 89 if (response.headers[HttpHeaders.SERVER] == null) { |
| 90 var value = httpResponse.headers.value(HttpHeaders.SERVER); |
| 91 httpResponse.headers.set(HttpHeaders.SERVER, '$value with Shelf'); |
| 92 } |
| 93 return httpResponse.addStream(response.read()) |
| 94 .then((_) => httpResponse.close()); |
| 95 } |
| 96 |
| 97 // TODO(kevmoo) A developer mode is needed to include error info in response |
| 98 // TODO(kevmoo) Make error output plugable. stderr, logging, etc |
| 99 Response _logError(String message) { |
| 100 stderr.writeln('ERROR - ${new DateTime.now()}'); |
| 101 stderr.writeln(message); |
| 102 return new Response.internalServerError(); |
| 103 } |
OLD | NEW |