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_server; | |
6 | |
7 import 'dart:async'; | |
8 import 'dart:collection'; | |
9 import 'dart:io'; | |
10 import 'dart:uri'; | |
11 | |
12 import 'scheduled_test.dart'; | |
13 import 'src/utils.dart'; | |
14 import 'src/scheduled_server/handler.dart'; | |
15 | |
16 typedef Future ScheduledHandler(HttpRequest request); | |
17 | |
18 /// A class representing an [HttpServer] that's scheduled to run in the course | |
19 /// of the test. This class allows the server's request handling to be scheduled | |
20 /// synchronously. | |
21 /// | |
22 /// The server expects requests to be received in the order [handle] is called, | |
23 /// and expects that no additional requests will be received. | |
24 class ScheduledServer { | |
25 /// The description of the server. | |
26 final String description; | |
27 | |
28 /// The wrapped server. | |
29 final Future<HttpServer> _server; | |
30 | |
31 /// The queue of handlers to run for upcoming requests. Each [Future] will | |
32 /// complete once the schedule reaches the point where that handler was | |
33 /// scheduled. | |
34 final _handlers = new Queue<Handler>(); | |
35 | |
36 /// The number of servers created. Used for auto-generating descriptions; | |
37 static var _count = 0; | |
38 | |
39 ScheduledServer._(this._server, this.description); | |
40 | |
41 /// Creates a new server listening on an automatically-allocated port on | |
42 /// localhost. [description] is used to refer to the server in debugging | |
43 /// messages. | |
44 factory ScheduledServer([String description]) { | |
45 var id = _count++; | |
Bob Nystrom
2013/03/13 21:02:35
Nit, but I think we should only increment the id w
nweiz
2013/03/13 23:50:59
I disagree. I think it's a more useful property th
| |
46 if (description == null) description = 'scheduled server $id'; | |
47 | |
48 var scheduledServer; | |
49 scheduledServer = new ScheduledServer._(schedule(() { | |
50 return HttpServer.bind("127.0.0.1", 0).then((server) { | |
51 server.listen(scheduledServer._handleRequest, | |
52 onError: (e) => currentSchedule.signalError(e)); | |
53 currentSchedule.onComplete.schedule(server.close); | |
54 return server; | |
55 }); | |
56 }, "starting '$description'"), description); | |
57 return scheduledServer; | |
58 } | |
59 | |
60 /// The port on which the server is listening. | |
61 Future<int> get port => _server.then((s) => s.port); | |
62 | |
63 /// The base URL of the server, including its port. | |
64 Future<Uri> get url => port.then((p) => Uri.parse("http://localhost:$p")); | |
65 | |
66 /// Schedules [handler] to handle a request to the server with [method] and | |
67 /// [path]. The schedule will block until an HTTP request is received. If that | |
Bob Nystrom
2013/03/13 21:02:35
block -> wait
nweiz
2013/03/13 23:50:59
Done.
| |
68 /// request doesn't have the expected [method] and [path], it will fail. | |
69 /// Otherwise, it will run [fn]. If [fn] returns a [Future], the schedule | |
70 /// will block until that [Future] completes. | |
Bob Nystrom
2013/03/13 21:02:35
block -> wait
It's more or less equivalent, but I
nweiz
2013/03/13 23:50:59
Done.
| |
71 /// | |
72 /// The request must be received at the point in the schedule at which | |
73 /// [handle] was called, or in the task immediately prior (to allow for | |
74 /// non-deterministic asynchronicity). Otherwise, an error will be thrown. | |
75 void handle(String method, String path, ScheduledHandler fn) { | |
76 var handler = new Handler(this, method, path, fn); | |
77 _handlers.add(handler); | |
78 schedule(() { | |
79 handler.ready = true; | |
80 return handler.result.catchError((e) { | |
81 // Close the server so that we don't leave a dangling request. | |
82 _server.then((s) => s.close()); | |
83 throw e; | |
84 }); | |
85 }, "'$description' waiting for $method $path"); | |
86 } | |
87 | |
88 /// The handler for incoming [HttpRequest]s to this server. This dispatches | |
89 /// the request to the first handler in the queue. It's that handler's | |
90 /// responsibility to check that the method/path are correct and that it's | |
91 /// being run at the correct time. | |
92 void _handleRequest(HttpRequest request) { | |
93 wrapFuture(new Future.of(() { | |
94 if (_handlers.isEmpty) { | |
95 throw "'$description' received ${request.method} ${request.uri.path} " | |
96 "unexpectedly."; | |
Bob Nystrom
2013/03/13 21:02:35
"when no more requests were expected"
nweiz
2013/03/13 23:50:59
Done.
| |
97 } | |
98 return _handlers.removeFirst().fn(request); | |
99 }).catchError((e) { | |
100 // Close the server so that we don't leave a dangling request. | |
101 _server.then((s) => s.close()); | |
102 throw e; | |
103 }), 'receiving ${request.method} ${request.uri}'); | |
104 } | |
105 } | |
OLD | NEW |