Skip to content

Commit 7e138a7

Browse files
author
Anna Gringauze
authored
Fix intermittent broken sockets on connection to asset server. (#1353)
* Limit number of simultaneous connections to prevent SocketExceptions * Add tests * Add reason to test expectations * Fix flaky event tests - remove tests waiting for COMPILER_UPDATE_DEPENDENCIES event since there is no way to start listening to debugger events before it is emitted, so the test was faiing randomly. - change expression evaluation tests to expect their events eventually, skipping potential COMPILER_UPDATE_DEPENDENCIES event that sometimes would get be picked up by the listener if it happened later that the listener setup. * Add logging in failing hot restart tests * Added logging to failing asset handler tests * Add more logging for failing tests * Removed verbose messages
1 parent da2b1fc commit 7e138a7

File tree

9 files changed

+147
-27
lines changed

9 files changed

+147
-27
lines changed

dwds/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
libraries present in medatata but not lodaded at runtime.
55
- Log failures to load kernel during expression evaluation.
66
- Show lowered final fields using their original dart names.
7+
- Limit simultaneous connections to asset server to prevent broken sockets.
78

89
## 11.1.1
910

dwds/lib/src/readers/proxy_server_asset_reader.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,13 @@ class ProxyServerAssetReader implements AssetReader {
2929
root ??= '';
3030
isHttps ??= false;
3131
var scheme = isHttps ? 'https://' : 'http://';
32+
var inner = HttpClient()
33+
..maxConnectionsPerHost = 200
34+
..idleTimeout = const Duration(seconds: 30)
35+
..connectionTimeout = const Duration(seconds: 30);
3236
_client = isHttps
33-
? IOClient(
34-
HttpClient()..badCertificateCallback = (cert, host, port) => true)
35-
: http.Client();
37+
? IOClient(inner..badCertificateCallback = (cert, host, port) => true)
38+
: IOClient(inner);
3639
var url = '$scheme$host:$assetServerPort/';
3740
if (root?.isNotEmpty ?? false) url += '$root/';
3841
_handler = proxyHandler(url, client: _client);

dwds/test/build_daemon_evaluate_test.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ void main() async {
8080
group('shared context with evaluation', () {
8181
setUpAll(() async {
8282
TestSetup.setCurrentLogWriter();
83-
await context.setUp(enableExpressionEvaluation: true, verbose: true);
83+
await context.setUp(
84+
enableExpressionEvaluation: true,
85+
verbose: false,
86+
);
8487
});
8588

8689
tearDownAll(() async {
@@ -519,7 +522,9 @@ void main() async {
519522
setUpAll(() async {
520523
TestSetup.setCurrentLogWriter();
521524
await context.setUp(
522-
enableExpressionEvaluation: false, verbose: false);
525+
enableExpressionEvaluation: false,
526+
verbose: false,
527+
);
523528
});
524529

525530
tearDownAll(() async {

dwds/test/chrome_proxy_service_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void main() {
3737
customLogWriter: (level, message,
3838
{loggerName, error, stackTrace, verbose}) =>
3939
printOnFailure('[$level] $loggerName: $message'));
40-
await context.setUp(verbose: true);
40+
await context.setUp(verbose: false);
4141
});
4242

4343
tearDownAll(() async {

dwds/test/events_test.dart

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,6 @@ void main() {
5555
await context.webDriver.driver.keyboard.sendChord([Keyboard.alt, 'd']);
5656
});
5757

58-
test('emits COMPILER_UPDATE_DEPENDENCIES event', () async {
59-
// The events stream is a broadcast stream so start listening before the
60-
// action.
61-
expect(
62-
context.testServer.dwds.events,
63-
emits(predicate((DwdsEvent event) =>
64-
event.type == 'COMPILER_UPDATE_DEPENDENCIES')));
65-
});
66-
6758
test('events can be listened to multiple times', () async {
6859
context.testServer.dwds.events.listen((_) {});
6960
context.testServer.dwds.events.listen((_) {});
@@ -102,7 +93,7 @@ void main() {
10293
var expression = "helloString('world')";
10394
expect(
10495
context.testServer.dwds.events,
105-
emits(predicate((DwdsEvent event) =>
96+
emitsThrough(predicate((DwdsEvent event) =>
10697
event.type == 'EVALUATE' &&
10798
event.payload['expression'] == expression)));
10899
await service.evaluate(
@@ -116,7 +107,7 @@ void main() {
116107
var expression = 'some-bad-expression';
117108
expect(
118109
context.testServer.dwds.events,
119-
emits(predicate((DwdsEvent event) =>
110+
emitsThrough(predicate((DwdsEvent event) =>
120111
event.type == 'EVALUATE' &&
121112
event.payload['expression'] == expression &&
122113
event.payload['exception'] is ErrorRef)));
@@ -153,7 +144,7 @@ void main() {
153144
test('emits EVALUATE_IN_FRAME events on RPC error', () async {
154145
expect(
155146
context.testServer.dwds.events,
156-
emits(predicate((DwdsEvent event) =>
147+
emitsThrough(predicate((DwdsEvent event) =>
157148
event.type == 'EVALUATE_IN_FRAME' &&
158149
event.payload['success'] == false &&
159150
event.payload['exception'] is RPCError &&
@@ -181,7 +172,7 @@ void main() {
181172
// so event is marked as success.
182173
expect(
183174
context.testServer.dwds.events,
184-
emits(predicate((DwdsEvent event) =>
175+
emitsThrough(predicate((DwdsEvent event) =>
185176
event.type == 'EVALUATE_IN_FRAME' &&
186177
event.payload['success'] == false &&
187178
event.payload['exception'] is ErrorRef)));
@@ -210,7 +201,7 @@ void main() {
210201
// so event is marked as success.
211202
expect(
212203
context.testServer.dwds.events,
213-
emits(predicate((DwdsEvent event) =>
204+
emitsThrough(predicate((DwdsEvent event) =>
214205
event.type == 'EVALUATE_IN_FRAME' &&
215206
event.payload['success'] == true)));
216207
try {

dwds/test/fixtures/context.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import 'package:dwds/src/utilities/dart_uri.dart';
1818
import 'package:dwds/src/utilities/shared.dart';
1919
import 'package:frontend_server_common/src/resident_runner.dart';
2020
import 'package:http/http.dart';
21+
import 'package:http/io_client.dart';
2122
import 'package:logging/logging.dart' as logging;
2223
import 'package:path/path.dart' as p;
2324
import 'package:shelf/shelf.dart';
@@ -133,7 +134,10 @@ class TestContext {
133134
try {
134135
configureLogWriter();
135136

136-
client = Client();
137+
client = IOClient(HttpClient()
138+
..maxConnectionsPerHost = 200
139+
..idleTimeout = const Duration(seconds: 30)
140+
..connectionTimeout = const Duration(seconds: 30));
137141

138142
var systemTempDir = Directory.systemTemp;
139143
_outputDir = systemTempDir.createTempSync('foo bar');
@@ -145,10 +149,22 @@ class TestContext {
145149
['--port=$chromeDriverPort', '--url-base=$chromeDriverUrlBase']);
146150
// On windows this takes a while to boot up, wait for the first line
147151
// of stdout as a signal that it is ready.
148-
await chromeDriver.stdout
152+
final stdOutLines = chromeDriver.stdout
149153
.transform(utf8.decoder)
150154
.transform(const LineSplitter())
151-
.first;
155+
.asBroadcastStream();
156+
157+
final stdErrLines = chromeDriver.stderr
158+
.transform(utf8.decoder)
159+
.transform(const LineSplitter())
160+
.asBroadcastStream();
161+
162+
stdOutLines
163+
.listen((line) => _logger.finest('ChromeDriver stdout: $line'));
164+
stdErrLines
165+
.listen((line) => _logger.warning('ChromeDriver stderr: $line'));
166+
167+
await stdOutLines.first;
152168
} catch (e) {
153169
throw StateError(
154170
'Could not start ChromeDriver. Is it installed?\nError: $e');
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Copyright (c) 2020, 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+
// @dart = 2.9
6+
7+
import 'dart:io';
8+
9+
import 'package:http/http.dart' as http;
10+
import 'package:http/io_client.dart';
11+
import 'package:shelf/shelf.dart';
12+
import 'package:shelf_proxy/shelf_proxy.dart';
13+
import 'package:test/test.dart';
14+
15+
import '../fixtures/context.dart';
16+
import '../fixtures/logging.dart';
17+
import '../fixtures/utilities.dart';
18+
19+
void setCurrentLogWriter() {
20+
configureLogWriter(
21+
customLogWriter: (level, message,
22+
{loggerName, error, stackTrace, verbose}) =>
23+
printOnFailure('[$level] $loggerName: $message'));
24+
}
25+
26+
void main() {
27+
group('Asset handler', () {
28+
final context = TestContext();
29+
Handler assetHandler;
30+
http.Client client;
31+
32+
setUpAll(() async {
33+
setCurrentLogWriter();
34+
await context.setUp(enableExpressionEvaluation: true, verbose: true);
35+
36+
client = IOClient(HttpClient()
37+
..maxConnectionsPerHost = 200
38+
..idleTimeout = const Duration(seconds: 30)
39+
..connectionTimeout = const Duration(seconds: 30));
40+
41+
var assetServerPort = daemonPort(context.workingDirectory);
42+
var pathToServe = context.pathToServe;
43+
44+
assetHandler = proxyHandler(
45+
'http://localhost:$assetServerPort/$pathToServe/',
46+
client: client);
47+
});
48+
49+
tearDownAll(() async {
50+
client.close();
51+
await context.tearDown();
52+
});
53+
54+
setUp(setCurrentLogWriter);
55+
56+
Future<void> readAsString(String path) async {
57+
var request = Request('GET', Uri.parse('http://foo:0000/$path'));
58+
var response = await assetHandler(request);
59+
var result = await response.readAsString();
60+
expect(result, isNotNull,
61+
reason: 'Failed to read $path: ${response.statusCode}');
62+
}
63+
64+
Future<void> readAsBytes(String path) async {
65+
var request = Request('GET', Uri.parse('http://foo:0000/$path'));
66+
var response = await assetHandler(request);
67+
var result = await response.read().toList();
68+
expect(result, isNotNull,
69+
reason: 'Failed to read $path: ${response.statusCode}');
70+
}
71+
72+
test('can read dill files', () async {
73+
var path = 'hello_world/main.unsound.ddc.full.dill';
74+
await readAsBytes(path);
75+
});
76+
77+
test('can read large number of resources simultaneously', () async {
78+
final n = 1000;
79+
var futures = [
80+
for (var i = 0; i < n; i++)
81+
readAsString('hello_world/main.unsound.ddc.js.map'),
82+
for (var i = 0; i < n; i++)
83+
readAsString('hello_world/main.unsound.ddc.js'),
84+
for (var i = 0; i < n; i++)
85+
readAsBytes('hello_world/main.unsound.ddc.full.dill'),
86+
];
87+
88+
await expectLater(Future.wait(futures), completes);
89+
});
90+
});
91+
}

dwds/test/restore_breakpoints_test.dart

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,24 @@ import 'package:vm_service/vm_service.dart';
1414
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
1515

1616
import 'fixtures/context.dart';
17+
import 'fixtures/logging.dart';
1718

1819
final context = TestContext();
1920
ChromeProxyService get service =>
2021
fetchChromeProxyService(context.debugConnection);
2122
WipConnection get tabConnection => context.tabConnection;
2223

24+
void setCurrentLogWriter() {
25+
configureLogWriter(
26+
customLogWriter: (level, message,
27+
{loggerName, error, stackTrace, verbose}) =>
28+
printOnFailure('[$level] $loggerName: $message'));
29+
}
30+
2331
void main() {
2432
setUpAll(() async {
25-
await context.setUp(restoreBreakpoints: true);
33+
setCurrentLogWriter();
34+
await context.setUp(restoreBreakpoints: true, verbose: true);
2635
});
2736

2837
tearDownAll(() async {
@@ -37,6 +46,7 @@ void main() {
3746
Stream<Event> isolateEventStream;
3847

3948
setUp(() async {
49+
setCurrentLogWriter();
4050
vm = await fetchChromeProxyService(context.debugConnection).getVM();
4151
isolate = await fetchChromeProxyService(context.debugConnection)
4252
.getIsolate(vm.isolates.first.id);
@@ -84,7 +94,7 @@ void main() {
8494

8595
var eventsDone = expectLater(
8696
isolateEventStream,
87-
emitsThrough(emitsInOrder([
97+
emits(emitsInOrder([
8898
predicate((Event event) => event.kind == EventKind.kIsolateExit),
8999
predicate((Event event) => event.kind == EventKind.kIsolateStart),
90100
predicate(

webdev/lib/src/serve/webdev_server.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ import 'package:build_daemon/data/build_status.dart' as daemon;
1111
import 'package:devtools_server/devtools_server.dart';
1212
import 'package:dwds/data/build_result.dart';
1313
import 'package:dwds/dwds.dart';
14-
import 'package:http/http.dart';
1514
import 'package:http/http.dart' as http;
15+
import 'package:http/io_client.dart';
1616
import 'package:http_multi_server/http_multi_server.dart';
1717
import 'package:shelf/shelf.dart';
1818
import 'package:shelf/shelf_io.dart' as shelf_io;
@@ -104,7 +104,10 @@ class WebDevServer {
104104
});
105105

106106
var cascade = Cascade();
107-
var client = Client();
107+
var client = IOClient(HttpClient()
108+
..maxConnectionsPerHost = 200
109+
..idleTimeout = const Duration(seconds: 30)
110+
..connectionTimeout = const Duration(seconds: 30));
108111
var assetHandler = proxyHandler(
109112
'http://localhost:${options.daemonPort}/${options.target}/',
110113
client: client);

0 commit comments

Comments
 (0)