Chromium Code Reviews| 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; |
| + } |
| +} |