Skip to content

Commit 0c7aa51

Browse files
Add socket profiling to Network page (#2191)
Add socket profiling to Network page; add overview UI
1 parent 4d9156e commit 0c7aa51

21 files changed

+1890
-432
lines changed

packages/devtools_app/lib/src/common_widgets.dart

-23
Original file line numberDiff line numberDiff line change
@@ -174,29 +174,6 @@ StatelessWidget stopRecordingButton({
174174
);
175175
}
176176

177-
/// Button to stop recording data.
178-
///
179-
/// * `paused`: Whether recording is in progress.
180-
/// * `includeTextWidth`: The minimum width the button can be before the text is
181-
/// omitted.
182-
/// * `onPressed`: The callback to be called upon pressing the button.
183-
StatelessWidget stopButton({
184-
Key key,
185-
@required bool paused,
186-
double includeTextWidth,
187-
@required VoidCallback onPressed,
188-
}) {
189-
return OutlineButton(
190-
key: key,
191-
onPressed: paused ? null : onPressed,
192-
child: MaterialIconLabel(
193-
Icons.stop,
194-
'Stop',
195-
includeTextWidth: includeTextWidth,
196-
),
197-
);
198-
}
199-
200177
// TODO(kenz): make recording info its own stateful widget that handles
201178
// listening to value notifiers and building info.
202179
Widget recordingInfo({

packages/devtools_app/lib/src/http/http_request_data.dart

+92-83
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,11 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import '../network/network_model.dart';
56
import '../trace_event.dart';
67
import '../utils.dart';
78
import 'http.dart';
89

9-
/// Contains all state relevant to completed and in-progress HTTP requests.
10-
class HttpRequests {
11-
HttpRequests({
12-
this.requests = const [],
13-
this.invalidRequests = const [],
14-
this.outstandingRequests = const {},
15-
}) : assert(requests != null),
16-
assert(invalidRequests != null),
17-
assert(outstandingRequests != null);
18-
19-
/// A list of HTTP requests.
20-
///
21-
/// Individual requests in this list can be either completed or in-progress.
22-
List<HttpRequestData> requests;
23-
24-
/// A list of invalid HTTP requests received.
25-
///
26-
/// These are requests that have completed but do not contain all the required
27-
/// information to display normally in the UI.
28-
List<HttpRequestData> invalidRequests;
29-
30-
/// A mapping of timeline IDs to instances of HttpRequestData which are
31-
/// currently in-progress.
32-
Map<String, HttpRequestData> outstandingRequests;
33-
34-
void clear() {
35-
requests.clear();
36-
outstandingRequests.clear();
37-
}
38-
}
39-
4010
/// Used to represent an instant event emitted during an HTTP request.
4111
class HttpInstantEvent {
4212
HttpInstantEvent._(this._event);
@@ -56,12 +26,12 @@ class HttpInstantEvent {
5626
}
5727

5828
/// An abstraction of an HTTP request made through dart:io.
59-
class HttpRequestData {
29+
class HttpRequestData extends NetworkRequest {
6030
HttpRequestData._(
61-
this._timelineMicrosBase,
31+
int timelineMicrosBase,
6232
this._startEvent,
6333
this._endEvent,
64-
);
34+
) : super(timelineMicrosBase);
6535

6636
/// Build an instance from timeline events.
6737
///
@@ -99,26 +69,53 @@ class HttpRequestData {
9969
return data;
10070
}
10171

102-
final int _timelineMicrosBase;
72+
static const _connectionInfoKey = 'connectionInfo';
73+
static const _contentTypeKey = 'content-type';
74+
static const _cookieKey = 'cookie';
75+
static const _errorKey = 'error';
76+
static const _filterKey = 'filterKey';
77+
static const _localPortKey = 'localPort';
78+
static const _methodKey = 'method';
79+
static const _requestHeadersKey = 'requestHeaders';
80+
static const _responseHeadersKey = 'responseHeaders';
81+
static const _statusCodeKey = 'statusCode';
82+
// TODO(kenz): modify this to `setCookie` once
83+
// https://github.com/dart-lang/sdk/issues/42822 is resolved
84+
static const _setCookieKey = 'set-cookie';
85+
static const _uriKey = 'uri';
86+
10387
final TraceEvent _startEvent;
10488
TraceEvent _endEvent;
10589

10690
// Do not add to this list directly! Call `_addInstantEvents` which is
10791
// responsible for calculating the time offsets of each event.
10892
final List<HttpInstantEvent> _instantEvents = [];
10993

110-
/// The duration of the HTTP request, in milliseconds.
94+
@override
11195
Duration get duration {
112-
if (_endEvent == null || _startEvent == null) {
113-
return null;
114-
}
96+
if (inProgress || !isValid) return null;
11597
// Timestamps are in microseconds
11698
final range = TimeRange()
11799
..start = Duration(microseconds: _startEvent.timestampMicros)
118100
..end = Duration(microseconds: _endEvent.timestampMicros);
119101
return range.duration;
120102
}
121103

104+
@override
105+
String get contentType {
106+
if (responseHeaders == null || responseHeaders[_contentTypeKey] == null) {
107+
return null;
108+
}
109+
return responseHeaders[_contentTypeKey].toString();
110+
}
111+
112+
@override
113+
String get type {
114+
// TODO(kenz): pull in a package or implement functionality to pretty print
115+
// the MIME type from the 'content-type' field in a response header.
116+
return 'http';
117+
}
118+
122119
/// Whether the request is safe to display in the UI.
123120
///
124121
/// It is possible to get invalid events if we receive an endEvent but no
@@ -135,14 +132,25 @@ class HttpRequestData {
135132

136133
/// A map of general information associated with an HTTP request.
137134
Map<String, dynamic> get general {
135+
if (_general != null) return _general;
136+
if (!isValid) return null;
138137
final copy = Map<String, dynamic>.from(_startEvent.args);
139-
if (_endEvent != null) {
138+
if (!inProgress) {
140139
copy.addAll(_endEvent.args);
141140
}
142-
copy.remove('requestHeaders');
143-
copy.remove('responseHeaders');
144-
copy.remove('filterKey');
145-
return copy;
141+
copy.remove(_requestHeadersKey);
142+
copy.remove(_responseHeadersKey);
143+
copy.remove(_filterKey);
144+
return _general = copy;
145+
}
146+
147+
Map<String, dynamic> _general;
148+
149+
@override
150+
int get port {
151+
if (general == null) return null;
152+
final Map<String, dynamic> connectionInfo = general[_connectionInfoKey];
153+
return connectionInfo != null ? connectionInfo[_localPortKey] : null;
146154
}
147155

148156
/// True if the HTTP request hasn't completed yet, determined by the lack of
@@ -152,15 +160,13 @@ class HttpRequestData {
152160
/// All instant events logged to the timeline for this HTTP request.
153161
List<HttpInstantEvent> get instantEvents => _instantEvents;
154162

155-
/// The HTTP method associated with this request.
163+
@override
156164
String get method {
157-
assert(_startEvent.args.containsKey('method'));
158-
return _startEvent.args['method'];
165+
if (!isValid) return null;
166+
assert(_startEvent.args.containsKey(_methodKey));
167+
return _startEvent.args[_methodKey];
159168
}
160169

161-
/// The name of the request (currently the URI).
162-
String get name => uri.toString();
163-
164170
/// A list of all cookies contained within the request headers.
165171
List<Cookie> get requestCookies {
166172
// The request may still be in progress, in which case we don't display any
@@ -169,27 +175,34 @@ class HttpRequestData {
169175
if (headers == null) {
170176
return [];
171177
}
172-
return _parseCookies(headers['cookie'] ?? []);
178+
return _parseCookies(headers[_cookieKey] ?? []);
173179
}
174180

175181
/// The request headers for the HTTP request.
176182
Map<String, dynamic> get requestHeaders {
177183
// The request may still be in progress, in which case we don't display any
178-
// headers.
179-
if (_endEvent == null) {
180-
return null;
181-
}
182-
return _endEvent.args['requestHeaders'];
184+
// headers, or the request may be invalid, in which case we also don't
185+
// display any headers.
186+
if (inProgress || !isValid) return null;
187+
return _endEvent.args[_requestHeadersKey];
183188
}
184189

185190
/// The time the HTTP request was issued.
186-
DateTime get requestTime {
187-
assert(_startEvent != null);
191+
@override
192+
DateTime get startTimestamp {
193+
if (!isValid) return null;
188194
return DateTime.fromMicrosecondsSinceEpoch(
189-
_getTimelineMicrosecondsSinceEpoch(_startEvent),
195+
timelineMicrosecondsSinceEpoch(_startEvent.timestampMicros),
190196
);
191197
}
192198

199+
@override
200+
DateTime get endTimestamp {
201+
if (inProgress || !isValid) return null;
202+
return DateTime.fromMicrosecondsSinceEpoch(
203+
timelineMicrosecondsSinceEpoch(_endEvent.timestampMicros));
204+
}
205+
193206
/// A list of all cookies contained within the response headers.
194207
List<Cookie> get responseCookies {
195208
// The request may still be in progress, in which case we don't display any
@@ -199,43 +212,43 @@ class HttpRequestData {
199212
return [];
200213
}
201214
return _parseCookies(
202-
headers['set-cookie'] ?? [],
215+
headers[_setCookieKey] ?? [],
203216
);
204217
}
205218

206219
/// The response headers for the HTTP request.
207220
Map<String, dynamic> get responseHeaders {
208221
// The request may still be in progress, in which case we don't display any
209-
// headers.
210-
if (_endEvent == null) {
211-
return null;
212-
}
213-
return _endEvent.args['responseHeaders'];
222+
// headers, or the request may be invalid, in which case we also don't
223+
// display any headers.
224+
if (inProgress || !isValid) return null;
225+
return _endEvent.args[_responseHeadersKey];
214226
}
215227

216228
/// A string representing the status of the request.
217229
///
218230
/// If the request completed, this will be an HTTP status code. If an error
219231
/// was encountered, this will return 'Error'.
232+
@override
220233
String get status {
234+
if (inProgress || !isValid) return null;
221235
String statusCode;
222-
if (_endEvent != null) {
223-
final endArgs = _endEvent.args;
224-
if (endArgs.containsKey('error')) {
225-
// This case occurs when an exception has been thrown, so there's no
226-
// status code to associate with the request.
227-
statusCode = 'Error';
228-
} else {
229-
statusCode = endArgs['statusCode'].toString();
230-
}
236+
final endArgs = _endEvent.args;
237+
if (endArgs.containsKey(_errorKey)) {
238+
// This case occurs when an exception has been thrown, so there's no
239+
// status code to associate with the request.
240+
statusCode = 'Error';
241+
} else {
242+
statusCode = endArgs[_statusCodeKey].toString();
231243
}
232244
return statusCode;
233245
}
234246

235-
/// The address the HTTP request was issued to.
236-
Uri get uri {
237-
assert(_startEvent.args.containsKey('uri'));
238-
return Uri.parse(_startEvent.args['uri']);
247+
@override
248+
String get uri {
249+
if (!isValid) return null;
250+
assert(_startEvent.args.containsKey(_uriKey));
251+
return _startEvent.args[_uriKey];
239252
}
240253

241254
/// Merges the information from another [HttpRequestData] into this instance.
@@ -276,10 +289,6 @@ class HttpRequestData {
276289
}
277290
}
278291

279-
int _getTimelineMicrosecondsSinceEpoch(TraceEvent event) {
280-
return _timelineMicrosBase + event.timestampMicros;
281-
}
282-
283292
@override
284-
String toString() => '$method $name';
293+
String toString() => '$method $uri';
285294
}

0 commit comments

Comments
 (0)