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 returns an |
| 67 /// [HttpMultiServer] listening to [port] on both loopback addresses. |
| 68 /// Otherwise, it returns 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 /// Returns an HttpConnectionsInfo object summarizing the total number of |
| 124 /// current connections handled by all the servers. |
| 125 HttpConnectionsInfo connectionsInfo() { |
| 126 var info = new HttpConnectionsInfo(); |
| 127 for (var server in _servers) { |
| 128 var subInfo = server.connectionsInfo(); |
| 129 info.total += subInfo.total; |
| 130 info.active += subInfo.active; |
| 131 info.idle += subInfo.idle; |
| 132 info.closing += subInfo.closing; |
| 133 } |
| 134 return info; |
| 135 } |
| 136 } |
OLD | NEW |