network: Fix bad HAR data (#9900)
diff --git a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
index bd9316d..0176d96 100644
--- a/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
+++ b/packages/devtools_app/lib/src/screens/network/har_data_entry.dart
@@ -6,6 +6,7 @@
import 'dart:typed_data';
import '../../screens/network/utils/http_utils.dart';
+import '../../shared/http/constants.dart' as shared_http;
import '../../shared/http/http_request_data.dart';
import 'constants.dart';
@@ -45,6 +46,15 @@
final requestPostData = responseData[NetworkEventKeys.postData.name];
final responseContent = responseData[NetworkEventKeys.content.name];
+ final statusValue = responseData[NetworkEventKeys.status.name];
+ final statusCode = switch (statusValue) {
+ int() => statusValue,
+ String() => int.tryParse(statusValue),
+ _ => null,
+ };
+ responseData[shared_http.HttpRequestDataKeys.statusCode.name] =
+ statusCode ?? -1;
+
return HarDataEntry(
DartIOHttpRequestData.fromJson(
modifiedRequestData,
@@ -148,7 +158,7 @@
},
// Response
NetworkEventKeys.response.name: <String, Object?>{
- NetworkEventKeys.status.name: e.status,
+ NetworkEventKeys.status.name: int.tryParse(e.status ?? '') ?? -1,
NetworkEventKeys.statusText.name:
e.general[NetworkEventKeys.reasonPhrase.name] ?? '',
NetworkEventKeys.httpVersion.name:
diff --git a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
index d428e86..381298f 100644
--- a/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
+++ b/packages/devtools_app/release_notes/NEXT_RELEASE_NOTES.md
@@ -40,7 +40,8 @@
## Network profiler updates
-TODO: Remove this section if there are not any updates.
+- Fixed exported response status in HAR files so that they parse as integers
+ instead of strings. [#9900](https://github.com/flutter/devtools/pull/9900)
## Logging updates
diff --git a/packages/devtools_app/test/screens/network/har_network_test.dart b/packages/devtools_app/test/screens/network/har_network_test.dart
index 98a8a78..a1c735f 100644
--- a/packages/devtools_app/test/screens/network/har_network_test.dart
+++ b/packages/devtools_app/test/screens/network/har_network_test.dart
@@ -8,7 +8,6 @@
import 'package:devtools_app/src/screens/network/constants.dart';
import 'package:devtools_app/src/screens/network/har_data_entry.dart';
import 'package:devtools_app/src/screens/network/har_network_data.dart';
-
import 'package:flutter_test/flutter_test.dart';
void main() {
@@ -58,10 +57,25 @@
});
group('HarDataEntry', () {
+ final entryJson =
+ ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
+ as Map<String, Object?>;
+
+ Map<String, Object?> copyWithStatus(Object? status) {
+ final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>;
+ final response = copy['response'] as Map<String, Object?>;
+ response['status'] = status;
+ return copy;
+ }
+
+ Map<String, Object?> copyWithRequestError(String error) {
+ final copy = jsonDecode(jsonEncode(entryJson)) as Map<String, Object?>;
+ final request = copy['request'] as Map<String, Object?>;
+ request['error'] = error;
+ return copy;
+ }
+
test('fromJson parses correctly', () {
- final entryJson =
- ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
- as Map<String, Object?>;
final harDataEntry = HarDataEntry.fromJson(entryJson);
expect(
@@ -71,12 +85,10 @@
expect(harDataEntry.request.method, 'GET');
expect(harDataEntry.request.requestHeaders, isNotEmpty);
expect(harDataEntry.request.requestCookies, isEmpty);
+ expect(harDataEntry.request.status, '200');
});
test('toJson serializes correctly', () {
- final entryJson =
- ((jsonData['log'] as Map<String, Object?>)['entries'] as List).first
- as Map<String, Object?>;
final harDataEntry = HarDataEntry.fromJson(entryJson);
final json = HarDataEntry.toJson(harDataEntry.request);
@@ -89,6 +101,11 @@
expect(request?['cookies'], isEmpty);
expect(json['cache'], isEmpty);
+ final response = json['response'] as Map<String, Object?>?;
+ expect(response, isNotNull);
+ expect(response?['status'], 200);
+ expect(response?['statusText'], '');
+
final timings = json['timings'] as Map<String, Object?>?;
expect(timings?['blocked'], NetworkEventDefaults.blocked);
expect(timings?['dns'], NetworkEventDefaults.dns);
@@ -98,5 +115,68 @@
expect(timings?['ssl'], NetworkEventDefaults.ssl);
expect(json['comment'], '');
});
+
+ test('handles "Error" status in JSON', () {
+ final errorJson = copyWithStatus('Error');
+ final harDataEntry = HarDataEntry.fromJson(errorJson);
+ expect(harDataEntry.request.status, '-1');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles "Cancelled" status in JSON', () {
+ final cancelledJson = copyWithStatus('Cancelled');
+ final harDataEntry = HarDataEntry.fromJson(cancelledJson);
+ expect(harDataEntry.request.status, '-1');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles -1 integer status in JSON', () {
+ final minusOneJson = copyWithStatus(-1);
+ final harDataEntry = HarDataEntry.fromJson(minusOneJson);
+ expect(harDataEntry.request.status, '-1');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles invalid string status in JSON', () {
+ final invalidJson = copyWithStatus('foo');
+ final harDataEntry = HarDataEntry.fromJson(invalidJson);
+ expect(harDataEntry.request.status, '-1');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles null status in JSON', () {
+ final nullJson = copyWithStatus(null);
+ final harDataEntry = HarDataEntry.fromJson(nullJson);
+ expect(harDataEntry.request.status, '-1');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles request connection error', () {
+ final errorJson = copyWithRequestError('connection failed');
+ final harDataEntry = HarDataEntry.fromJson(errorJson);
+ expect(harDataEntry.request.status, 'Error');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
+
+ test('handles request cancellation error', () {
+ final cancelledJson = copyWithRequestError('cancelled');
+ final harDataEntry = HarDataEntry.fromJson(cancelledJson);
+ expect(harDataEntry.request.status, 'Cancelled');
+
+ final serialized = HarDataEntry.toJson(harDataEntry.request);
+ expect((serialized['response'] as Map)['status'], -1);
+ });
});
}