Index: pkg/http_multi_server/lib/http_multi_server.dart |
diff --git a/pkg/http_multi_server/lib/http_multi_server.dart b/pkg/http_multi_server/lib/http_multi_server.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8beb6dfe1a59ebefecacd79c211d7ec1bfbbf550 |
--- /dev/null |
+++ b/pkg/http_multi_server/lib/http_multi_server.dart |
@@ -0,0 +1,134 @@ |
+// 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. |
+ |
+library http_multi_server; |
+ |
+import 'dart:async'; |
+import 'dart:io'; |
+ |
+import 'src/utils.dart'; |
+ |
+/// An implementation of `dart:io`'s [HttpServer] that wraps multiple servers |
+/// and forwards methods to all of them. |
+/// |
+/// This is useful for serving the same application on multiple network |
+/// interfaces while still having a unified way of controlling the servers. In |
+/// particular, it supports serving on both the IPv4 and IPv6 loopback addresses |
+/// using [HttpMultiServer.loopback]. |
+class HttpMultiServer extends StreamView<HttpRequest> implements HttpServer { |
+ /// The wrapped servers. |
+ final Set<HttpServer> _servers; |
+ |
+ String get serverHeader => _servers.first.serverHeader; |
+ set serverHeader(String value) { |
+ for (var server in _servers) { |
+ server.serverHeader = value; |
+ } |
+ } |
+ |
+ Duration get idleTimeout => _servers.first.idleTimeout; |
+ set idleTimeout(Duration value) { |
+ for (var server in _servers) { |
+ server.idleTimeout = value; |
+ } |
+ } |
+ |
+ /// Returns the port that one of the wrapped servers is listening on. |
+ /// |
+ /// If the wrapped servers are listening on different ports, it's not defined |
+ /// which port is returned. |
+ int get port => _servers.first.port; |
+ |
+ /// Returns the address that one of the wrapped servers is listening on. |
+ /// |
+ /// If the wrapped servers are listening on different addresses, it's not |
+ /// defined which address is returned. |
+ InternetAddress get address => _servers.first.address; |
+ |
+ set sessionTimeout(int value) { |
+ for (var server in _servers) { |
+ server.sessionTimeout = value; |
+ } |
+ } |
+ |
+ /// Creates an [HttpMultiServer] wrapping [servers]. |
+ /// |
+ /// All [servers] should have the same configuration and none should be |
+ /// listened to when this is called. |
+ HttpMultiServer(Iterable<HttpServer> servers) |
+ : _servers = servers.toSet(), |
+ super(mergeStreams(servers)); |
+ |
+ /// Creates an [HttpServer] listening on all available loopback addresses for |
+ /// this computer. |
+ /// |
+ /// 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.
|
+ /// [HttpMultiServer] listening to [port] on both loopback addresses. |
+ /// Otherwise, it will return a normal [HttpServer] listening only on the IPv4 |
+ /// address. |
+ /// |
+ /// If [port] is 0, the same ephemeral port is used for both the IPv4 and IPv6 |
+ /// addresses. |
+ static Future<HttpServer> loopback(int port, {int backlog}) { |
+ if (backlog == null) backlog = 0; |
+ |
+ return _loopback(port, (address, port) => |
+ HttpServer.bind(address, port, backlog: backlog)); |
+ } |
+ |
+ /// Like [loopback], but supports HTTPS requests. |
+ /// |
+ /// The certificate with nickname or distinguished name (DN) [certificateName] |
+ /// is looked up in the certificate database, and is used as the server |
+ /// certificate. If [requestClientCertificate] is true, the server will |
+ /// request clients to authenticate with a client certificate. |
+ static Future<HttpServer> loopbackSecure(int port, {int backlog, |
+ String certificateName, bool requestClientCertificate: false}) { |
+ if (backlog == null) backlog = 0; |
+ |
+ return _loopback(port, (address, port) => |
+ HttpServer.bindSecure(address, port, backlog: backlog, |
+ certificateName: certificateName, |
+ requestClientCertificate: requestClientCertificate)); |
+ } |
+ |
+ /// A helper method for initializing loopback servers. |
+ /// |
+ /// [bind] should forward to either [HttpServer.bind] or |
+ /// [HttpServer.bindSecure]. |
+ static Future<HttpServer> _loopback(int port, |
+ Future<HttpServer> bind(InternetAddress address, int port)) { |
+ return Future.wait([ |
+ supportsIpV6, |
+ bind(InternetAddress.LOOPBACK_IP_V4, port) |
+ ]).then((results) { |
+ var supportsIpV6 = results[0]; |
+ var v4Server = results[1]; |
+ |
+ if (!supportsIpV6) return v4Server; |
+ |
+ // Reuse the IPv4 server's port so that if [port] is 0, both servers use |
+ // the same ephemeral port. |
+ return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port) |
+ .then((v6Server) { |
+ return new HttpMultiServer([v4Server, v6Server]); |
+ }); |
+ }); |
+ } |
+ |
+ Future close({bool force: false}) => |
+ Future.wait(_servers.map((server) => server.close(force: force))); |
+ |
+ 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.
|
+ var info = new HttpConnectionsInfo(); |
+ for (var server in _servers) { |
+ var subInfo = server.connectionsInfo(); |
+ info.total += subInfo.total; |
+ info.active += subInfo.active; |
+ info.idle += subInfo.idle; |
+ info.closing += subInfo.closing; |
+ } |
+ return info; |
+ } |
+} |