-
Notifications
You must be signed in to change notification settings - Fork 394
Expand file tree
/
Copy pathserver.dart
More file actions
183 lines (161 loc) · 5.91 KB
/
server.dart
File metadata and controls
183 lines (161 loc) · 5.91 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// Copyright 2020 The Flutter Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file or at https://developers.google.com/open-source/licenses/bsd.
import 'dart:async';
import 'dart:convert';
import 'package:devtools_shared/devtools_deeplink.dart';
import 'package:devtools_shared/devtools_extensions.dart';
import 'package:devtools_shared/devtools_shared.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import '../analytics/analytics_controller.dart';
import '../development_helpers.dart';
import '../globals.dart';
import '../primitives/query_parameters.dart';
import '../primitives/storage.dart';
import '../primitives/utils.dart';
part '_analytics_api.dart';
part '_app_size_api.dart';
part '_deep_links_api.dart';
part '_dtd_api.dart';
part '_extensions_api.dart';
part '_preferences_api.dart';
part '_release_notes_api.dart';
part '_server_api.dart';
part '_survey_api.dart';
final _log = Logger('devtools_server_client');
/// Whether the DevTools server is available so that the HTTP API can be used.
///
/// A value of `true` here does not necessarily mean the legacy SSE API is
/// available.
///
/// Since the DevTools server is a web server, it is only available for the
/// web platform.
///
/// TODO(dantup): Since this relates only to non-SSE API, it could be available
/// for non-web?
///
/// In `framework_initialize_web.dart`, we test the DevTools server connection
/// by pinging the server and checking the response. If this is successful, we
/// set the [storage] global to an instance of [ServerConnectionStorage].
bool get isDevToolsServerAvailable =>
kIsWeb && storage is ServerConnectionStorage;
const _debugDevToolsServerEnvironmentVariable = String.fromEnvironment(
'debug_devtools_server',
);
/// Whether DevTools was run using the `dt run` command, which runs DevTools in
/// debug mode using `flutter run` and connects it to an instance of the
/// DevTools server.
bool get usingDebugDevToolsServer =>
_debugDevToolsServerEnvironmentVariable.isNotEmpty && !kReleaseMode;
String get devToolsServerUriAsString {
// Ensure we only use the debug DevTools server URI in non-release
// builds. By running `dt run`, an instance of DevTools run with `flutter run`
// can be connected to the DevTools server on a different port.
return usingDebugDevToolsServer
? _debugDevToolsServerEnvironmentVariable
: Uri.base.toString();
}
/// Helper to build a request URI to the DevTools server, which may not be on
/// the same origin as the DevTools app window.
Uri buildDevToolsServerRequestUri(String url) {
// [_debugDevToolsServerEnvironmentVariable] will be the empty string if the
// [_debugDevToolsServerFlag] environment variable declaration was not set
// using `--dart-define`.
const baseUri = _debugDevToolsServerEnvironmentVariable;
final uri = Uri.parse(path.join(baseUri, url));
final queryParams = DevToolsQueryParams.load();
// Forward the parent IDE name and the client-side analytics opt-out status
// to the server, so they can be propagated to any spawned subprocesses.
// Fail-safe: default to suppressing analytics if the controller is not yet
// initialized.
final newParams = <String, String>{
...uri.queryParameters,
if (queryParams.ide != null) 'ide': queryParams.ide!,
if (!isAnalyticsControllerInitialized || !isAnalyticsEnabled)
'suppress_analytics': 'true',
};
return uri.replace(queryParameters: newParams);
}
/// Helper to catch any server request which could fail.
///
/// Returns HttpRequest or null (if server failure).
Future<Response?> request(String url) async {
Response? response;
try {
response = await post(buildDevToolsServerRequestUri(url));
} catch (_) {}
return response;
}
Future<DevToolsJsonFile?> requestFile({
required String api,
required String fileKey,
required String filePath,
}) async {
if (isDevToolsServerAvailable) {
final url = Uri(path: api, queryParameters: {fileKey: filePath});
final resp = await request(url.toString());
if (resp?.statusOk ?? false) {
return _devToolsJsonFileFromResponse(resp!, filePath);
} else {
logWarning(resp, api);
}
}
return null;
}
Future<void> notifyForVmServiceConnection({
required String vmServiceUri,
required bool connected,
}) async {
if (isDevToolsServerAvailable) {
final uri = Uri(
path: apiNotifyForVmServiceConnection,
queryParameters: {
apiParameterValueKey: vmServiceUri,
apiParameterVmServiceConnected: connected.toString(),
},
);
final resp = await request(uri.toString());
final statusOk = resp?.statusOk ?? false;
if (!statusOk) {
logWarning(resp, apiNotifyForVmServiceConnection);
}
}
}
DevToolsJsonFile _devToolsJsonFileFromResponse(Response resp, String filePath) {
final data = json.decode(resp.body) as Map;
final lastModified = data['lastModifiedTime'];
final lastModifiedTime = lastModified != null
? DateTime.parse(lastModified)
: DateTime.now();
return DevToolsJsonFile(
name: filePath,
lastModifiedTime: lastModifiedTime,
data: data,
);
}
void logWarning(Response? response, String apiType) {
final respText = response?.body;
_log.warning(
'HttpRequest $apiType failed status = ${response?.statusCode}'
'${respText.isNullOrEmpty ? '' : ', responseText = $respText'}',
);
}
extension ResponseExtension on Response {
bool get statusOk => statusCode == 200;
bool get statusForbidden => statusCode == 403;
bool get statusError => statusCode == 500;
}
class ServerConnectionStorage implements Storage {
@override
Future<String?> getValue(String key) async {
final value = await getPreferenceValue(key);
return value == null ? null : '$value';
}
@override
Future<void> setValue(String key, String value) async {
await setPreferenceValue(key, value);
}
}