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 library http_multi_server; | |
6 | |
7 import 'dart:async'; | |
8 import 'dart:io'; | |
9 | |
10 import 'src/utils.dart'; | |
11 | |
12 /// An implementation of `dart:io`'s [HttpServer] that wraps multiple servers | |
13 /// and forwards methods to all of them. | |
14 /// | |
15 /// This is useful for serving the same application on multiple network | |
16 /// interfaces while still having a unified way of controlling the servers. In | |
17 /// particular, it supports serving on both the IPv4 and IPv6 loopback addresses | |
18 /// using [HttpMultiServer.loopback]. | |
19 class HttpMultiServer extends StreamView<HttpRequest> implements HttpServer { | |
20 /// The wrapped servers. | |
21 final Set<HttpServer> _servers; | |
22 | |
23 String get serverHeader => _servers.first.serverHeader; | |
24 set serverHeader(String value) { | |
25 for (var server in _servers) { | |
26 server.serverHeader = value; | |
27 } | |
28 } | |
29 | |
30 Duration get idleTimeout => _servers.first.idleTimeout; | |
31 set idleTimeout(Duration value) { | |
32 for (var server in _servers) { | |
33 server.idleTimeout = value; | |
34 } | |
35 } | |
36 | |
37 /// Returns the port that one of the wrapped servers is listening on. | |
38 /// | |
39 /// If the wrapped servers are listening on different ports, it's not defined | |
40 /// which port is returned. | |
41 int get port => _servers.first.port; | |
42 | |
43 /// Returns the address that one of the wrapped servers is listening on. | |
44 /// | |
45 /// If the wrapped servers are listening on different addresses, it's not | |
46 /// defined which address is returned. | |
47 InternetAddress get address => _servers.first.address; | |
48 | |
49 set sessionTimeout(int value) { | |
50 for (var server in _servers) { | |
51 server.sessionTimeout = value; | |
52 } | |
53 } | |
54 | |
55 /// Creates an [HttpMultiServer] wrapping [servers]. | |
56 /// | |
57 /// All [servers] should have the same configuration and none should be | |
58 /// listened to when this is called. | |
59 HttpMultiServer(Iterable<HttpServer> servers) | |
60 : _servers = servers.toSet(), | |
61 super(mergeStreams(servers)); | |
62 | |
63 /// Creates an [HttpServer] listening on all available loopback addresses for | |
64 /// this computer. | |
65 /// | |
66 /// If this computer supports both IPv4 and IPv6, this will return an | |
Bob Nystrom
2014/06/04 23:58:17
"will return" -> "returns", and below.
nweiz
2014/06/05 00:36:09
Done.
| |
67 /// [HttpMultiServer] listening to [port] on both loopback addresses. | |
68 /// Otherwise, it will return a normal [HttpServer] listening only on the IPv4 | |
69 /// address. | |
70 /// | |
71 /// If [port] is 0, the same ephemeral port is used for both the IPv4 and IPv6 | |
72 /// addresses. | |
73 static Future<HttpServer> loopback(int port, {int backlog}) { | |
74 if (backlog == null) backlog = 0; | |
75 | |
76 return _loopback(port, (address, port) => | |
77 HttpServer.bind(address, port, backlog: backlog)); | |
78 } | |
79 | |
80 /// Like [loopback], but supports HTTPS requests. | |
81 /// | |
82 /// The certificate with nickname or distinguished name (DN) [certificateName] | |
83 /// is looked up in the certificate database, and is used as the server | |
84 /// certificate. If [requestClientCertificate] is true, the server will | |
85 /// request clients to authenticate with a client certificate. | |
86 static Future<HttpServer> loopbackSecure(int port, {int backlog, | |
87 String certificateName, bool requestClientCertificate: false}) { | |
88 if (backlog == null) backlog = 0; | |
89 | |
90 return _loopback(port, (address, port) => | |
91 HttpServer.bindSecure(address, port, backlog: backlog, | |
92 certificateName: certificateName, | |
93 requestClientCertificate: requestClientCertificate)); | |
94 } | |
95 | |
96 /// A helper method for initializing loopback servers. | |
97 /// | |
98 /// [bind] should forward to either [HttpServer.bind] or | |
99 /// [HttpServer.bindSecure]. | |
100 static Future<HttpServer> _loopback(int port, | |
101 Future<HttpServer> bind(InternetAddress address, int port)) { | |
102 return Future.wait([ | |
103 supportsIpV6, | |
104 bind(InternetAddress.LOOPBACK_IP_V4, port) | |
105 ]).then((results) { | |
106 var supportsIpV6 = results[0]; | |
107 var v4Server = results[1]; | |
108 | |
109 if (!supportsIpV6) return v4Server; | |
110 | |
111 // Reuse the IPv4 server's port so that if [port] is 0, both servers use | |
112 // the same ephemeral port. | |
113 return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port) | |
114 .then((v6Server) { | |
115 return new HttpMultiServer([v4Server, v6Server]); | |
116 }); | |
117 }); | |
118 } | |
119 | |
120 Future close({bool force: false}) => | |
121 Future.wait(_servers.map((server) => server.close(force: force))); | |
122 | |
123 HttpConnectionsInfo connectionsInfo() { | |
Bob Nystrom
2014/06/04 23:58:17
Document this since it has interesting semantics.
nweiz
2014/06/05 00:36:09
Done.
| |
124 var info = new HttpConnectionsInfo(); | |
125 for (var server in _servers) { | |
126 var subInfo = server.connectionsInfo(); | |
127 info.total += subInfo.total; | |
128 info.active += subInfo.active; | |
129 info.idle += subInfo.idle; | |
130 info.closing += subInfo.closing; | |
131 } | |
132 return info; | |
133 } | |
134 } | |
OLD | NEW |