Skip to content

Commit cc4971d

Browse files
authored
network: Fix bad HAR data (#9900)
1 parent e41c353 commit cc4971d

3 files changed

Lines changed: 100 additions & 9 deletions

File tree

packages/devtools_app/lib/src/screens/network/har_data_entry.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:convert';
66
import 'dart:typed_data';
77

88
import '../../screens/network/utils/http_utils.dart';
9+
import '../../shared/http/constants.dart' as shared_http;
910
import '../../shared/http/http_request_data.dart';
1011
import 'constants.dart';
1112

@@ -45,6 +46,15 @@ class HarDataEntry {
4546
final requestPostData = responseData[NetworkEventKeys.postData.name];
4647
final responseContent = responseData[NetworkEventKeys.content.name];
4748

49+
final statusValue = responseData[NetworkEventKeys.status.name];
50+
final statusCode = switch (statusValue) {
51+
int() => statusValue,
52+
String() => int.tryParse(statusValue),
53+
_ => null,
54+
};
55+
responseData[shared_http.HttpRequestDataKeys.statusCode.name] =
56+
statusCode ?? -1;
57+
4858
return HarDataEntry(
4959
DartIOHttpRequestData.fromJson(
5060
modifiedRequestData,
@@ -148,7 +158,7 @@ class HarDataEntry {
148158
},
149159
// Response
150160
NetworkEventKeys.response.name: <String, Object?>{
151-
NetworkEventKeys.status.name: e.status,
161+
NetworkEventKeys.status.name: int.tryParse(e.status ?? '') ?? -1,
152162
NetworkEventKeys.statusText.name:
153163
e.general[NetworkEventKeys.reasonPhrase.name] ?? '',
154164
NetworkEventKeys.httpVersion.name:

packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ TODO: Remove this section if there are not any updates.
4040

4141
## Network profiler updates
4242

43-
TODO: Remove this section if there are not any updates.
43+
- Fixed exported response status in HAR files so that they parse as integers
44+
instead of strings. [#9900](https://github.com/flutter/devtools/pull/9900)
4445

4546
## Logging updates
4647

packages/devtools_app/test/screens/network/har_network_test.dart

Lines changed: 87 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import 'dart:io';
88
import 'package:devtools_app/src/screens/network/constants.dart';
99
import 'package:devtools_app/src/screens/network/har_data_entry.dart';
1010
import 'package:devtools_app/src/screens/network/har_network_data.dart';
11-
1211
import 'package:flutter_test/flutter_test.dart';
1312

1413
void main() {
@@ -58,10 +57,25 @@ void main() {
5857
});
5958

6059
group('HarDataEntry', () {
60+
final entryJson =
61+
((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
62+
as Map<String, Object?>;
63+
64+
Map<String, Object?> copyWithStatus(Object? status) {
65+
final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>;
66+
final response = copy['response'] as Map<String, Object?>;
67+
response['status'] = status;
68+
return copy;
69+
}
70+
71+
Map<String, Object?> copyWithRequestError(String error) {
72+
final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>;
73+
final request = copy['request'] as Map<String, Object?>;
74+
request['error'] = error;
75+
return copy;
76+
}
77+
6178
test('fromJson parses correctly', () {
62-
final entryJson =
63-
((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
64-
as Map<String, Object?>;
6579
final harDataEntry = HarDataEntry.fromJson(entryJson);
6680

6781
expect(
@@ -71,12 +85,10 @@ void main() {
7185
expect(harDataEntry.request.method, 'GET');
7286
expect(harDataEntry.request.requestHeaders, isNotEmpty);
7387
expect(harDataEntry.request.requestCookies, isEmpty);
88+
expect(harDataEntry.request.status, '200');
7489
});
7590

7691
test('toJson serializes correctly', () {
77-
final entryJson =
78-
((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
79-
as Map<String, Object?>;
8092
final harDataEntry = HarDataEntry.fromJson(entryJson);
8193
final json = HarDataEntry.toJson(harDataEntry.request);
8294

@@ -89,6 +101,11 @@ void main() {
89101
expect(request?['cookies'], isEmpty);
90102

91103
expect(json['cache'], isEmpty);
104+
final response = json['response'] as Map<String, Object?>?;
105+
expect(response, isNotNull);
106+
expect(response?['status'], 200);
107+
expect(response?['statusText'], '');
108+
92109
final timings = json['timings'] as Map<String, Object?>?;
93110
expect(timings?['blocked'], NetworkEventDefaults.blocked);
94111
expect(timings?['dns'], NetworkEventDefaults.dns);
@@ -98,5 +115,68 @@ void main() {
98115
expect(timings?['ssl'], NetworkEventDefaults.ssl);
99116
expect(json['comment'], '');
100117
});
118+
119+
test('handles "Error" status in JSON', () {
120+
final errorJson = copyWithStatus('Error');
121+
final harDataEntry = HarDataEntry.fromJson(errorJson);
122+
expect(harDataEntry.request.status, '-1');
123+
124+
final serialized = HarDataEntry.toJson(harDataEntry.request);
125+
expect((serialized['response'] as Map)['status'], -1);
126+
});
127+
128+
test('handles "Cancelled" status in JSON', () {
129+
final cancelledJson = copyWithStatus('Cancelled');
130+
final harDataEntry = HarDataEntry.fromJson(cancelledJson);
131+
expect(harDataEntry.request.status, '-1');
132+
133+
final serialized = HarDataEntry.toJson(harDataEntry.request);
134+
expect((serialized['response'] as Map)['status'], -1);
135+
});
136+
137+
test('handles -1 integer status in JSON', () {
138+
final minusOneJson = copyWithStatus(-1);
139+
final harDataEntry = HarDataEntry.fromJson(minusOneJson);
140+
expect(harDataEntry.request.status, '-1');
141+
142+
final serialized = HarDataEntry.toJson(harDataEntry.request);
143+
expect((serialized['response'] as Map)['status'], -1);
144+
});
145+
146+
test('handles invalid string status in JSON', () {
147+
final invalidJson = copyWithStatus('foo');
148+
final harDataEntry = HarDataEntry.fromJson(invalidJson);
149+
expect(harDataEntry.request.status, '-1');
150+
151+
final serialized = HarDataEntry.toJson(harDataEntry.request);
152+
expect((serialized['response'] as Map)['status'], -1);
153+
});
154+
155+
test('handles null status in JSON', () {
156+
final nullJson = copyWithStatus(null);
157+
final harDataEntry = HarDataEntry.fromJson(nullJson);
158+
expect(harDataEntry.request.status, '-1');
159+
160+
final serialized = HarDataEntry.toJson(harDataEntry.request);
161+
expect((serialized['response'] as Map)['status'], -1);
162+
});
163+
164+
test('handles request connection error', () {
165+
final errorJson = copyWithRequestError('connection failed');
166+
final harDataEntry = HarDataEntry.fromJson(errorJson);
167+
expect(harDataEntry.request.status, 'Error');
168+
169+
final serialized = HarDataEntry.toJson(harDataEntry.request);
170+
expect((serialized['response'] as Map)['status'], -1);
171+
});
172+
173+
test('handles request cancellation error', () {
174+
final cancelledJson = copyWithRequestError('cancelled');
175+
final harDataEntry = HarDataEntry.fromJson(cancelledJson);
176+
expect(harDataEntry.request.status, 'Cancelled');
177+
178+
final serialized = HarDataEntry.toJson(harDataEntry.request);
179+
expect((serialized['response'] as Map)['status'], -1);
180+
});
101181
});
102182
}

0 commit comments

Comments
 (0)