| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, 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 library scheduled_test.scheduled_server; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 import 'dart:collection'; | |
| 9 import 'dart:io'; | |
| 10 | |
| 11 import 'package:http_multi_server/http_multi_server.dart'; | |
| 12 import 'package:shelf/shelf.dart' as shelf; | |
| 13 import 'package:shelf/shelf_io.dart' as shelf_io; | |
| 14 | |
| 15 import 'scheduled_test.dart'; | |
| 16 import 'src/scheduled_server/handler.dart'; | |
| 17 import 'src/utils.dart'; | |
| 18 | |
| 19 /// A class representing an HTTP server that's scheduled to run in the course of | |
| 20 /// the test. This class allows the server's request handling to be scheduled | |
| 21 /// synchronously. | |
| 22 /// | |
| 23 /// The server expects requests to be received in the order [handle] is called, | |
| 24 /// and expects that no additional requests will be received. | |
| 25 class ScheduledServer { | |
| 26 /// The description of the server. | |
| 27 final String description; | |
| 28 | |
| 29 /// The wrapped server. | |
| 30 final Future<HttpServer> _server; | |
| 31 | |
| 32 /// The queue of handlers to run for upcoming requests. Each [Future] will | |
| 33 /// complete once the schedule reaches the point where that handler was | |
| 34 /// scheduled. | |
| 35 final _handlers = new Queue<Handler>(); | |
| 36 | |
| 37 /// The number of servers created. Used for auto-generating descriptions; | |
| 38 static var _count = 0; | |
| 39 | |
| 40 ScheduledServer._(this._server, this.description); | |
| 41 | |
| 42 /// Creates a new server listening on an automatically-allocated port on | |
| 43 /// localhost (both IPv4 and IPv6, if available). | |
| 44 /// | |
| 45 /// [description] is used to refer to the server in debugging messages. | |
| 46 factory ScheduledServer([String description]) { | |
| 47 var id = _count++; | |
| 48 if (description == null) description = 'scheduled server $id'; | |
| 49 | |
| 50 var scheduledServer; | |
| 51 scheduledServer = new ScheduledServer._(schedule(() { | |
| 52 return HttpMultiServer.loopback(0).then((server) { | |
| 53 shelf_io.serveRequests(server, scheduledServer._handleRequest); | |
| 54 currentSchedule.onComplete.schedule(() => server.close(force: true)); | |
| 55 return server; | |
| 56 }); | |
| 57 }, "starting '$description'"), description); | |
| 58 return scheduledServer; | |
| 59 } | |
| 60 | |
| 61 /// The port on which the server is listening. | |
| 62 Future<int> get port => _server.then((s) => s.port); | |
| 63 | |
| 64 /// The base URL of the server, including its port. | |
| 65 Future<Uri> get url => port.then((p) => Uri.parse("http://localhost:$p")); | |
| 66 | |
| 67 /// Schedules [handler] to handle a request to the server with [method] and | |
| 68 /// [path]. The schedule will wait until an HTTP request is received. If that | |
| 69 /// request doesn't have the expected [method] and [path], it will fail. | |
| 70 /// Otherwise, it will run [fn]. If [fn] returns a [Future], the schedule will | |
| 71 /// wait until that [Future] completes. | |
| 72 /// | |
| 73 /// The request must be received at the point in the schedule at which | |
| 74 /// [handle] was called, or in the task immediately prior (to allow for | |
| 75 /// non-deterministic asynchronicity). Otherwise, an error will be thrown. | |
| 76 void handle(String method, String path, shelf.Handler fn) { | |
| 77 var handler = new Handler(this, method, path, fn); | |
| 78 _handlers.add(handler); | |
| 79 schedule(() { | |
| 80 handler.ready = true; | |
| 81 return handler.result; | |
| 82 }, "'$description' waiting for $method $path"); | |
| 83 } | |
| 84 | |
| 85 /// The handler for incoming [shelf.Request]s to this server. | |
| 86 /// | |
| 87 /// This dispatches the request to the first handler in the queue. It's that | |
| 88 /// handler's responsibility to check that the method/path are correct and | |
| 89 /// that it's being run at the correct time. | |
| 90 Future<shelf.Response> _handleRequest(shelf.Request request) { | |
| 91 return wrapFuture(syncFuture(() { | |
| 92 if (_handlers.isEmpty) { | |
| 93 fail("'$description' received ${request.method} ${request.url.path} " | |
| 94 "when no more requests were expected."); | |
| 95 } | |
| 96 return _handlers.removeFirst().fn(request); | |
| 97 }), 'receiving ${request.method} ${request.url.path}').catchError((error) { | |
| 98 // Don't let errors bubble up to the shelf handler. It will print them to | |
| 99 // stderr, but the user will already be notified via the scheduled_test | |
| 100 // infrastructure. | |
| 101 return new shelf.Response.internalServerError( | |
| 102 body: error.toString(), | |
| 103 headers: {'content-type': 'text/plain'}); | |
| 104 }); | |
| 105 } | |
| 106 } | |
| OLD | NEW |