Skip to content

Commit a504b93

Browse files
authored
Addresses the feedbacks in #95738 (#97457)
1 parent 13f106b commit a504b93

File tree

4 files changed

+260
-82
lines changed

4 files changed

+260
-82
lines changed

packages/flutter_tools/lib/src/commands/daemon.dart

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,12 +1340,38 @@ class ProxyDomain extends Domain {
13401340
Future<String> connect(Map<String, dynamic> args) async {
13411341
final int targetPort = _getIntArg(args, 'port', required: true);
13421342
final String id = 'portForwarder_${targetPort}_${_id++}';
1343-
final Socket socket = await Socket.connect('127.0.0.1', targetPort);
1343+
1344+
Socket socket;
1345+
1346+
try {
1347+
socket = await Socket.connect(InternetAddress.loopbackIPv4, targetPort);
1348+
} on SocketException {
1349+
globals.logger.printTrace('Connecting to localhost:$targetPort failed with IPv4');
1350+
}
1351+
1352+
try {
1353+
// If connecting to IPv4 loopback interface fails, try IPv6.
1354+
socket ??= await Socket.connect(InternetAddress.loopbackIPv6, targetPort);
1355+
} on SocketException {
1356+
globals.logger.printError('Connecting to localhost:$targetPort failed');
1357+
}
1358+
1359+
if (socket == null) {
1360+
throw Exception('Failed to connect to the port');
1361+
}
1362+
13441363
_forwardedConnections[id] = socket;
13451364
socket.listen((List<int> data) {
13461365
sendEvent('proxy.data.$id', null, data);
1366+
}, onError: (dynamic error, StackTrace stackTrace) {
1367+
// Socket error, probably disconnected.
1368+
globals.logger.printTrace('Socket error: $error, $stackTrace');
13471369
});
1348-
unawaited(socket.done.then((dynamic _) {
1370+
1371+
unawaited(socket.done.catchError((dynamic error, StackTrace stackTrace) {
1372+
// Socket error, probably disconnected.
1373+
globals.logger.printTrace('Socket error: $error, $stackTrace');
1374+
}).then((dynamic _) {
13491375
sendEvent('proxy.disconnected.$id');
13501376
}));
13511377
return id;
@@ -1376,7 +1402,7 @@ class ProxyDomain extends Domain {
13761402
@override
13771403
Future<void> dispose() async {
13781404
for (final Socket connection in _forwardedConnections.values) {
1379-
await connection.close();
1405+
connection.destroy();
13801406
}
13811407
await _tempDirectory?.delete(recursive: true);
13821408
}

packages/flutter_tools/lib/src/daemon.dart

Lines changed: 62 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -98,39 +98,11 @@ class DaemonInputStreamConverter {
9898

9999
// Processes a single chunk received in the input stream.
100100
void _processChunk(List<int> chunk) {
101-
const int LF = 10; // The '\n' character
102101

103102
int start = 0;
104103
while (start < chunk.length) {
105104
if (state == _InputStreamParseState.json) {
106-
// Search for newline character.
107-
final int indexOfNewLine = chunk.indexOf(LF, start);
108-
if (indexOfNewLine < 0) {
109-
bytesBuilder.add(chunk.sublist(start));
110-
start = chunk.length;
111-
} else {
112-
bytesBuilder.add(chunk.sublist(start, indexOfNewLine + 1));
113-
start = indexOfNewLine + 1;
114-
115-
// Process chunk here
116-
final Uint8List combinedChunk = bytesBuilder.takeBytes();
117-
String jsonString = utf8.decode(combinedChunk).trim();
118-
if (jsonString.startsWith('[{') && jsonString.endsWith('}]')) {
119-
jsonString = jsonString.substring(1, jsonString.length - 1);
120-
final Map<String, Object?>? value = castStringKeyedMap(json.decode(jsonString));
121-
if (value != null) {
122-
// Check if we need to consume another binary blob.
123-
if (value[_binaryLengthKey] != null) {
124-
remainingBinaryLength = value[_binaryLengthKey]! as int;
125-
currentBinaryStream = StreamController<List<int>>();
126-
state = _InputStreamParseState.binary;
127-
_controller.add(DaemonMessage(value, currentBinaryStream.stream));
128-
} else {
129-
_controller.add(DaemonMessage(value));
130-
}
131-
}
132-
}
133-
}
105+
start += _processChunkInJsonMode(chunk, start);
134106
} else if (state == _InputStreamParseState.binary) {
135107
final int bytesSent = _addBinaryChunk(chunk, start, remainingBinaryLength);
136108
start += bytesSent;
@@ -146,6 +118,41 @@ class DaemonInputStreamConverter {
146118
}
147119
}
148120

121+
/// Processes a chunk in JSON mode, and returns the number of bytes processed.
122+
int _processChunkInJsonMode(List<int> chunk, int start) {
123+
const int LF = 10; // The '\n' character
124+
125+
// Search for newline character.
126+
final int indexOfNewLine = chunk.indexOf(LF, start);
127+
if (indexOfNewLine < 0) {
128+
bytesBuilder.add(chunk.sublist(start));
129+
return chunk.length - start;
130+
}
131+
132+
bytesBuilder.add(chunk.sublist(start, indexOfNewLine + 1));
133+
134+
// Process chunk here
135+
final Uint8List combinedChunk = bytesBuilder.takeBytes();
136+
String jsonString = utf8.decode(combinedChunk).trim();
137+
if (jsonString.startsWith('[{') && jsonString.endsWith('}]')) {
138+
jsonString = jsonString.substring(1, jsonString.length - 1);
139+
final Map<String, Object?>? value = castStringKeyedMap(json.decode(jsonString));
140+
if (value != null) {
141+
// Check if we need to consume another binary blob.
142+
if (value[_binaryLengthKey] != null) {
143+
remainingBinaryLength = value[_binaryLengthKey]! as int;
144+
currentBinaryStream = StreamController<List<int>>();
145+
state = _InputStreamParseState.binary;
146+
_controller.add(DaemonMessage(value, currentBinaryStream.stream));
147+
} else {
148+
_controller.add(DaemonMessage(value));
149+
}
150+
}
151+
}
152+
153+
return indexOfNewLine + 1 - start;
154+
}
155+
149156
int _addBinaryChunk(List<int> chunk, int start, int maximumSizeToRead) {
150157
if (start == 0 && chunk.length <= remainingBinaryLength) {
151158
currentBinaryStream.add(chunk);
@@ -170,6 +177,32 @@ class DaemonStreams {
170177
inputStream = DaemonInputStreamConverter(rawInputStream).convertedStream,
171178
_logger = logger;
172179

180+
/// Creates a [DaemonStreams] that uses stdin and stdout as the underlying streams.
181+
DaemonStreams.fromStdio(Stdio stdio, { required Logger logger })
182+
: this(stdio.stdin, stdio.stdout, logger: logger);
183+
184+
/// Creates a [DaemonStreams] that uses [Socket] as the underlying streams.
185+
DaemonStreams.fromSocket(Socket socket, { required Logger logger })
186+
: this(socket, socket, logger: logger);
187+
188+
/// Connects to a server and creates a [DaemonStreams] from the connection as the underlying streams.
189+
factory DaemonStreams.connect(String host, int port, { required Logger logger }) {
190+
final Future<Socket> socketFuture = Socket.connect(host, port);
191+
final StreamController<List<int>> inputStreamController = StreamController<List<int>>();
192+
final StreamController<List<int>> outputStreamController = StreamController<List<int>>();
193+
socketFuture.then((Socket socket) {
194+
inputStreamController.addStream(socket);
195+
socket.addStream(outputStreamController.stream);
196+
}).onError((Object error, StackTrace stackTrace) {
197+
logger.printError('Socket error: $error');
198+
logger.printTrace('$stackTrace');
199+
// Propagate the error to the streams.
200+
inputStreamController.addError(error, stackTrace);
201+
unawaited(outputStreamController.close());
202+
});
203+
return DaemonStreams(inputStreamController.stream, outputStreamController.sink, logger: logger);
204+
}
205+
173206
final StreamSink<List<int>> _outputSink;
174207
final Logger _logger;
175208

@@ -197,28 +230,6 @@ class DaemonStreams {
197230
Future<void> dispose() async {
198231
unawaited(_outputSink.close());
199232
}
200-
201-
/// Creates a [DaemonStreams] that uses stdin and stdout as the underlying streams.
202-
static DaemonStreams fromStdio(Stdio stdio, { required Logger logger }) {
203-
return DaemonStreams(stdio.stdin, stdio.stdout, logger: logger);
204-
}
205-
206-
/// Creates a [DaemonStreams] that uses [Socket] as the underlying streams.
207-
static DaemonStreams fromSocket(Socket socket, { required Logger logger }) {
208-
return DaemonStreams(socket, socket, logger: logger);
209-
}
210-
211-
/// Connects to a server and creates a [DaemonStreams] from the connection as the underlying streams.
212-
static DaemonStreams connect(String host, int port, { required Logger logger }) {
213-
final Future<Socket> socketFuture = Socket.connect(host, port);
214-
final StreamController<List<int>> inputStreamController = StreamController<List<int>>();
215-
final StreamController<List<int>> outputStreamController = StreamController<List<int>>();
216-
socketFuture.then((Socket socket) {
217-
inputStreamController.addStream(socket);
218-
socket.addStream(outputStreamController.stream);
219-
});
220-
return DaemonStreams(inputStreamController.stream, outputStreamController.sink, logger: logger);
221-
}
222233
}
223234

224235
/// Connection between a flutter daemon and a client.

packages/flutter_tools/lib/src/device.dart

Lines changed: 15 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,11 @@ class Category {
3939
String toString() => value;
4040

4141
static Category? fromString(String category) {
42-
switch (category) {
43-
case 'web':
44-
return web;
45-
case 'desktop':
46-
return desktop;
47-
case 'mobile':
48-
return mobile;
49-
}
50-
return null;
42+
return <String, Category>{
43+
'web': web,
44+
'desktop': desktop,
45+
'mobile': mobile,
46+
}[category];
5147
}
5248
}
5349

@@ -70,25 +66,16 @@ class PlatformType {
7066
String toString() => value;
7167

7268
static PlatformType? fromString(String platformType) {
73-
switch (platformType) {
74-
case 'web':
75-
return web;
76-
case 'android':
77-
return android;
78-
case 'ios':
79-
return ios;
80-
case 'linux':
81-
return linux;
82-
case 'macos':
83-
return macos;
84-
case 'windows':
85-
return windows;
86-
case 'fuchsia':
87-
return fuchsia;
88-
case 'custom':
89-
return custom;
90-
}
91-
return null;
69+
return <String, PlatformType>{
70+
'web': web,
71+
'android': android,
72+
'ios': ios,
73+
'linux': linux,
74+
'macos': macos,
75+
'windows': windows,
76+
'fuchsia': fuchsia,
77+
'custom': custom,
78+
}[platformType];
9279
}
9380
}
9481

0 commit comments

Comments
 (0)