Skip to content

Commit

Permalink
Split package:http_profile into multiple files
Browse files Browse the repository at this point in the history
  • Loading branch information
brianquinlan committed Feb 29, 2024
1 parent 69332d3 commit 4a0e236
Show file tree
Hide file tree
Showing 11 changed files with 899 additions and 756 deletions.
541 changes: 1 addition & 540 deletions pkgs/http_profile/lib/http_profile.dart

Large diffs are not rendered by default.

113 changes: 113 additions & 0 deletions pkgs/http_profile/lib/src/http_client_request_profile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of 'http_profile.dart';

/// Describes an event related to an HTTP request.
final class HttpProfileRequestEvent {
final int _timestamp;
final String _name;

HttpProfileRequestEvent({required DateTime timestamp, required String name})
: _timestamp = timestamp.microsecondsSinceEpoch,
_name = name;

Map<String, dynamic> _toJson() => <String, dynamic>{
'timestamp': _timestamp,
'event': _name,
};
}

/// A record of debugging information about an HTTP request.
final class HttpClientRequestProfile {
/// Whether HTTP profiling is enabled or not.
///
/// The value can be changed programmatically or through the DevTools Network
/// UX.
static bool get profilingEnabled => HttpClient.enableTimelineLogging;
static set profilingEnabled(bool enabled) =>
HttpClient.enableTimelineLogging = enabled;

final _data = <String, dynamic>{};

/// Records an event related to the request.
///
/// Usage example:
///
/// ```dart
/// profile.addEvent(
/// HttpProfileRequestEvent(
/// timestamp: DateTime.now(),
/// name: "Connection Established",
/// ),
/// );
/// profile.addEvent(
/// HttpProfileRequestEvent(
/// timestamp: DateTime.now(),
/// name: "Remote Disconnected",
/// ),
/// );
/// ```
void addEvent(HttpProfileRequestEvent event) {
(_data['events'] as List<Map<String, dynamic>>).add(event._toJson());
_updated();
}

/// Details about the request.
late final HttpProfileRequestData requestData;

/// Details about the response.
late final HttpProfileResponseData responseData;

void _updated() =>
_data['_lastUpdateTime'] = DateTime.now().microsecondsSinceEpoch;

HttpClientRequestProfile._({
required DateTime requestStartTime,
required String requestMethod,
required String requestUri,
}) {
_data['isolateId'] = Service.getIsolateId(Isolate.current)!;
_data['requestStartTimestamp'] = requestStartTime.microsecondsSinceEpoch;
_data['requestMethod'] = requestMethod;
_data['requestUri'] = requestUri;
_data['events'] = <Map<String, dynamic>>[];
_data['requestData'] = <String, dynamic>{};
requestData = HttpProfileRequestData._(_data, _updated);
_data['responseData'] = <String, dynamic>{};
responseData = HttpProfileResponseData._(
_data['responseData'] as Map<String, dynamic>, _updated);
_data['_requestBodyStream'] = requestData._body.stream;
_data['_responseBodyStream'] = responseData._body.stream;
// This entry is needed to support the updatedSince parameter of
// ext.dart.io.getHttpProfile.
_updated();
}

/// If HTTP profiling is enabled, returns an [HttpClientRequestProfile],
/// otherwise returns `null`.
static HttpClientRequestProfile? profile({
/// The time at which the request was initiated.
required DateTime requestStartTime,

/// The HTTP request method associated with the request.
required String requestMethod,

/// The URI to which the request was sent.
required String requestUri,
}) {
// Always return `null` in product mode so that the profiling code can be
// tree shaken away.
if (const bool.fromEnvironment('dart.vm.product') || !profilingEnabled) {
return null;
}
final requestProfile = HttpClientRequestProfile._(
requestStartTime: requestStartTime,
requestMethod: requestMethod,
requestUri: requestUri,
);
addHttpClientProfilingData(requestProfile._data);
return requestProfile;
}
}
14 changes: 14 additions & 0 deletions pkgs/http_profile/lib/src/http_profile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:async';
import 'dart:developer' show addHttpClientProfilingData, Service;
import 'dart:io' show HttpClient, HttpClientResponseCompressionState;
import 'dart:isolate' show Isolate;

import 'utils.dart';

part 'http_client_request_profile.dart';
part 'http_profile_request_data.dart';
part 'http_profile_response_data.dart';
183 changes: 183 additions & 0 deletions pkgs/http_profile/lib/src/http_profile_request_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

part of 'http_profile.dart';

final class HttpProfileProxyData {
final String? _host;
final String? _username;
final bool? _isDirect;
final int? _port;

HttpProfileProxyData({
String? host,
String? username,
bool? isDirect,
int? port,
}) : _host = host,
_username = username,
_isDirect = isDirect,
_port = port;

Map<String, dynamic> _toJson() => <String, dynamic>{
if (_host != null) 'host': _host,
if (_username != null) 'username': _username,
if (_isDirect != null) 'isDirect': _isDirect,
if (_port != null) 'port': _port,
};
}

/// Describes details about an HTTP request.
final class HttpProfileRequestData {
final Map<String, dynamic> _data;
final StreamController<List<int>> _body = StreamController<List<int>>();
bool _isClosed = false;
final void Function() _updated;

Map<String, dynamic> get _requestData =>
_data['requestData'] as Map<String, dynamic>;

/// The body of the request.
StreamSink<List<int>> get bodySink => _body.sink;

/// Information about the networking connection used in the HTTP request.
///
/// This information is meant to be used for debugging.
///
/// It can contain any arbitrary data as long as the values are of type
/// [String] or [int]. For example:
/// { 'localPort': 1285, 'remotePort': 443, 'connectionPoolId': '21x23' }
set connectionInfo(Map<String, dynamic /*String|int*/ > value) {
_checkAndUpdate();
for (final v in value.values) {
if (!(v is String || v is int)) {
throw ArgumentError(
'The values in connectionInfo must be of type String or int.',
);
}
}
_requestData['connectionInfo'] = {...value};
}

/// The content length of the request, in bytes.
set contentLength(int? value) {
_checkAndUpdate();
if (value == null) {
_requestData.remove('contentLength');
} else {
_requestData['contentLength'] = value;
}
}

/// Whether automatic redirect following was enabled for the request.
set followRedirects(bool value) {
_checkAndUpdate();
_requestData['followRedirects'] = value;
}

/// The request headers where duplicate headers are represented using a list
/// of values.
///
/// For example:
///
/// ```dart
/// // Foo: Bar
/// // Foo: Baz
///
/// profile?.requestData.headersListValues({'Foo', ['Bar', 'Baz']});
/// ```
set headersListValues(Map<String, List<String>>? value) {
_checkAndUpdate();
if (value == null) {
_requestData.remove('headers');
return;
}
_requestData['headers'] = {...value};
}

/// The request headers where duplicate headers are represented using a
/// comma-separated list of values.
///
/// For example:
///
/// ```dart
/// // Foo: Bar
/// // Foo: Baz
///
/// profile?.requestData.headersCommaValues({'Foo', 'Bar, Baz']});
/// ```
set headersCommaValues(Map<String, String>? value) {
_checkAndUpdate();
if (value == null) {
_requestData.remove('headers');
return;
}
_requestData['headers'] = splitHeaderValues(value);
}

/// The maximum number of redirects allowed during the request.
set maxRedirects(int value) {
_checkAndUpdate();
_requestData['maxRedirects'] = value;
}

/// The requested persistent connection state.
set persistentConnection(bool value) {
_checkAndUpdate();
_requestData['persistentConnection'] = value;
}

/// Proxy authentication details for the request.
set proxyDetails(HttpProfileProxyData value) {
_checkAndUpdate();
_requestData['proxyDetails'] = value._toJson();
}

HttpProfileRequestData._(
this._data,
this._updated,
);

void _checkAndUpdate() {
if (_isClosed) {
throw StateError('HttpProfileResponseData has been closed, no further '
'updates are allowed');
}
_updated();
}

/// Signal that the request, including the entire request body, has been
/// sent.
///
/// [bodySink] will be closed and the fields of [HttpProfileRequestData] will
/// no longer be changeable.
///
/// [endTime] is the time when the request was fully sent. It defaults to the
/// current time.
void close([DateTime? endTime]) {
_checkAndUpdate();
_isClosed = true;
unawaited(bodySink.close());
_data['requestEndTimestamp'] =
(endTime ?? DateTime.now()).microsecondsSinceEpoch;
}

/// Signal that sending the request has failed with an error.
///
/// [bodySink] will be closed and the fields of [HttpProfileRequestData] will
/// no longer be changeable.
///
/// [value] is a textual description of the error e.g. 'host does not exist'.
///
/// [endTime] is the time when the error occurred. It defaults to the current
/// time.
void closeWithError(String value, [DateTime? endTime]) {
_checkAndUpdate();
_isClosed = true;
unawaited(bodySink.close());
_requestData['error'] = value;
_data['requestEndTimestamp'] =
(endTime ?? DateTime.now()).microsecondsSinceEpoch;
}
}
Loading

0 comments on commit 4a0e236

Please sign in to comment.