OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2016, 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 import 'dart:io'; |
| 6 import 'dart:async'; |
| 7 |
| 8 class ChildServerProcess { |
| 9 /// [IOSink]s like [stdout] don't like to be piped more than one [Stream], but |
| 10 /// we want to pipe many of them (basically, every standard output and error |
| 11 /// of every child process we open), so we pipe to an accommodating consumer. |
| 12 static final _consoleOut = new _MultipleStreamConsumer(stdout); |
| 13 |
| 14 final Process process; |
| 15 final String host; |
| 16 final int port; |
| 17 ChildServerProcess._(this.process, this.host, this.port); |
| 18 |
| 19 get httpUri => Uri.parse('http://$host:$port'); |
| 20 |
| 21 static build(Future<Process> builder(String host, int port), |
| 22 {int defaultPort: 1024, |
| 23 int maxPort: 65535, |
| 24 String host: '0.0.0.0'}) async { |
| 25 var port = await _findUnusedPort(defaultPort, maxPort); |
| 26 var p = (await builder(host, port)) |
| 27 ..stdout.pipe(_consoleOut) |
| 28 ..stderr.pipe(_consoleOut); |
| 29 await _waitForServer(host, port); |
| 30 return new ChildServerProcess._(p, host, port); |
| 31 } |
| 32 |
| 33 static _waitForServer(String host, int port, |
| 34 {int attempts: 10, |
| 35 Duration retryDelay: const Duration(seconds: 1)}) async { |
| 36 var lastError; |
| 37 for (int i = 0; i < attempts; i++) { |
| 38 try { |
| 39 await (await Socket.connect(host, port)).close(); |
| 40 return; |
| 41 } catch (e) { |
| 42 lastError = e; |
| 43 await new Future.delayed(retryDelay); |
| 44 } |
| 45 } |
| 46 throw new StateError( |
| 47 'Failed to connect to $host:$port after $attempts attempts; ' |
| 48 'Last error:\n$lastError'); |
| 49 } |
| 50 |
| 51 static Future<int> _findUnusedPort(int fromPort, int toPort) async { |
| 52 var lastError; |
| 53 for (int port = fromPort; port <= toPort; port++) { |
| 54 try { |
| 55 await (await ServerSocket.bind(InternetAddress.ANY_IP_V4, port)) |
| 56 .close(); |
| 57 return port; |
| 58 } catch (e) { |
| 59 lastError = e; |
| 60 } |
| 61 } |
| 62 throw new StateError( |
| 63 'Failed to find an unused port between $fromPort and $toPort; ' |
| 64 'Last error:\n$lastError'); |
| 65 } |
| 66 } |
| 67 |
| 68 /// A consumer into which we can pipe as many streams as we want, that forwards |
| 69 /// everything to an [IOSink] (such as [stdout]). |
| 70 class _MultipleStreamConsumer extends StreamConsumer<List<int>> { |
| 71 final IOSink _sink; |
| 72 _MultipleStreamConsumer(this._sink); |
| 73 |
| 74 @override |
| 75 Future addStream(Stream<List<int>> stream) async { |
| 76 await for (var data in stream) { |
| 77 _sink.add(data); |
| 78 } |
| 79 } |
| 80 |
| 81 @override |
| 82 close() {} |
| 83 } |
OLD | NEW |