Skip to content

Commit c1d3481

Browse files
authored
Split package:http_profile into multiple files (#1143)
1 parent 69332d3 commit c1d3481

11 files changed

+899
-756
lines changed

pkgs/http_profile/lib/http_profile.dart

Lines changed: 1 addition & 540 deletions
Large diffs are not rendered by default.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright (c) 2023, 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+
part of 'http_profile.dart';
6+
7+
/// Describes an event related to an HTTP request.
8+
final class HttpProfileRequestEvent {
9+
final int _timestamp;
10+
final String _name;
11+
12+
HttpProfileRequestEvent({required DateTime timestamp, required String name})
13+
: _timestamp = timestamp.microsecondsSinceEpoch,
14+
_name = name;
15+
16+
Map<String, dynamic> _toJson() => <String, dynamic>{
17+
'timestamp': _timestamp,
18+
'event': _name,
19+
};
20+
}
21+
22+
/// A record of debugging information about an HTTP request.
23+
final class HttpClientRequestProfile {
24+
/// Whether HTTP profiling is enabled or not.
25+
///
26+
/// The value can be changed programmatically or through the DevTools Network
27+
/// UX.
28+
static bool get profilingEnabled => HttpClient.enableTimelineLogging;
29+
static set profilingEnabled(bool enabled) =>
30+
HttpClient.enableTimelineLogging = enabled;
31+
32+
final _data = <String, dynamic>{};
33+
34+
/// Records an event related to the request.
35+
///
36+
/// Usage example:
37+
///
38+
/// ```dart
39+
/// profile.addEvent(
40+
/// HttpProfileRequestEvent(
41+
/// timestamp: DateTime.now(),
42+
/// name: "Connection Established",
43+
/// ),
44+
/// );
45+
/// profile.addEvent(
46+
/// HttpProfileRequestEvent(
47+
/// timestamp: DateTime.now(),
48+
/// name: "Remote Disconnected",
49+
/// ),
50+
/// );
51+
/// ```
52+
void addEvent(HttpProfileRequestEvent event) {
53+
(_data['events'] as List<Map<String, dynamic>>).add(event._toJson());
54+
_updated();
55+
}
56+
57+
/// Details about the request.
58+
late final HttpProfileRequestData requestData;
59+
60+
/// Details about the response.
61+
late final HttpProfileResponseData responseData;
62+
63+
void _updated() =>
64+
_data['_lastUpdateTime'] = DateTime.now().microsecondsSinceEpoch;
65+
66+
HttpClientRequestProfile._({
67+
required DateTime requestStartTime,
68+
required String requestMethod,
69+
required String requestUri,
70+
}) {
71+
_data['isolateId'] = Service.getIsolateId(Isolate.current)!;
72+
_data['requestStartTimestamp'] = requestStartTime.microsecondsSinceEpoch;
73+
_data['requestMethod'] = requestMethod;
74+
_data['requestUri'] = requestUri;
75+
_data['events'] = <Map<String, dynamic>>[];
76+
_data['requestData'] = <String, dynamic>{};
77+
requestData = HttpProfileRequestData._(_data, _updated);
78+
_data['responseData'] = <String, dynamic>{};
79+
responseData = HttpProfileResponseData._(
80+
_data['responseData'] as Map<String, dynamic>, _updated);
81+
_data['_requestBodyStream'] = requestData._body.stream;
82+
_data['_responseBodyStream'] = responseData._body.stream;
83+
// This entry is needed to support the updatedSince parameter of
84+
// ext.dart.io.getHttpProfile.
85+
_updated();
86+
}
87+
88+
/// If HTTP profiling is enabled, returns an [HttpClientRequestProfile],
89+
/// otherwise returns `null`.
90+
static HttpClientRequestProfile? profile({
91+
/// The time at which the request was initiated.
92+
required DateTime requestStartTime,
93+
94+
/// The HTTP request method associated with the request.
95+
required String requestMethod,
96+
97+
/// The URI to which the request was sent.
98+
required String requestUri,
99+
}) {
100+
// Always return `null` in product mode so that the profiling code can be
101+
// tree shaken away.
102+
if (const bool.fromEnvironment('dart.vm.product') || !profilingEnabled) {
103+
return null;
104+
}
105+
final requestProfile = HttpClientRequestProfile._(
106+
requestStartTime: requestStartTime,
107+
requestMethod: requestMethod,
108+
requestUri: requestUri,
109+
);
110+
addHttpClientProfilingData(requestProfile._data);
111+
return requestProfile;
112+
}
113+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright (c) 2024, 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:async';
6+
import 'dart:developer' show Service, addHttpClientProfilingData;
7+
import 'dart:io' show HttpClient, HttpClientResponseCompressionState;
8+
import 'dart:isolate' show Isolate;
9+
10+
import 'utils.dart';
11+
12+
part 'http_client_request_profile.dart';
13+
part 'http_profile_request_data.dart';
14+
part 'http_profile_response_data.dart';
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
// Copyright (c) 2023, 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+
part of 'http_profile.dart';
6+
7+
final class HttpProfileProxyData {
8+
final String? _host;
9+
final String? _username;
10+
final bool? _isDirect;
11+
final int? _port;
12+
13+
HttpProfileProxyData({
14+
String? host,
15+
String? username,
16+
bool? isDirect,
17+
int? port,
18+
}) : _host = host,
19+
_username = username,
20+
_isDirect = isDirect,
21+
_port = port;
22+
23+
Map<String, dynamic> _toJson() => <String, dynamic>{
24+
if (_host != null) 'host': _host,
25+
if (_username != null) 'username': _username,
26+
if (_isDirect != null) 'isDirect': _isDirect,
27+
if (_port != null) 'port': _port,
28+
};
29+
}
30+
31+
/// Describes details about an HTTP request.
32+
final class HttpProfileRequestData {
33+
final Map<String, dynamic> _data;
34+
final StreamController<List<int>> _body = StreamController<List<int>>();
35+
bool _isClosed = false;
36+
final void Function() _updated;
37+
38+
Map<String, dynamic> get _requestData =>
39+
_data['requestData'] as Map<String, dynamic>;
40+
41+
/// The body of the request.
42+
StreamSink<List<int>> get bodySink => _body.sink;
43+
44+
/// Information about the networking connection used in the HTTP request.
45+
///
46+
/// This information is meant to be used for debugging.
47+
///
48+
/// It can contain any arbitrary data as long as the values are of type
49+
/// [String] or [int]. For example:
50+
/// { 'localPort': 1285, 'remotePort': 443, 'connectionPoolId': '21x23' }
51+
set connectionInfo(Map<String, dynamic /*String|int*/ > value) {
52+
_checkAndUpdate();
53+
for (final v in value.values) {
54+
if (!(v is String || v is int)) {
55+
throw ArgumentError(
56+
'The values in connectionInfo must be of type String or int.',
57+
);
58+
}
59+
}
60+
_requestData['connectionInfo'] = {...value};
61+
}
62+
63+
/// The content length of the request, in bytes.
64+
set contentLength(int? value) {
65+
_checkAndUpdate();
66+
if (value == null) {
67+
_requestData.remove('contentLength');
68+
} else {
69+
_requestData['contentLength'] = value;
70+
}
71+
}
72+
73+
/// Whether automatic redirect following was enabled for the request.
74+
set followRedirects(bool value) {
75+
_checkAndUpdate();
76+
_requestData['followRedirects'] = value;
77+
}
78+
79+
/// The request headers where duplicate headers are represented using a list
80+
/// of values.
81+
///
82+
/// For example:
83+
///
84+
/// ```dart
85+
/// // Foo: Bar
86+
/// // Foo: Baz
87+
///
88+
/// profile?.requestData.headersListValues({'Foo', ['Bar', 'Baz']});
89+
/// ```
90+
set headersListValues(Map<String, List<String>>? value) {
91+
_checkAndUpdate();
92+
if (value == null) {
93+
_requestData.remove('headers');
94+
return;
95+
}
96+
_requestData['headers'] = {...value};
97+
}
98+
99+
/// The request headers where duplicate headers are represented using a
100+
/// comma-separated list of values.
101+
///
102+
/// For example:
103+
///
104+
/// ```dart
105+
/// // Foo: Bar
106+
/// // Foo: Baz
107+
///
108+
/// profile?.requestData.headersCommaValues({'Foo', 'Bar, Baz']});
109+
/// ```
110+
set headersCommaValues(Map<String, String>? value) {
111+
_checkAndUpdate();
112+
if (value == null) {
113+
_requestData.remove('headers');
114+
return;
115+
}
116+
_requestData['headers'] = splitHeaderValues(value);
117+
}
118+
119+
/// The maximum number of redirects allowed during the request.
120+
set maxRedirects(int value) {
121+
_checkAndUpdate();
122+
_requestData['maxRedirects'] = value;
123+
}
124+
125+
/// The requested persistent connection state.
126+
set persistentConnection(bool value) {
127+
_checkAndUpdate();
128+
_requestData['persistentConnection'] = value;
129+
}
130+
131+
/// Proxy authentication details for the request.
132+
set proxyDetails(HttpProfileProxyData value) {
133+
_checkAndUpdate();
134+
_requestData['proxyDetails'] = value._toJson();
135+
}
136+
137+
HttpProfileRequestData._(
138+
this._data,
139+
this._updated,
140+
);
141+
142+
void _checkAndUpdate() {
143+
if (_isClosed) {
144+
throw StateError('HttpProfileResponseData has been closed, no further '
145+
'updates are allowed');
146+
}
147+
_updated();
148+
}
149+
150+
/// Signal that the request, including the entire request body, has been
151+
/// sent.
152+
///
153+
/// [bodySink] will be closed and the fields of [HttpProfileRequestData] will
154+
/// no longer be changeable.
155+
///
156+
/// [endTime] is the time when the request was fully sent. It defaults to the
157+
/// current time.
158+
void close([DateTime? endTime]) {
159+
_checkAndUpdate();
160+
_isClosed = true;
161+
unawaited(bodySink.close());
162+
_data['requestEndTimestamp'] =
163+
(endTime ?? DateTime.now()).microsecondsSinceEpoch;
164+
}
165+
166+
/// Signal that sending the request has failed with an error.
167+
///
168+
/// [bodySink] will be closed and the fields of [HttpProfileRequestData] will
169+
/// no longer be changeable.
170+
///
171+
/// [value] is a textual description of the error e.g. 'host does not exist'.
172+
///
173+
/// [endTime] is the time when the error occurred. It defaults to the current
174+
/// time.
175+
void closeWithError(String value, [DateTime? endTime]) {
176+
_checkAndUpdate();
177+
_isClosed = true;
178+
unawaited(bodySink.close());
179+
_requestData['error'] = value;
180+
_data['requestEndTimestamp'] =
181+
(endTime ?? DateTime.now()).microsecondsSinceEpoch;
182+
}
183+
}

0 commit comments

Comments
 (0)