Version 2.15.0-243.0.dev
Merge commit '732224bcced01f4a95f90ffd4a8ccdcf34ebcbd3' into 'dev'
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 9934b5b..b84e3fd 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -1528,6 +1528,8 @@
+
+
<h3>Requests</h3><dl><dt class="request"><a name="request_completion.getSuggestions">completion.getSuggestions</a></dt><dd><div class="box"><pre>request: {
"id": String
"method": "completion.getSuggestions"
diff --git a/pkg/analysis_server/lib/protocol/protocol_constants.dart b/pkg/analysis_server/lib/protocol/protocol_constants.dart
index 84b3a5c..2689506 100644
--- a/pkg/analysis_server/lib/protocol/protocol_constants.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_constants.dart
@@ -137,10 +137,22 @@
'replacementOffset';
const String COMPLETION_NOTIFICATION_RESULTS_RESULTS = 'results';
const String COMPLETION_REQUEST_GET_SUGGESTIONS = 'completion.getSuggestions';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2 = 'completion.getSuggestions2';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_FILE = 'file';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_MAX_RESULTS = 'maxResults';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTIONS_FILE = 'file';
const String COMPLETION_REQUEST_GET_SUGGESTIONS_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS =
'completion.getSuggestionDetails';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2 =
+ 'completion.getSuggestionDetails2';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_COMPLETION =
+ 'completion';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_FILE = 'file';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_LIBRARY_URI =
+ 'libraryUri';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_FILE = 'file';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_ID = 'id';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_LABEL = 'label';
@@ -152,7 +164,19 @@
'completion.setSubscriptions';
const String COMPLETION_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS =
'subscriptions';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_IS_INCOMPLETE =
+ 'isIncomplete';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_LIBRARY_URIS_TO_IMPORT =
+ 'libraryUrisToImport';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_REPLACEMENT_LENGTH =
+ 'replacementLength';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_REPLACEMENT_OFFSET =
+ 'replacementOffset';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_SUGGESTIONS = 'suggestions';
const String COMPLETION_RESPONSE_GET_SUGGESTIONS_ID = 'id';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS2_CHANGE = 'change';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS2_COMPLETION =
+ 'completion';
const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_CHANGE = 'change';
const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_COMPLETION =
'completion';
diff --git a/pkg/analysis_server/lib/protocol/protocol_generated.dart b/pkg/analysis_server/lib/protocol/protocol_generated.dart
index 301bfab..8cd904d 100644
--- a/pkg/analysis_server/lib/protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/protocol/protocol_generated.dart
@@ -4292,6 +4292,202 @@
);
}
+/// completion.getSuggestionDetails2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "completion": String
+/// "libraryUri": String
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestionDetails2Params implements RequestParams {
+ /// The path of the file into which this completion is being inserted.
+ String file;
+
+ /// The offset in the file where the completion will be inserted.
+ int offset;
+
+ /// The completion from the selected CompletionSuggestion. It could be a name
+ /// of a class, or a name of a constructor in form
+ /// "typeName.constructorName()", or an enumeration constant in form
+ /// "enumName.constantName", etc.
+ String completion;
+
+ /// The URI of the library to import, so that the element referenced in the
+ /// completion becomes accessible.
+ String libraryUri;
+
+ CompletionGetSuggestionDetails2Params(
+ this.file, this.offset, this.completion, this.libraryUri);
+
+ factory CompletionGetSuggestionDetails2Params.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String file;
+ if (json.containsKey('file')) {
+ file = jsonDecoder.decodeString(jsonPath + '.file', json['file']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'file');
+ }
+ int offset;
+ if (json.containsKey('offset')) {
+ offset = jsonDecoder.decodeInt(jsonPath + '.offset', json['offset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'offset');
+ }
+ String completion;
+ if (json.containsKey('completion')) {
+ completion = jsonDecoder.decodeString(
+ jsonPath + '.completion', json['completion']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'completion');
+ }
+ String libraryUri;
+ if (json.containsKey('libraryUri')) {
+ libraryUri = jsonDecoder.decodeString(
+ jsonPath + '.libraryUri', json['libraryUri']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'libraryUri');
+ }
+ return CompletionGetSuggestionDetails2Params(
+ file, offset, completion, libraryUri);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestionDetails2 params', json);
+ }
+ }
+
+ factory CompletionGetSuggestionDetails2Params.fromRequest(Request request) {
+ return CompletionGetSuggestionDetails2Params.fromJson(
+ RequestDecoder(request), 'params', request.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['file'] = file;
+ result['offset'] = offset;
+ result['completion'] = completion;
+ result['libraryUri'] = libraryUri;
+ return result;
+ }
+
+ @override
+ Request toRequest(String id) {
+ return Request(id, 'completion.getSuggestionDetails2', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestionDetails2Params) {
+ return file == other.file &&
+ offset == other.offset &&
+ completion == other.completion &&
+ libraryUri == other.libraryUri;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ file,
+ offset,
+ completion,
+ libraryUri,
+ );
+}
+
+/// completion.getSuggestionDetails2 result
+///
+/// {
+/// "completion": String
+/// "change": SourceChange
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestionDetails2Result implements ResponseResult {
+ /// The full text to insert, which possibly includes now an import prefix.
+ /// The client should insert this text, not the completion from the selected
+ /// CompletionSuggestion.
+ String completion;
+
+ /// A change for the client to apply to make the accepted completion
+ /// suggestion available. In most cases the change is to add a new import
+ /// directive to the file.
+ SourceChange change;
+
+ CompletionGetSuggestionDetails2Result(this.completion, this.change);
+
+ factory CompletionGetSuggestionDetails2Result.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String completion;
+ if (json.containsKey('completion')) {
+ completion = jsonDecoder.decodeString(
+ jsonPath + '.completion', json['completion']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'completion');
+ }
+ SourceChange change;
+ if (json.containsKey('change')) {
+ change = SourceChange.fromJson(
+ jsonDecoder, jsonPath + '.change', json['change']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'change');
+ }
+ return CompletionGetSuggestionDetails2Result(completion, change);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestionDetails2 result', json);
+ }
+ }
+
+ factory CompletionGetSuggestionDetails2Result.fromResponse(
+ Response response) {
+ return CompletionGetSuggestionDetails2Result.fromJson(
+ ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+ 'result',
+ response.result);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['completion'] = completion;
+ result['change'] = change.toJson();
+ return result;
+ }
+
+ @override
+ Response toResponse(String id) {
+ return Response(id, result: toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestionDetails2Result) {
+ return completion == other.completion && change == other.change;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ completion,
+ change,
+ );
+}
+
/// completion.getSuggestionDetails params
///
/// {
@@ -4481,6 +4677,261 @@
);
}
+/// completion.getSuggestions2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "maxResults": int
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestions2Params implements RequestParams {
+ /// The file containing the point at which suggestions are to be made.
+ String file;
+
+ /// The offset within the file at which suggestions are to be made.
+ int offset;
+
+ /// The maximum number of suggestions to return. If the number of suggestions
+ /// after filtering is greater than the maxResults, then isIncomplete is set
+ /// to true.
+ int maxResults;
+
+ CompletionGetSuggestions2Params(this.file, this.offset, this.maxResults);
+
+ factory CompletionGetSuggestions2Params.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String file;
+ if (json.containsKey('file')) {
+ file = jsonDecoder.decodeString(jsonPath + '.file', json['file']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'file');
+ }
+ int offset;
+ if (json.containsKey('offset')) {
+ offset = jsonDecoder.decodeInt(jsonPath + '.offset', json['offset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'offset');
+ }
+ int maxResults;
+ if (json.containsKey('maxResults')) {
+ maxResults =
+ jsonDecoder.decodeInt(jsonPath + '.maxResults', json['maxResults']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'maxResults');
+ }
+ return CompletionGetSuggestions2Params(file, offset, maxResults);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestions2 params', json);
+ }
+ }
+
+ factory CompletionGetSuggestions2Params.fromRequest(Request request) {
+ return CompletionGetSuggestions2Params.fromJson(
+ RequestDecoder(request), 'params', request.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['file'] = file;
+ result['offset'] = offset;
+ result['maxResults'] = maxResults;
+ return result;
+ }
+
+ @override
+ Request toRequest(String id) {
+ return Request(id, 'completion.getSuggestions2', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestions2Params) {
+ return file == other.file &&
+ offset == other.offset &&
+ maxResults == other.maxResults;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ file,
+ offset,
+ maxResults,
+ );
+}
+
+/// completion.getSuggestions2 result
+///
+/// {
+/// "replacementOffset": int
+/// "replacementLength": int
+/// "suggestions": List<CompletionSuggestion>
+/// "libraryUrisToImport": List<String>
+/// "isIncomplete": bool
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestions2Result implements ResponseResult {
+ /// The offset of the start of the text to be replaced. This will be
+ /// different from the offset used to request the completion suggestions if
+ /// there was a portion of an identifier before the original offset. In
+ /// particular, the replacementOffset will be the offset of the beginning of
+ /// said identifier.
+ int replacementOffset;
+
+ /// The length of the text to be replaced if the remainder of the identifier
+ /// containing the cursor is to be replaced when the suggestion is applied
+ /// (that is, the number of characters in the existing identifier).
+ int replacementLength;
+
+ /// The completion suggestions being reported. This list is filtered by the
+ /// already existing prefix, and sorted first by relevance, and (if the same)
+ /// by the suggestion text. The list will have at most maxResults items. If
+ /// the user types a new keystroke, the client is expected to either do local
+ /// filtering (when the returned list was complete), or ask the server again
+ /// (if isIncomplete was true).
+ ///
+ /// This list contains suggestions from both imported, and not yet imported
+ /// libraries. Items from not yet imported libraries will have
+ /// libraryUriToImportIndex set, which is an index into the
+ /// libraryUrisToImport in this response.
+ List<CompletionSuggestion> suggestions;
+
+ /// The list of libraries with declarations that are not yet available in the
+ /// file where completion was requested, most often because the library is
+ /// not yet imported. The declarations still might be included into the
+ /// suggestions, and the client should use getSuggestionDetails2 on selection
+ /// to make the library available in the file.
+ ///
+ /// Each item is the URI of a library, such as package:foo/bar.dart or
+ /// file:///home/me/workspace/foo/test/bar_test.dart.
+ List<String> libraryUrisToImport;
+
+ /// True if the number of suggestions after filtering was greater than the
+ /// requested maxResults.
+ bool isIncomplete;
+
+ CompletionGetSuggestions2Result(
+ this.replacementOffset,
+ this.replacementLength,
+ this.suggestions,
+ this.libraryUrisToImport,
+ this.isIncomplete);
+
+ factory CompletionGetSuggestions2Result.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ int replacementOffset;
+ if (json.containsKey('replacementOffset')) {
+ replacementOffset = jsonDecoder.decodeInt(
+ jsonPath + '.replacementOffset', json['replacementOffset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'replacementOffset');
+ }
+ int replacementLength;
+ if (json.containsKey('replacementLength')) {
+ replacementLength = jsonDecoder.decodeInt(
+ jsonPath + '.replacementLength', json['replacementLength']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'replacementLength');
+ }
+ List<CompletionSuggestion> suggestions;
+ if (json.containsKey('suggestions')) {
+ suggestions = jsonDecoder.decodeList(
+ jsonPath + '.suggestions',
+ json['suggestions'],
+ (String jsonPath, Object? json) =>
+ CompletionSuggestion.fromJson(jsonDecoder, jsonPath, json));
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'suggestions');
+ }
+ List<String> libraryUrisToImport;
+ if (json.containsKey('libraryUrisToImport')) {
+ libraryUrisToImport = jsonDecoder.decodeList(
+ jsonPath + '.libraryUrisToImport',
+ json['libraryUrisToImport'],
+ jsonDecoder.decodeString);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'libraryUrisToImport');
+ }
+ bool isIncomplete;
+ if (json.containsKey('isIncomplete')) {
+ isIncomplete = jsonDecoder.decodeBool(
+ jsonPath + '.isIncomplete', json['isIncomplete']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'isIncomplete');
+ }
+ return CompletionGetSuggestions2Result(replacementOffset,
+ replacementLength, suggestions, libraryUrisToImport, isIncomplete);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestions2 result', json);
+ }
+ }
+
+ factory CompletionGetSuggestions2Result.fromResponse(Response response) {
+ return CompletionGetSuggestions2Result.fromJson(
+ ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+ 'result',
+ response.result);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['replacementOffset'] = replacementOffset;
+ result['replacementLength'] = replacementLength;
+ result['suggestions'] = suggestions
+ .map((CompletionSuggestion value) => value.toJson())
+ .toList();
+ result['libraryUrisToImport'] = libraryUrisToImport;
+ result['isIncomplete'] = isIncomplete;
+ return result;
+ }
+
+ @override
+ Response toResponse(String id) {
+ return Response(id, result: toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestions2Result) {
+ return replacementOffset == other.replacementOffset &&
+ replacementLength == other.replacementLength &&
+ listEqual(suggestions, other.suggestions,
+ (CompletionSuggestion a, CompletionSuggestion b) => a == b) &&
+ listEqual(libraryUrisToImport, other.libraryUrisToImport,
+ (String a, String b) => a == b) &&
+ isIncomplete == other.isIncomplete;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ replacementOffset,
+ replacementLength,
+ suggestions,
+ libraryUrisToImport,
+ isIncomplete,
+ );
+}
+
/// completion.getSuggestions params
///
/// {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/add_return_type.dart b/pkg/analysis_server/lib/src/services/correction/dart/add_return_type.dart
index a66c22c..9e06b2f 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/add_return_type.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/add_return_type.dart
@@ -21,9 +21,18 @@
AssistKind get assistKind => DartAssistKind.ADD_RETURN_TYPE;
@override
+ bool get canBeAppliedInBulk => true;
+
+ @override
+ bool get canBeAppliedToFile => true;
+
+ @override
FixKind get fixKind => DartFixKind.ADD_RETURN_TYPE;
@override
+ FixKind? get multiFixKind => DartFixKind.ADD_RETURN_TYPE_MULTI;
+
+ @override
Future<void> compute(ChangeBuilder builder) async {
SyntacticEntity? insertBeforeEntity;
FunctionBody? body;
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/remove_unused_parameter.dart b/pkg/analysis_server/lib/src/services/correction/dart/remove_unused_parameter.dart
index 830345f..e18ebec 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/remove_unused_parameter.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/remove_unused_parameter.dart
@@ -12,9 +12,18 @@
class RemoveUnusedParameter extends CorrectionProducer {
@override
+ bool get canBeAppliedInBulk => true;
+
+ @override
+ bool get canBeAppliedToFile => true;
+
+ @override
FixKind get fixKind => DartFixKind.REMOVE_UNUSED_PARAMETER;
@override
+ FixKind? get multiFixKind => DartFixKind.REMOVE_UNUSED_PARAMETER_MULTI;
+
+ @override
Future<void> compute(ChangeBuilder builder) async {
var parameter = node;
if (parameter is! FormalParameter) {
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_eight_digit_hex.dart b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_eight_digit_hex.dart
index ff9e2eb..08271c9 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/replace_with_eight_digit_hex.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/replace_with_eight_digit_hex.dart
@@ -14,6 +14,12 @@
String _replacement = '';
@override
+ bool get canBeAppliedInBulk => true;
+
+ @override
+ bool get canBeAppliedToFile => true;
+
+ @override
List<Object> get fixArguments => [_replacement];
@override
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index e6be85e..04f4725 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -327,7 +327,6 @@
/// are in the [nonLintProducerMap].
static final Map<String, List<ProducerGenerator>> lintProducerMap = {
LintNames.always_declare_return_types: [
- // TODO(brianwilkerson) Consider applying in bulk.
AddReturnType.newInstance,
],
LintNames.always_require_non_null_named_parameters: [
@@ -393,7 +392,6 @@
RemoveTypeAnnotation.newInstance,
],
LintNames.avoid_unused_constructor_parameters: [
- // TODO(brianwilkerson) Consider applying in bulk.
RemoveUnusedParameter.newInstance,
],
LintNames.avoid_unnecessary_containers: [
@@ -614,7 +612,6 @@
RemoveThisExpression.newInstance,
],
LintNames.use_full_hex_values_for_flutter_colors: [
- // TODO(brianwilkerson) Consider applying in bulk.
ReplaceWithEightDigitHex.newInstance,
],
LintNames.use_function_type_syntax_for_parameters: [
diff --git a/pkg/analysis_server/test/integration/coverage.md b/pkg/analysis_server/test/integration/coverage.md
index 58d0edf..b91da0a 100644
--- a/pkg/analysis_server/test/integration/coverage.md
+++ b/pkg/analysis_server/test/integration/coverage.md
@@ -33,7 +33,9 @@
- [ ] completion.availableSuggestions
- [ ] completion.existingImports
- [ ] completion.getSuggestionDetails
+- [ ] completion.getSuggestionDetails2
- [x] completion.getSuggestions
+- [ ] completion.getSuggestions2
- [ ] completion.registerLibraryPaths
- [ ] completion.results
- [ ] completion.setSubscriptions
diff --git a/pkg/analysis_server/test/integration/support/integration_test_methods.dart b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
index 5fd4d70..9a707cf 100644
--- a/pkg/analysis_server/test/integration/support/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/support/integration_test_methods.dart
@@ -953,6 +953,81 @@
return CompletionGetSuggestionsResult.fromJson(decoder, 'result', result);
}
+ /// Request that completion suggestions for the given offset in the given
+ /// file be returned. The suggestions will be filtered using fuzzy matching
+ /// with the already existing prefix.
+ ///
+ /// Parameters
+ ///
+ /// file: FilePath
+ ///
+ /// The file containing the point at which suggestions are to be made.
+ ///
+ /// offset: int
+ ///
+ /// The offset within the file at which suggestions are to be made.
+ ///
+ /// maxResults: int
+ ///
+ /// The maximum number of suggestions to return. If the number of
+ /// suggestions after filtering is greater than the maxResults, then
+ /// isIncomplete is set to true.
+ ///
+ /// Returns
+ ///
+ /// replacementOffset: int
+ ///
+ /// The offset of the start of the text to be replaced. This will be
+ /// different from the offset used to request the completion suggestions if
+ /// there was a portion of an identifier before the original offset. In
+ /// particular, the replacementOffset will be the offset of the beginning
+ /// of said identifier.
+ ///
+ /// replacementLength: int
+ ///
+ /// The length of the text to be replaced if the remainder of the
+ /// identifier containing the cursor is to be replaced when the suggestion
+ /// is applied (that is, the number of characters in the existing
+ /// identifier).
+ ///
+ /// suggestions: List<CompletionSuggestion>
+ ///
+ /// The completion suggestions being reported. This list is filtered by the
+ /// already existing prefix, and sorted first by relevance, and (if the
+ /// same) by the suggestion text. The list will have at most maxResults
+ /// items. If the user types a new keystroke, the client is expected to
+ /// either do local filtering (when the returned list was complete), or ask
+ /// the server again (if isIncomplete was true).
+ ///
+ /// This list contains suggestions from both imported, and not yet imported
+ /// libraries. Items from not yet imported libraries will have
+ /// libraryUriToImportIndex set, which is an index into the
+ /// libraryUrisToImport in this response.
+ ///
+ /// libraryUrisToImport: List<String>
+ ///
+ /// The list of libraries with declarations that are not yet available in
+ /// the file where completion was requested, most often because the library
+ /// is not yet imported. The declarations still might be included into the
+ /// suggestions, and the client should use getSuggestionDetails2 on
+ /// selection to make the library available in the file.
+ ///
+ /// Each item is the URI of a library, such as package:foo/bar.dart or
+ /// file:///home/me/workspace/foo/test/bar_test.dart.
+ ///
+ /// isIncomplete: bool
+ ///
+ /// True if the number of suggestions after filtering was greater than the
+ /// requested maxResults.
+ Future<CompletionGetSuggestions2Result> sendCompletionGetSuggestions2(
+ String file, int offset, int maxResults) async {
+ var params =
+ CompletionGetSuggestions2Params(file, offset, maxResults).toJson();
+ var result = await server.send('completion.getSuggestions2', params);
+ var decoder = ResponseDecoder(null);
+ return CompletionGetSuggestions2Result.fromJson(decoder, 'result', result);
+ }
+
/// Subscribe for completion services. All previous subscriptions are
/// replaced by the given set of services.
///
@@ -1046,6 +1121,61 @@
decoder, 'result', result);
}
+ /// Clients must make this request when the user has selected a completion
+ /// suggestion with the libraryUriToImportIndex field set. The server will
+ /// respond with the text to insert, as well as any SourceChange that needs
+ /// to be applied in case the completion requires an additional import to be
+ /// added. The text to insert might be different from the original suggestion
+ /// to include an import prefix if the library will be imported with a prefix
+ /// to avoid shadowing conflicts in the file.
+ ///
+ /// Parameters
+ ///
+ /// file: FilePath
+ ///
+ /// The path of the file into which this completion is being inserted.
+ ///
+ /// offset: int
+ ///
+ /// The offset in the file where the completion will be inserted.
+ ///
+ /// completion: String
+ ///
+ /// The completion from the selected CompletionSuggestion. It could be a
+ /// name of a class, or a name of a constructor in form
+ /// "typeName.constructorName()", or an enumeration constant in form
+ /// "enumName.constantName", etc.
+ ///
+ /// libraryUri: String
+ ///
+ /// The URI of the library to import, so that the element referenced in the
+ /// completion becomes accessible.
+ ///
+ /// Returns
+ ///
+ /// completion: String
+ ///
+ /// The full text to insert, which possibly includes now an import prefix.
+ /// The client should insert this text, not the completion from the
+ /// selected CompletionSuggestion.
+ ///
+ /// change: SourceChange
+ ///
+ /// A change for the client to apply to make the accepted completion
+ /// suggestion available. In most cases the change is to add a new import
+ /// directive to the file.
+ Future<CompletionGetSuggestionDetails2Result>
+ sendCompletionGetSuggestionDetails2(
+ String file, int offset, String completion, String libraryUri) async {
+ var params = CompletionGetSuggestionDetails2Params(
+ file, offset, completion, libraryUri)
+ .toJson();
+ var result = await server.send('completion.getSuggestionDetails2', params);
+ var decoder = ResponseDecoder(null);
+ return CompletionGetSuggestionDetails2Result.fromJson(
+ decoder, 'result', result);
+ }
+
/// Reports the completion suggestions that should be presented to the user.
/// The set of suggestions included in the notification is always a complete
/// list that supersedes any previously reported suggestions.
diff --git a/pkg/analysis_server/test/integration/support/protocol_matchers.dart b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
index 8a094d4..5fd8c3c 100644
--- a/pkg/analysis_server/test/integration/support/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/support/protocol_matchers.dart
@@ -295,7 +295,8 @@
'requiredParameterCount': isInt,
'hasNamedParameters': isBool,
'parameterName': isString,
- 'parameterType': isString
+ 'parameterType': isString,
+ 'libraryUriToImportIndex': isInt
}));
/// CompletionSuggestionKind
@@ -2080,6 +2081,32 @@
MatchesJsonObject('completion.existingImports params',
{'file': isFilePath, 'imports': isExistingImports}));
+/// completion.getSuggestionDetails2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "completion": String
+/// "libraryUri": String
+/// }
+final Matcher isCompletionGetSuggestionDetails2Params = LazyMatcher(
+ () => MatchesJsonObject('completion.getSuggestionDetails2 params', {
+ 'file': isFilePath,
+ 'offset': isInt,
+ 'completion': isString,
+ 'libraryUri': isString
+ }));
+
+/// completion.getSuggestionDetails2 result
+///
+/// {
+/// "completion": String
+/// "change": SourceChange
+/// }
+final Matcher isCompletionGetSuggestionDetails2Result = LazyMatcher(() =>
+ MatchesJsonObject('completion.getSuggestionDetails2 result',
+ {'completion': isString, 'change': isSourceChange}));
+
/// completion.getSuggestionDetails params
///
/// {
@@ -2103,6 +2130,35 @@
'completion.getSuggestionDetails result', {'completion': isString},
optionalFields: {'change': isSourceChange}));
+/// completion.getSuggestions2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "maxResults": int
+/// }
+final Matcher isCompletionGetSuggestions2Params = LazyMatcher(() =>
+ MatchesJsonObject('completion.getSuggestions2 params',
+ {'file': isFilePath, 'offset': isInt, 'maxResults': isInt}));
+
+/// completion.getSuggestions2 result
+///
+/// {
+/// "replacementOffset": int
+/// "replacementLength": int
+/// "suggestions": List<CompletionSuggestion>
+/// "libraryUrisToImport": List<String>
+/// "isIncomplete": bool
+/// }
+final Matcher isCompletionGetSuggestions2Result =
+ LazyMatcher(() => MatchesJsonObject('completion.getSuggestions2 result', {
+ 'replacementOffset': isInt,
+ 'replacementLength': isInt,
+ 'suggestions': isListOf(isCompletionSuggestion),
+ 'libraryUrisToImport': isListOf(isString),
+ 'isIncomplete': isBool
+ }));
+
/// completion.getSuggestions params
///
/// {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/add_return_type_test.dart b/pkg/analysis_server/test/src/services/correction/fix/add_return_type_test.dart
index cfcabd6..1a40007 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/add_return_type_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/add_return_type_test.dart
@@ -12,10 +12,36 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(AddReturnTypeLintTest);
+ defineReflectiveTests(AddReturnTypeBulkTest);
});
}
@reflectiveTest
+class AddReturnTypeBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.always_declare_return_types;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+class A {
+ get foo => 0;
+ m(p) {
+ return p;
+ }
+}
+''');
+ await assertHasFix('''
+class A {
+ int get foo => 0;
+ dynamic m(p) {
+ return p;
+ }
+}
+''');
+ }
+}
+
+@reflectiveTest
class AddReturnTypeLintTest extends FixProcessorLintTest {
@override
FixKind get kind => DartFixKind.ADD_RETURN_TYPE;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
index e421d0e..fe01fbd 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/remove_unused_parameter_test.dart
@@ -12,10 +12,32 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(RemoveUnusedParameterTest);
+ defineReflectiveTests(RemoveUnusedParameterBulkTest);
});
}
@reflectiveTest
+class RemoveUnusedParameterBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.avoid_unused_constructor_parameters;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+class C {
+ int y;
+ C({int x = 0, this.y = 0, int z = 0});
+}
+''');
+ await assertHasFix('''
+class C {
+ int y;
+ C({this.y = 0});
+}
+''');
+ }
+}
+
+@reflectiveTest
class RemoveUnusedParameterTest extends FixProcessorLintTest {
@override
FixKind get kind => DartFixKind.REMOVE_UNUSED_PARAMETER;
diff --git a/pkg/analysis_server/test/src/services/correction/fix/replace_with_eight_digit_hex_test.dart b/pkg/analysis_server/test/src/services/correction/fix/replace_with_eight_digit_hex_test.dart
index bd4e2e1..fb67e7a 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/replace_with_eight_digit_hex_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/replace_with_eight_digit_hex_test.dart
@@ -12,10 +12,40 @@
void main() {
defineReflectiveSuite(() {
defineReflectiveTests(ReplaceWithEightDigitHexTest);
+ defineReflectiveTests(ReplaceWithEightDigitHexBulkTest);
});
}
@reflectiveTest
+class ReplaceWithEightDigitHexBulkTest extends BulkFixProcessorTest {
+ @override
+ String get lintCode => LintNames.use_full_hex_values_for_flutter_colors;
+
+ Future<void> test_singleFile() async {
+ await resolveTestCode('''
+library dart.ui;
+
+var c = Color(1);
+var c2 = Color(0x000001);
+
+class Color {
+ Color(int value);
+}
+''');
+ await assertHasFix('''
+library dart.ui;
+
+var c = Color(0x00000001);
+var c2 = Color(0x00000001);
+
+class Color {
+ Color(int value);
+}
+''');
+ }
+}
+
+@reflectiveTest
class ReplaceWithEightDigitHexTest extends FixProcessorLintTest {
@override
FixKind get kind => DartFixKind.REPLACE_WITH_EIGHT_DIGIT_HEX;
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index 03b9cbe..9f7e268 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -402,6 +402,26 @@
public void completion_getSuggestionDetails(String file, int id, String label, int offset, GetSuggestionDetailsConsumer consumer);
/**
+ * {@code completion.getSuggestionDetails2}
+ *
+ * Clients must make this request when the user has selected a completion suggestion with the
+ * libraryUriToImportIndex field set. The server will respond with the text to insert, as well as
+ * any SourceChange that needs to be applied in case the completion requires an additional import
+ * to be added. The text to insert might be different from the original suggestion to include an
+ * import prefix if the library will be imported with a prefix to avoid shadowing conflicts in the
+ * file.
+ *
+ * @param file The path of the file into which this completion is being inserted.
+ * @param offset The offset in the file where the completion will be inserted.
+ * @param completion The completion from the selected CompletionSuggestion. It could be a name of a
+ * class, or a name of a constructor in form "typeName.constructorName()", or an
+ * enumeration constant in form "enumName.constantName", etc.
+ * @param libraryUri The URI of the library to import, so that the element referenced in the
+ * completion becomes accessible.
+ */
+ public void completion_getSuggestionDetails2(String file, int offset, String completion, String libraryUri, GetSuggestionDetails2Consumer consumer);
+
+ /**
* {@code completion.getSuggestions}
*
* Request that completion suggestions for the given offset in the given file be returned.
@@ -412,6 +432,19 @@
public void completion_getSuggestions(String file, int offset, GetSuggestionsConsumer consumer);
/**
+ * {@code completion.getSuggestions2}
+ *
+ * Request that completion suggestions for the given offset in the given file be returned. The
+ * suggestions will be filtered using fuzzy matching with the already existing prefix.
+ *
+ * @param file The file containing the point at which suggestions are to be made.
+ * @param offset The offset within the file at which suggestions are to be made.
+ * @param maxResults The maximum number of suggestions to return. If the number of suggestions
+ * after filtering is greater than the maxResults, then isIncomplete is set to true.
+ */
+ public void completion_getSuggestions2(String file, int offset, int maxResults, GetSuggestions2Consumer consumer);
+
+ /**
* {@code completion.registerLibraryPaths}
*
* The client can make this request to express interest in certain libraries to receive completion
diff --git a/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestion.java b/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestion.java
index c9ffeeb..bf8d912 100644
--- a/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestion.java
+++ b/pkg/analysis_server/tool/spec/generated/java/types/CompletionSuggestion.java
@@ -176,9 +176,18 @@
private final String parameterType;
/**
+ * The index in the list of libraries that could be imported to make this suggestion accessible in
+ * the file where completion was requested. The server provides this list of libraries together
+ * with suggestions, so that information about the library can be shared for multiple suggestions.
+ * This field is omitted if the library is already imported, so that the suggestion can be inserted
+ * as is, or if getSuggestions was used rather than getSuggestions2.
+ */
+ private final Integer libraryUriToImportIndex;
+
+ /**
* Constructor for {@link CompletionSuggestion}.
*/
- public CompletionSuggestion(String kind, int relevance, String completion, String displayText, Integer replacementOffset, Integer replacementLength, int selectionOffset, int selectionLength, boolean isDeprecated, boolean isPotential, String docSummary, String docComplete, String declaringType, String defaultArgumentListString, int[] defaultArgumentListTextRanges, Element element, String returnType, List<String> parameterNames, List<String> parameterTypes, Integer requiredParameterCount, Boolean hasNamedParameters, String parameterName, String parameterType) {
+ public CompletionSuggestion(String kind, int relevance, String completion, String displayText, Integer replacementOffset, Integer replacementLength, int selectionOffset, int selectionLength, boolean isDeprecated, boolean isPotential, String docSummary, String docComplete, String declaringType, String defaultArgumentListString, int[] defaultArgumentListTextRanges, Element element, String returnType, List<String> parameterNames, List<String> parameterTypes, Integer requiredParameterCount, Boolean hasNamedParameters, String parameterName, String parameterType, Integer libraryUriToImportIndex) {
this.kind = kind;
this.relevance = relevance;
this.completion = completion;
@@ -202,6 +211,7 @@
this.hasNamedParameters = hasNamedParameters;
this.parameterName = parameterName;
this.parameterType = parameterType;
+ this.libraryUriToImportIndex = libraryUriToImportIndex;
}
@Override
@@ -231,7 +241,8 @@
ObjectUtilities.equals(other.requiredParameterCount, requiredParameterCount) &&
ObjectUtilities.equals(other.hasNamedParameters, hasNamedParameters) &&
ObjectUtilities.equals(other.parameterName, parameterName) &&
- ObjectUtilities.equals(other.parameterType, parameterType);
+ ObjectUtilities.equals(other.parameterType, parameterType) &&
+ ObjectUtilities.equals(other.libraryUriToImportIndex, libraryUriToImportIndex);
}
return false;
}
@@ -260,7 +271,8 @@
Boolean hasNamedParameters = jsonObject.get("hasNamedParameters") == null ? null : jsonObject.get("hasNamedParameters").getAsBoolean();
String parameterName = jsonObject.get("parameterName") == null ? null : jsonObject.get("parameterName").getAsString();
String parameterType = jsonObject.get("parameterType") == null ? null : jsonObject.get("parameterType").getAsString();
- return new CompletionSuggestion(kind, relevance, completion, displayText, replacementOffset, replacementLength, selectionOffset, selectionLength, isDeprecated, isPotential, docSummary, docComplete, declaringType, defaultArgumentListString, defaultArgumentListTextRanges, element, returnType, parameterNames, parameterTypes, requiredParameterCount, hasNamedParameters, parameterName, parameterType);
+ Integer libraryUriToImportIndex = jsonObject.get("libraryUriToImportIndex") == null ? null : jsonObject.get("libraryUriToImportIndex").getAsInt();
+ return new CompletionSuggestion(kind, relevance, completion, displayText, replacementOffset, replacementLength, selectionOffset, selectionLength, isDeprecated, isPotential, docSummary, docComplete, declaringType, defaultArgumentListString, defaultArgumentListTextRanges, element, returnType, parameterNames, parameterTypes, requiredParameterCount, hasNamedParameters, parameterName, parameterType, libraryUriToImportIndex);
}
public static List<CompletionSuggestion> fromJsonArray(JsonArray jsonArray) {
@@ -372,6 +384,17 @@
}
/**
+ * The index in the list of libraries that could be imported to make this suggestion accessible in
+ * the file where completion was requested. The server provides this list of libraries together
+ * with suggestions, so that information about the library can be shared for multiple suggestions.
+ * This field is omitted if the library is already imported, so that the suggestion can be inserted
+ * as is, or if getSuggestions was used rather than getSuggestions2.
+ */
+ public Integer getLibraryUriToImportIndex() {
+ return libraryUriToImportIndex;
+ }
+
+ /**
* The name of the optional parameter being suggested. This field is omitted if the suggestion is
* not the addition of an optional argument within an argument list.
*/
@@ -486,6 +509,7 @@
builder.append(hasNamedParameters);
builder.append(parameterName);
builder.append(parameterType);
+ builder.append(libraryUriToImportIndex);
return builder.toHashCode();
}
@@ -558,6 +582,9 @@
if (parameterType != null) {
jsonObject.addProperty("parameterType", parameterType);
}
+ if (libraryUriToImportIndex != null) {
+ jsonObject.addProperty("libraryUriToImportIndex", libraryUriToImportIndex);
+ }
return jsonObject;
}
@@ -610,7 +637,9 @@
builder.append("parameterName=");
builder.append(parameterName + ", ");
builder.append("parameterType=");
- builder.append(parameterType);
+ builder.append(parameterType + ", ");
+ builder.append("libraryUriToImportIndex=");
+ builder.append(libraryUriToImportIndex);
builder.append("]");
return builder.toString();
}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 7bb2fe0..aa08d74 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -1455,6 +1455,100 @@
</field>
</result>
</request>
+ <request method="getSuggestions2" experimental="true">
+ <p>
+ Request that completion suggestions for the given offset in the given
+ file be returned. The suggestions will be filtered using fuzzy matching
+ with the already existing prefix.
+ </p>
+ <params>
+ <field name="file">
+ <ref>FilePath</ref>
+ <p>
+ The file containing the point at which suggestions are to be made.
+ </p>
+ </field>
+ <field name="offset">
+ <ref>int</ref>
+ <p>
+ The offset within the file at which suggestions are to be made.
+ </p>
+ </field>
+ <field name="maxResults">
+ <ref>int</ref>
+ <p>
+ The maximum number of suggestions to return. If the number of
+ suggestions after filtering is greater than the <tt>maxResults</tt>,
+ then <tt>isIncomplete</tt> is set to <tt>true</tt>.
+ </p>
+ </field>
+ </params>
+ <result>
+ <field name="replacementOffset">
+ <ref>int</ref>
+ <p>
+ The offset of the start of the text to be replaced. This will be
+ different from the offset used to request the completion suggestions
+ if there was a portion of an identifier before the original offset.
+ In particular, the replacementOffset will be the offset of the
+ beginning of said identifier.
+ </p>
+ </field>
+ <field name="replacementLength">
+ <ref>int</ref>
+ <p>
+ The length of the text to be replaced if the remainder of the
+ identifier containing the cursor is to be replaced when the
+ suggestion is applied (that is, the number of characters in the
+ existing identifier).
+ </p>
+ </field>
+ <field name="suggestions">
+ <list>
+ <ref>CompletionSuggestion</ref>
+ </list>
+ <p>
+ The completion suggestions being reported. This list is filtered
+ by the already existing prefix, and sorted first by relevance,
+ and (if the same) by the suggestion text. The list will have at
+ most <tt>maxResults</tt> items. If the user types a new keystroke,
+ the client is expected to either do local filtering (when the
+ returned list was complete), or ask the server again (if
+ <tt>isIncomplete</tt> was <tt>true</tt>).
+ </p>
+ <p>
+ This list contains suggestions from both imported, and not yet
+ imported libraries. Items from not yet imported libraries will
+ have <tt>libraryUriToImportIndex</tt> set, which is an index into
+ the <tt>libraryUrisToImport</tt> in this response.
+ </p>
+ </field>
+ <field name="libraryUrisToImport">
+ <list>
+ <ref>String</ref>
+ </list>
+ <p>
+ The list of libraries with declarations that are not yet available
+ in the file where completion was requested, most often because
+ the library is not yet imported. The declarations still might be
+ included into the <tt>suggestions</tt>, and the client should use
+ <tt>getSuggestionDetails2</tt> on selection to make the library
+ available in the file.
+ </p>
+ <p>
+ Each item is the URI of a library, such as <tt>package:foo/bar.dart</tt>
+ or <tt>file:///home/me/workspace/foo/test/bar_test.dart</tt>.
+ </p>
+ </field>
+ <field name="isIncomplete">
+ <ref>bool</ref>
+ <p>
+ True if the number of suggestions after filtering was greater than
+ the requested <tt>maxResults</tt>.
+ </p>
+ </field>
+ </result>
+ </request>
<request method="setSubscriptions">
<p>
Subscribe for completion services. All previous subscriptions are
@@ -1551,6 +1645,66 @@
</field>
</result>
</request>
+ <request method="getSuggestionDetails2" experimental="true">
+ <p>
+ Clients must make this request when the user has selected a completion
+ suggestion with the <tt>libraryUriToImportIndex</tt> field set.
+ The server will respond with the text to insert, as well as any
+ <tt>SourceChange</tt> that needs to be applied in case the completion
+ requires an additional import to be added. The text to insert might be
+ different from the original suggestion to include an import prefix if the
+ library will be imported with a prefix to avoid shadowing
+ conflicts in the file.
+ </p>
+ <params>
+ <field name="file">
+ <ref>FilePath</ref>
+ <p>
+ The path of the file into which this completion is being inserted.
+ </p>
+ </field>
+ <field name="offset">
+ <ref>int</ref>
+ <p>
+ The offset in the file where the completion will be inserted.
+ </p>
+ </field>
+ <field name="completion">
+ <ref>String</ref>
+ <p>
+ The <tt>completion</tt> from the selected
+ <tt>CompletionSuggestion</tt>. It could be a name of a class, or a
+ name of a constructor in form "typeName.constructorName()", or an
+ enumeration constant in form "enumName.constantName", etc.
+ </p>
+ </field>
+ <field name="libraryUri">
+ <ref>String</ref>
+ <p>
+ The URI of the library to import, so that the element referenced
+ in the <tt>completion</tt> becomes accessible.
+ </p>
+ </field>
+ </params>
+ <result>
+ <field name="completion">
+ <ref>String</ref>
+ <p>
+ The full text to insert, which possibly includes now an import prefix.
+ The client should insert this text, not the <tt>completion</tt> from
+ the selected <tt>CompletionSuggestion</tt>.
+ </p>
+ </field>
+ <field name="change">
+ <ref>SourceChange</ref>
+ <p>
+ A change for the client to apply to make the accepted completion
+ suggestion available. In most cases the change is to add a new
+ import directive to the file.
+ </p>
+ </field>
+ </result>
+ </request>
<notification event="results">
<p>
Reports the completion suggestions that should be presented
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
index 4289f41..b2d7548 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_common.dart
@@ -612,6 +612,15 @@
/// if the parameterName field is omitted.
String? parameterType;
+ /// The index in the list of libraries that could be imported to make this
+ /// suggestion accessible in the file where completion was requested. The
+ /// server provides this list of libraries together with suggestions, so that
+ /// information about the library can be shared for multiple suggestions.
+ /// This field is omitted if the library is already imported, so that the
+ /// suggestion can be inserted as is, or if getSuggestions was used rather
+ /// than getSuggestions2.
+ int? libraryUriToImportIndex;
+
CompletionSuggestion(
this.kind,
this.relevance,
@@ -635,7 +644,8 @@
this.requiredParameterCount,
this.hasNamedParameters,
this.parameterName,
- this.parameterType});
+ this.parameterType,
+ this.libraryUriToImportIndex});
factory CompletionSuggestion.fromJson(
JsonDecoder jsonDecoder, String jsonPath, Object? json) {
@@ -774,6 +784,12 @@
parameterType = jsonDecoder.decodeString(
jsonPath + '.parameterType', json['parameterType']);
}
+ int? libraryUriToImportIndex;
+ if (json.containsKey('libraryUriToImportIndex')) {
+ libraryUriToImportIndex = jsonDecoder.decodeInt(
+ jsonPath + '.libraryUriToImportIndex',
+ json['libraryUriToImportIndex']);
+ }
return CompletionSuggestion(kind, relevance, completion, selectionOffset,
selectionLength, isDeprecated, isPotential,
displayText: displayText,
@@ -791,7 +807,8 @@
requiredParameterCount: requiredParameterCount,
hasNamedParameters: hasNamedParameters,
parameterName: parameterName,
- parameterType: parameterType);
+ parameterType: parameterType,
+ libraryUriToImportIndex: libraryUriToImportIndex);
} else {
throw jsonDecoder.mismatch(jsonPath, 'CompletionSuggestion', json);
}
@@ -871,6 +888,10 @@
if (parameterType != null) {
result['parameterType'] = parameterType;
}
+ var libraryUriToImportIndex = this.libraryUriToImportIndex;
+ if (libraryUriToImportIndex != null) {
+ result['libraryUriToImportIndex'] = libraryUriToImportIndex;
+ }
return result;
}
@@ -905,7 +926,8 @@
requiredParameterCount == other.requiredParameterCount &&
hasNamedParameters == other.hasNamedParameters &&
parameterName == other.parameterName &&
- parameterType == other.parameterType;
+ parameterType == other.parameterType &&
+ libraryUriToImportIndex == other.libraryUriToImportIndex;
}
return false;
}
@@ -935,6 +957,7 @@
hasNamedParameters,
parameterName,
parameterType,
+ libraryUriToImportIndex,
]);
}
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
index 84b3a5c..2689506 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_constants.dart
@@ -137,10 +137,22 @@
'replacementOffset';
const String COMPLETION_NOTIFICATION_RESULTS_RESULTS = 'results';
const String COMPLETION_REQUEST_GET_SUGGESTIONS = 'completion.getSuggestions';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2 = 'completion.getSuggestions2';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_FILE = 'file';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_MAX_RESULTS = 'maxResults';
+const String COMPLETION_REQUEST_GET_SUGGESTIONS2_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTIONS_FILE = 'file';
const String COMPLETION_REQUEST_GET_SUGGESTIONS_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS =
'completion.getSuggestionDetails';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2 =
+ 'completion.getSuggestionDetails2';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_COMPLETION =
+ 'completion';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_FILE = 'file';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_LIBRARY_URI =
+ 'libraryUri';
+const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS2_OFFSET = 'offset';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_FILE = 'file';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_ID = 'id';
const String COMPLETION_REQUEST_GET_SUGGESTION_DETAILS_LABEL = 'label';
@@ -152,7 +164,19 @@
'completion.setSubscriptions';
const String COMPLETION_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS =
'subscriptions';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_IS_INCOMPLETE =
+ 'isIncomplete';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_LIBRARY_URIS_TO_IMPORT =
+ 'libraryUrisToImport';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_REPLACEMENT_LENGTH =
+ 'replacementLength';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_REPLACEMENT_OFFSET =
+ 'replacementOffset';
+const String COMPLETION_RESPONSE_GET_SUGGESTIONS2_SUGGESTIONS = 'suggestions';
const String COMPLETION_RESPONSE_GET_SUGGESTIONS_ID = 'id';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS2_CHANGE = 'change';
+const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS2_COMPLETION =
+ 'completion';
const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_CHANGE = 'change';
const String COMPLETION_RESPONSE_GET_SUGGESTION_DETAILS_COMPLETION =
'completion';
diff --git a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
index 616ff13..2dcbd32 100644
--- a/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
+++ b/pkg/analysis_server_client/lib/src/protocol/protocol_generated.dart
@@ -4292,6 +4292,202 @@
);
}
+/// completion.getSuggestionDetails2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "completion": String
+/// "libraryUri": String
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestionDetails2Params implements RequestParams {
+ /// The path of the file into which this completion is being inserted.
+ String file;
+
+ /// The offset in the file where the completion will be inserted.
+ int offset;
+
+ /// The completion from the selected CompletionSuggestion. It could be a name
+ /// of a class, or a name of a constructor in form
+ /// "typeName.constructorName()", or an enumeration constant in form
+ /// "enumName.constantName", etc.
+ String completion;
+
+ /// The URI of the library to import, so that the element referenced in the
+ /// completion becomes accessible.
+ String libraryUri;
+
+ CompletionGetSuggestionDetails2Params(
+ this.file, this.offset, this.completion, this.libraryUri);
+
+ factory CompletionGetSuggestionDetails2Params.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String file;
+ if (json.containsKey('file')) {
+ file = jsonDecoder.decodeString(jsonPath + '.file', json['file']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'file');
+ }
+ int offset;
+ if (json.containsKey('offset')) {
+ offset = jsonDecoder.decodeInt(jsonPath + '.offset', json['offset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'offset');
+ }
+ String completion;
+ if (json.containsKey('completion')) {
+ completion = jsonDecoder.decodeString(
+ jsonPath + '.completion', json['completion']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'completion');
+ }
+ String libraryUri;
+ if (json.containsKey('libraryUri')) {
+ libraryUri = jsonDecoder.decodeString(
+ jsonPath + '.libraryUri', json['libraryUri']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'libraryUri');
+ }
+ return CompletionGetSuggestionDetails2Params(
+ file, offset, completion, libraryUri);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestionDetails2 params', json);
+ }
+ }
+
+ factory CompletionGetSuggestionDetails2Params.fromRequest(Request request) {
+ return CompletionGetSuggestionDetails2Params.fromJson(
+ RequestDecoder(request), 'params', request.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['file'] = file;
+ result['offset'] = offset;
+ result['completion'] = completion;
+ result['libraryUri'] = libraryUri;
+ return result;
+ }
+
+ @override
+ Request toRequest(String id) {
+ return Request(id, 'completion.getSuggestionDetails2', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestionDetails2Params) {
+ return file == other.file &&
+ offset == other.offset &&
+ completion == other.completion &&
+ libraryUri == other.libraryUri;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ file,
+ offset,
+ completion,
+ libraryUri,
+ );
+}
+
+/// completion.getSuggestionDetails2 result
+///
+/// {
+/// "completion": String
+/// "change": SourceChange
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestionDetails2Result implements ResponseResult {
+ /// The full text to insert, which possibly includes now an import prefix.
+ /// The client should insert this text, not the completion from the selected
+ /// CompletionSuggestion.
+ String completion;
+
+ /// A change for the client to apply to make the accepted completion
+ /// suggestion available. In most cases the change is to add a new import
+ /// directive to the file.
+ SourceChange change;
+
+ CompletionGetSuggestionDetails2Result(this.completion, this.change);
+
+ factory CompletionGetSuggestionDetails2Result.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String completion;
+ if (json.containsKey('completion')) {
+ completion = jsonDecoder.decodeString(
+ jsonPath + '.completion', json['completion']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'completion');
+ }
+ SourceChange change;
+ if (json.containsKey('change')) {
+ change = SourceChange.fromJson(
+ jsonDecoder, jsonPath + '.change', json['change']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'change');
+ }
+ return CompletionGetSuggestionDetails2Result(completion, change);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestionDetails2 result', json);
+ }
+ }
+
+ factory CompletionGetSuggestionDetails2Result.fromResponse(
+ Response response) {
+ return CompletionGetSuggestionDetails2Result.fromJson(
+ ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+ 'result',
+ response.result);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['completion'] = completion;
+ result['change'] = change.toJson();
+ return result;
+ }
+
+ @override
+ Response toResponse(String id) {
+ return Response(id, result: toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestionDetails2Result) {
+ return completion == other.completion && change == other.change;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ completion,
+ change,
+ );
+}
+
/// completion.getSuggestionDetails params
///
/// {
@@ -4481,6 +4677,261 @@
);
}
+/// completion.getSuggestions2 params
+///
+/// {
+/// "file": FilePath
+/// "offset": int
+/// "maxResults": int
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestions2Params implements RequestParams {
+ /// The file containing the point at which suggestions are to be made.
+ String file;
+
+ /// The offset within the file at which suggestions are to be made.
+ int offset;
+
+ /// The maximum number of suggestions to return. If the number of suggestions
+ /// after filtering is greater than the maxResults, then isIncomplete is set
+ /// to true.
+ int maxResults;
+
+ CompletionGetSuggestions2Params(this.file, this.offset, this.maxResults);
+
+ factory CompletionGetSuggestions2Params.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ String file;
+ if (json.containsKey('file')) {
+ file = jsonDecoder.decodeString(jsonPath + '.file', json['file']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'file');
+ }
+ int offset;
+ if (json.containsKey('offset')) {
+ offset = jsonDecoder.decodeInt(jsonPath + '.offset', json['offset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'offset');
+ }
+ int maxResults;
+ if (json.containsKey('maxResults')) {
+ maxResults =
+ jsonDecoder.decodeInt(jsonPath + '.maxResults', json['maxResults']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'maxResults');
+ }
+ return CompletionGetSuggestions2Params(file, offset, maxResults);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestions2 params', json);
+ }
+ }
+
+ factory CompletionGetSuggestions2Params.fromRequest(Request request) {
+ return CompletionGetSuggestions2Params.fromJson(
+ RequestDecoder(request), 'params', request.params);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['file'] = file;
+ result['offset'] = offset;
+ result['maxResults'] = maxResults;
+ return result;
+ }
+
+ @override
+ Request toRequest(String id) {
+ return Request(id, 'completion.getSuggestions2', toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestions2Params) {
+ return file == other.file &&
+ offset == other.offset &&
+ maxResults == other.maxResults;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ file,
+ offset,
+ maxResults,
+ );
+}
+
+/// completion.getSuggestions2 result
+///
+/// {
+/// "replacementOffset": int
+/// "replacementLength": int
+/// "suggestions": List<CompletionSuggestion>
+/// "libraryUrisToImport": List<String>
+/// "isIncomplete": bool
+/// }
+///
+/// Clients may not extend, implement or mix-in this class.
+class CompletionGetSuggestions2Result implements ResponseResult {
+ /// The offset of the start of the text to be replaced. This will be
+ /// different from the offset used to request the completion suggestions if
+ /// there was a portion of an identifier before the original offset. In
+ /// particular, the replacementOffset will be the offset of the beginning of
+ /// said identifier.
+ int replacementOffset;
+
+ /// The length of the text to be replaced if the remainder of the identifier
+ /// containing the cursor is to be replaced when the suggestion is applied
+ /// (that is, the number of characters in the existing identifier).
+ int replacementLength;
+
+ /// The completion suggestions being reported. This list is filtered by the
+ /// already existing prefix, and sorted first by relevance, and (if the same)
+ /// by the suggestion text. The list will have at most maxResults items. If
+ /// the user types a new keystroke, the client is expected to either do local
+ /// filtering (when the returned list was complete), or ask the server again
+ /// (if isIncomplete was true).
+ ///
+ /// This list contains suggestions from both imported, and not yet imported
+ /// libraries. Items from not yet imported libraries will have
+ /// libraryUriToImportIndex set, which is an index into the
+ /// libraryUrisToImport in this response.
+ List<CompletionSuggestion> suggestions;
+
+ /// The list of libraries with declarations that are not yet available in the
+ /// file where completion was requested, most often because the library is
+ /// not yet imported. The declarations still might be included into the
+ /// suggestions, and the client should use getSuggestionDetails2 on selection
+ /// to make the library available in the file.
+ ///
+ /// Each item is the URI of a library, such as package:foo/bar.dart or
+ /// file:///home/me/workspace/foo/test/bar_test.dart.
+ List<String> libraryUrisToImport;
+
+ /// True if the number of suggestions after filtering was greater than the
+ /// requested maxResults.
+ bool isIncomplete;
+
+ CompletionGetSuggestions2Result(
+ this.replacementOffset,
+ this.replacementLength,
+ this.suggestions,
+ this.libraryUrisToImport,
+ this.isIncomplete);
+
+ factory CompletionGetSuggestions2Result.fromJson(
+ JsonDecoder jsonDecoder, String jsonPath, Object? json) {
+ json ??= {};
+ if (json is Map) {
+ int replacementOffset;
+ if (json.containsKey('replacementOffset')) {
+ replacementOffset = jsonDecoder.decodeInt(
+ jsonPath + '.replacementOffset', json['replacementOffset']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'replacementOffset');
+ }
+ int replacementLength;
+ if (json.containsKey('replacementLength')) {
+ replacementLength = jsonDecoder.decodeInt(
+ jsonPath + '.replacementLength', json['replacementLength']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'replacementLength');
+ }
+ List<CompletionSuggestion> suggestions;
+ if (json.containsKey('suggestions')) {
+ suggestions = jsonDecoder.decodeList(
+ jsonPath + '.suggestions',
+ json['suggestions'],
+ (String jsonPath, Object? json) =>
+ CompletionSuggestion.fromJson(jsonDecoder, jsonPath, json));
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'suggestions');
+ }
+ List<String> libraryUrisToImport;
+ if (json.containsKey('libraryUrisToImport')) {
+ libraryUrisToImport = jsonDecoder.decodeList(
+ jsonPath + '.libraryUrisToImport',
+ json['libraryUrisToImport'],
+ jsonDecoder.decodeString);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'libraryUrisToImport');
+ }
+ bool isIncomplete;
+ if (json.containsKey('isIncomplete')) {
+ isIncomplete = jsonDecoder.decodeBool(
+ jsonPath + '.isIncomplete', json['isIncomplete']);
+ } else {
+ throw jsonDecoder.mismatch(jsonPath, 'isIncomplete');
+ }
+ return CompletionGetSuggestions2Result(replacementOffset,
+ replacementLength, suggestions, libraryUrisToImport, isIncomplete);
+ } else {
+ throw jsonDecoder.mismatch(
+ jsonPath, 'completion.getSuggestions2 result', json);
+ }
+ }
+
+ factory CompletionGetSuggestions2Result.fromResponse(Response response) {
+ return CompletionGetSuggestions2Result.fromJson(
+ ResponseDecoder(REQUEST_ID_REFACTORING_KINDS.remove(response.id)),
+ 'result',
+ response.result);
+ }
+
+ @override
+ Map<String, Object> toJson() {
+ var result = <String, Object>{};
+ result['replacementOffset'] = replacementOffset;
+ result['replacementLength'] = replacementLength;
+ result['suggestions'] = suggestions
+ .map((CompletionSuggestion value) => value.toJson())
+ .toList();
+ result['libraryUrisToImport'] = libraryUrisToImport;
+ result['isIncomplete'] = isIncomplete;
+ return result;
+ }
+
+ @override
+ Response toResponse(String id) {
+ return Response(id, result: toJson());
+ }
+
+ @override
+ String toString() => json.encode(toJson());
+
+ @override
+ bool operator ==(other) {
+ if (other is CompletionGetSuggestions2Result) {
+ return replacementOffset == other.replacementOffset &&
+ replacementLength == other.replacementLength &&
+ listEqual(suggestions, other.suggestions,
+ (CompletionSuggestion a, CompletionSuggestion b) => a == b) &&
+ listEqual(libraryUrisToImport, other.libraryUrisToImport,
+ (String a, String b) => a == b) &&
+ isIncomplete == other.isIncomplete;
+ }
+ return false;
+ }
+
+ @override
+ int get hashCode => Object.hash(
+ replacementOffset,
+ replacementLength,
+ suggestions,
+ libraryUrisToImport,
+ isIncomplete,
+ );
+}
+
/// completion.getSuggestions params
///
/// {
diff --git a/pkg/analyzer/lib/src/dart/analysis/driver.dart b/pkg/analyzer/lib/src/dart/analysis/driver.dart
index 0c9499c..557a2e8 100644
--- a/pkg/analyzer/lib/src/dart/analysis/driver.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/driver.dart
@@ -80,7 +80,7 @@
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
- static const int DATA_VERSION = 189;
+ static const int DATA_VERSION = 190;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.
diff --git a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
index e4c3cc4..ce99ad0 100644
--- a/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
+++ b/pkg/analyzer/lib/src/dart/analysis/experiments.g.dart
@@ -28,6 +28,7 @@
ExperimentalFeatures.nonfunction_type_aliases,
EnableString.set_literals: ExperimentalFeatures.set_literals,
EnableString.spread_collections: ExperimentalFeatures.spread_collections,
+ EnableString.super_parameters: ExperimentalFeatures.super_parameters,
EnableString.test_experiment: ExperimentalFeatures.test_experiment,
EnableString.triple_shift: ExperimentalFeatures.triple_shift,
EnableString.value_class: ExperimentalFeatures.value_class,
@@ -76,6 +77,9 @@
/// String to enable the experiment "spread-collections"
static const String spread_collections = 'spread-collections';
+ /// String to enable the experiment "super-parameters"
+ static const String super_parameters = 'super-parameters';
+
/// String to enable the experiment "test-experiment"
static const String test_experiment = 'test-experiment';
@@ -223,8 +227,18 @@
releaseVersion: Version.parse('2.0.0'),
);
- static final test_experiment = ExperimentalFeature(
+ static final super_parameters = ExperimentalFeature(
index: 13,
+ enableString: EnableString.super_parameters,
+ isEnabledByDefault: IsEnabledByDefault.super_parameters,
+ isExpired: IsExpired.super_parameters,
+ documentation: 'Super-Initializer Parameters',
+ experimentalReleaseVersion: null,
+ releaseVersion: null,
+ );
+
+ static final test_experiment = ExperimentalFeature(
+ index: 14,
enableString: EnableString.test_experiment,
isEnabledByDefault: IsEnabledByDefault.test_experiment,
isExpired: IsExpired.test_experiment,
@@ -235,7 +249,7 @@
);
static final triple_shift = ExperimentalFeature(
- index: 14,
+ index: 15,
enableString: EnableString.triple_shift,
isEnabledByDefault: IsEnabledByDefault.triple_shift,
isExpired: IsExpired.triple_shift,
@@ -245,7 +259,7 @@
);
static final value_class = ExperimentalFeature(
- index: 15,
+ index: 16,
enableString: EnableString.value_class,
isEnabledByDefault: IsEnabledByDefault.value_class,
isExpired: IsExpired.value_class,
@@ -255,7 +269,7 @@
);
static final variance = ExperimentalFeature(
- index: 16,
+ index: 17,
enableString: EnableString.variance,
isEnabledByDefault: IsEnabledByDefault.variance,
isExpired: IsExpired.variance,
@@ -307,6 +321,9 @@
/// Default state of the experiment "spread-collections"
static const bool spread_collections = true;
+ /// Default state of the experiment "super-parameters"
+ static const bool super_parameters = false;
+
/// Default state of the experiment "test-experiment"
static const bool test_experiment = false;
@@ -363,6 +380,9 @@
/// Expiration status of the experiment "spread-collections"
static const bool spread_collections = true;
+ /// Expiration status of the experiment "super-parameters"
+ static const bool super_parameters = false;
+
/// Expiration status of the experiment "test-experiment"
static const bool test_experiment = false;
@@ -423,6 +443,9 @@
bool get spread_collections =>
isEnabled(ExperimentalFeatures.spread_collections);
+ /// Current state for the flag "super-parameters"
+ bool get super_parameters => isEnabled(ExperimentalFeatures.super_parameters);
+
/// Current state for the flag "test-experiment"
bool get test_experiment => isEnabled(ExperimentalFeatures.test_experiment);
diff --git a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
index c32bd06..f9ac572 100644
--- a/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
+++ b/pkg/analyzer/lib/src/dart/ast/to_source_visitor.dart
@@ -54,7 +54,7 @@
sink.write(', ');
_visitNode(node.message);
}
- sink.write(');');
+ sink.write(')');
}
@override
@@ -565,6 +565,7 @@
_visitNode(node.typeParameters);
sink.write(' = ');
_visitNode(node.type);
+ sink.write(';');
}
@override
diff --git a/pkg/analyzer/lib/src/dart/element/type.dart b/pkg/analyzer/lib/src/dart/element/type.dart
index 17fa3c2..7561115 100644
--- a/pkg/analyzer/lib/src/dart/element/type.dart
+++ b/pkg/analyzer/lib/src/dart/element/type.dart
@@ -310,168 +310,6 @@
}
}
- /// Compares two function types [t] and [s] to see if their corresponding
- /// parameter types match [parameterRelation], return types match
- /// [returnRelation], and type parameter bounds match [boundsRelation].
- ///
- /// Used for the various relations on function types which have the same
- /// structural rules for handling optional parameters and arity, but use their
- /// own relation for comparing corresponding parameters or return types.
- ///
- /// If [parameterRelation] is omitted, uses [returnRelation] for both. This
- /// is convenient for Dart 1 type system methods.
- ///
- /// If [boundsRelation] is omitted, uses [returnRelation]. This is for
- /// backwards compatibility, and convenience for Dart 1 type system methods.
- static bool relate(FunctionType t, DartType? other,
- bool Function(DartType t, DartType s) returnRelation,
- {bool Function(ParameterElement t, ParameterElement s)? parameterRelation,
- bool Function(DartType bound2, DartType bound1,
- TypeParameterElement formal2, TypeParameterElement formal1)?
- boundsRelation}) {
- parameterRelation ??= (t, s) => returnRelation(t.type, s.type);
- boundsRelation ??= (t, s, _, __) => returnRelation(t, s);
-
- // Trivial base cases.
- if (other == null) {
- return false;
- } else if (identical(t, other) ||
- other.isDynamic ||
- other.isDartCoreFunction ||
- other.isDartCoreObject) {
- return true;
- } else if (other is! FunctionType) {
- return false;
- }
-
- // This type cast is safe, because we checked it above.
- FunctionType s = other;
- if (t.typeFormals.isNotEmpty) {
- var freshVariables = relateTypeFormals(t, s, boundsRelation);
- if (freshVariables == null) {
- return false;
- }
- t = t.instantiate(freshVariables);
- s = s.instantiate(freshVariables);
- } else if (s.typeFormals.isNotEmpty) {
- return false;
- }
-
- // Test the return types.
- DartType sRetType = s.returnType;
- if (!sRetType.isVoid && !returnRelation(t.returnType, sRetType)) {
- return false;
- }
-
- // Test the parameter types.
- return relateParameters(t.parameters, s.parameters, parameterRelation);
- }
-
- /// Compares parameters [tParams] and [sParams] of two function types, taking
- /// corresponding parameters from the lists, and see if they match
- /// [parameterRelation].
- ///
- /// Corresponding parameters are defined as a pair `(t, s)` where `t` is a
- /// parameter from [tParams] and `s` is a parameter from [sParams], and both
- /// `t` and `s` are at the same position (for positional parameters)
- /// or have the same name (for named parameters).
- ///
- /// Used for the various relations on function types which have the same
- /// structural rules for handling optional parameters and arity, but use their
- /// own relation for comparing the parameters.
- static bool relateParameters(
- List<ParameterElement> tParams,
- List<ParameterElement> sParams,
- bool Function(ParameterElement t, ParameterElement s) parameterRelation) {
- // TODO(jmesserly): this could be implemented with less allocation if we
- // wanted, by taking advantage of the fact that positional arguments must
- // appear before named ones.
- var tRequired = <ParameterElement>[];
- var tOptional = <ParameterElement>[];
- var tNamed = <String, ParameterElement>{};
- for (var p in tParams) {
- if (p.isRequiredPositional) {
- tRequired.add(p);
- } else if (p.isOptionalPositional) {
- tOptional.add(p);
- } else {
- assert(p.isNamed);
- tNamed[p.name] = p;
- }
- }
-
- var sRequired = <ParameterElement>[];
- var sOptional = <ParameterElement>[];
- var sNamed = <String, ParameterElement>{};
- for (var p in sParams) {
- if (p.isRequiredPositional) {
- sRequired.add(p);
- } else if (p.isOptionalPositional) {
- sOptional.add(p);
- } else {
- assert(p.isNamed);
- sNamed[p.name] = p;
- }
- }
-
- // If one function has positional and the other has named parameters,
- // they don't relate.
- if (sOptional.isNotEmpty && tNamed.isNotEmpty ||
- tOptional.isNotEmpty && sNamed.isNotEmpty) {
- return false;
- }
-
- // If the passed function includes more named parameters than we do, we
- // don't relate.
- if (tNamed.length < sNamed.length) {
- return false;
- }
-
- // For each named parameter in s, make sure we have a corresponding one
- // that relates.
- for (String key in sNamed.keys) {
- var tParam = tNamed[key];
- if (tParam == null) {
- return false;
- }
- var sParam = sNamed[key]!;
- if (!parameterRelation(tParam, sParam)) {
- return false;
- }
- }
-
- // Make sure all of the positional parameters (both required and optional)
- // relate to each other.
- var tPositional = tRequired;
- var sPositional = sRequired;
-
- if (tOptional.isNotEmpty) {
- tPositional = tPositional.toList()..addAll(tOptional);
- }
-
- if (sOptional.isNotEmpty) {
- sPositional = sPositional.toList()..addAll(sOptional);
- }
-
- // Check that s has enough required parameters.
- if (sRequired.length < tRequired.length) {
- return false;
- }
-
- // Check that s does not include more positional parameters than we do.
- if (tPositional.length < sPositional.length) {
- return false;
- }
-
- for (int i = 0; i < sPositional.length; i++) {
- if (!parameterRelation(tPositional[i], sPositional[i])) {
- return false;
- }
- }
-
- return true;
- }
-
/// Given two functions [f1] and [f2] where f1 and f2 are known to be
/// generic function types (both have type formals), this checks that they
/// have the same number of formals, and that those formals have bounds
diff --git a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
index 74e80c2..3833cc9 100644
--- a/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
+++ b/pkg/analyzer/lib/src/summary2/linked_element_factory.dart
@@ -33,10 +33,6 @@
return rootReference.getChild('dart:core').getChild('dynamic');
}
- bool get hasDartCore {
- return _libraryReaders.containsKey('dart:core');
- }
-
void addBundle(BundleReader bundle) {
addLibraries(bundle.libraryMap);
}
@@ -161,6 +157,11 @@
}
bool hasLibrary(String uriStr) {
+ // We already have the element, linked or read.
+ if (rootReference[uriStr]?.element is LibraryElementImpl) {
+ return true;
+ }
+ // No element yet, but we know how to read it.
return _libraryReaders[uriStr] != null;
}
diff --git a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
index b14053b..70b153a 100644
--- a/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
+++ b/pkg/analyzer/test/src/dart/ast/to_source_visitor_test.dart
@@ -1925,7 +1925,7 @@
void test_visitGenericTypeAlias() {
_assertSource(
- "typedef X<S> = S Function<T>(T)",
+ "typedef X<S> = S Function<T>(T);",
AstTestFactory.genericTypeAlias(
'X',
AstTestFactory.typeParameterList2(['S']),
diff --git a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
index 8348e13..434f6bf 100644
--- a/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
+++ b/pkg/analyzer_plugin/lib/protocol/protocol_common.dart
@@ -612,6 +612,15 @@
/// if the parameterName field is omitted.
String? parameterType;
+ /// The index in the list of libraries that could be imported to make this
+ /// suggestion accessible in the file where completion was requested. The
+ /// server provides this list of libraries together with suggestions, so that
+ /// information about the library can be shared for multiple suggestions.
+ /// This field is omitted if the library is already imported, so that the
+ /// suggestion can be inserted as is, or if getSuggestions was used rather
+ /// than getSuggestions2.
+ int? libraryUriToImportIndex;
+
CompletionSuggestion(
this.kind,
this.relevance,
@@ -635,7 +644,8 @@
this.requiredParameterCount,
this.hasNamedParameters,
this.parameterName,
- this.parameterType});
+ this.parameterType,
+ this.libraryUriToImportIndex});
factory CompletionSuggestion.fromJson(
JsonDecoder jsonDecoder, String jsonPath, Object? json) {
@@ -774,6 +784,12 @@
parameterType = jsonDecoder.decodeString(
jsonPath + '.parameterType', json['parameterType']);
}
+ int? libraryUriToImportIndex;
+ if (json.containsKey('libraryUriToImportIndex')) {
+ libraryUriToImportIndex = jsonDecoder.decodeInt(
+ jsonPath + '.libraryUriToImportIndex',
+ json['libraryUriToImportIndex']);
+ }
return CompletionSuggestion(kind, relevance, completion, selectionOffset,
selectionLength, isDeprecated, isPotential,
displayText: displayText,
@@ -791,7 +807,8 @@
requiredParameterCount: requiredParameterCount,
hasNamedParameters: hasNamedParameters,
parameterName: parameterName,
- parameterType: parameterType);
+ parameterType: parameterType,
+ libraryUriToImportIndex: libraryUriToImportIndex);
} else {
throw jsonDecoder.mismatch(jsonPath, 'CompletionSuggestion', json);
}
@@ -871,6 +888,10 @@
if (parameterType != null) {
result['parameterType'] = parameterType;
}
+ var libraryUriToImportIndex = this.libraryUriToImportIndex;
+ if (libraryUriToImportIndex != null) {
+ result['libraryUriToImportIndex'] = libraryUriToImportIndex;
+ }
return result;
}
@@ -905,7 +926,8 @@
requiredParameterCount == other.requiredParameterCount &&
hasNamedParameters == other.hasNamedParameters &&
parameterName == other.parameterName &&
- parameterType == other.parameterType;
+ parameterType == other.parameterType &&
+ libraryUriToImportIndex == other.libraryUriToImportIndex;
}
return false;
}
@@ -935,6 +957,7 @@
hasNamedParameters,
parameterName,
parameterType,
+ libraryUriToImportIndex,
]);
}
diff --git a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
index bb1ed53..f49f000 100644
--- a/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
+++ b/pkg/analyzer_plugin/test/integration/support/protocol_matchers.dart
@@ -164,7 +164,8 @@
'requiredParameterCount': isInt,
'hasNamedParameters': isBool,
'parameterName': isString,
- 'parameterType': isString
+ 'parameterType': isString,
+ 'libraryUriToImportIndex': isInt
}));
/// CompletionSuggestionKind
diff --git a/pkg/analyzer_plugin/tool/spec/common_types_spec.html b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
index bb13ac5..00b5663 100644
--- a/pkg/analyzer_plugin/tool/spec/common_types_spec.html
+++ b/pkg/analyzer_plugin/tool/spec/common_types_spec.html
@@ -380,6 +380,18 @@
omitted if the parameterName field is omitted.
</p>
</field>
+ <field name="libraryUriToImportIndex" experimental="true" optional="true">
+ <ref>int</ref>
+ <p>
+ The index in the list of libraries that could be imported to make
+ this suggestion accessible in the file where completion was requested.
+ The server provides this list of libraries together with suggestions,
+ so that information about the library can be shared for multiple
+ suggestions. This field is omitted if the library is already
+ imported, so that the suggestion can be inserted as is, or if
+ <tt>getSuggestions</tt> was used rather than <tt>getSuggestions2</tt>.
+ </p>
+ </field>
</object>
</type>
<type name="CompletionSuggestionKind">
diff --git a/pkg/compiler/test/analyses/api_allowed.json b/pkg/compiler/test/analyses/api_allowed.json
index 69d4c13..7fe7b5a 100644
--- a/pkg/compiler/test/analyses/api_allowed.json
+++ b/pkg/compiler/test/analyses/api_allowed.json
@@ -94,36 +94,16 @@
"org-dartlang-sdk:///lib/convert/json.dart": {
"Dynamic invocation of 'toJson'.": 1
},
- "org-dartlang-sdk:///lib/_http/crypto.dart": {
- "Dynamic invocation of '+'.": 2,
- "Dynamic invocation of '&'.": 3,
- "Dynamic invocation of 'unary-'.": 1,
- "Dynamic invocation of '-'.": 2
- },
- "org-dartlang-sdk:///lib/_http/http_date.dart": {
- "Dynamic access of 'length'.": 3,
- "Dynamic invocation of '<'.": 1,
- "Dynamic invocation of '>='.": 2,
- "Dynamic invocation of '[]'.": 7
- },
- "org-dartlang-sdk:///lib/_http/http_headers.dart": {
- "Dynamic invocation of 'toLowerCase'.": 1
- },
"org-dartlang-sdk:///lib/_http/http_impl.dart": {
- "Dynamic access of 'headers'.": 1,
- "Dynamic invocation of 'forEach'.": 1,
- "Dynamic access of 'connectionInfo'.": 4,
- "Dynamic access of 'localPort'.": 1,
- "Dynamic access of 'remoteAddress'.": 2,
- "Dynamic access of 'address'.": 4,
- "Dynamic access of 'remotePort'.": 2,
"Dynamic access of 'message'.": 3,
- "Dynamic invocation of 'call'.": 1,
"Dynamic invocation of 'destroy'.": 2,
+ "Dynamic access of 'address'.": 3,
"Dynamic access of 'type'.": 1,
"Dynamic invocation of 'setOption'.": 1,
"Dynamic access of 'host'.": 2,
"Dynamic access of 'port'.": 2,
+ "Dynamic access of 'remoteAddress'.": 1,
+ "Dynamic access of 'remotePort'.": 1,
"Dynamic invocation of 'dart._http::_toJSON'.": 3,
"Dynamic invocation of 'listen'.": 1,
"Dynamic invocation of 'close'.": 1
diff --git a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
index 82414c9..4102f5a 100644
--- a/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
+++ b/pkg/front_end/lib/src/api_prototype/experimental_flags_generated.dart
@@ -24,6 +24,7 @@
nonfunctionTypeAliases,
setLiterals,
spreadCollections,
+ superParameters,
testExperiment,
tripleShift,
valueClass,
@@ -45,6 +46,7 @@
const Version enableNonfunctionTypeAliasesVersion = const Version(2, 13);
const Version enableSetLiteralsVersion = const Version(2, 0);
const Version enableSpreadCollectionsVersion = const Version(2, 0);
+const Version enableSuperParametersVersion = const Version(2, 15);
const Version enableTestExperimentVersion = const Version(2, 15);
const Version enableTripleShiftVersion = const Version(2, 14);
const Version enableValueClassVersion = const Version(2, 15);
@@ -80,6 +82,8 @@
return ExperimentalFlag.setLiterals;
case "spread-collections":
return ExperimentalFlag.spreadCollections;
+ case "super-parameters":
+ return ExperimentalFlag.superParameters;
case "test-experiment":
return ExperimentalFlag.testExperiment;
case "triple-shift":
@@ -107,6 +111,7 @@
ExperimentalFlag.nonfunctionTypeAliases: true,
ExperimentalFlag.setLiterals: true,
ExperimentalFlag.spreadCollections: true,
+ ExperimentalFlag.superParameters: false,
ExperimentalFlag.testExperiment: false,
ExperimentalFlag.tripleShift: true,
ExperimentalFlag.valueClass: false,
@@ -128,6 +133,7 @@
ExperimentalFlag.nonfunctionTypeAliases: false,
ExperimentalFlag.setLiterals: true,
ExperimentalFlag.spreadCollections: true,
+ ExperimentalFlag.superParameters: false,
ExperimentalFlag.testExperiment: false,
ExperimentalFlag.tripleShift: false,
ExperimentalFlag.valueClass: false,
@@ -149,6 +155,7 @@
ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
ExperimentalFlag.setLiterals: const Version(2, 0),
ExperimentalFlag.spreadCollections: const Version(2, 0),
+ ExperimentalFlag.superParameters: const Version(2, 15),
ExperimentalFlag.testExperiment: const Version(2, 15),
ExperimentalFlag.tripleShift: const Version(2, 14),
ExperimentalFlag.valueClass: const Version(2, 15),
@@ -170,6 +177,7 @@
ExperimentalFlag.nonfunctionTypeAliases: const Version(2, 13),
ExperimentalFlag.setLiterals: const Version(2, 0),
ExperimentalFlag.spreadCollections: const Version(2, 0),
+ ExperimentalFlag.superParameters: const Version(2, 15),
ExperimentalFlag.testExperiment: const Version(2, 15),
ExperimentalFlag.tripleShift: const Version(2, 14),
ExperimentalFlag.valueClass: const Version(2, 15),
diff --git a/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect b/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
index 4ba2007..4789762 100644
--- a/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
+++ b/pkg/front_end/testcases/nnbd_mixed/mock_http_headers.dart.weak.outline.expect
@@ -170,8 +170,8 @@
Evaluated: SymbolLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> SymbolConstant(#noFolding)
Evaluated: ListLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> ListConstant(const <Type*>[])
Evaluated: MapLiteral @ org-dartlang-testcase:///mock_http_headers.dart:13:7 -> MapConstant(const <Symbol*, dynamic>{})
-Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:782:8 -> SymbolConstant(#clear)
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:782:8 -> ListConstant(const <Type*>[])
-Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:782:8 -> ListConstant(const <dynamic>[])
-Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:782:8 -> MapConstant(const <Symbol*, dynamic>{})
+Evaluated: SymbolLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:707:8 -> SymbolConstant(#clear)
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:707:8 -> ListConstant(const <Type*>[])
+Evaluated: ListLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:707:8 -> ListConstant(const <dynamic>[])
+Evaluated: MapLiteral @ org-dartlang-sdk:///sdk/lib/_http/http.dart:707:8 -> MapConstant(const <Symbol*, dynamic>{})
Extra constant evaluation: evaluated: 268, effectively constant: 91
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
index ef7f8ee..463adf9 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/enum_from_lib_used_as_type.dart.expect
@@ -22,6 +22,6 @@
synthetic constructor •() → self::Class
: super core::Object::•()
;
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3266,getterSelectorId:3267] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3252,getterSelectorId:3253] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::Enum e) → core::int
return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
}
diff --git a/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect b/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
index 4a68920..3a69351 100644
--- a/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
+++ b/pkg/vm/testcases/transformations/type_flow/transformer/tree_shake_enum_from_lib.dart.expect
@@ -51,6 +51,6 @@
synthetic constructor •() → self::ConstClass
: super core::Object::•()
;
-[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3270,getterSelectorId:3271] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
+[@vm.procedure-attributes.metadata=methodOrSetterCalledDynamically:false,getterCalledDynamically:false,hasThisUses:false,hasTearOffUses:false,methodOrSetterSelectorId:3256,getterSelectorId:3257] method method([@vm.inferred-type.metadata=dart.core::Null? (value: null)] self::ConstEnum e) → core::int
return [@vm.inferred-type.metadata=!] e.{core::_Enum::index}{core::int};
}
diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc
index be2bfa6..7d4c1a4 100644
--- a/runtime/vm/heap/heap.cc
+++ b/runtime/vm/heap/heap.cc
@@ -445,7 +445,9 @@
new_space_.Evacuate(reason);
RecordAfterGC(GCType::kScavenge);
PrintStats();
- NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
+#if defined(SUPPORT_TIMELINE)
+ PrintStatsToTimeline(&tbes, reason);
+#endif
last_gc_was_old_space_ = false;
}
}
@@ -473,7 +475,9 @@
new_space_.Scavenge(reason);
RecordAfterGC(GCType::kScavenge);
PrintStats();
- NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
+#if defined(SUPPORT_TIMELINE)
+ PrintStatsToTimeline(&tbes, reason);
+#endif
last_gc_was_old_space_ = false;
}
if (reason == GCReason::kNewSpace) {
@@ -522,7 +526,9 @@
old_space_.CollectGarbage(type == GCType::kMarkCompact, true /* finish */);
RecordAfterGC(type);
PrintStats();
- NOT_IN_PRODUCT(PrintStatsToTimeline(&tbes, reason));
+#if defined(SUPPORT_TIMELINE)
+ PrintStatsToTimeline(&tbes, reason);
+#endif
// Some Code objects may have been collected so invalidate handler cache.
thread->isolate_group()->ForEachIsolate(
@@ -1176,7 +1182,7 @@
}
void Heap::PrintStatsToTimeline(TimelineEventScope* event, GCReason reason) {
-#if !defined(PRODUCT)
+#if defined(SUPPORT_TIMELINE)
if ((event == NULL) || !event->enabled()) {
return;
}
@@ -1209,7 +1215,7 @@
RoundWordsToKB(stats_.before_.old_.external_in_words));
event->FormatArgument(arguments + 12, "After.Old.External (kB)", "%" Pd "",
RoundWordsToKB(stats_.after_.old_.external_in_words));
-#endif // !defined(PRODUCT)
+#endif // defined(SUPPORT_TIMELINE)
}
Heap::Space Heap::SpaceForExternal(intptr_t size) const {
diff --git a/sdk/lib/_http/crypto.dart b/sdk/lib/_http/crypto.dart
index f29a15b..337f299 100644
--- a/sdk/lib/_http/crypto.dart
+++ b/sdk/lib/_http/crypto.dart
@@ -21,7 +21,7 @@
// -1 : '\r' or '\n'
// 0 : = (Padding character).
// >0 : Base 64 alphabet index of given byte.
- static const List<int> _decodeTable = const [
+ static const List<int> _decodeTable = [
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -2, -2, -1, -2, -2, //
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, //
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63, //
@@ -40,18 +40,16 @@
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
];
- static Random _rng = new Random.secure();
-
static Uint8List getRandomBytes(int count) {
- final Uint8List result = new Uint8List(count);
+ final Uint8List result = Uint8List(count);
for (int i = 0; i < count; i++) {
- result[i] = _rng.nextInt(0xff);
+ result[i] = Random.secure().nextInt(0xff);
}
return result;
}
static String bytesToHex(List<int> bytes) {
- var result = new StringBuffer();
+ var result = StringBuffer();
for (var part in bytes) {
result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}');
}
@@ -66,7 +64,7 @@
}
final String lookup = urlSafe ? _encodeTableUrlSafe : _encodeTable;
// Size of 24 bit chunks.
- final int remainderLength = len.remainder(3) as int;
+ final int remainderLength = len.remainder(3);
final int chunkLength = len - remainderLength;
// Size of base output.
int outputLen = ((len ~/ 3) * 4) + ((remainderLength > 0) ? 4 : 0);
@@ -74,7 +72,7 @@
if (addLineSeparator) {
outputLen += ((outputLen - 1) ~/ LINE_LENGTH) << 1;
}
- List<int> out = new List<int>.filled(outputLen, 0);
+ List<int> out = List<int>.filled(outputLen, 0);
// Encode 24 bit chunks.
int j = 0, i = 0, c = 0;
@@ -111,14 +109,14 @@
out[j++] = PAD;
}
- return new String.fromCharCodes(out);
+ return String.fromCharCodes(out);
}
static List<int> base64StringToBytes(String input,
[bool ignoreInvalidCharacters = true]) {
int len = input.length;
if (len == 0) {
- return new List<int>.empty();
+ return List<int>.empty();
}
// Count '\r', '\n' and illegal characters, For illegal characters,
@@ -129,13 +127,13 @@
if (c < 0) {
extrasLen++;
if (c == -2 && !ignoreInvalidCharacters) {
- throw new FormatException('Invalid character: ${input[i]}');
+ throw FormatException('Invalid character: ${input[i]}');
}
}
}
if ((len - extrasLen) % 4 != 0) {
- throw new FormatException('''Size of Base 64 characters in Input
+ throw FormatException('''Size of Base 64 characters in Input
must be a multiple of 4. Input: $input''');
}
@@ -147,7 +145,7 @@
if (currentCodeUnit == PAD) padLength++;
}
int outputLen = (((len - extrasLen) * 6) >> 3) - padLength;
- List<int> out = new List<int>.filled(outputLen, 0);
+ List<int> out = List<int>.filled(outputLen, 0);
for (int i = 0, o = 0; o < outputLen;) {
// Accumulate 4 valid 6 bit Base 64 characters into an int.
@@ -183,20 +181,19 @@
final bool _bigEndianWords;
int _lengthInBytes = 0;
List<int> _pendingData;
- List<int> _currentChunk;
- List<int> _h;
+ final List<int> _currentChunk;
+ final List<int> _h;
bool _digestCalled = false;
_HashBase(this._chunkSizeInWords, int digestSizeInWords, this._bigEndianWords)
: _pendingData = [],
- _currentChunk = new List.filled(_chunkSizeInWords, 0),
- _h = new List.filled(digestSizeInWords, 0);
+ _currentChunk = List.filled(_chunkSizeInWords, 0),
+ _h = List.filled(digestSizeInWords, 0);
// Update the hasher with more data.
add(List<int> data) {
if (_digestCalled) {
- throw new StateError(
- 'Hash update method called after digest was retrieved');
+ throw StateError('Hash update method called after digest was retrieved');
}
_lengthInBytes += data.length;
_pendingData.addAll(data);
@@ -211,7 +208,7 @@
_digestCalled = true;
_finalizeData();
_iterate();
- assert(_pendingData.length == 0);
+ assert(_pendingData.isEmpty);
return _resultAsBytes();
}
@@ -227,8 +224,8 @@
_updateHash(List<int> m);
// Helper methods.
- _add32(x, y) => (x + y) & _MASK_32;
- _roundUp(val, n) => (val + n - 1) & -n;
+ int _add32(int x, int y) => (x + y) & _MASK_32;
+ int _roundUp(int val, int n) => (val + n - 1) & -n;
// Rotate left limiting to unsigned 32-bit values.
int _rotl32(int val, int shift) {
@@ -266,7 +263,7 @@
// Convert a 32-bit word to four bytes.
List<int> _wordToBytes(int word) {
- List<int> bytes = new List.filled(_BYTES_PER_WORD, 0);
+ List<int> bytes = List.filled(_BYTES_PER_WORD, 0);
bytes[0] = (word >> (_bigEndianWords ? 24 : 0)) & _MASK_8;
bytes[1] = (word >> (_bigEndianWords ? 16 : 8)) & _MASK_8;
bytes[2] = (word >> (_bigEndianWords ? 8 : 16)) & _MASK_8;
@@ -323,10 +320,10 @@
// Returns a new instance of this Hash.
_MD5 newInstance() {
- return new _MD5();
+ return _MD5();
}
- static const _k = const [
+ static const _k = [
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, //
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, //
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, //
@@ -340,7 +337,7 @@
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
];
- static const _r = const [
+ static const _r = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, //
20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, //
16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, //
@@ -357,8 +354,8 @@
var c = _h[2];
var d = _h[3];
- var t0;
- var t1;
+ int t0;
+ int t1;
for (var i = 0; i < 64; i++) {
if (i < 16) {
@@ -392,7 +389,7 @@
// The SHA1 hasher is used to compute an SHA1 message digest.
class _SHA1 extends _HashBase {
- List<int> _w;
+ final List<int> _w;
// Construct a SHA1 hasher object.
_SHA1()
@@ -407,7 +404,7 @@
// Returns a new instance of this Hash.
_SHA1 newInstance() {
- return new _SHA1();
+ return _SHA1();
}
// Compute one iteration of the SHA1 algorithm with a chunk of
diff --git a/sdk/lib/_http/http.dart b/sdk/lib/_http/http.dart
index bc88899..4193db2 100644
--- a/sdk/lib/_http/http.dart
+++ b/sdk/lib/_http/http.dart
@@ -9,7 +9,6 @@
show
HashMap,
HashSet,
- Queue,
ListQueue,
LinkedList,
LinkedListEntry,
@@ -34,240 +33,226 @@
part 'websocket.dart';
part 'websocket_impl.dart';
-/**
- * A server that delivers content, such as web pages, using the HTTP protocol.
- *
- * The HttpServer is a [Stream] that provides [HttpRequest] objects. Each
- * HttpRequest has an associated [HttpResponse] object.
- * The server responds to a request by writing to that HttpResponse object.
- * The following example shows how to bind an HttpServer to an IPv6
- * [InternetAddress] on port 80 (the standard port for HTTP servers)
- * and how to listen for requests.
- * Port 80 is the default HTTP port. However, on most systems accessing
- * this requires super-user privileges. For local testing consider
- * using a non-reserved port (1024 and above).
- *
- * import 'dart:io';
- *
- * main() {
- * HttpServer
- * .bind(InternetAddress.anyIPv6, 80)
- * .then((server) {
- * server.listen((HttpRequest request) {
- * request.response.write('Hello, world!');
- * request.response.close();
- * });
- * });
- * }
- *
- * Incomplete requests, in which all or part of the header is missing, are
- * ignored, and no exceptions or HttpRequest objects are generated for them.
- * Likewise, when writing to an HttpResponse, any [Socket] exceptions are
- * ignored and any future writes are ignored.
- *
- * The HttpRequest exposes the request headers and provides the request body,
- * if it exists, as a Stream of data. If the body is unread, it is drained
- * when the server writes to the HttpResponse or closes it.
- *
- * ## Bind with a secure HTTPS connection
- *
- * Use [bindSecure] to create an HTTPS server.
- *
- * The server presents a certificate to the client. The certificate
- * chain and the private key are set in the [SecurityContext]
- * object that is passed to [bindSecure].
- *
- * import 'dart:io';
- * import "dart:isolate";
- *
- * main() {
- * SecurityContext context = new SecurityContext();
- * var chain =
- * Platform.script.resolve('certificates/server_chain.pem')
- * .toFilePath();
- * var key =
- * Platform.script.resolve('certificates/server_key.pem')
- * .toFilePath();
- * context.useCertificateChain(chain);
- * context.usePrivateKey(key, password: 'dartdart');
- *
- * HttpServer
- * .bindSecure(InternetAddress.anyIPv6,
- * 443,
- * context)
- * .then((server) {
- * server.listen((HttpRequest request) {
- * request.response.write('Hello, world!');
- * request.response.close();
- * });
- * });
- * }
- *
- * The certificates and keys are PEM files, which can be created and
- * managed with the tools in OpenSSL.
- *
- * ## Connect to a server socket
- *
- * You can use the [listenOn] constructor to attach an HTTP server to
- * a [ServerSocket].
- *
- * import 'dart:io';
- *
- * main() {
- * ServerSocket.bind(InternetAddress.anyIPv6, 80)
- * .then((serverSocket) {
- * HttpServer httpserver = new HttpServer.listenOn(serverSocket);
- * serverSocket.listen((Socket socket) {
- * socket.write('Hello, client.');
- * });
- * });
- * }
- *
- * ## Other resources
- *
- * * HttpServer is a Stream. Refer to the [Stream] class for information
- * about the streaming qualities of an HttpServer.
- * Pausing the subscription of the stream, pauses at the OS level.
- *
- * * The [shelf](https://pub.dev/packages/shelf)
- * package on pub.dev contains a set of high-level classes that,
- * together with this class, makes it easy to provide content through HTTP
- * servers.
- */
+/// A server that delivers content, such as web pages, using the HTTP protocol.
+///
+/// The HttpServer is a [Stream] that provides [HttpRequest] objects. Each
+/// HttpRequest has an associated [HttpResponse] object.
+/// The server responds to a request by writing to that HttpResponse object.
+/// The following example shows how to bind an HttpServer to an IPv6
+/// [InternetAddress] on port 80 (the standard port for HTTP servers)
+/// and how to listen for requests.
+/// Port 80 is the default HTTP port. However, on most systems accessing
+/// this requires super-user privileges. For local testing consider
+/// using a non-reserved port (1024 and above).
+///
+/// import 'dart:io';
+///
+/// main() {
+/// HttpServer
+/// .bind(InternetAddress.anyIPv6, 80)
+/// .then((server) {
+/// server.listen((HttpRequest request) {
+/// request.response.write('Hello, world!');
+/// request.response.close();
+/// });
+/// });
+/// }
+///
+/// Incomplete requests, in which all or part of the header is missing, are
+/// ignored, and no exceptions or HttpRequest objects are generated for them.
+/// Likewise, when writing to an HttpResponse, any [Socket] exceptions are
+/// ignored and any future writes are ignored.
+///
+/// The HttpRequest exposes the request headers and provides the request body,
+/// if it exists, as a Stream of data. If the body is unread, it is drained
+/// when the server writes to the HttpResponse or closes it.
+///
+/// ## Bind with a secure HTTPS connection
+///
+/// Use [bindSecure] to create an HTTPS server.
+///
+/// The server presents a certificate to the client. The certificate
+/// chain and the private key are set in the [SecurityContext]
+/// object that is passed to [bindSecure].
+///
+/// import 'dart:io';
+/// import "dart:isolate";
+///
+/// main() {
+/// SecurityContext context = new SecurityContext();
+/// var chain =
+/// Platform.script.resolve('certificates/server_chain.pem')
+/// .toFilePath();
+/// var key =
+/// Platform.script.resolve('certificates/server_key.pem')
+/// .toFilePath();
+/// context.useCertificateChain(chain);
+/// context.usePrivateKey(key, password: 'dartdart');
+///
+/// HttpServer
+/// .bindSecure(InternetAddress.anyIPv6,
+/// 443,
+/// context)
+/// .then((server) {
+/// server.listen((HttpRequest request) {
+/// request.response.write('Hello, world!');
+/// request.response.close();
+/// });
+/// });
+/// }
+///
+/// The certificates and keys are PEM files, which can be created and
+/// managed with the tools in OpenSSL.
+///
+/// ## Connect to a server socket
+///
+/// You can use the [listenOn] constructor to attach an HTTP server to
+/// a [ServerSocket].
+///
+/// import 'dart:io';
+///
+/// main() {
+/// ServerSocket.bind(InternetAddress.anyIPv6, 80)
+/// .then((serverSocket) {
+/// HttpServer httpserver = new HttpServer.listenOn(serverSocket);
+/// serverSocket.listen((Socket socket) {
+/// socket.write('Hello, client.');
+/// });
+/// });
+/// }
+///
+/// ## Other resources
+///
+/// * HttpServer is a Stream. Refer to the [Stream] class for information
+/// about the streaming qualities of an HttpServer.
+/// Pausing the subscription of the stream, pauses at the OS level.
+///
+/// * The [shelf](https://pub.dev/packages/shelf)
+/// package on pub.dev contains a set of high-level classes that,
+/// together with this class, makes it easy to provide content through HTTP
+/// servers.
abstract class HttpServer implements Stream<HttpRequest> {
- /**
- * Gets and sets the default value of the `Server` header for all responses
- * generated by this [HttpServer].
- *
- * If [serverHeader] is `null`, no `Server` header will be added to each
- * response.
- *
- * The default value is `null`.
- */
+ /// Gets and sets the default value of the `Server` header for all responses
+ /// generated by this [HttpServer].
+ ///
+ /// If [serverHeader] is `null`, no `Server` header will be added to each
+ /// response.
+ ///
+ /// The default value is `null`.
String? serverHeader;
- /**
- * Default set of headers added to all response objects.
- *
- * By default the following headers are in this set:
- *
- * Content-Type: text/plain; charset=utf-8
- * X-Frame-Options: SAMEORIGIN
- * X-Content-Type-Options: nosniff
- * X-XSS-Protection: 1; mode=block
- *
- * If the `Server` header is added here and the `serverHeader` is set as
- * well then the value of `serverHeader` takes precedence.
- */
+ /// Default set of headers added to all response objects.
+ ///
+ /// By default the following headers are in this set:
+ ///
+ /// Content-Type: text/plain; charset=utf-8
+ /// X-Frame-Options: SAMEORIGIN
+ /// X-Content-Type-Options: nosniff
+ /// X-XSS-Protection: 1; mode=block
+ ///
+ /// If the `Server` header is added here and the `serverHeader` is set as
+ /// well then the value of `serverHeader` takes precedence.
HttpHeaders get defaultResponseHeaders;
- /**
- * Whether the [HttpServer] should compress the content, if possible.
- *
- * The content can only be compressed when the response is using
- * chunked Transfer-Encoding and the incoming request has `gzip`
- * as an accepted encoding in the Accept-Encoding header.
- *
- * The default value is `false` (compression disabled).
- * To enable, set `autoCompress` to `true`.
- */
+ /// Whether the [HttpServer] should compress the content, if possible.
+ ///
+ /// The content can only be compressed when the response is using
+ /// chunked Transfer-Encoding and the incoming request has `gzip`
+ /// as an accepted encoding in the Accept-Encoding header.
+ ///
+ /// The default value is `false` (compression disabled).
+ /// To enable, set `autoCompress` to `true`.
bool autoCompress = false;
- /**
- * Gets or sets the timeout used for idle keep-alive connections. If no
- * further request is seen within [idleTimeout] after the previous request was
- * completed, the connection is dropped.
- *
- * Default is 120 seconds.
- *
- * Note that it may take up to `2 * idleTimeout` before a idle connection is
- * aborted.
- *
- * To disable, set [idleTimeout] to `null`.
- */
+ /// Gets or sets the timeout used for idle keep-alive connections. If no
+ /// further request is seen within [idleTimeout] after the previous request was
+ /// completed, the connection is dropped.
+ ///
+ /// Default is 120 seconds.
+ ///
+ /// Note that it may take up to `2 * idleTimeout` before a idle connection is
+ /// aborted.
+ ///
+ /// To disable, set [idleTimeout] to `null`.
Duration? idleTimeout = const Duration(seconds: 120);
- /**
- * Starts listening for HTTP requests on the specified [address] and
- * [port].
- *
- * The [address] can either be a [String] or an
- * [InternetAddress]. If [address] is a [String], [bind] will
- * perform a [InternetAddress.lookup] and use the first value in the
- * list. To listen on the loopback adapter, which will allow only
- * incoming connections from the local host, use the value
- * [InternetAddress.loopbackIPv4] or
- * [InternetAddress.loopbackIPv6]. To allow for incoming
- * connection from the network use either one of the values
- * [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
- * bind to all interfaces or the IP address of a specific interface.
- *
- * If an IP version 6 (IPv6) address is used, both IP version 6
- * (IPv6) and version 4 (IPv4) connections will be accepted. To
- * restrict this to version 6 (IPv6) only, use [v6Only] to set
- * version 6 only. However, if the address is
- * [InternetAddress.loopbackIPv6], only IP version 6 (IPv6) connections
- * will be accepted.
- *
- * If [port] has the value 0 an ephemeral port will be chosen by
- * the system. The actual port used can be retrieved using the
- * [port] getter.
- *
- * The optional argument [backlog] can be used to specify the listen
- * backlog for the underlying OS listen setup. If [backlog] has the
- * value of 0 (the default) a reasonable value will be chosen by
- * the system.
- *
- * The optional argument [shared] specifies whether additional HttpServer
- * objects can bind to the same combination of `address`, `port` and `v6Only`.
- * If `shared` is `true` and more `HttpServer`s from this isolate or other
- * isolates are bound to the port, then the incoming connections will be
- * distributed among all the bound `HttpServer`s. Connections can be
- * distributed over multiple isolates this way.
- */
+ /// Starts listening for HTTP requests on the specified [address] and
+ /// [port].
+ ///
+ /// The [address] can either be a [String] or an
+ /// [InternetAddress]. If [address] is a [String], [bind] will
+ /// perform a [InternetAddress.lookup] and use the first value in the
+ /// list. To listen on the loopback adapter, which will allow only
+ /// incoming connections from the local host, use the value
+ /// [InternetAddress.loopbackIPv4] or
+ /// [InternetAddress.loopbackIPv6]. To allow for incoming
+ /// connection from the network use either one of the values
+ /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
+ /// bind to all interfaces or the IP address of a specific interface.
+ ///
+ /// If an IP version 6 (IPv6) address is used, both IP version 6
+ /// (IPv6) and version 4 (IPv4) connections will be accepted. To
+ /// restrict this to version 6 (IPv6) only, use [v6Only] to set
+ /// version 6 only. However, if the address is
+ /// [InternetAddress.loopbackIPv6], only IP version 6 (IPv6) connections
+ /// will be accepted.
+ ///
+ /// If [port] has the value 0 an ephemeral port will be chosen by
+ /// the system. The actual port used can be retrieved using the
+ /// [port] getter.
+ ///
+ /// The optional argument [backlog] can be used to specify the listen
+ /// backlog for the underlying OS listen setup. If [backlog] has the
+ /// value of 0 (the default) a reasonable value will be chosen by
+ /// the system.
+ ///
+ /// The optional argument [shared] specifies whether additional HttpServer
+ /// objects can bind to the same combination of `address`, `port` and `v6Only`.
+ /// If `shared` is `true` and more `HttpServer`s from this isolate or other
+ /// isolates are bound to the port, then the incoming connections will be
+ /// distributed among all the bound `HttpServer`s. Connections can be
+ /// distributed over multiple isolates this way.
static Future<HttpServer> bind(address, int port,
{int backlog = 0, bool v6Only = false, bool shared = false}) =>
_HttpServer.bind(address, port, backlog, v6Only, shared);
- /**
- * The [address] can either be a [String] or an
- * [InternetAddress]. If [address] is a [String], [bind] will
- * perform a [InternetAddress.lookup] and use the first value in the
- * list. To listen on the loopback adapter, which will allow only
- * incoming connections from the local host, use the value
- * [InternetAddress.loopbackIPv4] or
- * [InternetAddress.loopbackIPv6]. To allow for incoming
- * connection from the network use either one of the values
- * [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
- * bind to all interfaces or the IP address of a specific interface.
- *
- * If an IP version 6 (IPv6) address is used, both IP version 6
- * (IPv6) and version 4 (IPv4) connections will be accepted. To
- * restrict this to version 6 (IPv6) only, use [v6Only] to set
- * version 6 only.
- *
- * If [port] has the value 0 an ephemeral port will be chosen by
- * the system. The actual port used can be retrieved using the
- * [port] getter.
- *
- * The optional argument [backlog] can be used to specify the listen
- * backlog for the underlying OS listen setup. If [backlog] has the
- * value of 0 (the default) a reasonable value will be chosen by
- * the system.
- *
- * If [requestClientCertificate] is true, the server will
- * request clients to authenticate with a client certificate.
- * The server will advertise the names of trusted issuers of client
- * certificates, getting them from a [SecurityContext], where they have been
- * set using [SecurityContext.setClientAuthorities].
- *
- * The optional argument [shared] specifies whether additional HttpServer
- * objects can bind to the same combination of `address`, `port` and `v6Only`.
- * If `shared` is `true` and more `HttpServer`s from this isolate or other
- * isolates are bound to the port, then the incoming connections will be
- * distributed among all the bound `HttpServer`s. Connections can be
- * distributed over multiple isolates this way.
- */
+ /// The [address] can either be a [String] or an
+ /// [InternetAddress]. If [address] is a [String], [bind] will
+ /// perform a [InternetAddress.lookup] and use the first value in the
+ /// list. To listen on the loopback adapter, which will allow only
+ /// incoming connections from the local host, use the value
+ /// [InternetAddress.loopbackIPv4] or
+ /// [InternetAddress.loopbackIPv6]. To allow for incoming
+ /// connection from the network use either one of the values
+ /// [InternetAddress.anyIPv4] or [InternetAddress.anyIPv6] to
+ /// bind to all interfaces or the IP address of a specific interface.
+ ///
+ /// If an IP version 6 (IPv6) address is used, both IP version 6
+ /// (IPv6) and version 4 (IPv4) connections will be accepted. To
+ /// restrict this to version 6 (IPv6) only, use [v6Only] to set
+ /// version 6 only.
+ ///
+ /// If [port] has the value 0 an ephemeral port will be chosen by
+ /// the system. The actual port used can be retrieved using the
+ /// [port] getter.
+ ///
+ /// The optional argument [backlog] can be used to specify the listen
+ /// backlog for the underlying OS listen setup. If [backlog] has the
+ /// value of 0 (the default) a reasonable value will be chosen by
+ /// the system.
+ ///
+ /// If [requestClientCertificate] is true, the server will
+ /// request clients to authenticate with a client certificate.
+ /// The server will advertise the names of trusted issuers of client
+ /// certificates, getting them from a [SecurityContext], where they have been
+ /// set using [SecurityContext.setClientAuthorities].
+ ///
+ /// The optional argument [shared] specifies whether additional HttpServer
+ /// objects can bind to the same combination of `address`, `port` and `v6Only`.
+ /// If `shared` is `true` and more `HttpServer`s from this isolate or other
+ /// isolates are bound to the port, then the incoming connections will be
+ /// distributed among all the bound `HttpServer`s. Connections can be
+ /// distributed over multiple isolates this way.
static Future<HttpServer> bindSecure(
address, int port, SecurityContext context,
@@ -278,113 +263,89 @@
_HttpServer.bindSecure(address, port, context, backlog, v6Only,
requestClientCertificate, shared);
- /**
- * Attaches the HTTP server to an existing [ServerSocket]. When the
- * [HttpServer] is closed, the [HttpServer] will just detach itself,
- * closing current connections but not closing [serverSocket].
- */
+ /// Attaches the HTTP server to an existing [ServerSocket]. When the
+ /// [HttpServer] is closed, the [HttpServer] will just detach itself,
+ /// closing current connections but not closing [serverSocket].
factory HttpServer.listenOn(ServerSocket serverSocket) =>
- new _HttpServer.listenOn(serverSocket);
+ _HttpServer.listenOn(serverSocket);
- /**
- * Permanently stops this [HttpServer] from listening for new
- * connections. This closes the [Stream] of [HttpRequest]s with a
- * done event. The returned future completes when the server is
- * stopped. For a server started using [bind] or [bindSecure] this
- * means that the port listened on no longer in use.
- *
- * If [force] is `true`, active connections will be closed immediately.
- */
+ /// Permanently stops this [HttpServer] from listening for new
+ /// connections. This closes the [Stream] of [HttpRequest]s with a
+ /// done event. The returned future completes when the server is
+ /// stopped. For a server started using [bind] or [bindSecure] this
+ /// means that the port listened on no longer in use.
+ ///
+ /// If [force] is `true`, active connections will be closed immediately.
Future close({bool force = false});
- /**
- * The port that the server is listening on.
- *
- * This is the actual port used when a port of zero is
- * specified in the [bind] or [bindSecure] call.
- */
+ /// The port that the server is listening on.
+ ///
+ /// This is the actual port used when a port of zero is
+ /// specified in the [bind] or [bindSecure] call.
int get port;
- /**
- * The address that the server is listening on.
- *
- * This is the actual address used when the original address
- * was specified as a hostname.
- */
+ /// The address that the server is listening on.
+ ///
+ /// This is the actual address used when the original address
+ /// was specified as a hostname.
InternetAddress get address;
- /**
- * Sets the timeout, in seconds, for sessions of this [HttpServer].
- *
- * The default timeout is 20 minutes.
- */
+ /// Sets the timeout, in seconds, for sessions of this [HttpServer].
+ ///
+ /// The default timeout is 20 minutes.
set sessionTimeout(int timeout);
- /**
- * A [HttpConnectionsInfo] object summarizing the number of
- * current connections handled by the server.
- */
+ /// A [HttpConnectionsInfo] object summarizing the number of
+ /// current connections handled by the server.
HttpConnectionsInfo connectionsInfo();
}
-/**
- * Summary statistics about an [HttpServer]s current socket connections.
- */
+/// Summary statistics about an [HttpServer]s current socket connections.
class HttpConnectionsInfo {
- /**
- * Total number of socket connections.
- */
+ /// Total number of socket connections.
int total = 0;
- /**
- * Number of active connections where actual request/response
- * processing is active.
- */
+ /// Number of active connections where actual request/response
+ /// processing is active.
int active = 0;
- /**
- * Number of idle connections held by clients as persistent connections.
- */
+ /// Number of idle connections held by clients as persistent connections.
int idle = 0;
- /**
- * Number of connections which are preparing to close.
- *
- * Note: These connections are also part of the [active] count as they might
- * still be sending data to the client before finally closing.
- */
+ /// Number of connections which are preparing to close.
+ ///
+ /// Note: These connections are also part of the [active] count as they might
+ /// still be sending data to the client before finally closing.
int closing = 0;
}
-/**
- * Headers for HTTP requests and responses.
- *
- * In some situations, headers are immutable:
- *
- * * [HttpRequest] and [HttpClientResponse] always have immutable headers.
- *
- * * [HttpResponse] and [HttpClientRequest] have immutable headers
- * from the moment the body is written to.
- *
- * In these situations, the mutating methods throw exceptions.
- *
- * For all operations on HTTP headers the header name is
- * case-insensitive.
- *
- * To set the value of a header use the `set()` method:
- *
- * request.headers.set(HttpHeaders.cacheControlHeader,
- * 'max-age=3600, must-revalidate');
- *
- * To retrieve the value of a header use the `value()` method:
- *
- * print(request.headers.value(HttpHeaders.userAgentHeader));
- *
- * An `HttpHeaders` object holds a list of values for each name
- * as the standard allows. In most cases a name holds only a single value,
- * The most common mode of operation is to use `set()` for setting a value,
- * and `value()` for retrieving a value.
- */
+/// Headers for HTTP requests and responses.
+///
+/// In some situations, headers are immutable:
+///
+/// * [HttpRequest] and [HttpClientResponse] always have immutable headers.
+///
+/// * [HttpResponse] and [HttpClientRequest] have immutable headers
+/// from the moment the body is written to.
+///
+/// In these situations, the mutating methods throw exceptions.
+///
+/// For all operations on HTTP headers the header name is
+/// case-insensitive.
+///
+/// To set the value of a header use the `set()` method:
+///
+/// request.headers.set(HttpHeaders.cacheControlHeader,
+/// 'max-age=3600, must-revalidate');
+///
+/// To retrieve the value of a header use the `value()` method:
+///
+/// print(request.headers.value(HttpHeaders.userAgentHeader));
+///
+/// An `HttpHeaders` object holds a list of values for each name
+/// as the standard allows. In most cases a name holds only a single value,
+/// The most common mode of operation is to use `set()` for setting a value,
+/// and `value()` for retrieving a value.
abstract class HttpHeaders {
static const acceptHeader = "accept";
static const acceptCharsetHeader = "accept-charset";
@@ -559,7 +520,7 @@
static const SET_COOKIE = setCookieHeader;
// TODO(39783): Document this.
- static const generalHeaders = const [
+ static const generalHeaders = [
cacheControlHeader,
connectionHeader,
dateHeader,
@@ -574,7 +535,7 @@
@Deprecated("Use generalHeaders instead")
static const GENERAL_HEADERS = generalHeaders;
- static const entityHeaders = const [
+ static const entityHeaders = [
allowHeader,
contentEncodingHeader,
contentLanguageHeader,
@@ -590,7 +551,7 @@
@Deprecated("Use entityHeaders instead")
static const ENTITY_HEADERS = entityHeaders;
- static const responseHeaders = const [
+ static const responseHeaders = [
acceptRangesHeader,
ageHeader,
etagHeader,
@@ -605,7 +566,7 @@
@Deprecated("Use responseHeaders instead")
static const RESPONSE_HEADERS = responseHeaders;
- static const requestHeaders = const [
+ static const requestHeaders = [
acceptHeader,
acceptCharsetHeader,
acceptEncodingHeader,
@@ -630,207 +591,165 @@
@Deprecated("Use requestHeaders instead")
static const REQUEST_HEADERS = requestHeaders;
- /**
- * The date specified by the [dateHeader] header, if any.
- */
+ /// The date specified by the [dateHeader] header, if any.
DateTime? date;
- /**
- * The date and time specified by the [expiresHeader] header, if any.
- */
+ /// The date and time specified by the [expiresHeader] header, if any.
DateTime? expires;
- /**
- * The date and time specified by the [ifModifiedSinceHeader] header, if any.
- */
+ /// The date and time specified by the [ifModifiedSinceHeader] header, if any.
DateTime? ifModifiedSince;
- /**
- * The value of the [hostHeader] header, if any.
- */
+ /// The value of the [hostHeader] header, if any.
String? host;
- /**
- * The value of the port part of the [hostHeader] header, if any.
- */
+ /// The value of the port part of the [hostHeader] header, if any.
int? port;
- /**
- * The [ContentType] of the [contentTypeHeader] header, if any.
- */
+ /// The [ContentType] of the [contentTypeHeader] header, if any.
ContentType? contentType;
- /**
- * The value of the [contentLengthHeader] header, if any.
- *
- * The value is negative if there is no content length set.
- */
+ /// The value of the [contentLengthHeader] header, if any.
+ ///
+ /// The value is negative if there is no content length set.
int contentLength = -1;
- /**
- * Whether the connection is persistent (keep-alive).
- */
+ /// Whether the connection is persistent (keep-alive).
late bool persistentConnection;
- /**
- * Whether the connection uses chunked transfer encoding.
- *
- * Reflects and modifies the value of the [transferEncodingHeader] header.
- */
+ /// Whether the connection uses chunked transfer encoding.
+ ///
+ /// Reflects and modifies the value of the [transferEncodingHeader] header.
late bool chunkedTransferEncoding;
- /**
- * The values for the header named [name].
- *
- * Returns null if there is no header with the provided name,
- * otherwise returns a new list containing the current values.
- * Not that modifying the list does not change the header.
- */
+ /// The values for the header named [name].
+ ///
+ /// Returns null if there is no header with the provided name,
+ /// otherwise returns a new list containing the current values.
+ /// Not that modifying the list does not change the header.
List<String>? operator [](String name);
- /**
- * Convenience method for the value for a single valued header.
- *
- * The value must not have more than one value.
- *
- * Returns `null` if there is no header with the provided name.
- */
+ /// Convenience method for the value for a single valued header.
+ ///
+ /// The value must not have more than one value.
+ ///
+ /// Returns `null` if there is no header with the provided name.
String? value(String name);
- /**
- * Adds a header value.
- *
- * The header named [name] will have a string value derived from [value]
- * added to its list of values.
- *
- * Some headers are single valued, and for these, adding a value will
- * replace a previous value. If the [value] is a [DateTime], an
- * HTTP date format will be applied. If the value is an [Iterable],
- * each element will be added separately. For all other
- * types the default [Object.toString] method will be used.
- *
- * Header names are converted to lower-case unless
- * [preserveHeaderCase] is set to true. If two header names are
- * the same when converted to lower-case, they are considered to be
- * the same header, with one set of values.
- *
- * The current case of the a header name is that of the name used by
- * the last [set] or [add] call for that header.
- */
+ /// Adds a header value.
+ ///
+ /// The header named [name] will have a string value derived from [value]
+ /// added to its list of values.
+ ///
+ /// Some headers are single valued, and for these, adding a value will
+ /// replace a previous value. If the [value] is a [DateTime], an
+ /// HTTP date format will be applied. If the value is an [Iterable],
+ /// each element will be added separately. For all other
+ /// types the default [Object.toString] method will be used.
+ ///
+ /// Header names are converted to lower-case unless
+ /// [preserveHeaderCase] is set to true. If two header names are
+ /// the same when converted to lower-case, they are considered to be
+ /// the same header, with one set of values.
+ ///
+ /// The current case of the a header name is that of the name used by
+ /// the last [set] or [add] call for that header.
void add(String name, Object value,
{@Since("2.8") bool preserveHeaderCase = false});
- /**
- * Sets the header [name] to [value].
- *
- * Removes all existing values for the header named [name] and
- * then [add]s [value] to it.
- */
+ /// Sets the header [name] to [value].
+ ///
+ /// Removes all existing values for the header named [name] and
+ /// then [add]s [value] to it.
void set(String name, Object value,
{@Since("2.8") bool preserveHeaderCase = false});
- /**
- * Removes a specific value for a header name.
- *
- * Some headers have system supplied values which cannot be removed.
- * For all other headers and values, the [value] is converted to a string
- * in the same way as for [add], then that string value is removed from the
- * current values of [name].
- * If there are no remaining values for [name], the header is no longer
- * considered present.
- */
+ /// Removes a specific value for a header name.
+ ///
+ /// Some headers have system supplied values which cannot be removed.
+ /// For all other headers and values, the [value] is converted to a string
+ /// in the same way as for [add], then that string value is removed from the
+ /// current values of [name].
+ /// If there are no remaining values for [name], the header is no longer
+ /// considered present.
void remove(String name, Object value);
- /**
- * Removes all values for the specified header name.
- *
- * Some headers have system supplied values which cannot be removed.
- * All other values for [name] are removed.
- * If there are no remaining values for [name], the header is no longer
- * considered present.
- */
+ /// Removes all values for the specified header name.
+ ///
+ /// Some headers have system supplied values which cannot be removed.
+ /// All other values for [name] are removed.
+ /// If there are no remaining values for [name], the header is no longer
+ /// considered present.
void removeAll(String name);
- /**
- * Performs the [action] on each header.
- *
- * The [action] function is called with each header's name and a list
- * of the header's values. The casing of the name string is determined by
- * the last [add] or [set] operation for that particular header,
- * which defaults to lower-casing the header name unless explicitly
- * set to preserve the case.
- */
- void forEach(void action(String name, List<String> values));
+ /// Performs the [action] on each header.
+ ///
+ /// The [action] function is called with each header's name and a list
+ /// of the header's values. The casing of the name string is determined by
+ /// the last [add] or [set] operation for that particular header,
+ /// which defaults to lower-casing the header name unless explicitly
+ /// set to preserve the case.
+ void forEach(void Function(String name, List<String> values) action);
- /**
- * Disables folding for the header named [name] when sending the HTTP header.
- *
- * By default, multiple header values are folded into a
- * single header line by separating the values with commas.
- *
- * The 'set-cookie' header has folding disabled by default.
- */
+ /// Disables folding for the header named [name] when sending the HTTP header.
+ ///
+ /// By default, multiple header values are folded into a
+ /// single header line by separating the values with commas.
+ ///
+ /// The 'set-cookie' header has folding disabled by default.
void noFolding(String name);
- /**
- * Removes all headers.
- *
- * Some headers have system supplied values which cannot be removed.
- * All other header values are removed, and header names with not
- * remaining values are no longer considered present.
- */
+ /// Removes all headers.
+ ///
+ /// Some headers have system supplied values which cannot be removed.
+ /// All other header values are removed, and header names with not
+ /// remaining values are no longer considered present.
void clear();
}
-/**
- * Representation of a header value in the form:
- * ```plaintext
- * value; parameter1=value1; parameter2=value2
- * ```
- *
- * [HeaderValue] can be used to conveniently build and parse header
- * values on this form.
- *
- * Parameter values can be omitted, in which case the value is parsed as `null`.
- * Values can be doubled quoted to allow characters outside of the RFC 7230
- * token characters and backslash sequences can be used to represent the double
- * quote and backslash characters themselves.
- *
- * To build an "accepts" header with the value
- *
- * text/plain; q=0.3, text/html
- *
- * use code like this:
- *
- * HttpClientRequest request = ...;
- * var v = new HeaderValue("text/plain", {"q": "0.3"});
- * request.headers.add(HttpHeaders.acceptHeader, v);
- * request.headers.add(HttpHeaders.acceptHeader, "text/html");
- *
- * To parse the header values use the [parse] static method.
- *
- * HttpRequest request = ...;
- * List<String> values = request.headers[HttpHeaders.acceptHeader];
- * values.forEach((value) {
- * HeaderValue v = HeaderValue.parse(value);
- * // Use v.value and v.parameters
- * });
- *
- * An instance of [HeaderValue] is immutable.
- */
+/// Representation of a header value in the form:
+/// ```plaintext
+/// value; parameter1=value1; parameter2=value2
+/// ```
+///
+/// [HeaderValue] can be used to conveniently build and parse header
+/// values on this form.
+///
+/// Parameter values can be omitted, in which case the value is parsed as `null`.
+/// Values can be doubled quoted to allow characters outside of the RFC 7230
+/// token characters and backslash sequences can be used to represent the double
+/// quote and backslash characters themselves.
+///
+/// To build an "accepts" header with the value
+///
+/// text/plain; q=0.3, text/html
+///
+/// use code like this:
+///
+/// HttpClientRequest request = ...;
+/// var v = new HeaderValue("text/plain", {"q": "0.3"});
+/// request.headers.add(HttpHeaders.acceptHeader, v);
+/// request.headers.add(HttpHeaders.acceptHeader, "text/html");
+///
+/// To parse the header values use the [parse] static method.
+///
+/// HttpRequest request = ...;
+/// List<String> values = request.headers[HttpHeaders.acceptHeader];
+/// values.forEach((value) {
+/// HeaderValue v = HeaderValue.parse(value);
+/// // Use v.value and v.parameters
+/// });
+///
+/// An instance of [HeaderValue] is immutable.
abstract class HeaderValue {
- /**
- * Creates a new header value object setting the value and parameters.
- */
+ /// Creates a new header value object setting the value and parameters.
factory HeaderValue(
[String value = "", Map<String, String?> parameters = const {}]) {
- return new _HeaderValue(value, parameters);
+ return _HeaderValue(value, parameters);
}
- /**
- * Creates a new header value object from parsing a header value
- * string with both value and optional parameters.
- */
+ /// Creates a new header value object from parsing a header value
+ /// string with both value and optional parameters.
static HeaderValue parse(String value,
{String parameterSeparator = ";",
String? valueSeparator,
@@ -841,646 +760,534 @@
preserveBackslash: preserveBackslash);
}
- /**
- * The value of the header.
- */
+ /// The value of the header.
String get value;
- /**
- * A map of parameters.
- *
- * This map cannot be modified.
- */
+ /// A map of parameters.
+ ///
+ /// This map cannot be modified.
Map<String, String?> get parameters;
- /**
- * Returns the formatted string representation in the form:
- * ```plaintext
- * value; parameter1=value1; parameter2=value2
- * ```
- */
+ /// Returns the formatted string representation in the form:
+ /// ```plaintext
+ /// value; parameter1=value1; parameter2=value2
+ /// ```
String toString();
}
abstract class HttpSession implements Map {
- /**
- * The id of the current session.
- */
+ /// The id of the current session.
String get id;
- /**
- * Destroys the session.
- *
- * This terminates the session and any further
- * connections with this id will be given a new id and session.
- */
+ /// Destroys the session.
+ ///
+ /// This terminates the session and any further
+ /// connections with this id will be given a new id and session.
void destroy();
- /**
- * Sets a callback that will be called when the session is timed out.
- *
- * Calling this again will overwrite the previous value.
- */
- void set onTimeout(void callback());
+ /// Sets a callback that will be called when the session is timed out.
+ ///
+ /// Calling this again will overwrite the previous value.
+ void set onTimeout(void Function() callback);
- /**
- * Whether the session has not yet been sent to the client.
- */
+ /// Whether the session has not yet been sent to the client.
bool get isNew;
}
-/**
- * A MIME/IANA media type used as the value of the
- * [HttpHeaders.contentTypeHeader] header.
- *
- * A [ContentType] is immutable.
- */
+/// A MIME/IANA media type used as the value of the
+/// [HttpHeaders.contentTypeHeader] header.
+///
+/// A [ContentType] is immutable.
abstract class ContentType implements HeaderValue {
- /**
- * Content type for plain text using UTF-8 encoding.
- *
- * text/plain; charset=utf-8
- */
- static final text = new ContentType("text", "plain", charset: "utf-8");
+ /// Content type for plain text using UTF-8 encoding.
+ ///
+ /// text/plain; charset=utf-8
+ static final text = ContentType("text", "plain", charset: "utf-8");
@Deprecated("Use text instead")
static final TEXT = text;
- /**
- * Content type for HTML using UTF-8 encoding.
- *
- * text/html; charset=utf-8
- */
- static final html = new ContentType("text", "html", charset: "utf-8");
+ /// Content type for HTML using UTF-8 encoding.
+ ///
+ /// text/html; charset=utf-8
+ static final html = ContentType("text", "html", charset: "utf-8");
@Deprecated("Use html instead")
static final HTML = html;
- /**
- * Content type for JSON using UTF-8 encoding.
- *
- * application/json; charset=utf-8
- */
- static final json = new ContentType("application", "json", charset: "utf-8");
+ /// Content type for JSON using UTF-8 encoding.
+ ///
+ /// application/json; charset=utf-8
+ static final json = ContentType("application", "json", charset: "utf-8");
@Deprecated("Use json instead")
static final JSON = json;
- /**
- * Content type for binary data.
- *
- * application/octet-stream
- */
- static final binary = new ContentType("application", "octet-stream");
+ /// Content type for binary data.
+ ///
+ /// application/octet-stream
+ static final binary = ContentType("application", "octet-stream");
@Deprecated("Use binary instead")
static final BINARY = binary;
- /**
- * Creates a new content type object setting the primary type and
- * sub type. The charset and additional parameters can also be set
- * using [charset] and [parameters]. If charset is passed and
- * [parameters] contains charset as well the passed [charset] will
- * override the value in parameters. Keys passed in parameters will be
- * converted to lower case. The `charset` entry, whether passed as `charset`
- * or in `parameters`, will have its value converted to lower-case.
- */
+ /// Creates a new content type object setting the primary type and
+ /// sub type. The charset and additional parameters can also be set
+ /// using [charset] and [parameters]. If charset is passed and
+ /// [parameters] contains charset as well the passed [charset] will
+ /// override the value in parameters. Keys passed in parameters will be
+ /// converted to lower case. The `charset` entry, whether passed as `charset`
+ /// or in `parameters`, will have its value converted to lower-case.
factory ContentType(String primaryType, String subType,
{String? charset, Map<String, String?> parameters = const {}}) {
- return new _ContentType(primaryType, subType, charset, parameters);
+ return _ContentType(primaryType, subType, charset, parameters);
}
- /**
- * Creates a new content type object from parsing a Content-Type
- * header value. As primary type, sub type and parameter names and
- * values are not case sensitive all these values will be converted
- * to lower case. Parsing this string
- *
- * text/html; charset=utf-8
- *
- * will create a content type object with primary type "text",
- * subtype "html" and parameter "charset" with value "utf-8".
- * There may be more parameters supplied, but they are not recognized
- * by this class.
- */
+ /// Creates a new content type object from parsing a Content-Type
+ /// header value. As primary type, sub type and parameter names and
+ /// values are not case sensitive all these values will be converted
+ /// to lower case. Parsing this string
+ ///
+ /// text/html; charset=utf-8
+ ///
+ /// will create a content type object with primary type "text",
+ /// subtype "html" and parameter "charset" with value "utf-8".
+ /// There may be more parameters supplied, but they are not recognized
+ /// by this class.
static ContentType parse(String value) {
return _ContentType.parse(value);
}
- /**
- * Gets the MIME type and subtype, without any parameters.
- *
- * For the full content type `text/html;charset=utf-8`,
- * the [mimeType] value is the string `text/html`.
- */
+ /// Gets the MIME type and subtype, without any parameters.
+ ///
+ /// For the full content type `text/html;charset=utf-8`,
+ /// the [mimeType] value is the string `text/html`.
String get mimeType;
- /**
- * Gets the primary type.
- *
- * For the full content type `text/html;charset=utf-8`,
- * the [primaryType] value is the string `text`.
- */
+ /// Gets the primary type.
+ ///
+ /// For the full content type `text/html;charset=utf-8`,
+ /// the [primaryType] value is the string `text`.
String get primaryType;
- /**
- * Gets the subtype.
- *
- * For the full content type `text/html;charset=utf-8`,
- * the [subType] value is the string `html`.
- * May be the empty string.
- */
+ /// Gets the subtype.
+ ///
+ /// For the full content type `text/html;charset=utf-8`,
+ /// the [subType] value is the string `html`.
+ /// May be the empty string.
String get subType;
- /**
- * Gets the character set, if any.
- *
- * For the full content type `text/html;charset=utf-8`,
- * the [charset] value is the string `utf-8`.
- */
+ /// Gets the character set, if any.
+ ///
+ /// For the full content type `text/html;charset=utf-8`,
+ /// the [charset] value is the string `utf-8`.
String? get charset;
}
-/**
- * Representation of a cookie. For cookies received by the server as Cookie
- * header values only [name] and [value] properties will be set. When building a
- * cookie for the 'set-cookie' header in the server and when receiving cookies
- * in the client as 'set-cookie' headers all fields can be used.
- */
+/// Representation of a cookie. For cookies received by the server as Cookie
+/// header values only [name] and [value] properties will be set. When building a
+/// cookie for the 'set-cookie' header in the server and when receiving cookies
+/// in the client as 'set-cookie' headers all fields can be used.
abstract class Cookie {
- /**
- * The name of the cookie.
- *
- * Must be a `token` as specified in RFC 6265.
- *
- * The allowed characters in a `token` are the visible ASCII characters,
- * U+0021 (`!`) through U+007E (`~`), except the separator characters:
- * `(`, `)`, `<`, `>`, `@`, `,`, `;`, `:`, `\`, `"`, `/`, `[`, `]`, `?`, `=`,
- * `{`, and `}`.
- */
+ /// The name of the cookie.
+ ///
+ /// Must be a `token` as specified in RFC 6265.
+ ///
+ /// The allowed characters in a `token` are the visible ASCII characters,
+ /// U+0021 (`!`) through U+007E (`~`), except the separator characters:
+ /// `(`, `)`, `<`, `>`, `@`, `,`, `;`, `:`, `\`, `"`, `/`, `[`, `]`, `?`, `=`,
+ /// `{`, and `}`.
late String name;
- /**
- * The value of the cookie.
- *
- * Must be a `cookie-value` as specified in RFC 6265.
- *
- * The allowed characters in a cookie value are the visible ASCII characters,
- * U+0021 (`!`) through U+007E (`~`) except the characters:
- * `"`, `,`, `;` and `\`.
- * Cookie values may be wrapped in a single pair of double quotes
- * (U+0022, `"`).
- */
+ /// The value of the cookie.
+ ///
+ /// Must be a `cookie-value` as specified in RFC 6265.
+ ///
+ /// The allowed characters in a cookie value are the visible ASCII characters,
+ /// U+0021 (`!`) through U+007E (`~`) except the characters:
+ /// `"`, `,`, `;` and `\`.
+ /// Cookie values may be wrapped in a single pair of double quotes
+ /// (U+0022, `"`).
late String value;
- /**
- * The time at which the cookie expires.
- */
+ /// The time at which the cookie expires.
DateTime? expires;
- /**
- * The number of seconds until the cookie expires. A zero or negative value
- * means the cookie has expired.
- */
+ /// The number of seconds until the cookie expires. A zero or negative value
+ /// means the cookie has expired.
int? maxAge;
- /**
- * The domain that the cookie applies to.
- */
+ /// The domain that the cookie applies to.
String? domain;
- /**
- * The path within the [domain] that the cookie applies to.
- */
+ /// The path within the [domain] that the cookie applies to.
String? path;
- /**
- * Whether to only send this cookie on secure connections.
- */
+ /// Whether to only send this cookie on secure connections.
bool secure = false;
- /**
- * Whether the cookie is only sent in the HTTP request and is not made
- * available to client side scripts.
- */
+ /// Whether the cookie is only sent in the HTTP request and is not made
+ /// available to client side scripts.
bool httpOnly = false;
- /**
- * Creates a new cookie setting the name and value.
- *
- * [name] and [value] must be composed of valid characters according to RFC
- * 6265.
- *
- * By default the value of `httpOnly` will be set to `true`.
- */
- factory Cookie(String name, String value) => new _Cookie(name, value);
+ /// Creates a new cookie setting the name and value.
+ ///
+ /// [name] and [value] must be composed of valid characters according to RFC
+ /// 6265.
+ ///
+ /// By default the value of `httpOnly` will be set to `true`.
+ factory Cookie(String name, String value) => _Cookie(name, value);
- /**
- * Creates a new cookie by parsing a header value from a 'set-cookie'
- * header.
- */
+ /// Creates a new cookie by parsing a header value from a 'set-cookie'
+ /// header.
factory Cookie.fromSetCookieValue(String value) {
- return new _Cookie.fromSetCookieValue(value);
+ return _Cookie.fromSetCookieValue(value);
}
- /**
- * Returns the formatted string representation of the cookie. The
- * string representation can be used for setting the Cookie or
- * 'set-cookie' headers
- */
+ /// Returns the formatted string representation of the cookie. The
+ /// string representation can be used for setting the Cookie or
+ /// 'set-cookie' headers
String toString();
}
-/**
- * A server-side object
- * that contains the content of and information about an HTTP request.
- *
- * __Note__: Check out the
- * [http_server](https://pub.dev/packages/http_server)
- * package, which makes working with the low-level
- * dart:io HTTP server subsystem easier.
- *
- * `HttpRequest` objects are generated by an [HttpServer],
- * which listens for HTTP requests on a specific host and port.
- * For each request received, the HttpServer, which is a [Stream],
- * generates an `HttpRequest` object and adds it to the stream.
- *
- * An `HttpRequest` object delivers the body content of the request
- * as a stream of byte lists.
- * The object also contains information about the request,
- * such as the method, URI, and headers.
- *
- * In the following code, an HttpServer listens
- * for HTTP requests. When the server receives a request,
- * it uses the HttpRequest object's `method` property to dispatch requests.
- *
- * final HOST = InternetAddress.loopbackIPv4;
- * final PORT = 80;
- *
- * HttpServer.bind(HOST, PORT).then((_server) {
- * _server.listen((HttpRequest request) {
- * switch (request.method) {
- * case 'GET':
- * handleGetRequest(request);
- * break;
- * case 'POST':
- * ...
- * }
- * },
- * onError: handleError); // listen() failed.
- * }).catchError(handleError);
- *
- * An HttpRequest object provides access to the associated [HttpResponse]
- * object through the response property.
- * The server writes its response to the body of the HttpResponse object.
- * For example, here's a function that responds to a request:
- *
- * void handleGetRequest(HttpRequest req) {
- * HttpResponse res = req.response;
- * res.write('Received request ${req.method}: ${req.uri.path}');
- * res.close();
- * }
- */
+/// A server-side object
+/// that contains the content of and information about an HTTP request.
+///
+/// __Note__: Check out the
+/// [http_server](https://pub.dev/packages/http_server)
+/// package, which makes working with the low-level
+/// dart:io HTTP server subsystem easier.
+///
+/// `HttpRequest` objects are generated by an [HttpServer],
+/// which listens for HTTP requests on a specific host and port.
+/// For each request received, the HttpServer, which is a [Stream],
+/// generates an `HttpRequest` object and adds it to the stream.
+///
+/// An `HttpRequest` object delivers the body content of the request
+/// as a stream of byte lists.
+/// The object also contains information about the request,
+/// such as the method, URI, and headers.
+///
+/// In the following code, an HttpServer listens
+/// for HTTP requests. When the server receives a request,
+/// it uses the HttpRequest object's `method` property to dispatch requests.
+///
+/// final HOST = InternetAddress.loopbackIPv4;
+/// final PORT = 80;
+///
+/// HttpServer.bind(HOST, PORT).then((_server) {
+/// _server.listen((HttpRequest request) {
+/// switch (request.method) {
+/// case 'GET':
+/// handleGetRequest(request);
+/// break;
+/// case 'POST':
+/// ...
+/// }
+/// },
+/// onError: handleError); // listen() failed.
+/// }).catchError(handleError);
+///
+/// An HttpRequest object provides access to the associated [HttpResponse]
+/// object through the response property.
+/// The server writes its response to the body of the HttpResponse object.
+/// For example, here's a function that responds to a request:
+///
+/// void handleGetRequest(HttpRequest req) {
+/// HttpResponse res = req.response;
+/// res.write('Received request ${req.method}: ${req.uri.path}');
+/// res.close();
+/// }
abstract class HttpRequest implements Stream<Uint8List> {
- /**
- * The content length of the request body.
- *
- * If the size of the request body is not known in advance,
- * this value is -1.
- */
+ /// The content length of the request body.
+ ///
+ /// If the size of the request body is not known in advance,
+ /// this value is -1.
int get contentLength;
- /**
- * The method, such as 'GET' or 'POST', for the request.
- */
+ /// The method, such as 'GET' or 'POST', for the request.
String get method;
- /**
- * The URI for the request.
- *
- * This provides access to the
- * path and query string for the request.
- */
+ /// The URI for the request.
+ ///
+ /// This provides access to the
+ /// path and query string for the request.
Uri get uri;
- /**
- * The requested URI for the request.
- *
- * The returned URI is reconstructed by using http-header fields, to access
- * otherwise lost information, e.g. host and scheme.
- *
- * To reconstruct the scheme, first 'X-Forwarded-Proto' is checked, and then
- * falling back to server type.
- *
- * To reconstruct the host, first 'X-Forwarded-Host' is checked, then 'Host'
- * and finally calling back to server.
- */
+ /// The requested URI for the request.
+ ///
+ /// The returned URI is reconstructed by using http-header fields, to access
+ /// otherwise lost information, e.g. host and scheme.
+ ///
+ /// To reconstruct the scheme, first 'X-Forwarded-Proto' is checked, and then
+ /// falling back to server type.
+ ///
+ /// To reconstruct the host, first 'X-Forwarded-Host' is checked, then 'Host'
+ /// and finally calling back to server.
Uri get requestedUri;
- /**
- * The request headers.
- *
- * The returned [HttpHeaders] are immutable.
- */
+ /// The request headers.
+ ///
+ /// The returned [HttpHeaders] are immutable.
HttpHeaders get headers;
- /**
- * The cookies in the request, from the "Cookie" headers.
- */
+ /// The cookies in the request, from the "Cookie" headers.
List<Cookie> get cookies;
- /**
- * The persistent connection state signaled by the client.
- */
+ /// The persistent connection state signaled by the client.
bool get persistentConnection;
- /**
- * The client certificate of the client making the request.
- *
- * This value is null if the connection is not a secure TLS or SSL connection,
- * or if the server does not request a client certificate, or if the client
- * does not provide one.
- */
+ /// The client certificate of the client making the request.
+ ///
+ /// This value is null if the connection is not a secure TLS or SSL connection,
+ /// or if the server does not request a client certificate, or if the client
+ /// does not provide one.
X509Certificate? get certificate;
- /**
- * The session for the given request.
- *
- * If the session is being initialized by this call,
- * [HttpSession.isNew] is true for the returned session.
- * See [HttpServer.sessionTimeout] on how to change default timeout.
- */
+ /// The session for the given request.
+ ///
+ /// If the session is being initialized by this call,
+ /// [HttpSession.isNew] is true for the returned session.
+ /// See [HttpServer.sessionTimeout] on how to change default timeout.
HttpSession get session;
- /**
- * The HTTP protocol version used in the request,
- * either "1.0" or "1.1".
- */
+ /// The HTTP protocol version used in the request,
+ /// either "1.0" or "1.1".
String get protocolVersion;
- /**
- * Information about the client connection.
- *
- * Returns `null` if the socket is not available.
- */
+ /// Information about the client connection.
+ ///
+ /// Returns `null` if the socket is not available.
HttpConnectionInfo? get connectionInfo;
- /**
- * The [HttpResponse] object, used for sending back the response to the
- * client.
- *
- * If the [contentLength] of the body isn't 0, and the body isn't being read,
- * any write calls on the [HttpResponse] automatically drain the request
- * body.
- */
+ /// The [HttpResponse] object, used for sending back the response to the
+ /// client.
+ ///
+ /// If the [contentLength] of the body isn't 0, and the body isn't being read,
+ /// any write calls on the [HttpResponse] automatically drain the request
+ /// body.
HttpResponse get response;
}
-/**
- * An HTTP response, which returns the headers and data
- * from the server to the client in response to an HTTP request.
- *
- * Every HttpRequest object provides access to the associated [HttpResponse]
- * object through the `response` property.
- * The server sends its response to the client by writing to the
- * HttpResponse object.
- *
- * ## Writing the response
- *
- * This class implements [IOSink].
- * After the header has been set up, the methods
- * from IOSink, such as `writeln()`, can be used to write
- * the body of the HTTP response.
- * Use the `close()` method to close the response and send it to the client.
- *
- * server.listen((HttpRequest request) {
- * request.response.write('Hello, world!');
- * request.response.close();
- * });
- *
- * When one of the IOSink methods is used for the
- * first time, the request header is sent. Calling any methods that
- * change the header after it is sent throws an exception.
- *
- * ## Setting the headers
- *
- * The HttpResponse object has a number of properties for setting up
- * the HTTP headers of the response.
- * When writing string data through the IOSink, the encoding used
- * is determined from the "charset" parameter of the
- * "Content-Type" header.
- *
- * HttpResponse response = ...
- * response.headers.contentType
- * = new ContentType("application", "json", charset: "utf-8");
- * response.write(...); // Strings written will be UTF-8 encoded.
- *
- * If no charset is provided the default of ISO-8859-1 (Latin 1) will
- * be used.
- *
- * HttpResponse response = ...
- * response.headers.add(HttpHeaders.contentTypeHeader, "text/plain");
- * response.write(...); // Strings written will be ISO-8859-1 encoded.
- *
- * An exception is thrown if you use the `write()` method
- * while an unsupported content-type is set.
- */
+/// An HTTP response, which returns the headers and data
+/// from the server to the client in response to an HTTP request.
+///
+/// Every HttpRequest object provides access to the associated [HttpResponse]
+/// object through the `response` property.
+/// The server sends its response to the client by writing to the
+/// HttpResponse object.
+///
+/// ## Writing the response
+///
+/// This class implements [IOSink].
+/// After the header has been set up, the methods
+/// from IOSink, such as `writeln()`, can be used to write
+/// the body of the HTTP response.
+/// Use the `close()` method to close the response and send it to the client.
+///
+/// server.listen((HttpRequest request) {
+/// request.response.write('Hello, world!');
+/// request.response.close();
+/// });
+///
+/// When one of the IOSink methods is used for the
+/// first time, the request header is sent. Calling any methods that
+/// change the header after it is sent throws an exception.
+///
+/// ## Setting the headers
+///
+/// The HttpResponse object has a number of properties for setting up
+/// the HTTP headers of the response.
+/// When writing string data through the IOSink, the encoding used
+/// is determined from the "charset" parameter of the
+/// "Content-Type" header.
+///
+/// HttpResponse response = ...
+/// response.headers.contentType
+/// = new ContentType("application", "json", charset: "utf-8");
+/// response.write(...); // Strings written will be UTF-8 encoded.
+///
+/// If no charset is provided the default of ISO-8859-1 (Latin 1) will
+/// be used.
+///
+/// HttpResponse response = ...
+/// response.headers.add(HttpHeaders.contentTypeHeader, "text/plain");
+/// response.write(...); // Strings written will be ISO-8859-1 encoded.
+///
+/// An exception is thrown if you use the `write()` method
+/// while an unsupported content-type is set.
abstract class HttpResponse implements IOSink {
// TODO(ajohnsen): Add documentation of how to pipe a file to the response.
- /**
- * Gets and sets the content length of the response. If the size of
- * the response is not known in advance set the content length to
- * -1, which is also the default if not set.
- */
+ /// Gets and sets the content length of the response. If the size of
+ /// the response is not known in advance set the content length to
+ /// -1, which is also the default if not set.
int contentLength = -1;
- /**
- * The status code of the response.
- *
- * Any integer value is accepted. For
- * the official HTTP status codes use the fields from
- * [HttpStatus]. If no status code is explicitly set the default
- * value [HttpStatus.ok] is used.
- *
- * The status code must be set before the body is written
- * to. Setting the status code after writing to the response body or
- * closing the response will throw a `StateError`.
- */
+ /// The status code of the response.
+ ///
+ /// Any integer value is accepted. For
+ /// the official HTTP status codes use the fields from
+ /// [HttpStatus]. If no status code is explicitly set the default
+ /// value [HttpStatus.ok] is used.
+ ///
+ /// The status code must be set before the body is written
+ /// to. Setting the status code after writing to the response body or
+ /// closing the response will throw a `StateError`.
int statusCode = HttpStatus.ok;
- /**
- * The reason phrase for the response.
- *
- * If no reason phrase is explicitly set, a default reason phrase is provided.
- *
- * The reason phrase must be set before the body is written
- * to. Setting the reason phrase after writing to the response body
- * or closing the response will throw a [StateError].
- */
+ /// The reason phrase for the response.
+ ///
+ /// If no reason phrase is explicitly set, a default reason phrase is provided.
+ ///
+ /// The reason phrase must be set before the body is written
+ /// to. Setting the reason phrase after writing to the response body
+ /// or closing the response will throw a [StateError].
late String reasonPhrase;
- /**
- * Gets and sets the persistent connection state. The initial value
- * of this property is the persistent connection state from the
- * request.
- */
+ /// Gets and sets the persistent connection state. The initial value
+ /// of this property is the persistent connection state from the
+ /// request.
late bool persistentConnection;
- /**
- * Set and get the [deadline] for the response. The deadline is timed from the
- * time it's set. Setting a new deadline will override any previous deadline.
- * When a deadline is exceeded, the response will be closed and any further
- * data ignored.
- *
- * To disable a deadline, set the [deadline] to `null`.
- *
- * The [deadline] is `null` by default.
- */
+ /// Set and get the [deadline] for the response. The deadline is timed from the
+ /// time it's set. Setting a new deadline will override any previous deadline.
+ /// When a deadline is exceeded, the response will be closed and any further
+ /// data ignored.
+ ///
+ /// To disable a deadline, set the [deadline] to `null`.
+ ///
+ /// The [deadline] is `null` by default.
Duration? deadline;
- /**
- * Gets or sets if the [HttpResponse] should buffer output.
- *
- * Default value is `true`.
- *
- * __Note__: Disabling buffering of the output can result in very poor
- * performance, when writing many small chunks.
- */
+ /// Gets or sets if the [HttpResponse] should buffer output.
+ ///
+ /// Default value is `true`.
+ ///
+ /// __Note__: Disabling buffering of the output can result in very poor
+ /// performance, when writing many small chunks.
bool bufferOutput = true;
- /**
- * Returns the response headers.
- *
- * The response headers can be modified until the response body is
- * written to or closed. After that they become immutable.
- */
+ /// Returns the response headers.
+ ///
+ /// The response headers can be modified until the response body is
+ /// written to or closed. After that they become immutable.
HttpHeaders get headers;
- /**
- * Cookies to set in the client (in the 'set-cookie' header).
- */
+ /// Cookies to set in the client (in the 'set-cookie' header).
List<Cookie> get cookies;
- /**
- * Respond with a redirect to [location].
- *
- * The URI in [location] should be absolute, but there are no checks
- * to enforce that.
- *
- * By default the HTTP status code `HttpStatus.movedTemporarily`
- * (`302`) is used for the redirect, but an alternative one can be
- * specified using the [status] argument.
- *
- * This method will also call `close`, and the returned future is
- * the future returned by `close`.
- */
+ /// Respond with a redirect to [location].
+ ///
+ /// The URI in [location] should be absolute, but there are no checks
+ /// to enforce that.
+ ///
+ /// By default the HTTP status code `HttpStatus.movedTemporarily`
+ /// (`302`) is used for the redirect, but an alternative one can be
+ /// specified using the [status] argument.
+ ///
+ /// This method will also call `close`, and the returned future is
+ /// the future returned by `close`.
Future redirect(Uri location, {int status = HttpStatus.movedTemporarily});
- /**
- * Detaches the underlying socket from the HTTP server. When the
- * socket is detached the HTTP server will no longer perform any
- * operations on it.
- *
- * This is normally used when a HTTP upgrade request is received
- * and the communication should continue with a different protocol.
- *
- * If [writeHeaders] is `true`, the status line and [headers] will be written
- * to the socket before it's detached. If `false`, the socket is detached
- * immediately, without any data written to the socket. Default is `true`.
- */
+ /// Detaches the underlying socket from the HTTP server. When the
+ /// socket is detached the HTTP server will no longer perform any
+ /// operations on it.
+ ///
+ /// This is normally used when a HTTP upgrade request is received
+ /// and the communication should continue with a different protocol.
+ ///
+ /// If [writeHeaders] is `true`, the status line and [headers] will be written
+ /// to the socket before it's detached. If `false`, the socket is detached
+ /// immediately, without any data written to the socket. Default is `true`.
Future<Socket> detachSocket({bool writeHeaders = true});
- /**
- * Gets information about the client connection. Returns `null` if the
- * socket is not available.
- */
+ /// Gets information about the client connection. Returns `null` if the
+ /// socket is not available.
HttpConnectionInfo? get connectionInfo;
}
-/**
- * A client that receives content, such as web pages, from
- * a server using the HTTP protocol.
- *
- * HttpClient contains a number of methods to send an [HttpClientRequest]
- * to an Http server and receive an [HttpClientResponse] back.
- * For example, you can use the [get], [getUrl], [post], and [postUrl] methods
- * for GET and POST requests, respectively.
- *
- * ## Making a simple GET request: an example
- *
- * A `getUrl` request is a two-step process, triggered by two [Future]s.
- * When the first future completes with a [HttpClientRequest], the underlying
- * network connection has been established, but no data has been sent.
- * In the callback function for the first future, the HTTP headers and body
- * can be set on the request. Either the first write to the request object
- * or a call to [close] sends the request to the server.
- *
- * When the HTTP response is received from the server,
- * the second future, which is returned by close,
- * completes with an [HttpClientResponse] object.
- * This object provides access to the headers and body of the response.
- * The body is available as a stream implemented by HttpClientResponse.
- * If a body is present, it must be read. Otherwise, it leads to resource
- * leaks. Consider using [HttpClientResponse.drain] if the body is unused.
- *
- * HttpClient client = new HttpClient();
- * client.getUrl(Uri.parse("http://www.example.com/"))
- * .then((HttpClientRequest request) {
- * // Optionally set up headers...
- * // Optionally write to the request object...
- * // Then call close.
- * ...
- * return request.close();
- * })
- * .then((HttpClientResponse response) {
- * // Process the response.
- * ...
- * });
- *
- * The future for [HttpClientRequest] is created by methods such as
- * [getUrl] and [open].
- *
- * ## HTTPS connections
- *
- * An HttpClient can make HTTPS requests, connecting to a server using
- * the TLS (SSL) secure networking protocol. Calling [getUrl] with an
- * https: scheme will work automatically, if the server's certificate is
- * signed by a root CA (certificate authority) on the default list of
- * well-known trusted CAs, compiled by Mozilla.
- *
- * To add a custom trusted certificate authority, or to send a client
- * certificate to servers that request one, pass a [SecurityContext] object
- * as the optional `context` argument to the `HttpClient` constructor.
- * The desired security options can be set on the [SecurityContext] object.
- *
- * ## Headers
- *
- * All HttpClient requests set the following header by default:
- *
- * Accept-Encoding: gzip
- *
- * This allows the HTTP server to use gzip compression for the body if
- * possible. If this behavior is not desired set the
- * `Accept-Encoding` header to something else.
- * To turn off gzip compression of the response, clear this header:
- *
- * request.headers.removeAll(HttpHeaders.acceptEncodingHeader)
- *
- * ## Closing the HttpClient
- *
- * The HttpClient supports persistent connections and caches network
- * connections to reuse them for multiple requests whenever
- * possible. This means that network connections can be kept open for
- * some time after a request has completed. Use HttpClient.close
- * to force the HttpClient object to shut down and to close the idle
- * network connections.
- *
- * ## Turning proxies on and off
- *
- * By default the HttpClient uses the proxy configuration available
- * from the environment, see [findProxyFromEnvironment]. To turn off
- * the use of proxies set the [findProxy] property to
- * `null`.
- *
- * HttpClient client = new HttpClient();
- * client.findProxy = null;
- */
+/// A client that receives content, such as web pages, from
+/// a server using the HTTP protocol.
+///
+/// HttpClient contains a number of methods to send an [HttpClientRequest]
+/// to an Http server and receive an [HttpClientResponse] back.
+/// For example, you can use the [get], [getUrl], [post], and [postUrl] methods
+/// for GET and POST requests, respectively.
+///
+/// ## Making a simple GET request: an example
+///
+/// A `getUrl` request is a two-step process, triggered by two [Future]s.
+/// When the first future completes with a [HttpClientRequest], the underlying
+/// network connection has been established, but no data has been sent.
+/// In the callback function for the first future, the HTTP headers and body
+/// can be set on the request. Either the first write to the request object
+/// or a call to [close] sends the request to the server.
+///
+/// When the HTTP response is received from the server,
+/// the second future, which is returned by close,
+/// completes with an [HttpClientResponse] object.
+/// This object provides access to the headers and body of the response.
+/// The body is available as a stream implemented by HttpClientResponse.
+/// If a body is present, it must be read. Otherwise, it leads to resource
+/// leaks. Consider using [HttpClientResponse.drain] if the body is unused.
+///
+/// HttpClient client = new HttpClient();
+/// client.getUrl(Uri.parse("http://www.example.com/"))
+/// .then((HttpClientRequest request) {
+/// // Optionally set up headers...
+/// // Optionally write to the request object...
+/// // Then call close.
+/// ...
+/// return request.close();
+/// })
+/// .then((HttpClientResponse response) {
+/// // Process the response.
+/// ...
+/// });
+///
+/// The future for [HttpClientRequest] is created by methods such as
+/// [getUrl] and [open].
+///
+/// ## HTTPS connections
+///
+/// An HttpClient can make HTTPS requests, connecting to a server using
+/// the TLS (SSL) secure networking protocol. Calling [getUrl] with an
+/// https: scheme will work automatically, if the server's certificate is
+/// signed by a root CA (certificate authority) on the default list of
+/// well-known trusted CAs, compiled by Mozilla.
+///
+/// To add a custom trusted certificate authority, or to send a client
+/// certificate to servers that request one, pass a [SecurityContext] object
+/// as the optional `context` argument to the `HttpClient` constructor.
+/// The desired security options can be set on the [SecurityContext] object.
+///
+/// ## Headers
+///
+/// All HttpClient requests set the following header by default:
+///
+/// Accept-Encoding: gzip
+///
+/// This allows the HTTP server to use gzip compression for the body if
+/// possible. If this behavior is not desired set the
+/// `Accept-Encoding` header to something else.
+/// To turn off gzip compression of the response, clear this header:
+///
+/// request.headers.removeAll(HttpHeaders.acceptEncodingHeader)
+///
+/// ## Closing the HttpClient
+///
+/// The HttpClient supports persistent connections and caches network
+/// connections to reuse them for multiple requests whenever
+/// possible. This means that network connections can be kept open for
+/// some time after a request has completed. Use HttpClient.close
+/// to force the HttpClient object to shut down and to close the idle
+/// network connections.
+///
+/// ## Turning proxies on and off
+///
+/// By default the HttpClient uses the proxy configuration available
+/// from the environment, see [findProxyFromEnvironment]. To turn off
+/// the use of proxies set the [findProxy] property to
+/// `null`.
+///
+/// HttpClient client = new HttpClient();
+/// client.findProxy = null;
abstract class HttpClient {
static const int defaultHttpPort = 80;
@Deprecated("Use defaultHttpPort instead")
@@ -1529,44 +1336,40 @@
/// `null`.
Duration? connectionTimeout;
- /**
- * Gets and sets the maximum number of live connections, to a single host.
- *
- * Increasing this number may lower performance and take up unwanted
- * system resources.
- *
- * To disable, set to `null`.
- *
- * Default is `null`.
- */
+ /// Gets and sets the maximum number of live connections, to a single host.
+ ///
+ /// Increasing this number may lower performance and take up unwanted
+ /// system resources.
+ ///
+ /// To disable, set to `null`.
+ ///
+ /// Default is `null`.
int? maxConnectionsPerHost;
- /**
- * Gets and sets whether the body of a response will be automatically
- * uncompressed.
- *
- * The body of an HTTP response can be compressed. In most
- * situations providing the un-compressed body is most
- * convenient. Therefore the default behavior is to un-compress the
- * body. However in some situations (e.g. implementing a transparent
- * proxy) keeping the uncompressed stream is required.
- *
- * NOTE: Headers in the response are never modified. This means
- * that when automatic un-compression is turned on the value of the
- * header `Content-Length` will reflect the length of the original
- * compressed body. Likewise the header `Content-Encoding` will also
- * have the original value indicating compression.
- *
- * NOTE: Automatic un-compression is only performed if the
- * `Content-Encoding` header value is `gzip`.
- *
- * This value affects all responses produced by this client after the
- * value is changed.
- *
- * To disable, set to `false`.
- *
- * Default is `true`.
- */
+ /// Gets and sets whether the body of a response will be automatically
+ /// uncompressed.
+ ///
+ /// The body of an HTTP response can be compressed. In most
+ /// situations providing the un-compressed body is most
+ /// convenient. Therefore the default behavior is to un-compress the
+ /// body. However in some situations (e.g. implementing a transparent
+ /// proxy) keeping the uncompressed stream is required.
+ ///
+ /// NOTE: Headers in the response are never modified. This means
+ /// that when automatic un-compression is turned on the value of the
+ /// header `Content-Length` will reflect the length of the original
+ /// compressed body. Likewise the header `Content-Encoding` will also
+ /// have the original value indicating compression.
+ ///
+ /// NOTE: Automatic un-compression is only performed if the
+ /// `Content-Encoding` header value is `gzip`.
+ ///
+ /// This value affects all responses produced by this client after the
+ /// value is changed.
+ ///
+ /// To disable, set to `false`.
+ ///
+ /// Default is `true`.
bool autoUncompress = true;
/// Gets and sets the default value of the `User-Agent` header for all requests
@@ -1581,275 +1384,240 @@
factory HttpClient({SecurityContext? context}) {
HttpOverrides? overrides = HttpOverrides.current;
if (overrides == null) {
- return new _HttpClient(context);
+ return _HttpClient(context);
}
return overrides.createHttpClient(context);
}
- /**
- * Opens a HTTP connection.
- *
- * The HTTP method to use is specified in [method], the server is
- * specified using [host] and [port], and the path (including
- * a possible query) is specified using [path].
- * The path may also contain a URI fragment, which will be ignored.
- *
- * The `Host` header for the request will be set to the value [host]:[port]
- * (if [host] is an IP address, it will still be used in the `Host` header).
- * This can be overridden through the [HttpClientRequest] interface before
- * the request is sent.
- *
- * For additional information on the sequence of events during an
- * HTTP transaction, and the objects returned by the futures, see
- * the overall documentation for the class [HttpClient].
- */
+ /// Opens a HTTP connection.
+ ///
+ /// The HTTP method to use is specified in [method], the server is
+ /// specified using [host] and [port], and the path (including
+ /// a possible query) is specified using [path].
+ /// The path may also contain a URI fragment, which will be ignored.
+ ///
+ /// The `Host` header for the request will be set to the value [host]:[port]
+ /// (if [host] is an IP address, it will still be used in the `Host` header).
+ /// This can be overridden through the [HttpClientRequest] interface before
+ /// the request is sent.
+ ///
+ /// For additional information on the sequence of events during an
+ /// HTTP transaction, and the objects returned by the futures, see
+ /// the overall documentation for the class [HttpClient].
Future<HttpClientRequest> open(
String method, String host, int port, String path);
- /**
- * Opens a HTTP connection.
- *
- * The HTTP method is specified in [method] and the URL to use in
- * [url].
- *
- * The `Host` header for the request will be set to the value
- * [Uri.host]:[Uri.port] from [url] (if `url.host` is an IP address, it will
- * still be used in the `Host` header). This can be overridden through the
- * [HttpClientRequest] interface before the request is sent.
- *
- * For additional information on the sequence of events during an
- * HTTP transaction, and the objects returned by the futures, see
- * the overall documentation for the class [HttpClient].
- */
+ /// Opens a HTTP connection.
+ ///
+ /// The HTTP method is specified in [method] and the URL to use in
+ /// [url].
+ ///
+ /// The `Host` header for the request will be set to the value
+ /// [Uri.host]:[Uri.port] from [url] (if `url.host` is an IP address, it will
+ /// still be used in the `Host` header). This can be overridden through the
+ /// [HttpClientRequest] interface before the request is sent.
+ ///
+ /// For additional information on the sequence of events during an
+ /// HTTP transaction, and the objects returned by the futures, see
+ /// the overall documentation for the class [HttpClient].
Future<HttpClientRequest> openUrl(String method, Uri url);
- /**
- * Opens a HTTP connection using the GET method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using
- * [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the GET method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using
+ /// [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> get(String host, int port, String path);
- /**
- * Opens a HTTP connection using the GET method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the GET method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> getUrl(Uri url);
- /**
- * Opens a HTTP connection using the POST method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using
- * [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the POST method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using
+ /// [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> post(String host, int port, String path);
- /**
- * Opens a HTTP connection using the POST method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the POST method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> postUrl(Uri url);
- /**
- * Opens a HTTP connection using the PUT method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the PUT method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> put(String host, int port, String path);
- /**
- * Opens a HTTP connection using the PUT method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the PUT method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> putUrl(Uri url);
- /**
- * Opens a HTTP connection using the DELETE method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the DELETE method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> delete(String host, int port, String path);
- /**
- * Opens a HTTP connection using the DELETE method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the DELETE method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> deleteUrl(Uri url);
- /**
- * Opens a HTTP connection using the PATCH method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the PATCH method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> patch(String host, int port, String path);
- /**
- * Opens a HTTP connection using the PATCH method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the PATCH method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> patchUrl(Uri url);
- /**
- * Opens a HTTP connection using the HEAD method.
- *
- * The server is specified using [host] and [port], and the path
- * (including a possible query) is specified using [path].
- *
- * See [open] for details.
- */
+ /// Opens a HTTP connection using the HEAD method.
+ ///
+ /// The server is specified using [host] and [port], and the path
+ /// (including a possible query) is specified using [path].
+ ///
+ /// See [open] for details.
Future<HttpClientRequest> head(String host, int port, String path);
- /**
- * Opens a HTTP connection using the HEAD method.
- *
- * The URL to use is specified in [url].
- *
- * See [openUrl] for details.
- */
+ /// Opens a HTTP connection using the HEAD method.
+ ///
+ /// The URL to use is specified in [url].
+ ///
+ /// See [openUrl] for details.
Future<HttpClientRequest> headUrl(Uri url);
- /**
- * Sets the function to be called when a site is requesting
- * authentication.
- *
- * The URL requested, the authentication scheme and the security realm
- * from the server are passed in the arguments [f.url], [f.scheme] and
- * [f.realm].
- *
- * The function returns a [Future] which should complete when the
- * authentication has been resolved. If credentials cannot be
- * provided the [Future] should complete with `false`. If
- * credentials are available the function should add these using
- * [addCredentials] before completing the [Future] with the value
- * `true`.
- *
- * If the [Future] completes with `true` the request will be retried
- * using the updated credentials, however, the retried request will not
- * carry the original request payload. Otherwise response processing will
- * continue normally.
- *
- * If it is known that the remote server requires authentication for all
- * requests, it is advisable to use [addCredentials] directly, or manually
- * set the `'authorization'` header on the request to avoid the overhead
- * of a failed request, or issues due to missing request payload on retried
- * request.
- */
- void set authenticate(Future<bool> f(Uri url, String scheme, String? realm)?);
+ /// Sets the function to be called when a site is requesting
+ /// authentication.
+ ///
+ /// The URL requested, the authentication scheme and the security realm
+ /// from the server are passed in the arguments [f.url], [f.scheme] and
+ /// [f.realm].
+ ///
+ /// The function returns a [Future] which should complete when the
+ /// authentication has been resolved. If credentials cannot be
+ /// provided the [Future] should complete with `false`. If
+ /// credentials are available the function should add these using
+ /// [addCredentials] before completing the [Future] with the value
+ /// `true`.
+ ///
+ /// If the [Future] completes with `true` the request will be retried
+ /// using the updated credentials, however, the retried request will not
+ /// carry the original request payload. Otherwise response processing will
+ /// continue normally.
+ ///
+ /// If it is known that the remote server requires authentication for all
+ /// requests, it is advisable to use [addCredentials] directly, or manually
+ /// set the `'authorization'` header on the request to avoid the overhead
+ /// of a failed request, or issues due to missing request payload on retried
+ /// request.
+ void set authenticate(
+ Future<bool> Function(Uri url, String scheme, String? realm)? f);
- /**
- * Add credentials to be used for authorizing HTTP requests.
- */
+ /// Add credentials to be used for authorizing HTTP requests.
void addCredentials(Uri url, String realm, HttpClientCredentials credentials);
- /**
- * Sets the function used to resolve the proxy server to be used for
- * opening a HTTP connection to the specified [url]. If this
- * function is not set, direct connections will always be used.
- *
- * The string returned by [f] must be in the format used by browser
- * PAC (proxy auto-config) scripts. That is either
- *
- * "DIRECT"
- *
- * for using a direct connection or
- *
- * "PROXY host:port"
- *
- * for using the proxy server `host` on port `port`.
- *
- * A configuration can contain several configuration elements
- * separated by semicolons, e.g.
- *
- * "PROXY host:port; PROXY host2:port2; DIRECT"
- *
- * The static function [findProxyFromEnvironment] on this class can
- * be used to implement proxy server resolving based on environment
- * variables.
- */
- void set findProxy(String f(Uri url)?);
+ /// Sets the function used to resolve the proxy server to be used for
+ /// opening a HTTP connection to the specified [url]. If this
+ /// function is not set, direct connections will always be used.
+ ///
+ /// The string returned by [f] must be in the format used by browser
+ /// PAC (proxy auto-config) scripts. That is either
+ ///
+ /// "DIRECT"
+ ///
+ /// for using a direct connection or
+ ///
+ /// "PROXY host:port"
+ ///
+ /// for using the proxy server `host` on port `port`.
+ ///
+ /// A configuration can contain several configuration elements
+ /// separated by semicolons, e.g.
+ ///
+ /// "PROXY host:port; PROXY host2:port2; DIRECT"
+ ///
+ /// The static function [findProxyFromEnvironment] on this class can
+ /// be used to implement proxy server resolving based on environment
+ /// variables.
+ void set findProxy(String Function(Uri url)? f);
- /**
- * Function for resolving the proxy server to be used for a HTTP
- * connection from the proxy configuration specified through
- * environment variables.
- *
- * The following environment variables are taken into account:
- *
- * http_proxy
- * https_proxy
- * no_proxy
- * HTTP_PROXY
- * HTTPS_PROXY
- * NO_PROXY
- *
- * [:http_proxy:] and [:HTTP_PROXY:] specify the proxy server to use for
- * http:// urls. Use the format [:hostname:port:]. If no port is used a
- * default of 1080 will be used. If both are set the lower case one takes
- * precedence.
- *
- * [:https_proxy:] and [:HTTPS_PROXY:] specify the proxy server to use for
- * https:// urls. Use the format [:hostname:port:]. If no port is used a
- * default of 1080 will be used. If both are set the lower case one takes
- * precedence.
- *
- * [:no_proxy:] and [:NO_PROXY:] specify a comma separated list of
- * postfixes of hostnames for which not to use the proxy
- * server. E.g. the value "localhost,127.0.0.1" will make requests
- * to both "localhost" and "127.0.0.1" not use a proxy. If both are set
- * the lower case one takes precedence.
- *
- * To activate this way of resolving proxies assign this function to
- * the [findProxy] property on the [HttpClient].
- *
- * HttpClient client = new HttpClient();
- * client.findProxy = HttpClient.findProxyFromEnvironment;
- *
- * If you don't want to use the system environment you can use a
- * different one by wrapping the function.
- *
- * HttpClient client = new HttpClient();
- * client.findProxy = (url) {
- * return HttpClient.findProxyFromEnvironment(
- * url, environment: {"http_proxy": ..., "no_proxy": ...});
- * }
- *
- * If a proxy requires authentication it is possible to configure
- * the username and password as well. Use the format
- * [:username:password@hostname:port:] to include the username and
- * password. Alternatively the API [addProxyCredentials] can be used
- * to set credentials for proxies which require authentication.
- */
+ /// Function for resolving the proxy server to be used for a HTTP
+ /// connection from the proxy configuration specified through
+ /// environment variables.
+ ///
+ /// The following environment variables are taken into account:
+ ///
+ /// http_proxy
+ /// https_proxy
+ /// no_proxy
+ /// HTTP_PROXY
+ /// HTTPS_PROXY
+ /// NO_PROXY
+ ///
+ /// [:http_proxy:] and [:HTTP_PROXY:] specify the proxy server to use for
+ /// http:// urls. Use the format [:hostname:port:]. If no port is used a
+ /// default of 1080 will be used. If both are set the lower case one takes
+ /// precedence.
+ ///
+ /// [:https_proxy:] and [:HTTPS_PROXY:] specify the proxy server to use for
+ /// https:// urls. Use the format [:hostname:port:]. If no port is used a
+ /// default of 1080 will be used. If both are set the lower case one takes
+ /// precedence.
+ ///
+ /// [:no_proxy:] and [:NO_PROXY:] specify a comma separated list of
+ /// postfixes of hostnames for which not to use the proxy
+ /// server. E.g. the value "localhost,127.0.0.1" will make requests
+ /// to both "localhost" and "127.0.0.1" not use a proxy. If both are set
+ /// the lower case one takes precedence.
+ ///
+ /// To activate this way of resolving proxies assign this function to
+ /// the [findProxy] property on the [HttpClient].
+ ///
+ /// HttpClient client = new HttpClient();
+ /// client.findProxy = HttpClient.findProxyFromEnvironment;
+ ///
+ /// If you don't want to use the system environment you can use a
+ /// different one by wrapping the function.
+ ///
+ /// HttpClient client = new HttpClient();
+ /// client.findProxy = (url) {
+ /// return HttpClient.findProxyFromEnvironment(
+ /// url, environment: {"http_proxy": ..., "no_proxy": ...});
+ /// }
+ ///
+ /// If a proxy requires authentication it is possible to configure
+ /// the username and password as well. Use the format
+ /// [:username:password@hostname:port:] to include the username and
+ /// password. Alternatively the API [addProxyCredentials] can be used
+ /// to set credentials for proxies which require authentication.
static String findProxyFromEnvironment(Uri url,
{Map<String, String>? environment}) {
HttpOverrides? overrides = HttpOverrides.current;
@@ -1859,58 +1627,54 @@
return overrides.findProxyFromEnvironment(url, environment);
}
- /**
- * Sets the function to be called when a proxy is requesting
- * authentication.
- *
- * Information on the proxy in use, the authentication scheme
- * and the security realm for the authentication
- * are passed in the arguments [f.host], [f.port], [f.scheme] and [f.realm].
- *
- * The function returns a [Future] which should complete when the
- * authentication has been resolved. If credentials cannot be
- * provided the [Future] should complete with `false`. If
- * credentials are available the function should add these using
- * [addProxyCredentials] before completing the [Future] with the value
- * `true`.
- *
- * If the [Future] completes with `true` the request will be retried
- * using the updated credentials. Otherwise response processing will
- * continue normally.
- */
+ /// Sets the function to be called when a proxy is requesting
+ /// authentication.
+ ///
+ /// Information on the proxy in use, the authentication scheme
+ /// and the security realm for the authentication
+ /// are passed in the arguments [f.host], [f.port], [f.scheme] and [f.realm].
+ ///
+ /// The function returns a [Future] which should complete when the
+ /// authentication has been resolved. If credentials cannot be
+ /// provided the [Future] should complete with `false`. If
+ /// credentials are available the function should add these using
+ /// [addProxyCredentials] before completing the [Future] with the value
+ /// `true`.
+ ///
+ /// If the [Future] completes with `true` the request will be retried
+ /// using the updated credentials. Otherwise response processing will
+ /// continue normally.
void set authenticateProxy(
- Future<bool> f(String host, int port, String scheme, String? realm)?);
+ Future<bool> Function(
+ String host, int port, String scheme, String? realm)?
+ f);
- /**
- * Add credentials to be used for authorizing HTTP proxies.
- */
+ /// Add credentials to be used for authorizing HTTP proxies.
void addProxyCredentials(
String host, int port, String realm, HttpClientCredentials credentials);
- /**
- * Sets a callback that will decide whether to accept a secure connection
- * with a server certificate that cannot be authenticated by any of our
- * trusted root certificates.
- *
- * When an secure HTTP request if made, using this HttpClient, and the
- * server returns a server certificate that cannot be authenticated, the
- * callback is called asynchronously with the [X509Certificate] object and
- * the server's hostname and port. If the value of [badCertificateCallback]
- * is `null`, the bad certificate is rejected, as if the callback
- * returned `false`
- *
- * If the callback returns true, the secure connection is accepted and the
- * [:Future<HttpClientRequest>:] that was returned from the call making the
- * request completes with a valid HttpRequest object. If the callback returns
- * false, the [:Future<HttpClientRequest>:] completes with an exception.
- *
- * If a bad certificate is received on a connection attempt, the library calls
- * the function that was the value of badCertificateCallback at the time
- * the request is made, even if the value of badCertificateCallback
- * has changed since then.
- */
+ /// Sets a callback that will decide whether to accept a secure connection
+ /// with a server certificate that cannot be authenticated by any of our
+ /// trusted root certificates.
+ ///
+ /// When an secure HTTP request if made, using this HttpClient, and the
+ /// server returns a server certificate that cannot be authenticated, the
+ /// callback is called asynchronously with the [X509Certificate] object and
+ /// the server's hostname and port. If the value of [badCertificateCallback]
+ /// is `null`, the bad certificate is rejected, as if the callback
+ /// returned `false`
+ ///
+ /// If the callback returns true, the secure connection is accepted and the
+ /// [:Future<HttpClientRequest>:] that was returned from the call making the
+ /// request completes with a valid HttpRequest object. If the callback returns
+ /// false, the [:Future<HttpClientRequest>:] completes with an exception.
+ ///
+ /// If a bad certificate is received on a connection attempt, the library calls
+ /// the function that was the value of badCertificateCallback at the time
+ /// the request is made, even if the value of badCertificateCallback
+ /// has changed since then.
void set badCertificateCallback(
- bool callback(X509Certificate cert, String host, int port)?);
+ bool Function(X509Certificate cert, String host, int port)? callback);
/// Shuts down the HTTP client.
///
@@ -1923,84 +1687,72 @@
void close({bool force = false});
}
-/**
- * HTTP request for a client connection.
- *
- * To set up a request, set the headers using the headers property
- * provided in this class and write the data to the body of the request.
- * HttpClientRequest is an [IOSink]. Use the methods from IOSink,
- * such as writeCharCode(), to write the body of the HTTP
- * request. When one of the IOSink methods is used for the first
- * time, the request header is sent. Calling any methods that
- * change the header after it is sent throws an exception.
- *
- * When writing string data through the [IOSink] the
- * encoding used is determined from the "charset" parameter of
- * the "Content-Type" header.
- *
- * HttpClientRequest request = ...
- * request.headers.contentType
- * = new ContentType("application", "json", charset: "utf-8");
- * request.write(...); // Strings written will be UTF-8 encoded.
- *
- * If no charset is provided the default of ISO-8859-1 (Latin 1) is
- * be used.
- *
- * HttpClientRequest request = ...
- * request.headers.add(HttpHeaders.contentTypeHeader, "text/plain");
- * request.write(...); // Strings written will be ISO-8859-1 encoded.
- *
- * An exception is thrown if you use an unsupported encoding and the
- * `write()` method being used takes a string parameter.
- */
+/// HTTP request for a client connection.
+///
+/// To set up a request, set the headers using the headers property
+/// provided in this class and write the data to the body of the request.
+/// HttpClientRequest is an [IOSink]. Use the methods from IOSink,
+/// such as writeCharCode(), to write the body of the HTTP
+/// request. When one of the IOSink methods is used for the first
+/// time, the request header is sent. Calling any methods that
+/// change the header after it is sent throws an exception.
+///
+/// When writing string data through the [IOSink] the
+/// encoding used is determined from the "charset" parameter of
+/// the "Content-Type" header.
+///
+/// HttpClientRequest request = ...
+/// request.headers.contentType
+/// = new ContentType("application", "json", charset: "utf-8");
+/// request.write(...); // Strings written will be UTF-8 encoded.
+///
+/// If no charset is provided the default of ISO-8859-1 (Latin 1) is
+/// be used.
+///
+/// HttpClientRequest request = ...
+/// request.headers.add(HttpHeaders.contentTypeHeader, "text/plain");
+/// request.write(...); // Strings written will be ISO-8859-1 encoded.
+///
+/// An exception is thrown if you use an unsupported encoding and the
+/// `write()` method being used takes a string parameter.
abstract class HttpClientRequest implements IOSink {
- /**
- * The requested persistent connection state.
- *
- * The default value is `true`.
- */
+ /// The requested persistent connection state.
+ ///
+ /// The default value is `true`.
bool persistentConnection = true;
- /**
- * Whether to follow redirects automatically.
- *
- * Set this property to `false` if this request should not
- * automatically follow redirects. The default is `true`.
- *
- * Automatic redirect will only happen for "GET" and "HEAD" requests
- * and only for the status codes [HttpStatus.movedPermanently]
- * (301), [HttpStatus.found] (302),
- * [HttpStatus.movedTemporarily] (302, alias for
- * [HttpStatus.found]), [HttpStatus.seeOther] (303),
- * [HttpStatus.temporaryRedirect] (307) and
- * [HttpStatus.permanentRedirect] (308). For
- * [HttpStatus.seeOther] (303) automatic redirect will also happen
- * for "POST" requests with the method changed to "GET" when
- * following the redirect.
- *
- * All headers added to the request will be added to the redirection
- * request(s). However, any body send with the request will not be
- * part of the redirection request(s).
- */
+ /// Whether to follow redirects automatically.
+ ///
+ /// Set this property to `false` if this request should not
+ /// automatically follow redirects. The default is `true`.
+ ///
+ /// Automatic redirect will only happen for "GET" and "HEAD" requests
+ /// and only for the status codes [HttpStatus.movedPermanently]
+ /// (301), [HttpStatus.found] (302),
+ /// [HttpStatus.movedTemporarily] (302, alias for
+ /// [HttpStatus.found]), [HttpStatus.seeOther] (303),
+ /// [HttpStatus.temporaryRedirect] (307) and
+ /// [HttpStatus.permanentRedirect] (308). For
+ /// [HttpStatus.seeOther] (303) automatic redirect will also happen
+ /// for "POST" requests with the method changed to "GET" when
+ /// following the redirect.
+ ///
+ /// All headers added to the request will be added to the redirection
+ /// request(s). However, any body send with the request will not be
+ /// part of the redirection request(s).
bool followRedirects = true;
- /**
- * Set this property to the maximum number of redirects to follow
- * when [followRedirects] is `true`. If this number is exceeded
- * an error event will be added with a [RedirectException].
- *
- * The default value is 5.
- */
+ /// Set this property to the maximum number of redirects to follow
+ /// when [followRedirects] is `true`. If this number is exceeded
+ /// an error event will be added with a [RedirectException].
+ ///
+ /// The default value is 5.
int maxRedirects = 5;
- /**
- * The method of the request.
- */
+ /// The method of the request.
String get method;
- /**
- * The uri of the request.
- */
+ /// The uri of the request.
Uri get uri;
/// Gets and sets the content length of the request.
@@ -2009,28 +1761,22 @@
/// -1, which is also the default.
int contentLength = -1;
- /**
- * Gets or sets if the [HttpClientRequest] should buffer output.
- *
- * Default value is `true`.
- *
- * __Note__: Disabling buffering of the output can result in very poor
- * performance, when writing many small chunks.
- */
+ /// Gets or sets if the [HttpClientRequest] should buffer output.
+ ///
+ /// Default value is `true`.
+ ///
+ /// __Note__: Disabling buffering of the output can result in very poor
+ /// performance, when writing many small chunks.
bool bufferOutput = true;
- /**
- * Returns the client request headers.
- *
- * The client request headers can be modified until the client
- * request body is written to or closed. After that they become
- * immutable.
- */
+ /// Returns the client request headers.
+ ///
+ /// The client request headers can be modified until the client
+ /// request body is written to or closed. After that they become
+ /// immutable.
HttpHeaders get headers;
- /**
- * Cookies to present to the server (in the 'cookie' header).
- */
+ /// Cookies to present to the server (in the 'cookie' header).
List<Cookie> get cookies;
/// A [HttpClientResponse] future that will complete once the response is
@@ -2040,9 +1786,7 @@
/// complete with an error.
Future<HttpClientResponse> get done;
- /**
- * Close the request for input. Returns the value of [done].
- */
+ /// Close the request for input. Returns the value of [done].
Future<HttpClientResponse> close();
/// Gets information about the client connection.
@@ -2079,48 +1823,40 @@
void abort([Object? exception, StackTrace? stackTrace]);
}
-/**
- * HTTP response for a client connection.
- *
- * The body of a [HttpClientResponse] object is a
- * [Stream] of data from the server. Listen to the body to handle
- * the data and be notified when the entire body is received.
- *
- * new HttpClient().get('localhost', 80, '/file.txt')
- * .then((HttpClientRequest request) => request.close())
- * .then((HttpClientResponse response) {
- * response.transform(utf8.decoder).listen((contents) {
- * // handle data
- * });
- * });
- */
+/// HTTP response for a client connection.
+///
+/// The body of a [HttpClientResponse] object is a
+/// [Stream] of data from the server. Listen to the body to handle
+/// the data and be notified when the entire body is received.
+///
+/// new HttpClient().get('localhost', 80, '/file.txt')
+/// .then((HttpClientRequest request) => request.close())
+/// .then((HttpClientResponse response) {
+/// response.transform(utf8.decoder).listen((contents) {
+/// // handle data
+/// });
+/// });
abstract class HttpClientResponse implements Stream<List<int>> {
- /**
- * Returns the status code.
- *
- * The status code must be set before the body is written
- * to. Setting the status code after writing to the body will throw
- * a `StateError`.
- */
+ /// Returns the status code.
+ ///
+ /// The status code must be set before the body is written
+ /// to. Setting the status code after writing to the body will throw
+ /// a `StateError`.
int get statusCode;
- /**
- * Returns the reason phrase associated with the status code.
- *
- * The reason phrase must be set before the body is written
- * to. Setting the reason phrase after writing to the body will throw
- * a `StateError`.
- */
+ /// Returns the reason phrase associated with the status code.
+ ///
+ /// The reason phrase must be set before the body is written
+ /// to. Setting the reason phrase after writing to the body will throw
+ /// a `StateError`.
String get reasonPhrase;
- /**
- * Returns the content length of the response body. Returns -1 if the size of
- * the response body is not known in advance.
- *
- * If the content length needs to be set, it must be set before the
- * body is written to. Setting the content length after writing to the body
- * will throw a `StateError`.
- */
+ /// Returns the content length of the response body. Returns -1 if the size of
+ /// the response body is not known in advance.
+ ///
+ /// If the content length needs to be set, it must be set before the
+ /// body is written to. Setting the content length after writing to the body
+ /// will throw a `StateError`.
int get contentLength;
/// The compression state of the response.
@@ -2131,82 +1867,64 @@
@Since("2.4")
HttpClientResponseCompressionState get compressionState;
- /**
- * Gets the persistent connection state returned by the server.
- *
- * If the persistent connection state needs to be set, it must be
- * set before the body is written to. Setting the persistent connection state
- * after writing to the body will throw a `StateError`.
- */
+ /// Gets the persistent connection state returned by the server.
+ ///
+ /// If the persistent connection state needs to be set, it must be
+ /// set before the body is written to. Setting the persistent connection state
+ /// after writing to the body will throw a `StateError`.
bool get persistentConnection;
- /**
- * Returns whether the status code is one of the normal redirect
- * codes [HttpStatus.movedPermanently], [HttpStatus.found],
- * [HttpStatus.movedTemporarily], [HttpStatus.seeOther] and
- * [HttpStatus.temporaryRedirect].
- */
+ /// Returns whether the status code is one of the normal redirect
+ /// codes [HttpStatus.movedPermanently], [HttpStatus.found],
+ /// [HttpStatus.movedTemporarily], [HttpStatus.seeOther] and
+ /// [HttpStatus.temporaryRedirect].
bool get isRedirect;
- /**
- * Returns the series of redirects this connection has been through. The
- * list will be empty if no redirects were followed. [redirects] will be
- * updated both in the case of an automatic and a manual redirect.
- */
+ /// Returns the series of redirects this connection has been through. The
+ /// list will be empty if no redirects were followed. [redirects] will be
+ /// updated both in the case of an automatic and a manual redirect.
List<RedirectInfo> get redirects;
- /**
- * Redirects this connection to a new URL. The default value for
- * [method] is the method for the current request. The default value
- * for [url] is the value of the [HttpHeaders.locationHeader] header of
- * the current response. All body data must have been read from the
- * current response before calling [redirect].
- *
- * All headers added to the request will be added to the redirection
- * request. However, any body sent with the request will not be
- * part of the redirection request.
- *
- * If [followLoops] is set to `true`, redirect will follow the redirect,
- * even if the URL was already visited. The default value is `false`.
- *
- * The method will ignore [HttpClientRequest.maxRedirects]
- * and will always perform the redirect.
- */
+ /// Redirects this connection to a new URL. The default value for
+ /// [method] is the method for the current request. The default value
+ /// for [url] is the value of the [HttpHeaders.locationHeader] header of
+ /// the current response. All body data must have been read from the
+ /// current response before calling [redirect].
+ ///
+ /// All headers added to the request will be added to the redirection
+ /// request. However, any body sent with the request will not be
+ /// part of the redirection request.
+ ///
+ /// If [followLoops] is set to `true`, redirect will follow the redirect,
+ /// even if the URL was already visited. The default value is `false`.
+ ///
+ /// The method will ignore [HttpClientRequest.maxRedirects]
+ /// and will always perform the redirect.
Future<HttpClientResponse> redirect(
[String? method, Uri? url, bool? followLoops]);
- /**
- * Returns the client response headers.
- *
- * The client response headers are immutable.
- */
+ /// Returns the client response headers.
+ ///
+ /// The client response headers are immutable.
HttpHeaders get headers;
- /**
- * Detach the underlying socket from the HTTP client. When the
- * socket is detached the HTTP client will no longer perform any
- * operations on it.
- *
- * This is normally used when a HTTP upgrade is negotiated and the
- * communication should continue with a different protocol.
- */
+ /// Detach the underlying socket from the HTTP client. When the
+ /// socket is detached the HTTP client will no longer perform any
+ /// operations on it.
+ ///
+ /// This is normally used when a HTTP upgrade is negotiated and the
+ /// communication should continue with a different protocol.
Future<Socket> detachSocket();
- /**
- * Cookies set by the server (from the 'set-cookie' header).
- */
+ /// Cookies set by the server (from the 'set-cookie' header).
List<Cookie> get cookies;
- /**
- * Returns the certificate of the HTTPS server providing the response.
- * Returns null if the connection is not a secure TLS or SSL connection.
- */
+ /// Returns the certificate of the HTTPS server providing the response.
+ /// Returns null if the connection is not a secure TLS or SSL connection.
X509Certificate? get certificate;
- /**
- * Gets information about the client connection. Returns `null` if the socket
- * is not available.
- */
+ /// Gets information about the client connection. Returns `null` if the socket
+ /// is not available.
HttpConnectionInfo? get connectionInfo;
}
@@ -2255,62 +1973,46 @@
abstract class HttpClientCredentials {}
-/**
- * Represents credentials for basic authentication.
- */
+/// Represents credentials for basic authentication.
abstract class HttpClientBasicCredentials extends HttpClientCredentials {
factory HttpClientBasicCredentials(String username, String password) =>
- new _HttpClientBasicCredentials(username, password);
+ _HttpClientBasicCredentials(username, password);
}
-/**
- * Represents credentials for digest authentication. Digest
- * authentication is only supported for servers using the MD5
- * algorithm and quality of protection (qop) of either "none" or
- * "auth".
- */
+/// Represents credentials for digest authentication. Digest
+/// authentication is only supported for servers using the MD5
+/// algorithm and quality of protection (qop) of either "none" or
+/// "auth".
abstract class HttpClientDigestCredentials extends HttpClientCredentials {
factory HttpClientDigestCredentials(String username, String password) =>
- new _HttpClientDigestCredentials(username, password);
+ _HttpClientDigestCredentials(username, password);
}
-/**
- * Information about an [HttpRequest], [HttpResponse], [HttpClientRequest], or
- * [HttpClientResponse] connection.
- */
+/// Information about an [HttpRequest], [HttpResponse], [HttpClientRequest], or
+/// [HttpClientResponse] connection.
abstract class HttpConnectionInfo {
InternetAddress get remoteAddress;
int get remotePort;
int get localPort;
}
-/**
- * Redirect information.
- */
+/// Redirect information.
abstract class RedirectInfo {
- /**
- * Returns the status code used for the redirect.
- */
+ /// Returns the status code used for the redirect.
int get statusCode;
- /**
- * Returns the method used for the redirect.
- */
+ /// Returns the method used for the redirect.
String get method;
- /**
- * Returns the location for the redirect.
- */
+ /// Returns the location for the redirect.
Uri get location;
}
-/**
- * When detaching a socket from either the [:HttpServer:] or the
- * [:HttpClient:] due to a HTTP connection upgrade there might be
- * unparsed data already read from the socket. This unparsed data
- * together with the detached socket is returned in an instance of
- * this class.
- */
+/// When detaching a socket from either the [:HttpServer:] or the
+/// [:HttpClient:] due to a HTTP connection upgrade there might be
+/// unparsed data already read from the socket. This unparsed data
+/// together with the detached socket is returned in an instance of
+/// this class.
abstract class DetachedSocket {
Socket get socket;
List<int> get unparsedData;
@@ -2323,7 +2025,7 @@
const HttpException(this.message, {this.uri});
String toString() {
- var b = new StringBuffer()
+ var b = StringBuffer()
..write('HttpException: ')
..write(message);
var uri = this.uri;
diff --git a/sdk/lib/_http/http_date.dart b/sdk/lib/_http/http_date.dart
index 5fd2f34..df8d643 100644
--- a/sdk/lib/_http/http_date.dart
+++ b/sdk/lib/_http/http_date.dart
@@ -4,10 +4,8 @@
part of dart._http;
-/**
- * Utility functions for working with dates with HTTP specific date
- * formats.
- */
+/// Utility functions for working with dates with HTTP specific date
+/// formats.
class HttpDate {
// From RFC-2616 section "3.3.1 Full Date",
// http://tools.ietf.org/html/rfc2616#section-3.3.1
@@ -32,14 +30,12 @@
// | "May" | "Jun" | "Jul" | "Aug"
// | "Sep" | "Oct" | "Nov" | "Dec"
- /**
- * Format a date according to
- * [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
- * e.g. `Thu, 1 Jan 1970 00:00:00 GMT`.
- */
+ /// Format a date according to
+ /// [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
+ /// e.g. `Thu, 1 Jan 1970 00:00:00 GMT`.
static String format(DateTime date) {
- const List wkday = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
- const List month = const [
+ const List wkday = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
+ const List month = [
"Jan",
"Feb",
"Mar",
@@ -55,7 +51,7 @@
];
DateTime d = date.toUtc();
- StringBuffer sb = new StringBuffer()
+ StringBuffer sb = StringBuffer()
..write(wkday[d.weekday - 1])
..write(", ")
..write(d.day <= 9 ? "0" : "")
@@ -74,24 +70,22 @@
return sb.toString();
}
- /**
- * Parse a date string in either of the formats
- * [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
- * [RFC-850](http://tools.ietf.org/html/rfc850 "RFC-850") or
- * ANSI C's asctime() format. These formats are listed here.
- *
- * Thu, 1 Jan 1970 00:00:00 GMT
- * Thursday, 1-Jan-1970 00:00:00 GMT
- * Thu Jan 1 00:00:00 1970
- *
- * For more information see [RFC-2616 section
- * 3.1.1](http://tools.ietf.org/html/rfc2616#section-3.3.1
- * "RFC-2616 section 3.1.1").
- */
+ /// Parse a date string in either of the formats
+ /// [RFC-1123](http://tools.ietf.org/html/rfc1123 "RFC-1123"),
+ /// [RFC-850](http://tools.ietf.org/html/rfc850 "RFC-850") or
+ /// ANSI C's asctime() format. These formats are listed here.
+ ///
+ /// Thu, 1 Jan 1970 00:00:00 GMT
+ /// Thursday, 1-Jan-1970 00:00:00 GMT
+ /// Thu Jan 1 00:00:00 1970
+ ///
+ /// For more information see [RFC-2616 section
+ /// 3.1.1](http://tools.ietf.org/html/rfc2616#section-3.3.1
+ /// "RFC-2616 section 3.1.1").
static DateTime parse(String date) {
final int SP = 32;
- const List wkdays = const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
- const List weekdays = const [
+ const List wkdays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
+ const List weekdays = [
"Monday",
"Tuesday",
"Wednesday",
@@ -100,7 +94,7 @@
"Saturday",
"Sunday"
];
- const List months = const [
+ const List months = [
"Jan",
"Feb",
"Mar",
@@ -124,11 +118,11 @@
void expect(String s) {
if (date.length - index < s.length) {
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
String tmp = date.substring(index, index + s.length);
if (tmp != s) {
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
index += s.length;
}
@@ -139,7 +133,7 @@
int pos = date.indexOf(",", index);
if (pos == -1) {
int pos = date.indexOf(" ", index);
- if (pos == -1) throw new HttpException("Invalid HTTP date $date");
+ if (pos == -1) throw HttpException("Invalid HTTP date $date");
tmp = date.substring(index, pos);
index = pos + 1;
weekday = wkdays.indexOf(tmp);
@@ -158,22 +152,22 @@
return formatRfc850;
}
}
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
int expectMonth(String separator) {
int pos = date.indexOf(separator, index);
- if (pos - index != 3) throw new HttpException("Invalid HTTP date $date");
+ if (pos - index != 3) throw HttpException("Invalid HTTP date $date");
tmp = date.substring(index, pos);
index = pos + 1;
int month = months.indexOf(tmp);
if (month != -1) return month;
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
int expectNum(String separator) {
int pos;
- if (separator.length > 0) {
+ if (separator.isNotEmpty) {
pos = date.indexOf(separator, index);
} else {
pos = date.length;
@@ -184,13 +178,13 @@
int value = int.parse(tmp);
return value;
} on FormatException {
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
}
void expectEnd() {
if (index != date.length) {
- throw new HttpException("Invalid HTTP date $date");
+ throw HttpException("Invalid HTTP date $date");
}
}
@@ -220,12 +214,12 @@
expect("GMT");
}
expectEnd();
- return new DateTime.utc(year, month + 1, day, hours, minutes, seconds, 0);
+ return DateTime.utc(year, month + 1, day, hours, minutes, seconds, 0);
}
// Parse a cookie date string.
static DateTime _parseCookieDate(String date) {
- const List monthsLowerCase = const [
+ const List monthsLowerCase = [
"jan",
"feb",
"mar",
@@ -243,7 +237,7 @@
int position = 0;
Never error() {
- throw new HttpException("Invalid cookie date $date");
+ throw HttpException("Invalid cookie date $date");
}
bool isEnd() => position == date.length;
@@ -287,7 +281,7 @@
return int.parse(s.substring(0, index));
}
- var tokens = [];
+ var tokens = <String>[];
while (!isEnd()) {
while (!isEnd() && isDelimiter(date[position])) position++;
int start = position;
@@ -302,7 +296,7 @@
String? yearStr;
for (var token in tokens) {
- if (token.length < 1) continue;
+ if (token.isEmpty) continue;
if (timeStr == null &&
token.length >= 5 &&
isDigit(token[0]) &&
@@ -347,6 +341,6 @@
if (minute > 59) error();
if (second > 59) error();
- return new DateTime.utc(year, month, dayOfMonth, hour, minute, second, 0);
+ return DateTime.utc(year, month, dayOfMonth, hour, minute, second, 0);
}
}
diff --git a/sdk/lib/_http/http_headers.dart b/sdk/lib/_http/http_headers.dart
index 09e5748..2ccf23a 100644
--- a/sdk/lib/_http/http_headers.dart
+++ b/sdk/lib/_http/http_headers.dart
@@ -24,7 +24,7 @@
_HttpHeaders(this.protocolVersion,
{int defaultPortForScheme = HttpClient.defaultHttpPort,
_HttpHeaders? initialHeaders})
- : _headers = new HashMap<String, List<String>>(),
+ : _headers = HashMap<String, List<String>>(),
_defaultPortForScheme = defaultPortForScheme {
if (initialHeaders != null) {
initialHeaders._headers.forEach((name, value) => _headers[name] = value);
@@ -48,7 +48,7 @@
if (values == null) return null;
assert(values.isNotEmpty);
if (values.length > 1) {
- throw new HttpException("More than one value for header $name");
+ throw HttpException("More than one value for header $name");
}
return values[0];
}
@@ -99,7 +99,7 @@
List<String>? values = _headers[name];
if (values != null) {
values.remove(_valueToString(value));
- if (values.length == 0) {
+ if (values.isEmpty) {
_headers.remove(name);
_originalHeaderNames?.remove(name);
}
@@ -116,7 +116,7 @@
_originalHeaderNames?.remove(name);
}
- void forEach(void action(String name, List<String> values)) {
+ void forEach(void Function(String name, List<String> values) action) {
_headers.forEach((String name, List<String> values) {
String originalName = _originalHeaderName(name);
action(originalName, values);
@@ -139,7 +139,7 @@
remove(HttpHeaders.connectionHeader, "close");
} else {
if (_contentLength < 0) {
- throw new HttpException(
+ throw HttpException(
"Trying to set 'Connection: Keep-Alive' on HTTP 1.0 headers with "
"no ContentLength");
}
@@ -162,7 +162,7 @@
if (protocolVersion == "1.0" &&
persistentConnection &&
contentLength == -1) {
- throw new HttpException(
+ throw HttpException(
"Trying to clear ContentLength on HTTP 1.0 headers with "
"'Connection: Keep-Alive' set");
}
@@ -184,7 +184,7 @@
void set chunkedTransferEncoding(bool chunkedTransferEncoding) {
_checkMutable();
if (chunkedTransferEncoding && protocolVersion == "1.0") {
- throw new HttpException(
+ throw HttpException(
"Trying to set 'Transfer-Encoding: Chunked' on HTTP 1.0 headers");
}
if (chunkedTransferEncoding == _chunkedTransferEncoding) return;
@@ -377,7 +377,7 @@
} else if (value is String) {
contentLength = int.parse(value);
} else {
- throw new HttpException("Unexpected type for header named $name");
+ throw HttpException("Unexpected type for header named $name");
}
}
@@ -395,7 +395,7 @@
} else if (value is String) {
_set(HttpHeaders.dateHeader, value);
} else {
- throw new HttpException("Unexpected type for header named $name");
+ throw HttpException("Unexpected type for header named $name");
}
}
@@ -405,7 +405,7 @@
} else if (value is String) {
_set(HttpHeaders.expiresHeader, value);
} else {
- throw new HttpException("Unexpected type for header named $name");
+ throw HttpException("Unexpected type for header named $name");
}
}
@@ -415,7 +415,7 @@
} else if (value is String) {
_set(HttpHeaders.ifModifiedSinceHeader, value);
} else {
- throw new HttpException("Unexpected type for header named $name");
+ throw HttpException("Unexpected type for header named $name");
}
}
@@ -448,11 +448,11 @@
}
_set(HttpHeaders.hostHeader, value);
} else {
- throw new HttpException("Unexpected type for header named $name");
+ throw HttpException("Unexpected type for header named $name");
}
}
- void _addConnection(String name, value) {
+ void _addConnection(String name, String value) {
var lowerCaseValue = value.toLowerCase();
if (lowerCaseValue == 'close') {
_persistentConnection = false;
@@ -487,7 +487,7 @@
}
void _checkMutable() {
- if (!_mutable) throw new HttpException("HTTP headers are not mutable");
+ if (!_mutable) throw HttpException("HTTP headers are not mutable");
}
void _updateHostHeader() {
@@ -548,17 +548,22 @@
}
String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuffer sb = StringBuffer();
_headers.forEach((String name, List<String> values) {
String originalName = _originalHeaderName(name);
- sb..write(originalName)..write(": ");
+ sb
+ ..write(originalName)
+ ..write(": ");
bool fold = _foldHeader(name);
for (int i = 0; i < values.length; i++) {
if (i > 0) {
if (fold) {
sb.write(", ");
} else {
- sb..write("\n")..write(originalName)..write(": ");
+ sb
+ ..write("\n")
+ ..write(originalName)
+ ..write(": ");
}
}
sb.write(values[i]);
@@ -620,7 +625,7 @@
skipWS();
String value = parseValue();
try {
- cookies.add(new _Cookie(name, value));
+ cookies.add(_Cookie(name, value));
} catch (_) {
// Skip it, invalid cookie data.
}
@@ -635,7 +640,9 @@
List<String>? values = _headers[HttpHeaders.cookieHeader];
if (values != null) {
- values.forEach((headerValue) => parseCookieString(headerValue));
+ for (var headerValue in values) {
+ parseCookieString(headerValue);
+ }
}
return cookies;
}
@@ -643,7 +650,7 @@
static String _validateField(String field) {
for (var i = 0; i < field.length; i++) {
if (!_HttpParser._isTokenChar(field.codeUnitAt(i))) {
- throw new FormatException(
+ throw FormatException(
"Invalid HTTP header field name: ${json.encode(field)}", field, i);
}
}
@@ -652,9 +659,9 @@
static Object _validateValue(Object value) {
if (value is! String) return value;
- for (var i = 0; i < (value as String).length; i++) {
- if (!_HttpParser._isValueChar((value as String).codeUnitAt(i))) {
- throw new FormatException(
+ for (var i = 0; i < (value).length; i++) {
+ if (!_HttpParser._isValueChar((value).codeUnitAt(i))) {
+ throw FormatException(
"Invalid HTTP header field value: ${json.encode(value)}", value, i);
}
}
@@ -675,7 +682,7 @@
// TODO(40614): Remove once non-nullability is sound.
Map<String, String?>? nullableParameters = parameters;
if (nullableParameters != null && nullableParameters.isNotEmpty) {
- _parameters = new HashMap<String, String?>.from(nullableParameters);
+ _parameters = HashMap<String, String?>.from(nullableParameters);
}
}
@@ -684,7 +691,7 @@
String? valueSeparator,
bool preserveBackslash = false}) {
// Parse the string.
- var result = new _HeaderValue();
+ var result = _HeaderValue();
result._parse(value, parameterSeparator, valueSeparator, preserveBackslash);
return result;
}
@@ -701,12 +708,10 @@
if (token.isEmpty) {
return false;
}
- final delimiters = "\"(),/:;<=>?@[\]{}";
+ final delimiters = "\"(),/:;<=>?@[]{}";
for (int i = 0; i < token.length; i++) {
int codeUnit = token.codeUnitAt(i);
- if (codeUnit <= 32 ||
- codeUnit >= 127 ||
- delimiters.indexOf(token[i]) >= 0) {
+ if (codeUnit <= 32 || codeUnit >= 127 || delimiters.contains(token[i])) {
return false;
}
}
@@ -714,12 +719,14 @@
}
String toString() {
- StringBuffer sb = new StringBuffer();
+ StringBuffer sb = StringBuffer();
sb.write(_value);
- var parameters = this._parameters;
- if (parameters != null && parameters.length > 0) {
+ var parameters = _parameters;
+ if (parameters != null && parameters.isNotEmpty) {
parameters.forEach((String name, String? value) {
- sb..write("; ")..write(name);
+ sb
+ ..write("; ")
+ ..write(name);
if (value != null) {
sb.write("=");
if (_isToken(value)) {
@@ -737,7 +744,9 @@
start = i;
}
}
- sb..write(value.substring(start))..write('"');
+ sb
+ ..write(value.substring(start))
+ ..write('"');
}
}
});
@@ -773,7 +782,7 @@
void expect(String expected) {
if (done() || s[index] != expected) {
- throw new HttpException("Failed to parse header value");
+ throw HttpException("Failed to parse header value");
}
index++;
}
@@ -806,13 +815,13 @@
String parseParameterValue() {
if (!done() && s[index] == "\"") {
// Parse quoted value.
- StringBuffer sb = new StringBuffer();
+ StringBuffer sb = StringBuffer();
index++;
while (!done()) {
var char = s[index];
if (char == "\\") {
if (index + 1 == s.length) {
- throw new HttpException("Failed to parse header value");
+ throw HttpException("Failed to parse header value");
}
if (preserveBackslash && s[index + 1] != "\"") {
sb.write(char);
@@ -826,7 +835,7 @@
sb.write(char);
index++;
}
- throw new HttpException("Failed to parse header value");
+ throw HttpException("Failed to parse header value");
} else {
// Parse non-quoted value.
return parseValue();
@@ -901,7 +910,7 @@
_ContentType._();
static _ContentType parse(String value) {
- var result = new _ContentType._();
+ var result = _ContentType._();
result._parse(value, ";", null, false);
int index = result._value.indexOf("/");
if (index == -1 || index == (result._value.length - 1)) {
@@ -1033,8 +1042,8 @@
}
_name = _validateName(parseName());
- if (done() || _name.length == 0) {
- throw new HttpException("Failed to parse header value [$s]");
+ if (done() || _name.isEmpty) {
+ throw HttpException("Failed to parse header value [$s]");
}
index++; // Skip the = character.
_value = _validateValue(parseValue());
@@ -1044,20 +1053,31 @@
}
String toString() {
- StringBuffer sb = new StringBuffer();
- sb..write(_name)..write("=")..write(_value);
+ StringBuffer sb = StringBuffer();
+ sb
+ ..write(_name)
+ ..write("=")
+ ..write(_value);
var expires = this.expires;
if (expires != null) {
- sb..write("; Expires=")..write(HttpDate.format(expires));
+ sb
+ ..write("; Expires=")
+ ..write(HttpDate.format(expires));
}
if (maxAge != null) {
- sb..write("; Max-Age=")..write(maxAge);
+ sb
+ ..write("; Max-Age=")
+ ..write(maxAge);
}
if (domain != null) {
- sb..write("; Domain=")..write(domain);
+ sb
+ ..write("; Domain=")
+ ..write(domain);
}
if (path != null) {
- sb..write("; Path=")..write(path);
+ sb
+ ..write("; Path=")
+ ..write(path);
}
if (secure) sb.write("; Secure");
if (httpOnly) sb.write("; HttpOnly");
@@ -1065,7 +1085,7 @@
}
static String _validateName(String newName) {
- const separators = const [
+ const separators = [
"(",
")",
"<",
@@ -1084,13 +1104,13 @@
"{",
"}"
];
- if (newName == null) throw new ArgumentError.notNull("name");
+ if (newName == null) throw ArgumentError.notNull("name");
for (int i = 0; i < newName.length; i++) {
int codeUnit = newName.codeUnitAt(i);
if (codeUnit <= 32 ||
codeUnit >= 127 ||
- separators.indexOf(newName[i]) >= 0) {
- throw new FormatException(
+ separators.contains(newName[i])) {
+ throw FormatException(
"Invalid character in cookie name, code unit: '$codeUnit'",
newName,
i);
@@ -1100,7 +1120,7 @@
}
static String _validateValue(String newValue) {
- if (newValue == null) throw new ArgumentError.notNull("value");
+ if (newValue == null) throw ArgumentError.notNull("value");
// Per RFC 6265, consider surrounding "" as part of the value, but otherwise
// double quotes are not allowed.
int start = 0;
@@ -1119,7 +1139,7 @@
(codeUnit >= 0x2D && codeUnit <= 0x3A) ||
(codeUnit >= 0x3C && codeUnit <= 0x5B) ||
(codeUnit >= 0x5D && codeUnit <= 0x7E))) {
- throw new FormatException(
+ throw FormatException(
"Invalid character in cookie value, code unit: '$codeUnit'",
newValue,
i);
diff --git a/sdk/lib/_http/http_impl.dart b/sdk/lib/_http/http_impl.dart
index 6845f45..9ff1eef 100644
--- a/sdk/lib/_http/http_impl.dart
+++ b/sdk/lib/_http/http_impl.dart
@@ -7,7 +7,7 @@
abstract class HttpProfiler {
static const _kType = 'HttpProfile';
- static Map<int, _HttpProfileData> _profile = {};
+ static final Map<int, _HttpProfileData> _profile = {};
static _HttpProfileData startRequest(
String method,
@@ -96,21 +96,22 @@
_updated();
}
- Map formatHeaders(r) {
- final headers = <String, List<String>>{};
- r.headers.forEach((name, values) {
- headers[name] = values;
+ Map formatHeaders(HttpHeaders headers) {
+ final newHeaders = <String, List<String>>{};
+ headers.forEach((name, values) {
+ newHeaders[name] = values;
});
- return headers;
+ return newHeaders;
}
- Map? formatConnectionInfo(r) => r.connectionInfo == null
- ? null
- : {
- 'localPort': r.connectionInfo?.localPort,
- 'remoteAddress': r.connectionInfo?.remoteAddress.address,
- 'remotePort': r.connectionInfo?.remotePort,
- };
+ Map? formatConnectionInfo(HttpConnectionInfo? connectionInfo) =>
+ connectionInfo == null
+ ? null
+ : {
+ 'localPort': connectionInfo.localPort,
+ 'remoteAddress': connectionInfo.remoteAddress.address,
+ 'remotePort': connectionInfo.remotePort,
+ };
void finishRequest({
required HttpClientRequest request,
@@ -121,8 +122,8 @@
requestDetails = <String, dynamic>{
// TODO(bkonyi): consider exposing certificate information?
// 'certificate': response.certificate,
- 'headers': formatHeaders(request),
- 'connectionInfo': formatConnectionInfo(request),
+ 'headers': formatHeaders(request.headers),
+ 'connectionInfo': formatConnectionInfo(request.connectionInfo),
'contentLength': request.contentLength,
'cookies': [
for (final cookie in request.cookies) cookie.toString(),
@@ -153,9 +154,9 @@
}
responseDetails = <String, dynamic>{
- 'headers': formatHeaders(response),
+ 'headers': formatHeaders(response.headers),
'compressionState': response.compressionState.toString(),
- 'connectionInfo': formatConnectionInfo(response),
+ 'connectionInfo': formatConnectionInfo(response.connectionInfo),
'contentLength': response.contentLength,
'cookies': [
for (final cookie in response.cookies) cookie.toString(),
@@ -281,7 +282,7 @@
int get lastUpdateTime => _lastUpdateTime;
int _lastUpdateTime = 0;
- TimelineTask _timeline;
+ final TimelineTask _timeline;
late TimelineTask _responseTimeline;
}
@@ -313,7 +314,7 @@
// Start with 1024 bytes.
static const int _INIT_SIZE = 1024;
- static final _emptyList = new Uint8List(0);
+ static final _emptyList = Uint8List(0);
int _length = 0;
Uint8List _buffer;
@@ -321,7 +322,7 @@
_CopyingBytesBuilder([int initialCapacity = 0])
: _buffer = (initialCapacity <= 0)
? _emptyList
- : new Uint8List(_pow2roundup(initialCapacity));
+ : Uint8List(_pow2roundup(initialCapacity));
void add(List<int> bytes) {
int bytesLength = bytes.length;
@@ -361,23 +362,22 @@
} else {
newSize = _pow2roundup(newSize);
}
- var newBuffer = new Uint8List(newSize);
+ var newBuffer = Uint8List(newSize);
newBuffer.setRange(0, _buffer.length, _buffer);
_buffer = newBuffer;
}
Uint8List takeBytes() {
if (_length == 0) return _emptyList;
- var buffer =
- new Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length);
+ var buffer = Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length);
clear();
return buffer;
}
Uint8List toBytes() {
if (_length == 0) return _emptyList;
- return new Uint8List.fromList(
- new Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length));
+ return Uint8List.fromList(
+ Uint8List.view(_buffer.buffer, _buffer.offsetInBytes, _length));
}
int get length => _length;
@@ -405,12 +405,12 @@
const int _OUTGOING_BUFFER_SIZE = 8 * 1024;
-typedef void _BytesConsumer(List<int> bytes);
+typedef _BytesConsumer = void Function(List<int> bytes);
class _HttpIncoming extends Stream<Uint8List> {
final int _transferLength;
- final Completer _dataCompleter = new Completer();
- Stream<Uint8List> _stream;
+ final _dataCompleter = Completer<bool>();
+ final Stream<Uint8List> _stream;
bool fullBodyRead = false;
@@ -436,17 +436,17 @@
_HttpIncoming(this.headers, this._transferLength, this._stream);
- StreamSubscription<Uint8List> listen(void onData(Uint8List event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<Uint8List> listen(void Function(Uint8List event)? onData,
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
hasSubscriber = true;
return _stream.handleError((error) {
- throw new HttpException(error.message, uri: uri);
+ throw HttpException(error.message, uri: uri);
}).listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
// Is completed once all data have been received.
- Future get dataDone => _dataCompleter.future;
+ Future<bool> get dataDone => _dataCompleter.future;
void close(bool closing) {
fullBodyRead = true;
@@ -519,8 +519,8 @@
}
}
- StreamSubscription<Uint8List> listen(void onData(Uint8List event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<Uint8List> listen(void Function(Uint8List event)? onData,
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
return _incoming.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@@ -621,7 +621,7 @@
List<String>? values = headers[HttpHeaders.setCookieHeader];
if (values != null) {
for (var value in values) {
- cookies.add(new Cookie.fromSetCookieValue(value));
+ cookies.add(Cookie.fromSetCookieValue(value));
}
}
_cookies = cookies;
@@ -654,15 +654,15 @@
if (url == null) {
String? location = headers.value(HttpHeaders.locationHeader);
if (location == null) {
- throw new StateError("Response has no Location header for redirect");
+ throw StateError("Response has no Location header for redirect");
}
url = Uri.parse(location);
}
if (followLoops != true) {
for (var redirect in redirects) {
if (redirect.location == url) {
- return new Future.error(
- new RedirectException("Redirect loop detected", redirects));
+ return Future.error(
+ RedirectException("Redirect loop detected", redirects));
}
}
}
@@ -670,21 +670,21 @@
._openUrlFromRequest(method, url, _httpRequest)
.then((request) {
request._responseRedirects
- ..addAll(this.redirects)
- ..add(new _RedirectInfo(statusCode, method!, url!));
+ ..addAll(redirects)
+ ..add(_RedirectInfo(statusCode, method!, url!));
return request.close();
});
}
- StreamSubscription<Uint8List> listen(void onData(Uint8List event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<Uint8List> listen(void Function(Uint8List event)? onData,
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
if (_incoming.upgraded) {
_profileData?.finishResponseWithError('Connection was upgraded');
// If upgraded, the connection is already 'removed' form the client.
// Since listening to upgraded data is 'bogus', simply close and
// return empty stream subscription.
_httpRequest._httpClientConnection.destroy();
- return new Stream<Uint8List>.empty().listen(null, onDone: onDone);
+ return Stream<Uint8List>.empty().listen(null, onDone: onDone);
}
Stream<Uint8List> stream = _incoming;
if (compressionState == HttpClientResponseCompressionState.decompressed) {
@@ -781,7 +781,7 @@
if (proxyAuth) {
var authenticateProxy = _httpClient._authenticateProxy;
if (authenticateProxy == null) {
- return new Future.value(false);
+ return Future.value(false);
}
var proxy = _httpRequest._proxy;
if (!proxy.isDirect) {
@@ -791,7 +791,7 @@
}
var authenticate = _httpClient._authenticate;
if (authenticate == null) {
- return new Future.value(false);
+ return Future.value(false);
}
return authenticate(_httpRequest.uri, scheme.toString(), realm);
}
@@ -801,7 +801,7 @@
_HeaderValue header =
_HeaderValue.parse(challenge[0], parameterSeparator: ",");
_AuthenticationScheme scheme =
- new _AuthenticationScheme.fromString(header.value);
+ _AuthenticationScheme.fromString(header.value);
String? realm = header.parameters["realm"];
// See if any matching credentials are available.
@@ -890,7 +890,7 @@
class _StreamSinkImpl<T> implements StreamSink<T> {
final StreamConsumer<T> _target;
- final _doneCompleter = new Completer<void>();
+ final _doneCompleter = Completer<void>();
StreamController<T>? _controllerInstance;
Completer? _controllerCompleter;
bool _isClosed = false;
@@ -915,7 +915,7 @@
Future addStream(Stream<T> stream) {
if (_isBound) {
- throw new StateError("StreamSink is already bound to a stream");
+ throw StateError("StreamSink is already bound to a stream");
}
_isBound = true;
if (_hasError) return done;
@@ -935,10 +935,10 @@
Future flush() {
if (_isBound) {
- throw new StateError("StreamSink is bound to a stream");
+ throw StateError("StreamSink is bound to a stream");
}
var controller = _controllerInstance;
- if (controller == null) return new Future.value(this);
+ if (controller == null) return Future.value(this);
// Adding an empty stream-controller will return a future that will complete
// when all data is done.
_isBound = true;
@@ -951,7 +951,7 @@
Future close() {
if (_isBound) {
- throw new StateError("StreamSink is bound to a stream");
+ throw StateError("StreamSink is bound to a stream");
}
if (!_isClosed) {
_isClosed = true;
@@ -986,14 +986,14 @@
StreamController<T> get _controller {
if (_isBound) {
- throw new StateError("StreamSink is bound to a stream");
+ throw StateError("StreamSink is bound to a stream");
}
if (_isClosed) {
- throw new StateError("StreamSink is closed");
+ throw StateError("StreamSink is closed");
}
if (_controllerInstance == null) {
- _controllerInstance = new StreamController<T>(sync: true);
- _controllerCompleter = new Completer();
+ _controllerInstance = StreamController<T>(sync: true);
+ _controllerCompleter = Completer();
_target.addStream(_controller.stream).then((_) {
if (_isBound) {
// A new stream takes over - forward values to that stream.
@@ -1034,7 +1034,7 @@
void set encoding(Encoding value) {
if (!_encodingMutable) {
- throw new StateError("IOSink encoding is not mutable");
+ throw StateError("IOSink encoding is not mutable");
}
_encoding = value;
}
@@ -1072,7 +1072,7 @@
}
void writeCharCode(int charCode) {
- write(new String.fromCharCode(charCode));
+ write(String.fromCharCode(charCode));
}
}
@@ -1092,7 +1092,7 @@
_HttpProfileData? profileData,
{_HttpHeaders? initialHeaders})
: _uri = uri,
- headers = new _HttpHeaders(protocolVersion,
+ headers = _HttpHeaders(protocolVersion,
defaultPortForScheme: uri.scheme == 'https'
? HttpClient.defaultHttpsPort
: HttpClient.defaultHttpPort,
@@ -1115,7 +1115,7 @@
bool get bufferOutput => _bufferOutput;
void set bufferOutput(bool bufferOutput) {
- if (_outgoing.headersWritten) throw new StateError("Header already sent");
+ if (_outgoing.headersWritten) throw StateError("Header already sent");
_bufferOutput = bufferOutput;
}
@@ -1134,7 +1134,7 @@
}
void add(List<int> data) {
- if (data.length == 0) return;
+ if (data.isEmpty) return;
_profileData?.appendRequestData(Uint8List.fromList(data));
super.add(data);
}
@@ -1186,25 +1186,25 @@
int get statusCode => _statusCode;
void set statusCode(int statusCode) {
- if (_outgoing.headersWritten) throw new StateError("Header already sent");
+ if (_outgoing.headersWritten) throw StateError("Header already sent");
_statusCode = statusCode;
}
String get reasonPhrase => _findReasonPhrase(statusCode);
void set reasonPhrase(String reasonPhrase) {
- if (_outgoing.headersWritten) throw new StateError("Header already sent");
+ if (_outgoing.headersWritten) throw StateError("Header already sent");
_reasonPhrase = reasonPhrase;
}
Future redirect(Uri location, {int status = HttpStatus.movedTemporarily}) {
- if (_outgoing.headersWritten) throw new StateError("Header already sent");
+ if (_outgoing.headersWritten) throw StateError("Header already sent");
statusCode = status;
headers.set(HttpHeaders.locationHeader, location.toString());
return close();
}
Future<Socket> detachSocket({bool writeHeaders = true}) {
- if (_outgoing.headersWritten) throw new StateError("Headers already sent");
+ if (_outgoing.headersWritten) throw StateError("Headers already sent");
deadline = null; // Be sure to stop any deadline.
var future = _httpRequest!._httpConnection.detachSocket();
if (writeHeaders) {
@@ -1233,13 +1233,13 @@
_deadline = d;
if (d == null) return;
- _deadlineTimer = new Timer(d, () {
+ _deadlineTimer = Timer(d, () {
_httpRequest!._httpConnection.destroy();
});
}
void _writeHeader() {
- BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
+ BytesBuilder buffer = _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
// Write status line.
if (headers.protocolVersion == "1.1") {
@@ -1270,7 +1270,7 @@
}
}
if (!found) {
- var cookie = new Cookie(_DART_SESSION_ID, session.id);
+ var cookie = Cookie(_DART_SESSION_ID, session.id);
cookies.add(cookie
..httpOnly = true
..path = "/");
@@ -1393,10 +1393,9 @@
// The HttpClient this request belongs to.
final _HttpClient _httpClient;
final _HttpClientConnection _httpClientConnection;
- final _HttpProfileData? _profileData;
final Completer<HttpClientResponse> _responseCompleter =
- new Completer<HttpClientResponse>();
+ Completer<HttpClientResponse>();
final _Proxy _proxy;
@@ -1407,20 +1406,19 @@
int _maxRedirects = 5;
- List<RedirectInfo> _responseRedirects = [];
+ final List<RedirectInfo> _responseRedirects = [];
bool _aborted = false;
_HttpClientRequest(
_HttpOutgoing outgoing,
- Uri uri,
+ this.uri,
this.method,
this._proxy,
this._httpClient,
this._httpClientConnection,
- this._profileData,
- ) : uri = uri,
- super(uri, "1.1", outgoing, _profileData) {
+ _HttpProfileData? _profileData,
+ ) : super(uri, "1.1", outgoing, _profileData) {
_profileData?.requestEvent('Request sent');
// GET and HEAD have 'content-length: 0' by default.
if (method == "GET" || method == "HEAD") {
@@ -1453,13 +1451,13 @@
int get maxRedirects => _maxRedirects;
void set maxRedirects(int maxRedirects) {
- if (_outgoing.headersWritten) throw new StateError("Request already sent");
+ if (_outgoing.headersWritten) throw StateError("Request already sent");
_maxRedirects = maxRedirects;
}
bool get followRedirects => _followRedirects;
void set followRedirects(bool followRedirects) {
- if (_outgoing.headersWritten) throw new StateError("Request already sent");
+ if (_outgoing.headersWritten) throw StateError("Request already sent");
_followRedirects = followRedirects;
}
@@ -1482,8 +1480,8 @@
} else {
// End with exception, too many redirects.
future = response.drain().then<HttpClientResponse>((_) {
- return new Future<HttpClientResponse>.error(new RedirectException(
- "Redirect limit exceeded", response.redirects));
+ return Future<HttpClientResponse>.error(
+ RedirectException("Redirect limit exceeded", response.redirects));
});
}
} else if (response._shouldAuthenticateProxy) {
@@ -1491,7 +1489,7 @@
} else if (response._shouldAuthenticate) {
future = response._authenticate(false);
} else {
- future = new Future<HttpClientResponse>.value(response);
+ future = Future<HttpClientResponse>.value(response);
}
future.then((v) {
if (!_responseCompleter.isCompleted) {
@@ -1517,7 +1515,7 @@
String result = uri.path;
if (result.isEmpty) result = "/";
if (uri.hasQuery) {
- result = "${result}?${uri.query}";
+ result = "$result?${uri.query}";
}
return result;
}
@@ -1541,7 +1539,7 @@
}
void add(List<int> data) {
- if (data.length == 0 || _aborted) return;
+ if (data.isEmpty || _aborted) return;
super.add(data);
}
@@ -1555,7 +1553,7 @@
_outgoing.setHeader(Uint8List(0), 0);
return;
}
- BytesBuilder buffer = new _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
+ BytesBuilder buffer = _CopyingBytesBuilder(_OUTGOING_BUFFER_SIZE);
// Write the request method.
buffer.add(method.codeUnits);
@@ -1569,11 +1567,14 @@
buffer.addByte(_CharCode.LF);
// Add the cookies to the headers.
- if (!cookies.isEmpty) {
- StringBuffer sb = new StringBuffer();
+ if (cookies.isNotEmpty) {
+ StringBuffer sb = StringBuffer();
for (int i = 0; i < cookies.length; i++) {
if (i > 0) sb.write("; ");
- sb..write(cookies[i].name)..write("=")..write(cookies[i].value);
+ sb
+ ..write(cookies[i].name)
+ ..write("=")
+ ..write(cookies[i].value);
}
headers.add(HttpHeaders.cookieHeader, sb.toString());
}
@@ -1614,7 +1615,7 @@
void addSlice(List<int> chunk, int start, int end, bool isLast) {
if (chunk is Uint8List) {
- _consume(new Uint8List.view(
+ _consume(Uint8List.view(
chunk.buffer, chunk.offsetInBytes + start, end - start));
} else {
_consume(chunk.sublist(start, end - start));
@@ -1633,7 +1634,7 @@
// Most notable is the GZip compression, that uses a double-buffering system,
// one before gzip (_gzipBuffer) and one after (_buffer).
class _HttpOutgoing implements StreamConsumer<List<int>> {
- static const List<int> _footerAndChunk0Length = const [
+ static const List<int> _footerAndChunk0Length = [
_CharCode.CR,
_CharCode.LF,
0x30,
@@ -1643,7 +1644,7 @@
_CharCode.LF
];
- static const List<int> _chunk0Length = const [
+ static const List<int> _chunk0Length = [
0x30,
_CharCode.CR,
_CharCode.LF,
@@ -1651,7 +1652,7 @@
_CharCode.LF
];
- final Completer<Socket> _doneCompleter = new Completer<Socket>();
+ final Completer<Socket> _doneCompleter = Completer<Socket>();
final Socket socket;
bool ignoreBody = false;
@@ -1736,7 +1737,7 @@
Future addStream(Stream<List<int>> stream) {
if (_socketError) {
stream.listen(null).cancel();
- return new Future.value(outbound);
+ return Future.value(outbound);
}
if (ignoreBody) {
stream.drain().catchError((_) {});
@@ -1749,11 +1750,11 @@
// Use new stream so we are able to pause (see below listen). The
// alternative is to use stream.extand, but that won't give us a way of
// pausing.
- var controller = new StreamController<List<int>>(sync: true);
+ var controller = StreamController<List<int>>(sync: true);
void onData(List<int> data) {
if (_socketError) return;
- if (data.length == 0) return;
+ if (data.isEmpty) return;
if (chunked) {
if (_gzip) {
_gzipAdd = controller.add;
@@ -1768,11 +1769,11 @@
if (contentLength != null) {
_bytesWritten += data.length;
if (_bytesWritten > contentLength) {
- controller.addError(new HttpException(
- "Content size exceeds specified contentLength. "
- "$_bytesWritten bytes written while expected "
- "$contentLength. "
- "[${new String.fromCharCodes(data)}]"));
+ controller.addError(
+ HttpException("Content size exceeds specified contentLength. "
+ "$_bytesWritten bytes written while expected "
+ "$contentLength. "
+ "[${String.fromCharCodes(data)}]"));
return;
}
}
@@ -1818,8 +1819,8 @@
var outbound = this.outbound!;
// If we earlier saw an error, return immediate. The notification to
// _Http*Connection is already done.
- if (_socketError) return new Future.value(outbound);
- if (outbound._isConnectionClosed) return new Future.value(outbound);
+ if (_socketError) return Future.value(outbound);
+ if (outbound._isConnectionClosed) return Future.value(outbound);
if (!headersWritten && !ignoreBody) {
if (outbound.headers.contentLength == -1) {
// If no body was written, ignoreBody is false (it's not a HEAD
@@ -1828,25 +1829,25 @@
outbound.headers.chunkedTransferEncoding = false;
outbound.headers.contentLength = 0;
} else if (outbound.headers.contentLength > 0) {
- var error = new HttpException(
+ var error = HttpException(
"No content even though contentLength was specified to be "
"greater than 0: ${outbound.headers.contentLength}.",
uri: outbound._uri);
_doneCompleter.completeError(error);
- return _closeFuture = new Future.error(error);
+ return _closeFuture = Future.error(error);
}
}
// If contentLength was specified, validate it.
var contentLength = this.contentLength;
if (contentLength != null) {
if (_bytesWritten < contentLength) {
- var error = new HttpException(
+ var error = HttpException(
"Content size below specified contentLength. "
" $_bytesWritten bytes written but expected "
"$contentLength.",
uri: outbound._uri);
_doneCompleter.completeError(error);
- return _closeFuture = new Future.error(error);
+ return _closeFuture = Future.error(error);
}
}
@@ -1857,7 +1858,7 @@
if (_gzip) {
_gzipAdd = socket.add;
if (_gzipBufferLength > 0) {
- _gzipSink!.add(new Uint8List.view(_gzipBuffer!.buffer,
+ _gzipSink!.add(Uint8List.view(_gzipBuffer!.buffer,
_gzipBuffer!.offsetInBytes, _gzipBufferLength));
}
_gzipBuffer = null;
@@ -1868,8 +1869,8 @@
}
// Add any remaining data in the buffer.
if (_length > 0) {
- socket.add(new Uint8List.view(
- _buffer!.buffer, _buffer!.offsetInBytes, _length));
+ socket.add(
+ Uint8List.view(_buffer!.buffer, _buffer!.offsetInBytes, _length));
}
// Clear references, for better GC.
_buffer = null;
@@ -1907,10 +1908,10 @@
void set gzip(bool value) {
_gzip = value;
if (value) {
- _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
+ _gzipBuffer = Uint8List(_OUTGOING_BUFFER_SIZE);
assert(_gzipSink == null);
- _gzipSink = new ZLibEncoder(gzip: true)
- .startChunkedConversion(new _HttpGZipSink((data) {
+ _gzipSink =
+ ZLibEncoder(gzip: true).startChunkedConversion(_HttpGZipSink((data) {
// We are closing down prematurely, due to an error. Discard.
if (_gzipAdd == null) return;
_addChunk(_chunkHeader(data.length), _gzipAdd!);
@@ -1924,7 +1925,7 @@
(error is SocketException || error is TlsException) &&
outbound is HttpResponse;
- void _addGZipChunk(List<int> chunk, void add(List<int> data)) {
+ void _addGZipChunk(List<int> chunk, void Function(List<int> data) add) {
var bufferOutput = outbound!.bufferOutput;
if (!bufferOutput) {
add(chunk);
@@ -1932,9 +1933,9 @@
}
var gzipBuffer = _gzipBuffer!;
if (chunk.length > gzipBuffer.length - _gzipBufferLength) {
- add(new Uint8List.view(
+ add(Uint8List.view(
gzipBuffer.buffer, gzipBuffer.offsetInBytes, _gzipBufferLength));
- _gzipBuffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
+ _gzipBuffer = Uint8List(_OUTGOING_BUFFER_SIZE);
_gzipBufferLength = 0;
}
if (chunk.length > _OUTGOING_BUFFER_SIZE) {
@@ -1947,14 +1948,13 @@
}
}
- void _addChunk(List<int> chunk, void add(List<int> data)) {
+ void _addChunk(List<int> chunk, void Function(List<int> data) add) {
var bufferOutput = outbound!.bufferOutput;
if (!bufferOutput) {
if (_buffer != null) {
// If _buffer is not null, we have not written the header yet. Write
// it now.
- add(new Uint8List.view(
- _buffer!.buffer, _buffer!.offsetInBytes, _length));
+ add(Uint8List.view(_buffer!.buffer, _buffer!.offsetInBytes, _length));
_buffer = null;
_length = 0;
}
@@ -1962,8 +1962,8 @@
return;
}
if (chunk.length > _buffer!.length - _length) {
- add(new Uint8List.view(_buffer!.buffer, _buffer!.offsetInBytes, _length));
- _buffer = new Uint8List(_OUTGOING_BUFFER_SIZE);
+ add(Uint8List.view(_buffer!.buffer, _buffer!.offsetInBytes, _length));
+ _buffer = Uint8List(_OUTGOING_BUFFER_SIZE);
_length = 0;
}
if (chunk.length > _OUTGOING_BUFFER_SIZE) {
@@ -1975,7 +1975,7 @@
}
List<int> _chunkHeader(int length) {
- const hexDigits = const [
+ const hexDigits = [
0x30,
0x31,
0x32,
@@ -2004,7 +2004,7 @@
size++;
len >>= 4;
}
- var footerAndHeader = new Uint8List(size + 2);
+ var footerAndHeader = Uint8List(size + 2);
if (_pendingChunkedFooter == 2) {
footerAndHeader[0] = _CharCode.CR;
footerAndHeader[1] = _CharCode.LF;
@@ -2038,7 +2038,7 @@
_HttpClientConnection(this.key, this._socket, this._httpClient,
[this._proxyTunnel = false, this._context])
- : _httpParser = new _HttpParser.responseParser() {
+ : _httpParser = _HttpParser.responseParser() {
_httpParser.listenToStream(_socket);
// Set up handlers on the parser here, so we are sure to get 'onDone' from
@@ -2049,7 +2049,7 @@
_subscription!.pause();
// We assume the response is not here, until we have send the request.
if (_nextResponseCompleter == null) {
- throw new HttpException(
+ throw HttpException(
"Unexpected response (unsolicited response without request).",
uri: _currentUri);
}
@@ -2063,7 +2063,7 @@
_subscription!.resume();
}).catchError((dynamic error, StackTrace stackTrace) {
_nextResponseCompleter!.completeError(
- new HttpException(error.message, uri: _currentUri), stackTrace);
+ HttpException(error.message, uri: _currentUri), stackTrace);
_nextResponseCompleter = null;
});
} else {
@@ -2072,10 +2072,10 @@
}
}, onError: (dynamic error, StackTrace stackTrace) {
_nextResponseCompleter?.completeError(
- new HttpException(error.message, uri: _currentUri), stackTrace);
+ HttpException(error.message, uri: _currentUri), stackTrace);
_nextResponseCompleter = null;
}, onDone: () {
- _nextResponseCompleter?.completeError(new HttpException(
+ _nextResponseCompleter?.completeError(HttpException(
"Connection closed before response was received",
uri: _currentUri));
_nextResponseCompleter = null;
@@ -2086,8 +2086,7 @@
_HttpClientRequest send(Uri uri, int port, String method, _Proxy proxy,
_HttpProfileData? profileData) {
if (closed) {
- throw new HttpException("Socket closed before request was sent",
- uri: uri);
+ throw HttpException("Socket closed before request was sent", uri: uri);
}
_currentUri = uri;
// Start with pausing the parser.
@@ -2098,10 +2097,10 @@
}
_ProxyCredentials? proxyCreds; // Credentials used to authorize proxy.
_SiteCredentials? creds; // Credentials used to authorize this request.
- var outgoing = new _HttpOutgoing(_socket);
+ var outgoing = _HttpOutgoing(_socket);
// Create new request object, wrapping the outgoing connection.
- var request = new _HttpClientRequest(
+ var request = _HttpClientRequest(
outgoing, uri, method, proxy, _httpClient, this, profileData);
// For the Host header an IPv6 address must be enclosed in []'s.
var host = uri.host;
@@ -2119,13 +2118,13 @@
String auth = _CryptoUtils.bytesToBase64(
utf8.encode("${proxy.username}:${proxy.password}"));
request.headers.set(HttpHeaders.proxyAuthorizationHeader, "Basic $auth");
- } else if (!proxy.isDirect && _httpClient._proxyCredentials.length > 0) {
+ } else if (!proxy.isDirect && _httpClient._proxyCredentials.isNotEmpty) {
proxyCreds = _httpClient._findProxyCredentials(proxy);
if (proxyCreds != null) {
proxyCreds.authorize(request);
}
}
- if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+ if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
// If the URL contains user information use that for basic
// authorization.
String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
@@ -2146,7 +2145,7 @@
profileData?.finishRequest(request: request);
// Request sent, set up response completer.
- var nextResponseCompleter = new Completer<_HttpIncoming>();
+ var nextResponseCompleter = Completer<_HttpIncoming>();
_nextResponseCompleter = nextResponseCompleter;
// Listen for response.
@@ -2203,7 +2202,7 @@
// If we see a state error, we failed to get the 'first'
// element.
.catchError((error) {
- throw new HttpException("Connection closed before data was received",
+ throw HttpException("Connection closed before data was received",
uri: uri);
}, test: (error) => error is StateError).catchError((error, stackTrace) {
// We are done with the socket.
@@ -2222,8 +2221,8 @@
}
Future<Socket> detachSocket() {
- return _streamFuture!.then(
- (_) => new _DetachedSocket(_socket, _httpParser.detachIncoming()));
+ return _streamFuture!
+ .then((_) => _DetachedSocket(_socket, _httpParser.detachIncoming()));
}
void destroy() {
@@ -2258,7 +2257,7 @@
String host,
int port,
_Proxy proxy,
- bool callback(X509Certificate certificate),
+ bool Function(X509Certificate certificate) callback,
_HttpProfileData? profileData) {
final method = "CONNECT";
final uri = Uri(host: host, port: port);
@@ -2288,7 +2287,7 @@
final error = "Proxy failed to establish tunnel "
"(${response.statusCode} ${response.reasonPhrase})";
profileData?.requestEvent(error);
- throw new HttpException(error, uri: request.uri);
+ throw HttpException(error, uri: request.uri);
}
var socket = (response as _HttpClientResponse)
._httpRequest
@@ -2299,7 +2298,7 @@
}).then((secureSocket) {
String key = _HttpClientConnection.makeKey(true, host, port);
profileData?.requestEvent('Proxy tunnel established');
- return new _HttpClientConnection(
+ return _HttpClientConnection(
key, secureSocket, request._httpClient, true);
});
}
@@ -2317,7 +2316,7 @@
void startTimer() {
assert(_idleTimer == null);
- _idleTimer = new Timer(_httpClient.idleTimeout, () {
+ _idleTimer = Timer(_httpClient.idleTimeout, () {
_idleTimer = null;
close();
});
@@ -2338,10 +2337,10 @@
final int port;
final bool isSecure;
final SecurityContext? context;
- final Set<_HttpClientConnection> _idle = new HashSet();
- final Set<_HttpClientConnection> _active = new HashSet();
- final Set<ConnectionTask> _socketTasks = new HashSet();
- final Queue _pending = new ListQueue();
+ final Set<_HttpClientConnection> _idle = HashSet();
+ final Set<_HttpClientConnection> _active = HashSet();
+ final Set<ConnectionTask> _socketTasks = HashSet();
+ final _pending = ListQueue<void Function()>();
int _connecting = 0;
_ConnectionTarget(
@@ -2415,12 +2414,12 @@
if (hasIdle) {
var connection = takeIdle();
client._connectionsChanged();
- return new Future.value(new _ConnectionInfo(connection, proxy));
+ return Future.value(_ConnectionInfo(connection, proxy));
}
var maxConnectionsPerHost = client.maxConnectionsPerHost;
if (maxConnectionsPerHost != null &&
_active.length + _connecting >= maxConnectionsPerHost) {
- var completer = new Completer<_ConnectionInfo>();
+ var completer = Completer<_ConnectionInfo>();
_pending.add(() {
completer
.complete(connect(uriHost, uriPort, proxy, client, profileData));
@@ -2452,7 +2451,7 @@
socket.setOption(SocketOption.tcpNoDelay, true);
}
var connection =
- new _HttpClientConnection(key, socket, client, false, context);
+ _HttpClientConnection(key, socket, client, false, context);
if (isSecure && !proxy.isDirect) {
connection._dispose = true;
return connection
@@ -2462,12 +2461,12 @@
._getConnectionTarget(uriHost, uriPort, true)
.addNewActive(tunnel);
_socketTasks.remove(task);
- return new _ConnectionInfo(tunnel, proxy);
+ return _ConnectionInfo(tunnel, proxy);
});
} else {
addNewActive(connection);
_socketTasks.remove(task);
- return new _ConnectionInfo(connection, proxy);
+ return _ConnectionInfo(connection, proxy);
}
}, onError: (error) {
// When there is a timeout, there is a race in which the connectionTask
@@ -2481,8 +2480,8 @@
_socketTasks.remove(task);
task.cancel();
throw SocketException(
- "HTTP connection timed out after ${connectionTimeout}, "
- "host: ${host}, port: ${port}");
+ "HTTP connection timed out after $connectionTimeout, "
+ "host: $host, port: $port");
}
_socketTasks.remove(task);
_checkPending();
@@ -2495,13 +2494,14 @@
}
}
-typedef bool BadCertificateCallback(X509Certificate cr, String host, int port);
+typedef BadCertificateCallback = bool Function(
+ X509Certificate cr, String host, int port);
class _HttpClient implements HttpClient {
bool _closing = false;
bool _closingForcefully = false;
final Map<String, _ConnectionTarget> _connectionTargets =
- new HashMap<String, _ConnectionTarget>();
+ HashMap<String, _ConnectionTarget>();
final List<_Credentials> _credentials = [];
final List<_ProxyCredentials> _proxyCredentials = [];
final SecurityContext? _context;
@@ -2536,7 +2536,7 @@
}
set badCertificateCallback(
- bool callback(X509Certificate cert, String host, int port)?) {
+ bool Function(X509Certificate cert, String host, int port)? callback) {
_badCertificateCallback = callback;
}
@@ -2560,8 +2560,8 @@
query = path.substring(queryStart + 1, fragmentStart);
path = path.substring(0, queryStart);
}
- Uri uri = new Uri(
- scheme: "http", host: host, port: port, path: path, query: query);
+ Uri uri =
+ Uri(scheme: "http", host: host, port: port, path: path, query: query);
return _openUrl(method, uri);
}
@@ -2607,27 +2607,30 @@
!force || !_connectionTargets.values.any((s) => s._active.isNotEmpty));
}
- set authenticate(Future<bool> f(Uri url, String scheme, String? realm)?) {
+ set authenticate(
+ Future<bool> Function(Uri url, String scheme, String? realm)? f) {
_authenticate = f;
}
void addCredentials(Uri url, String realm, HttpClientCredentials cr) {
_credentials
- .add(new _SiteCredentials(url, realm, cr as _HttpClientCredentials));
+ .add(_SiteCredentials(url, realm, cr as _HttpClientCredentials));
}
set authenticateProxy(
- Future<bool> f(String host, int port, String scheme, String? realm)?) {
+ Future<bool> Function(
+ String host, int port, String scheme, String? realm)?
+ f) {
_authenticateProxy = f;
}
void addProxyCredentials(
String host, int port, String realm, HttpClientCredentials cr) {
_proxyCredentials.add(
- new _ProxyCredentials(host, port, realm, cr as _HttpClientCredentials));
+ _ProxyCredentials(host, port, realm, cr as _HttpClientCredentials));
}
- set findProxy(String f(Uri uri)?) => _findProxy = f;
+ set findProxy(String Function(Uri uri)? f) => _findProxy = f;
static void _startRequestTimelineEvent(
TimelineTask? timeline, String method, Uri uri) {
@@ -2649,21 +2652,20 @@
Future<_HttpClientRequest> _openUrl(String method, Uri uri) {
if (_closing) {
- throw new StateError("Client is closed");
+ throw StateError("Client is closed");
}
// Ignore any fragments on the request URI.
uri = uri.removeFragment();
if (method == null) {
- throw new ArgumentError(method);
+ throw ArgumentError(method);
}
if (method != "CONNECT") {
if (uri.host.isEmpty) {
- throw new ArgumentError("No host specified in URI $uri");
+ throw ArgumentError("No host specified in URI $uri");
} else if (uri.scheme != "http" && uri.scheme != "https") {
- throw new ArgumentError(
- "Unsupported scheme '${uri.scheme}' in URI $uri");
+ throw ArgumentError("Unsupported scheme '${uri.scheme}' in URI $uri");
}
}
@@ -2683,9 +2685,9 @@
// TODO(sgjesse): Keep a map of these as normally only a few
// configuration strings will be used.
try {
- proxyConf = new _ProxyConfiguration(findProxy(uri));
+ proxyConf = _ProxyConfiguration(findProxy(uri));
} catch (error, stackTrace) {
- return new Future.error(error, stackTrace);
+ return Future.error(error, stackTrace);
}
}
_HttpProfileData? profileData;
@@ -2784,7 +2786,7 @@
_ConnectionTarget _getConnectionTarget(String host, int port, bool isSecure) {
String key = _HttpClientConnection.makeKey(isSecure, host, port);
return _connectionTargets.putIfAbsent(key, () {
- return new _ConnectionTarget(key, host, port, isSecure, _context);
+ return _ConnectionTarget(key, host, port, isSecure, _context);
});
}
@@ -2798,7 +2800,7 @@
Iterator<_Proxy> proxies = proxyConf.proxies.iterator;
Future<_ConnectionInfo> connect(error, stackTrace) {
- if (!proxies.moveNext()) return new Future.error(error, stackTrace);
+ if (!proxies.moveNext()) return Future.error(error, stackTrace);
_Proxy proxy = proxies.current;
String host = proxy.isDirect ? uriHost : proxy.host!;
int port = proxy.isDirect ? uriPort : proxy.port!;
@@ -2808,7 +2810,7 @@
.catchError(connect);
}
- return connect(new HttpException("No proxies given"), StackTrace.current);
+ return connect(HttpException("No proxies given"), StackTrace.current);
}
_SiteCredentials? _findCredentials(Uri url, [_AuthenticationScheme? scheme]) {
@@ -2884,13 +2886,13 @@
var pos = option.lastIndexOf(":");
if (option.indexOf("]") > pos) option = "$option:1080";
} else {
- if (option.indexOf(":") == -1) option = "$option:1080";
+ if (!option.contains(":")) option = "$option:1080";
}
return "PROXY $option";
}
// Default to using the process current environment.
- if (environment == null) environment = _platformEnvironmentCache;
+ environment ??= _platformEnvironmentCache;
String? proxyCfg;
@@ -2916,7 +2918,8 @@
return "DIRECT";
}
- static Map<String, String> _platformEnvironmentCache = Platform.environment;
+ static final Map<String, String> _platformEnvironmentCache =
+ Platform.environment;
}
class _HttpConnection extends LinkedListEntry<_HttpConnection>
@@ -2927,8 +2930,8 @@
static const _DETACHED = 3;
// Use HashMap, as we don't need to keep order.
- static Map<int, _HttpConnection> _connections =
- new HashMap<int, _HttpConnection>();
+ static final Map<int, _HttpConnection> _connections =
+ HashMap<int, _HttpConnection>();
final /*_ServerSocket*/ _socket;
final _HttpServer _httpServer;
@@ -2939,7 +2942,7 @@
Future? _streamFuture;
_HttpConnection(this._socket, this._httpServer)
- : _httpParser = new _HttpParser.requestParser() {
+ : _httpParser = _HttpParser.requestParser() {
_connections[_serviceId] = this;
_httpParser.listenToStream(_socket);
_subscription = _httpParser.listen((incoming) {
@@ -2952,8 +2955,8 @@
// stream paused until the request has been send.
_subscription!.pause();
_state = _ACTIVE;
- var outgoing = new _HttpOutgoing(_socket);
- var response = new _HttpResponse(
+ var outgoing = _HttpOutgoing(_socket);
+ var response = _HttpResponse(
incoming.uri!,
incoming.headers.protocolVersion,
outgoing,
@@ -2963,7 +2966,7 @@
if (incoming.statusCode == HttpStatus.badRequest) {
response.statusCode = HttpStatus.badRequest;
}
- var request = new _HttpRequest(response, incoming, _httpServer, this);
+ var request = _HttpRequest(response, incoming, _httpServer, this);
_streamFuture = outgoing.done.then((_) {
response.deadline = null;
if (_state == _DETACHED) return;
@@ -3020,7 +3023,7 @@
return _streamFuture!.then((_) {
_connections.remove(_serviceId);
- return new _DetachedSocket(_socket, detachedIncoming);
+ return _DetachedSocket(_socket, detachedIncoming);
});
}
@@ -3083,7 +3086,7 @@
with _ServiceObject
implements HttpServer {
// Use default Map so we keep order.
- static Map<int, _HttpServer> _servers = new Map<int, _HttpServer>();
+ static final Map<int, _HttpServer> _servers = <int, _HttpServer>{};
String? serverHeader;
final HttpHeaders defaultResponseHeaders = _initDefaultResponseHeaders();
@@ -3097,7 +3100,7 @@
return ServerSocket.bind(address, port,
backlog: backlog, v6Only: v6Only, shared: shared)
.then<HttpServer>((socket) {
- return new _HttpServer._(socket, true);
+ return _HttpServer._(socket, true);
});
}
@@ -3115,12 +3118,12 @@
requestClientCertificate: requestClientCertificate,
shared: shared)
.then<HttpServer>((socket) {
- return new _HttpServer._(socket, true);
+ return _HttpServer._(socket, true);
});
}
_HttpServer._(this._serverSocket, this._closeServer)
- : _controller = new StreamController<HttpRequest>(sync: true) {
+ : _controller = StreamController<HttpRequest>(sync: true) {
_controller.onCancel = close;
idleTimeout = const Duration(seconds: 120);
_servers[_serviceId] = this;
@@ -3128,14 +3131,14 @@
_HttpServer.listenOn(this._serverSocket)
: _closeServer = false,
- _controller = new StreamController<HttpRequest>(sync: true) {
+ _controller = StreamController<HttpRequest>(sync: true) {
_controller.onCancel = close;
idleTimeout = const Duration(seconds: 120);
_servers[_serviceId] = this;
}
static HttpHeaders _initDefaultResponseHeaders() {
- var defaultResponseHeaders = new _HttpHeaders('1.1');
+ var defaultResponseHeaders = _HttpHeaders('1.1');
defaultResponseHeaders.contentType = ContentType.text;
defaultResponseHeaders.set('X-Frame-Options', 'SAMEORIGIN');
defaultResponseHeaders.set('X-Content-Type-Options', 'nosniff');
@@ -3153,7 +3156,7 @@
}
_idleTimeout = duration;
if (duration != null) {
- _idleTimer = new Timer.periodic(duration, (_) {
+ _idleTimer = Timer.periodic(duration, (_) {
for (var idle in _idleConnections.toList()) {
if (idle.isMarkedIdle) {
idle.destroy();
@@ -3165,14 +3168,17 @@
}
}
- StreamSubscription<HttpRequest> listen(void onData(HttpRequest event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<HttpRequest> listen(
+ void Function(HttpRequest event)? onData,
+ {Function? onError,
+ void Function()? onDone,
+ bool? cancelOnError}) {
_serverSocket.listen((Socket socket) {
if (socket.address.type != InternetAddressType.unix) {
socket.setOption(SocketOption.tcpNoDelay, true);
}
// Accept the client connection.
- _HttpConnection connection = new _HttpConnection(socket, this);
+ _HttpConnection connection = _HttpConnection(socket, this);
_idleConnections.add(connection);
}, onError: (error, stackTrace) {
// Ignore HandshakeExceptions as they are bound to a single request,
@@ -3191,7 +3197,7 @@
if (_serverSocket != null && _closeServer) {
result = _serverSocket.close();
} else {
- result = new Future.value();
+ result = Future.value();
}
idleTimeout = null;
if (force) {
@@ -3220,12 +3226,12 @@
}
int get port {
- if (closed) throw new HttpException("HttpServer is not bound to a socket");
+ if (closed) throw HttpException("HttpServer is not bound to a socket");
return _serverSocket.port;
}
InternetAddress get address {
- if (closed) throw new HttpException("HttpServer is not bound to a socket");
+ if (closed) throw HttpException("HttpServer is not bound to a socket");
return _serverSocket.address;
}
@@ -3262,20 +3268,20 @@
_sessionManagerInstance ??= _HttpSessionManager();
HttpConnectionsInfo connectionsInfo() {
- HttpConnectionsInfo result = new HttpConnectionsInfo();
+ HttpConnectionsInfo result = HttpConnectionsInfo();
result.total = _activeConnections.length + _idleConnections.length;
- _activeConnections.forEach((_HttpConnection conn) {
+ for (var conn in _activeConnections) {
if (conn._isActive) {
result.active++;
} else {
assert(conn._isClosing);
result.closing++;
}
- });
- _idleConnections.forEach((_HttpConnection conn) {
+ }
+ for (var conn in _idleConnections) {
result.idle++;
assert(conn._isIdle);
- });
+ }
return result;
}
@@ -3322,10 +3328,10 @@
// Set of currently connected clients.
final LinkedList<_HttpConnection> _activeConnections =
- new LinkedList<_HttpConnection>();
+ LinkedList<_HttpConnection>();
final LinkedList<_HttpConnection> _idleConnections =
- new LinkedList<_HttpConnection>();
- StreamController<HttpRequest> _controller;
+ LinkedList<_HttpConnection>();
+ final StreamController<HttpRequest> _controller;
}
class _ProxyConfiguration {
@@ -3334,12 +3340,12 @@
_ProxyConfiguration(String configuration) : proxies = <_Proxy>[] {
if (configuration == null) {
- throw new HttpException("Invalid proxy configuration $configuration");
+ throw HttpException("Invalid proxy configuration $configuration");
}
List<String> list = configuration.split(";");
- list.forEach((String proxy) {
+ for (var proxy in list) {
proxy = proxy.trim();
- if (!proxy.isEmpty) {
+ if (proxy.isNotEmpty) {
if (proxy.startsWith(PROXY_PREFIX)) {
String? username;
String? password;
@@ -3352,8 +3358,7 @@
proxy = proxy.substring(at + 1).trim();
int colon = userinfo.indexOf(":");
if (colon == -1 || colon == 0 || colon == proxy.length - 1) {
- throw new HttpException(
- "Invalid proxy configuration $configuration");
+ throw HttpException("Invalid proxy configuration $configuration");
}
username = userinfo.substring(0, colon).trim();
password = userinfo.substring(colon + 1).trim();
@@ -3361,8 +3366,7 @@
// Look for proxy host and port.
int colon = proxy.lastIndexOf(":");
if (colon == -1 || colon == 0 || colon == proxy.length - 1) {
- throw new HttpException(
- "Invalid proxy configuration $configuration");
+ throw HttpException("Invalid proxy configuration $configuration");
}
String host = proxy.substring(0, colon).trim();
if (host.startsWith("[") && host.endsWith("]")) {
@@ -3372,22 +3376,21 @@
int port;
try {
port = int.parse(portString);
- } on FormatException catch (e) {
- throw new HttpException(
- "Invalid proxy configuration $configuration, "
+ } on FormatException {
+ throw HttpException("Invalid proxy configuration $configuration, "
"invalid port '$portString'");
}
- proxies.add(new _Proxy(host, port, username, password));
+ proxies.add(_Proxy(host, port, username, password));
} else if (proxy.trim() == DIRECT_PREFIX) {
- proxies.add(new _Proxy.direct());
+ proxies.add(_Proxy.direct());
} else {
- throw new HttpException("Invalid proxy configuration $configuration");
+ throw HttpException("Invalid proxy configuration $configuration");
}
}
- });
+ }
}
- const _ProxyConfiguration.direct() : proxies = const [const _Proxy.direct()];
+ const _ProxyConfiguration.direct() : proxies = const [_Proxy.direct()];
final List<_Proxy> proxies;
}
@@ -3434,8 +3437,8 @@
_DetachedSocket(this._socket, this._incoming);
- StreamSubscription<Uint8List> listen(void onData(Uint8List event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<Uint8List> listen(void Function(Uint8List event)? onData,
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
return _incoming.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@@ -3511,9 +3514,9 @@
class _AuthenticationScheme {
final int _scheme;
- static const UNKNOWN = const _AuthenticationScheme(-1);
- static const BASIC = const _AuthenticationScheme(0);
- static const DIGEST = const _AuthenticationScheme(1);
+ static const UNKNOWN = _AuthenticationScheme(-1);
+ static const BASIC = _AuthenticationScheme(0);
+ static const DIGEST = _AuthenticationScheme(1);
const _AuthenticationScheme(this._scheme);
@@ -3551,7 +3554,7 @@
// http://tools.ietf.org/html/draft-reschke-basicauth-enc-06. For
// now always use UTF-8 encoding.
var creds = credentials as _HttpClientDigestCredentials;
- var hasher = new _MD5()
+ var hasher = _MD5()
..add(utf8.encode(creds.username))
..add([_CharCode.COLON])
..add(realm.codeUnits)
@@ -3662,7 +3665,7 @@
String authorization(_Credentials credentials, _HttpClientRequest request) {
String requestUri = request._requestUri();
- _MD5 hasher = new _MD5()
+ _MD5 hasher = _MD5()
..add(request.method.codeUnits)
..add([_CharCode.COLON])
..add(requestUri.codeUnits);
@@ -3671,7 +3674,7 @@
bool isAuth = false;
String cnonce = "";
String nc = "";
- hasher = new _MD5()
+ hasher = _MD5()
..add(credentials.ha1!.codeUnits)
..add([_CharCode.COLON]);
if (credentials.qop == "auth") {
@@ -3698,7 +3701,7 @@
}
var response = _CryptoUtils.bytesToHex(hasher.close());
- StringBuffer buffer = new StringBuffer()
+ StringBuffer buffer = StringBuffer()
..write('Digest ')
..write('username="$username"')
..write(', realm="${credentials.realm}"')
diff --git a/sdk/lib/_http/http_parser.dart b/sdk/lib/_http/http_parser.dart
index 52b16ae..0a8bba4 100644
--- a/sdk/lib/_http/http_parser.dart
+++ b/sdk/lib/_http/http_parser.dart
@@ -7,18 +7,18 @@
// Global constants.
class _Const {
// Bytes for "HTTP".
- static const HTTP = const [72, 84, 84, 80];
+ static const HTTP = [72, 84, 84, 80];
// Bytes for "HTTP/1.".
- static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46];
+ static const HTTP1DOT = [72, 84, 84, 80, 47, 49, 46];
// Bytes for "HTTP/1.0".
- static const HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
+ static const HTTP10 = [72, 84, 84, 80, 47, 49, 46, 48];
// Bytes for "HTTP/1.1".
- static const HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
+ static const HTTP11 = [72, 84, 84, 80, 47, 49, 46, 49];
static const bool T = true;
static const bool F = false;
// Loopup-map for the following characters: '()<>@,;:\\"/[]?={} \t'.
- static const SEPARATOR_MAP = const [
+ static const SEPARATOR_MAP = [
F, F, F, F, F, F, F, F, F, T, F, F, F, F, F, F, F, F, F, F, F, F, F, F, //
F, F, F, F, F, F, F, F, T, F, T, F, F, F, F, F, T, T, F, F, T, F, F, T, //
F, F, F, F, F, F, F, F, F, F, T, T, T, T, T, T, T, F, F, F, F, F, F, F, //
@@ -39,15 +39,12 @@
static const int LF = 10;
static const int CR = 13;
static const int SP = 32;
- static const int AMPERSAND = 38;
static const int COMMA = 44;
- static const int DASH = 45;
static const int SLASH = 47;
static const int ZERO = 48;
static const int ONE = 49;
static const int COLON = 58;
static const int SEMI_COLON = 59;
- static const int EQUAL = 61;
}
// States of the HTTP parser state machine.
@@ -99,19 +96,17 @@
static const int RESPONSE = 0;
}
-/**
- * The _HttpDetachedStreamSubscription takes a subscription and some extra data,
- * and makes it possible to "inject" the data in from of other data events
- * from the subscription.
- *
- * It does so by overriding pause/resume, so that once the
- * _HttpDetachedStreamSubscription is resumed, it'll deliver the data before
- * resuming the underlying subscription.
- */
+/// The _HttpDetachedStreamSubscription takes a subscription and some extra data,
+/// and makes it possible to "inject" the data in from of other data events
+/// from the subscription.
+///
+/// It does so by overriding pause/resume, so that once the
+/// _HttpDetachedStreamSubscription is resumed, it'll deliver the data before
+/// resuming the underlying subscription.
class _HttpDetachedStreamSubscription implements StreamSubscription<Uint8List> {
- StreamSubscription<Uint8List> _subscription;
+ final StreamSubscription<Uint8List> _subscription;
Uint8List? _injectData;
- Function? _userOnData;
+ void Function(Uint8List data)? _userOnData;
bool _isCanceled = false;
bool _scheduled = false;
int _pauseCount = 1;
@@ -130,12 +125,12 @@
return _subscription.cancel();
}
- void onData(void handleData(Uint8List data)?) {
+ void onData(void Function(Uint8List data)? handleData) {
_userOnData = handleData;
_subscription.onData(handleData);
}
- void onDone(void handleDone()?) {
+ void onDone(void Function()? handleDone) {
_subscription.onDone(handleDone);
}
@@ -170,7 +165,7 @@
scheduleMicrotask(() {
_scheduled = false;
if (_pauseCount > 0 || _isCanceled) return;
- var data = _injectData;
+ var data = _injectData!;
_injectData = null;
// To ensure that 'subscription.isPaused' is false, we resume the
// subscription here. This is fine as potential events are delayed.
@@ -186,8 +181,8 @@
_HttpDetachedIncoming(this.subscription, this.bufferedData);
- StreamSubscription<Uint8List> listen(void onData(Uint8List event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<Uint8List> listen(void Function(Uint8List event)? onData,
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
var subscription = this.subscription;
if (subscription != null) {
subscription
@@ -197,37 +192,34 @@
if (bufferedData == null) {
return subscription..resume();
}
- return new _HttpDetachedStreamSubscription(
- subscription, bufferedData, onData)
+ return _HttpDetachedStreamSubscription(subscription, bufferedData, onData)
..resume();
} else {
// TODO(26379): add test for this branch.
- return new Stream<Uint8List>.fromIterable([bufferedData!]).listen(onData,
+ return Stream<Uint8List>.fromIterable([bufferedData!]).listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
}
}
-/**
- * HTTP parser which parses the data stream given to [consume].
- *
- * If an HTTP parser error occurs, the parser will signal an error to either
- * the current _HttpIncoming or the _parser itself.
- *
- * The connection upgrades (e.g. switching from HTTP/1.1 to the
- * WebSocket protocol) is handled in a special way. If connection
- * upgrade is specified in the headers, then on the callback to
- * [:responseStart:] the [:upgrade:] property on the [:HttpParser:]
- * object will be [:true:] indicating that from now on the protocol is
- * not HTTP anymore and no more callbacks will happen, that is
- * [:dataReceived:] and [:dataEnd:] are not called in this case as
- * there is no more HTTP data. After the upgrade the method
- * [:readUnparsedData:] can be used to read any remaining bytes in the
- * HTTP parser which are part of the protocol the connection is
- * upgrading to. These bytes cannot be processed by the HTTP parser
- * and should be handled according to whatever protocol is being
- * upgraded to.
- */
+/// HTTP parser which parses the data stream given to [consume].
+///
+/// If an HTTP parser error occurs, the parser will signal an error to either
+/// the current _HttpIncoming or the _parser itself.
+///
+/// The connection upgrades (e.g. switching from HTTP/1.1 to the
+/// WebSocket protocol) is handled in a special way. If connection
+/// upgrade is specified in the headers, then on the callback to
+/// [:responseStart:] the [:upgrade:] property on the [:HttpParser:]
+/// object will be [:true:] indicating that from now on the protocol is
+/// not HTTP anymore and no more callbacks will happen, that is
+/// [:dataReceived:] and [:dataEnd:] are not called in this case as
+/// there is no more HTTP data. After the upgrade the method
+/// [:readUnparsedData:] can be used to read any remaining bytes in the
+/// HTTP parser which are part of the protocol the connection is
+/// upgrading to. These bytes cannot be processed by the HTTP parser
+/// and should be handled according to whatever protocol is being
+/// upgraded to.
class _HttpParser extends Stream<_HttpIncoming> {
// State.
bool _parserCalled = false;
@@ -265,7 +257,7 @@
_HttpHeaders? _headers;
// The limit for parsing chunk size
- int _chunkSizeLimit = 0x7FFFFFFF;
+ static const _chunkSizeLimit = 0x7FFFFFFF;
// The current incoming connection.
_HttpIncoming? _incoming;
@@ -276,15 +268,15 @@
StreamController<Uint8List>? _bodyController;
factory _HttpParser.requestParser() {
- return new _HttpParser._(true);
+ return _HttpParser._(true);
}
factory _HttpParser.responseParser() {
- return new _HttpParser._(false);
+ return _HttpParser._(false);
}
_HttpParser._(this._requestParser)
- : _controller = new StreamController<_HttpIncoming>(sync: true) {
+ : _controller = StreamController<_HttpIncoming>(sync: true) {
_controller
..onListen = () {
_paused = false;
@@ -303,8 +295,11 @@
_reset();
}
- StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ StreamSubscription<_HttpIncoming> listen(
+ void Function(_HttpIncoming event)? onData,
+ {Function? onError,
+ void Function()? onDone,
+ bool? cancelOnError}) {
return _controller.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@@ -372,11 +367,11 @@
}
var incoming = _createIncoming(_transferLength);
if (_requestParser) {
- incoming.method = new String.fromCharCodes(_method);
- incoming.uri = Uri.parse(new String.fromCharCodes(_uriOrReasonPhrase));
+ incoming.method = String.fromCharCodes(_method);
+ incoming.uri = Uri.parse(String.fromCharCodes(_uriOrReasonPhrase));
} else {
incoming.statusCode = _statusCode;
- incoming.reasonPhrase = new String.fromCharCodes(_uriOrReasonPhrase);
+ incoming.reasonPhrase = String.fromCharCodes(_uriOrReasonPhrase);
}
_method.clear();
_uriOrReasonPhrase.clear();
@@ -539,7 +534,7 @@
case _State.REQUEST_LINE_URI:
if (byte == _CharCode.SP) {
- if (_uriOrReasonPhrase.length == 0) {
+ if (_uriOrReasonPhrase.isEmpty) {
throw HttpException("Invalid request, empty URI");
}
_state = _State.REQUEST_LINE_HTTP_VERSION;
@@ -632,7 +627,7 @@
break;
case _State.HEADER_START:
- _headers = new _HttpHeaders(version!);
+ _headers = _HttpHeaders(version!);
if (byte == _CharCode.CR) {
_state = _State.HEADER_ENDING;
} else if (byte == _CharCode.LF) {
@@ -687,8 +682,8 @@
if (byte == _CharCode.SP || byte == _CharCode.HT) {
_state = _State.HEADER_VALUE_START;
} else {
- String headerField = new String.fromCharCodes(_headerField);
- String headerValue = new String.fromCharCodes(_headerValue);
+ String headerField = String.fromCharCodes(_headerField);
+ String headerValue = String.fromCharCodes(_headerValue);
const errorIfBothText = "Both Content-Length and Transfer-Encoding "
"are specified, at most one is allowed";
if (headerField == HttpHeaders.contentLengthHeader) {
@@ -829,7 +824,7 @@
// Always present the data as a view. This way we can handle all
// cases like this, and the user will not experience different data
// typed (which could lead to polymorphic user code).
- Uint8List data = new Uint8List.view(
+ Uint8List data = Uint8List.view(
buffer.buffer, buffer.offsetInBytes + _index, dataAvailable);
_bodyController!.add(data);
if (_remainingContent != -1) {
@@ -953,7 +948,7 @@
_HttpDetachedIncoming detachIncoming() {
// Simulate detached by marking as upgraded.
_state = _State.UPGRADED;
- return new _HttpDetachedIncoming(_socketSubscription, readUnparsedData());
+ return _HttpDetachedIncoming(_socketSubscription, readUnparsedData());
}
Uint8List? readUnparsedData() {
@@ -1099,7 +1094,6 @@
default:
throw UnsupportedError("Unexpected state: $_state");
- break;
}
throw HttpException(
"$method exceeds the $_headerTotalSizeLimit size limit");
@@ -1109,10 +1103,9 @@
assert(_incoming == null);
assert(_bodyController == null);
assert(!_bodyPaused);
- var controller =
- _bodyController = new StreamController<Uint8List>(sync: true);
- var incoming = _incoming =
- new _HttpIncoming(_headers!, transferLength, controller.stream);
+ var controller = _bodyController = StreamController<Uint8List>(sync: true);
+ var incoming =
+ _incoming = _HttpIncoming(_headers!, transferLength, controller.stream);
controller
..onListen = () {
if (incoming != _incoming) return;
diff --git a/sdk/lib/_http/http_session.dart b/sdk/lib/_http/http_session.dart
index 407192a..edf811f 100644
--- a/sdk/lib/_http/http_session.dart
+++ b/sdk/lib/_http/http_session.dart
@@ -13,16 +13,16 @@
bool _destroyed = false;
bool _isNew = true;
DateTime _lastSeen;
- Function? _timeoutCallback;
- _HttpSessionManager _sessionManager;
+ void Function()? _timeoutCallback;
+ final _HttpSessionManager _sessionManager;
// Pointers in timeout queue.
_HttpSession? _prev;
_HttpSession? _next;
final String id;
- final Map _data = new HashMap();
+ final Map _data = HashMap();
- _HttpSession(this._sessionManager, this.id) : _lastSeen = new DateTime.now();
+ _HttpSession(this._sessionManager, this.id) : _lastSeen = DateTime.now();
void destroy() {
assert(!_destroyed);
@@ -34,7 +34,7 @@
// Mark the session as seen. This will reset the timeout and move the node to
// the end of the timeout queue.
void _markSeen() {
- _lastSeen = new DateTime.now();
+ _lastSeen = DateTime.now();
_sessionManager._bumpToEnd(this);
}
@@ -42,7 +42,7 @@
bool get isNew => _isNew;
- void set onTimeout(void callback()?) {
+ void set onTimeout(void Function()? callback) {
_timeoutCallback = callback;
}
@@ -79,7 +79,7 @@
}
Map<K, V> cast<K, V>() => _data.cast<K, V>();
- update(key, update(value), {ifAbsent()?}) =>
+ update(key, update(value), {Function()? ifAbsent}) =>
_data.update(key, update, ifAbsent: ifAbsent);
void updateAll(update(key, value)) {
@@ -101,7 +101,7 @@
// * In a map, mapping from ID to HttpSession.
// * In a linked list, used as a timeout queue.
class _HttpSessionManager {
- Map<String, _HttpSession> _sessions;
+ final Map<String, _HttpSession> _sessions;
int _sessionTimeout = 20 * 60; // 20 mins.
_HttpSession? _head;
_HttpSession? _tail;
@@ -124,7 +124,7 @@
while (_sessions.containsKey(id)) {
id = createSessionId();
}
- var session = _sessions[id] = new _HttpSession(this, id);
+ var session = _sessions[id] = _HttpSession(this, id);
_addToTimeoutQueue(session);
return session;
}
@@ -187,9 +187,9 @@
assert(_timer == null);
var head = _head;
if (head != null) {
- int seconds = new DateTime.now().difference(head.lastSeen).inSeconds;
- _timer = new Timer(
- new Duration(seconds: _sessionTimeout - seconds), _timerTimeout);
+ int seconds = DateTime.now().difference(head.lastSeen).inSeconds;
+ _timer =
+ Timer(Duration(seconds: _sessionTimeout - seconds), _timerTimeout);
}
}
diff --git a/sdk/lib/_http/overrides.dart b/sdk/lib/_http/overrides.dart
index 1fcc81d..5c49590 100644
--- a/sdk/lib/_http/overrides.dart
+++ b/sdk/lib/_http/overrides.dart
@@ -4,7 +4,7 @@
part of dart._http;
-final _httpOverridesToken = new Object();
+final _httpOverridesToken = Object();
const _asyncRunZoned = runZoned;
@@ -46,12 +46,12 @@
}
/// Runs [body] in a fresh [Zone] using the provided overrides.
- static R runZoned<R>(R body(),
+ static R runZoned<R>(R Function() body,
{HttpClient Function(SecurityContext?)? createHttpClient,
String Function(Uri uri, Map<String, String>? environment)?
findProxyFromEnvironment}) {
HttpOverrides overrides =
- new _HttpOverridesScope(createHttpClient, findProxyFromEnvironment);
+ _HttpOverridesScope(createHttpClient, findProxyFromEnvironment);
return _asyncRunZoned<R>(body,
zoneValues: {_httpOverridesToken: overrides});
}
@@ -60,7 +60,7 @@
///
/// Note that [overrides] should be an instance of a class that extends
/// [HttpOverrides].
- static R runWithHttpOverrides<R>(R body(), HttpOverrides overrides) {
+ static R runWithHttpOverrides<R>(R Function() body, HttpOverrides overrides) {
return _asyncRunZoned<R>(body,
zoneValues: {_httpOverridesToken: overrides});
}
@@ -70,7 +70,7 @@
/// When this override is installed, this function overrides the behavior of
/// `new HttpClient`.
HttpClient createHttpClient(SecurityContext? context) {
- return new _HttpClient(context);
+ return _HttpClient(context);
}
/// Resolves the proxy server to be used for HTTP connections.
diff --git a/sdk/lib/_http/websocket.dart b/sdk/lib/_http/websocket.dart
index 4b9bbff..9503ae3 100644
--- a/sdk/lib/_http/websocket.dart
+++ b/sdk/lib/_http/websocket.dart
@@ -4,9 +4,7 @@
part of dart._http;
-/**
- * WebSocket status codes used when closing a WebSocket connection.
- */
+/// WebSocket status codes used when closing a WebSocket connection.
abstract class WebSocketStatus {
static const int normalClosure = 1000;
static const int goingAway = 1001;
@@ -69,8 +67,7 @@
/// * `serverNoContextTakeover`: false
/// * `clientMaxWindowBits`: null (default maximal window size of 15 bits)
/// * `serverMaxWindowBits`: null (default maximal window size of 15 bits)
- static const CompressionOptions compressionDefault =
- const CompressionOptions();
+ static const CompressionOptions compressionDefault = CompressionOptions();
@Deprecated("Use compressionDefault instead")
static const CompressionOptions DEFAULT = compressionDefault;
@@ -79,7 +76,7 @@
/// Disables compression when used as compression configuration for a
/// [WebSocket].
static const CompressionOptions compressionOff =
- const CompressionOptions(enabled: false);
+ CompressionOptions(enabled: false);
@Deprecated("Use compressionOff instead")
static const CompressionOptions OFF = compressionOff;
@@ -132,17 +129,17 @@
/// negotiated max window bits.
_CompressionMaxWindowBits _createServerResponseHeader(
HeaderValue? requested) {
- var info = new _CompressionMaxWindowBits("", 0);
+ var info = _CompressionMaxWindowBits("", 0);
String? part = requested?.parameters[_serverMaxWindowBits];
if (part != null) {
if (part.length >= 2 && part.startsWith('0')) {
- throw new ArgumentError("Illegal 0 padding on value.");
+ throw ArgumentError("Illegal 0 padding on value.");
} else {
int mwb = serverMaxWindowBits ??
int.tryParse(part) ??
_WebSocketImpl.DEFAULT_WINDOW_BITS;
- info.headerValue = "; server_max_window_bits=${mwb}";
+ info.headerValue = "; server_max_window_bits=$mwb";
info.maxWindowBits = mwb;
}
} else {
@@ -185,7 +182,7 @@
/// [_CompressionMaxWindowBits] object with the response headers and
/// negotiated `maxWindowBits` value.
_CompressionMaxWindowBits _createHeader([HeaderValue? requested]) {
- var info = new _CompressionMaxWindowBits("", 0);
+ var info = _CompressionMaxWindowBits("", 0);
if (!enabled) {
return info;
}
@@ -194,15 +191,13 @@
if (clientNoContextTakeover &&
(requested == null ||
- (requested != null &&
- requested.parameters.containsKey(_clientNoContextTakeover)))) {
+ (requested.parameters.containsKey(_clientNoContextTakeover)))) {
info.headerValue += "; client_no_context_takeover";
}
if (serverNoContextTakeover &&
(requested == null ||
- (requested != null &&
- requested.parameters.containsKey(_serverNoContextTakeover)))) {
+ (requested.parameters.containsKey(_serverNoContextTakeover)))) {
info.headerValue += "; server_no_context_takeover";
}
@@ -217,100 +212,89 @@
}
}
-/**
- * The [WebSocketTransformer] provides the ability to upgrade a
- * [HttpRequest] to a [WebSocket] connection. It supports both
- * upgrading a single [HttpRequest] and upgrading a stream of
- * [HttpRequest]s.
- *
- * To upgrade a single [HttpRequest] use the static [upgrade] method.
- *
- * HttpServer server;
- * server.listen((request) {
- * if (...) {
- * WebSocketTransformer.upgrade(request).then((websocket) {
- * ...
- * });
- * } else {
- * // Do normal HTTP request processing.
- * }
- * });
- *
- * To transform a stream of [HttpRequest] events as it implements a
- * stream transformer that transforms a stream of HttpRequest into a
- * stream of WebSockets by upgrading each HttpRequest from the HTTP or
- * HTTPS server, to the WebSocket protocol.
- *
- * server.transform(new WebSocketTransformer()).listen((webSocket) => ...);
- *
- * This transformer strives to implement WebSockets as specified by RFC6455.
- */
+/// The [WebSocketTransformer] provides the ability to upgrade a
+/// [HttpRequest] to a [WebSocket] connection. It supports both
+/// upgrading a single [HttpRequest] and upgrading a stream of
+/// [HttpRequest]s.
+///
+/// To upgrade a single [HttpRequest] use the static [upgrade] method.
+///
+/// HttpServer server;
+/// server.listen((request) {
+/// if (...) {
+/// WebSocketTransformer.upgrade(request).then((websocket) {
+/// ...
+/// });
+/// } else {
+/// // Do normal HTTP request processing.
+/// }
+/// });
+///
+/// To transform a stream of [HttpRequest] events as it implements a
+/// stream transformer that transforms a stream of HttpRequest into a
+/// stream of WebSockets by upgrading each HttpRequest from the HTTP or
+/// HTTPS server, to the WebSocket protocol.
+///
+/// server.transform(new WebSocketTransformer()).listen((webSocket) => ...);
+///
+/// This transformer strives to implement WebSockets as specified by RFC6455.
abstract class WebSocketTransformer
implements StreamTransformer<HttpRequest, WebSocket> {
- /**
- * Create a new [WebSocketTransformer].
- *
- * If [protocolSelector] is provided, [protocolSelector] will be called to
- * select what protocol to use, if any were provided by the client.
- * [protocolSelector] is should return either a [String] or a [Future]
- * completing with a [String]. The [String] must exist in the list of
- * protocols.
- *
- * If [compression] is provided, the [WebSocket] created will be configured
- * to negotiate with the specified [CompressionOptions]. If none is specified
- * then the [WebSocket] will be created with the default [CompressionOptions].
- */
+ /// Create a new [WebSocketTransformer].
+ ///
+ /// If [protocolSelector] is provided, [protocolSelector] will be called to
+ /// select what protocol to use, if any were provided by the client.
+ /// [protocolSelector] is should return either a [String] or a [Future]
+ /// completing with a [String]. The [String] must exist in the list of
+ /// protocols.
+ ///
+ /// If [compression] is provided, the [WebSocket] created will be configured
+ /// to negotiate with the specified [CompressionOptions]. If none is specified
+ /// then the [WebSocket] will be created with the default [CompressionOptions].
factory WebSocketTransformer(
- {/*String|Future<String>*/ protocolSelector(List<String> protocols)?,
+ {/*String|Future<String>*/ Function(List<String> protocols)?
+ protocolSelector,
CompressionOptions compression = CompressionOptions.compressionDefault}) {
- return new _WebSocketTransformerImpl(protocolSelector, compression);
+ return _WebSocketTransformerImpl(protocolSelector, compression);
}
- /**
- * Upgrades a [HttpRequest] to a [WebSocket] connection. If the
- * request is not a valid WebSocket upgrade request an HTTP response
- * with status code 500 will be returned. Otherwise the returned
- * future will complete with the [WebSocket] when the upgrade process
- * is complete.
- *
- * If [protocolSelector] is provided, [protocolSelector] will be called to
- * select what protocol to use, if any were provided by the client.
- * [protocolSelector] is should return either a [String] or a [Future]
- * completing with a [String]. The [String] must exist in the list of
- * protocols.
- *
- * If [compression] is provided, the [WebSocket] created will be configured
- * to negotiate with the specified [CompressionOptions]. If none is specified
- * then the [WebSocket] will be created with the default [CompressionOptions].
- */
+ /// Upgrades a [HttpRequest] to a [WebSocket] connection. If the
+ /// request is not a valid WebSocket upgrade request an HTTP response
+ /// with status code 500 will be returned. Otherwise the returned
+ /// future will complete with the [WebSocket] when the upgrade process
+ /// is complete.
+ ///
+ /// If [protocolSelector] is provided, [protocolSelector] will be called to
+ /// select what protocol to use, if any were provided by the client.
+ /// [protocolSelector] is should return either a [String] or a [Future]
+ /// completing with a [String]. The [String] must exist in the list of
+ /// protocols.
+ ///
+ /// If [compression] is provided, the [WebSocket] created will be configured
+ /// to negotiate with the specified [CompressionOptions]. If none is specified
+ /// then the [WebSocket] will be created with the default [CompressionOptions].
static Future<WebSocket> upgrade(HttpRequest request,
- {protocolSelector(List<String> protocols)?,
+ {Function(List<String> protocols)? protocolSelector,
CompressionOptions compression = CompressionOptions.compressionDefault}) {
return _WebSocketTransformerImpl._upgrade(
request, protocolSelector, compression);
}
- /**
- * Checks whether the request is a valid WebSocket upgrade request.
- */
+ /// Checks whether the request is a valid WebSocket upgrade request.
static bool isUpgradeRequest(HttpRequest request) {
return _WebSocketTransformerImpl._isUpgradeRequest(request);
}
}
-/**
- * A two-way HTTP communication object for client or server applications.
- *
- * The stream exposes the messages received. A text message will be of type
- * `String` and a binary message will be of type `List<int>`.
- */
+/// A two-way HTTP communication object for client or server applications.
+///
+/// The stream exposes the messages received. A text message will be of type
+/// `String` and a binary message will be of type `List<int>`.
abstract class WebSocket
implements
Stream<dynamic /*String|List<int>*/ >,
StreamSink<dynamic /*String|List<int>*/ > {
- /**
- * Possible states of the connection.
- */
+ /// Possible states of the connection.
static const int connecting = 0;
static const int open = 1;
static const int closing = 2;
@@ -325,52 +309,48 @@
@Deprecated("Use closed instead")
static const int CLOSED = closed;
- /**
- * The interval between ping signals.
- *
- * A ping message is sent every [pingInterval], starting at the first
- * [pingInterval] after a new value has been assigned or a pong message has
- * been received. If a ping message is not answered by a pong message from the
- * peer, the `WebSocket` is assumed disconnected and the connection is closed
- * with a [WebSocketStatus.goingAway] close code. When a ping signal is sent,
- * the pong message must be received within [pingInterval].
- *
- * There are never two outstanding pings at any given time, and the next ping
- * timer starts when the pong is received.
- *
- * Set the [pingInterval] to `null` to disable sending ping messages.
- *
- * The default value is `null`.
- */
+ /// The interval between ping signals.
+ ///
+ /// A ping message is sent every [pingInterval], starting at the first
+ /// [pingInterval] after a new value has been assigned or a pong message has
+ /// been received. If a ping message is not answered by a pong message from the
+ /// peer, the `WebSocket` is assumed disconnected and the connection is closed
+ /// with a [WebSocketStatus.goingAway] close code. When a ping signal is sent,
+ /// the pong message must be received within [pingInterval].
+ ///
+ /// There are never two outstanding pings at any given time, and the next ping
+ /// timer starts when the pong is received.
+ ///
+ /// Set the [pingInterval] to `null` to disable sending ping messages.
+ ///
+ /// The default value is `null`.
Duration? pingInterval;
- /**
- * Create a new WebSocket connection. The URL supplied in [url]
- * must use the scheme `ws` or `wss`.
- *
- * The [protocols] argument is specifying the subprotocols the
- * client is willing to speak.
- *
- * The [headers] argument is specifying additional HTTP headers for
- * setting up the connection. This would typically be the `Origin`
- * header and potentially cookies. The keys of the map are the header
- * fields and the values are either String or List<String>.
- *
- * If [headers] is provided, there are a number of headers
- * which are controlled by the WebSocket connection process. These
- * headers are:
- *
- * - `connection`
- * - `sec-websocket-key`
- * - `sec-websocket-protocol`
- * - `sec-websocket-version`
- * - `upgrade`
- *
- * If any of these are passed in the `headers` map they will be ignored.
- *
- * If the `url` contains user information this will be passed as basic
- * authentication when setting up the connection.
- */
+ /// Create a new WebSocket connection. The URL supplied in [url]
+ /// must use the scheme `ws` or `wss`.
+ ///
+ /// The [protocols] argument is specifying the subprotocols the
+ /// client is willing to speak.
+ ///
+ /// The [headers] argument is specifying additional HTTP headers for
+ /// setting up the connection. This would typically be the `Origin`
+ /// header and potentially cookies. The keys of the map are the header
+ /// fields and the values are either String or List<String>.
+ ///
+ /// If [headers] is provided, there are a number of headers
+ /// which are controlled by the WebSocket connection process. These
+ /// headers are:
+ ///
+ /// - `connection`
+ /// - `sec-websocket-key`
+ /// - `sec-websocket-protocol`
+ /// - `sec-websocket-version`
+ /// - `upgrade`
+ ///
+ /// If any of these are passed in the `headers` map they will be ignored.
+ ///
+ /// If the `url` contains user information this will be passed as basic
+ /// authentication when setting up the connection.
static Future<WebSocket> connect(String url,
{Iterable<String>? protocols,
Map<String, dynamic>? headers,
@@ -384,106 +364,82 @@
' instead of `extends` if implementing this abstract class.')
WebSocket();
- /**
- * Creates a WebSocket from an already-upgraded socket.
- *
- * The initial WebSocket handshake must have occurred prior to this call. A
- * WebSocket client can automatically perform the handshake using
- * [WebSocket.connect], while a server can do so using
- * [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest],
- * [HttpResponse.detachSocket] may be called.
- *
- * [protocol] should be the protocol negotiated by this handshake, if any.
- *
- * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will
- * act as the client and mask the messages it sends. If it's `true`, it will
- * act as the server and will not mask its messages.
- *
- * If [compression] is provided, the [WebSocket] created will be configured
- * to negotiate with the specified [CompressionOptions]. If none is specified
- * then the [WebSocket] will be created with the default [CompressionOptions].
- */
+ /// Creates a WebSocket from an already-upgraded socket.
+ ///
+ /// The initial WebSocket handshake must have occurred prior to this call. A
+ /// WebSocket client can automatically perform the handshake using
+ /// [WebSocket.connect], while a server can do so using
+ /// [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest],
+ /// [HttpResponse.detachSocket] may be called.
+ ///
+ /// [protocol] should be the protocol negotiated by this handshake, if any.
+ ///
+ /// [serverSide] must be passed explicitly. If it's `false`, the WebSocket will
+ /// act as the client and mask the messages it sends. If it's `true`, it will
+ /// act as the server and will not mask its messages.
+ ///
+ /// If [compression] is provided, the [WebSocket] created will be configured
+ /// to negotiate with the specified [CompressionOptions]. If none is specified
+ /// then the [WebSocket] will be created with the default [CompressionOptions].
factory WebSocket.fromUpgradedSocket(Socket socket,
{String? protocol,
bool? serverSide,
CompressionOptions compression = CompressionOptions.compressionDefault}) {
if (serverSide == null) {
- throw new ArgumentError("The serverSide argument must be passed "
+ throw ArgumentError("The serverSide argument must be passed "
"explicitly to WebSocket.fromUpgradedSocket.");
}
- return new _WebSocketImpl._fromSocket(
+ return _WebSocketImpl._fromSocket(
socket, protocol, compression, serverSide);
}
- /**
- * Returns the current state of the connection.
- */
+ /// Returns the current state of the connection.
int get readyState;
- /**
- * The extensions property is initially the empty string. After the
- * WebSocket connection is established this string reflects the
- * extensions used by the server.
- */
+ /// The extensions property is initially the empty string. After the
+ /// WebSocket connection is established this string reflects the
+ /// extensions used by the server.
String get extensions;
- /**
- * The protocol property is initially the empty string. After the
- * WebSocket connection is established the value is the subprotocol
- * selected by the server. If no subprotocol is negotiated the
- * value will remain [:null:].
- */
+ /// The protocol property is initially the empty string. After the
+ /// WebSocket connection is established the value is the subprotocol
+ /// selected by the server. If no subprotocol is negotiated the
+ /// value will remain [:null:].
String? get protocol;
- /**
- * The close code set when the WebSocket connection is closed. If
- * there is no close code available this property will be [:null:]
- */
+ /// The close code set when the WebSocket connection is closed. If
+ /// there is no close code available this property will be [:null:]
int? get closeCode;
- /**
- * The close reason set when the WebSocket connection is closed. If
- * there is no close reason available this property will be [:null:]
- */
+ /// The close reason set when the WebSocket connection is closed. If
+ /// there is no close reason available this property will be [:null:]
String? get closeReason;
- /**
- * Closes the WebSocket connection. Set the optional [code] and [reason]
- * arguments to send close information to the remote peer. If they are
- * omitted, the peer will see [WebSocketStatus.noStatusReceived] code
- * with no reason.
- */
+ /// Closes the WebSocket connection. Set the optional [code] and [reason]
+ /// arguments to send close information to the remote peer. If they are
+ /// omitted, the peer will see [WebSocketStatus.noStatusReceived] code
+ /// with no reason.
Future close([int? code, String? reason]);
- /**
- * Sends data on the WebSocket connection. The data in [data] must
- * be either a `String`, or a `List<int>` holding bytes.
- */
+ /// Sends data on the WebSocket connection. The data in [data] must
+ /// be either a `String`, or a `List<int>` holding bytes.
void add(/*String|List<int>*/ data);
- /**
- * Sends data from a stream on WebSocket connection. Each data event from
- * [stream] will be send as a single WebSocket frame. The data from [stream]
- * must be either `String`s, or `List<int>`s holding bytes.
- */
+ /// Sends data from a stream on WebSocket connection. Each data event from
+ /// [stream] will be send as a single WebSocket frame. The data from [stream]
+ /// must be either `String`s, or `List<int>`s holding bytes.
Future addStream(Stream stream);
- /**
- * Sends a text message with the text represented by [bytes].
- *
- * The [bytes] should be valid UTF-8 encoded Unicode characters. If they are
- * not, the receiving end will close the connection.
- */
+ /// Sends a text message with the text represented by [bytes].
+ ///
+ /// The [bytes] should be valid UTF-8 encoded Unicode characters. If they are
+ /// not, the receiving end will close the connection.
void addUtf8Text(List<int> bytes);
- /**
- * Gets the user agent used for WebSocket connections.
- */
+ /// Gets the user agent used for WebSocket connections.
static String? get userAgent => _WebSocketImpl.userAgent;
- /**
- * Sets the user agent to use for WebSocket connections.
- */
+ /// Sets the user agent to use for WebSocket connections.
static set userAgent(String? userAgent) {
_WebSocketImpl.userAgent = userAgent;
}
diff --git a/sdk/lib/_http/websocket_impl.dart b/sdk/lib/_http/websocket_impl.dart
index fe2c5a7..e43c3e5 100644
--- a/sdk/lib/_http/websocket_impl.dart
+++ b/sdk/lib/_http/websocket_impl.dart
@@ -41,11 +41,9 @@
_EncodedString(this.bytes);
}
-/**
- * Stores the header and integer value derived from negotiation of
- * client_max_window_bits and server_max_window_bits. headerValue will be
- * set in the Websocket response headers.
- */
+/// Stores the header and integer value derived from negotiation of
+/// client_max_window_bits and server_max_window_bits. headerValue will be
+/// set in the Websocket response headers.
class _CompressionMaxWindowBits {
String headerValue;
int maxWindowBits;
@@ -53,15 +51,13 @@
String toString() => headerValue;
}
-/**
- * The web socket protocol transformer handles the protocol byte stream
- * which is supplied through the `handleData`. As the protocol is processed,
- * it'll output frame data as either a List<int> or String.
- *
- * Important information about usage: Be sure you use cancelOnError, so the
- * socket will be closed when the processor encounter an error. Not using it
- * will lead to undefined behaviour.
- */
+/// The web socket protocol transformer handles the protocol byte stream
+/// which is supplied through the `handleData`. As the protocol is processed,
+/// it'll output frame data as either a List<int> or String.
+///
+/// Important information about usage: Be sure you use cancelOnError, so the
+/// socket will be closed when the processor encounter an error. Not using it
+/// will lead to undefined behaviour.
class _WebSocketProtocolTransformer extends StreamTransformerBase<List<int>,
dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ >
implements EventSink<List<int>> {
@@ -89,23 +85,23 @@
int _remainingPayloadBytes = -1;
int _unmaskingIndex = 0;
int _currentMessageType = _WebSocketMessageType.NONE;
- int closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
+ int closeCode = WebSocketStatus.noStatusReceived;
String closeReason = "";
EventSink<dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ >? _eventSink;
final bool _serverSide;
final Uint8List _maskingBytes = Uint8List(4);
- final BytesBuilder _payload = new BytesBuilder(copy: false);
+ final BytesBuilder _payload = BytesBuilder(copy: false);
- _WebSocketPerMessageDeflate? _deflate;
+ final _WebSocketPerMessageDeflate? _deflate;
_WebSocketProtocolTransformer([this._serverSide = false, this._deflate]);
Stream<dynamic /*List<int>|_WebSocketPing|_WebSocketPong*/ > bind(
Stream<List<int>> stream) {
- return new Stream.eventTransformed(stream, (EventSink eventSink) {
+ return Stream.eventTransformed(stream, (EventSink eventSink) {
if (_eventSink != null) {
- throw new StateError("WebSocket transformer already used.");
+ throw StateError("WebSocket transformer already used.");
}
_eventSink = eventSink;
return this;
@@ -122,18 +118,16 @@
_eventSink!.close();
}
- /**
- * Process data received from the underlying communication channel.
- */
+ /// Process data received from the underlying communication channel.
void add(List<int> bytes) {
- var buffer = bytes is Uint8List ? bytes : new Uint8List.fromList(bytes);
+ var buffer = bytes is Uint8List ? bytes : Uint8List.fromList(bytes);
int index = 0;
int lastIndex = buffer.length;
if (_state == CLOSED) {
- throw new WebSocketException("Data on closed connection");
+ throw WebSocketException("Data on closed connection");
}
if (_state == FAILURE) {
- throw new WebSocketException("Data on failed connection");
+ throw WebSocketException("Data on failed connection");
}
while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) {
int byte = buffer[index];
@@ -143,7 +137,7 @@
if ((byte & (RSV2 | RSV3)) != 0) {
// The RSV2, RSV3 bits must both be zero.
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
_opcode = (byte & OPCODE);
@@ -159,29 +153,29 @@
if (_opcode <= _WebSocketOpcode.BINARY) {
if (_opcode == _WebSocketOpcode.CONTINUATION) {
if (_currentMessageType == _WebSocketMessageType.NONE) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
} else {
assert(_opcode == _WebSocketOpcode.TEXT ||
_opcode == _WebSocketOpcode.BINARY);
if (_currentMessageType != _WebSocketMessageType.NONE) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
_currentMessageType = _opcode;
}
} else if (_opcode >= _WebSocketOpcode.CLOSE &&
_opcode <= _WebSocketOpcode.PONG) {
// Control frames cannot be fragmented.
- if (!_fin) throw new WebSocketException("Protocol error");
+ if (!_fin) throw WebSocketException("Protocol error");
} else {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
_state = LEN_FIRST;
} else if (_state == LEN_FIRST) {
_masked = (byte & 0x80) != 0;
_len = byte & 0x7F;
if (_isControlFrame() && _len > 125) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
if (_len == 126) {
_len = 0;
@@ -219,7 +213,7 @@
_unmask(index, payloadLength, buffer);
}
// Control frame and data frame share _payloads.
- _payload.add(new Uint8List.view(
+ _payload.add(Uint8List.view(
buffer.buffer, buffer.offsetInBytes + index, payloadLength));
index += payloadLength;
if (_isControlFrame()) {
@@ -227,7 +221,7 @@
} else {
if (_currentMessageType != _WebSocketMessageType.TEXT &&
_currentMessageType != _WebSocketMessageType.BINARY) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
if (_remainingPayloadBytes == 0) _messageFrameEnd();
}
@@ -261,8 +255,8 @@
for (int i = 3; i >= 0; i--) {
mask = (mask << 8) | _maskingBytes[(_unmaskingIndex + i) & 3];
}
- Int32x4 blockMask = new Int32x4(mask, mask, mask, mask);
- Int32x4List blockBuffer = new Int32x4List.view(
+ Int32x4 blockMask = Int32x4(mask, mask, mask, mask);
+ Int32x4List blockBuffer = Int32x4List.view(
buffer.buffer, buffer.offsetInBytes + index, blockCount);
for (int i = 0; i < blockBuffer.length; i++) {
blockBuffer[i] ^= blockMask;
@@ -282,12 +276,12 @@
void _lengthDone() {
if (_masked) {
if (!_serverSide) {
- throw new WebSocketException("Received masked frame from server");
+ throw WebSocketException("Received masked frame from server");
}
_state = MASK;
} else {
if (_serverSide) {
- throw new WebSocketException("Received unmasked frame from client");
+ throw WebSocketException("Received unmasked frame from client");
}
_remainingPayloadBytes = _len;
_startPayload();
@@ -310,10 +304,10 @@
_eventSink!.close();
break;
case _WebSocketOpcode.PING:
- _eventSink!.add(new _WebSocketPing());
+ _eventSink!.add(_WebSocketPing());
break;
case _WebSocketOpcode.PONG:
- _eventSink!.add(new _WebSocketPong());
+ _eventSink!.add(_WebSocketPong());
break;
}
_prepareForNextFrame();
@@ -351,13 +345,13 @@
case _WebSocketOpcode.CLOSE:
closeCode = WebSocketStatus.noStatusReceived;
var payload = _payload.takeBytes();
- if (payload.length > 0) {
+ if (payload.isNotEmpty) {
if (payload.length == 1) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
closeCode = payload[0] << 8 | payload[1];
if (closeCode == WebSocketStatus.noStatusReceived) {
- throw new WebSocketException("Protocol error");
+ throw WebSocketException("Protocol error");
}
if (payload.length > 2) {
closeReason = utf8.decode(payload.sublist(2));
@@ -368,11 +362,11 @@
break;
case _WebSocketOpcode.PING:
- _eventSink!.add(new _WebSocketPing(_payload.takeBytes()));
+ _eventSink!.add(_WebSocketPing(_payload.takeBytes()));
break;
case _WebSocketOpcode.PONG:
- _eventSink!.add(new _WebSocketPong(_payload.takeBytes()));
+ _eventSink!.add(_WebSocketPong(_payload.takeBytes()));
break;
}
_prepareForNextFrame();
@@ -398,21 +392,22 @@
class _WebSocketPing {
final List<int>? payload;
- _WebSocketPing([this.payload = null]);
+ _WebSocketPing([this.payload]);
}
class _WebSocketPong {
final List<int>? payload;
- _WebSocketPong([this.payload = null]);
+ _WebSocketPong([this.payload]);
}
-typedef /*String|Future<String>*/ _ProtocolSelector(List<String> protocols);
+typedef /*String|Future<String>*/ _ProtocolSelector = Function(
+ List<String> protocols);
class _WebSocketTransformerImpl
extends StreamTransformerBase<HttpRequest, WebSocket>
implements WebSocketTransformer {
final StreamController<WebSocket> _controller =
- new StreamController<WebSocket>(sync: true);
+ StreamController<WebSocket>(sync: true);
final _ProtocolSelector? _protocolSelector;
final CompressionOptions _compression;
@@ -455,8 +450,8 @@
response
..statusCode = HttpStatus.badRequest
..close();
- return new Future.error(
- new WebSocketException("Invalid WebSocket upgrade request"));
+ return Future.error(
+ WebSocketException("Invalid WebSocket upgrade request"));
}
Future<WebSocket> upgrade(String? protocol) {
@@ -466,7 +461,7 @@
..headers.add(HttpHeaders.connectionHeader, "Upgrade")
..headers.add(HttpHeaders.upgradeHeader, "websocket");
String key = request.headers.value("Sec-WebSocket-Key")!;
- _SHA1 sha1 = new _SHA1();
+ _SHA1 sha1 = _SHA1();
sha1.add("$key$_webSocketGUID".codeUnits);
String accept = _CryptoUtils.bytesToBase64(sha1.close());
response.headers.add("Sec-WebSocket-Accept", accept);
@@ -478,7 +473,7 @@
response.headers.contentLength = 0;
return response.detachSocket().then<WebSocket>((socket) =>
- new _WebSocketImpl._fromSocket(
+ _WebSocketImpl._fromSocket(
socket, protocol, compression, true, deflate));
}
@@ -488,10 +483,10 @@
// consisting of multiple protocols. To unify all of them, first join
// the lists with ', ' and then tokenize.
var tokenizedProtocols = _tokenizeFieldValue(protocols.join(', '));
- return new Future<String>(() => protocolSelector(tokenizedProtocols))
+ return Future<String>(() => protocolSelector(tokenizedProtocols))
.then<String>((protocol) {
- if (tokenizedProtocols.indexOf(protocol) < 0) {
- throw new WebSocketException(
+ if (!tokenizedProtocols.contains(protocol)) {
+ throw WebSocketException(
"Selected protocol is not in the list of available protocols");
}
return protocol;
@@ -523,7 +518,7 @@
var clientNoContextTakeover =
(hv.parameters.containsKey(_clientNoContextTakeover) &&
compression.clientNoContextTakeover);
- var deflate = new _WebSocketPerMessageDeflate(
+ var deflate = _WebSocketPerMessageDeflate(
serverNoContextTakeover: serverNoContextTakeover,
clientNoContextTakeover: clientNoContextTakeover,
serverMaxWindowBits: info.maxWindowBits,
@@ -585,11 +580,11 @@
this.clientNoContextTakeover = false,
this.serverSide = false});
- RawZLibFilter _ensureDecoder() => decoder ??= new RawZLibFilter.inflateFilter(
+ RawZLibFilter _ensureDecoder() => decoder ??= RawZLibFilter.inflateFilter(
windowBits: serverSide ? clientMaxWindowBits : serverMaxWindowBits,
raw: true);
- RawZLibFilter _ensureEncoder() => encoder ??= new RawZLibFilter.deflateFilter(
+ RawZLibFilter _ensureEncoder() => encoder ??= RawZLibFilter.deflateFilter(
windowBits: serverSide ? serverMaxWindowBits : clientMaxWindowBits,
raw: true);
@@ -601,8 +596,7 @@
data.addAll(const [0x00, 0x00, 0xff, 0xff]);
decoder.process(data, 0, data.length);
- final result = new BytesBuilder();
- List<int> out;
+ final result = BytesBuilder();
while (true) {
final out = decoder.processed();
@@ -626,11 +620,11 @@
if (msg is! Uint8List) {
for (var i = 0; i < msg.length; i++) {
if (msg[i] < 0 || 255 < msg[i]) {
- throw new ArgumentError("List element is not a byte value "
+ throw ArgumentError("List element is not a byte value "
"(value ${msg[i]} at index $i)");
}
}
- buffer = new Uint8List.fromList(msg);
+ buffer = Uint8List.fromList(msg);
} else {
buffer = msg;
}
@@ -657,7 +651,7 @@
// then an empty uncompressed deflate block is used for this purpose. The
// 0x00 block has the BFINAL header bit set to 0 and the BTYPE header set to
// 00 along with 5 bits of padding. This block decodes to zero bytes.
- if (result.length == 0) {
+ if (result.isEmpty) {
return [0x00];
}
@@ -671,16 +665,16 @@
final _WebSocketImpl webSocket;
EventSink<List<int>>? _eventSink;
- _WebSocketPerMessageDeflate? _deflateHelper;
+ final _WebSocketPerMessageDeflate? _deflateHelper;
_WebSocketOutgoingTransformer(this.webSocket)
: _deflateHelper = webSocket._deflate;
Stream<List<int>> bind(Stream stream) {
- return new Stream<List<int>>.eventTransformed(stream,
+ return Stream<List<int>>.eventTransformed(stream,
(EventSink<List<int>> eventSink) {
if (_eventSink != null) {
- throw new StateError("WebSocket transformer already used");
+ throw StateError("WebSocket transformer already used");
}
_eventSink = eventSink;
return this;
@@ -710,7 +704,7 @@
opcode = _WebSocketOpcode.TEXT;
messageData = message.bytes;
} else {
- throw new ArgumentError(message);
+ throw ArgumentError(message);
}
var deflateHelper = _deflateHelper;
if (deflateHelper != null) {
@@ -768,7 +762,7 @@
} else if (dataLength > 125) {
headerSize += 2;
}
- Uint8List header = new Uint8List(headerSize);
+ Uint8List header = Uint8List(headerSize);
int index = 0;
// Set FIN and opcode.
@@ -803,12 +797,12 @@
list = data;
} else {
if (data is Uint8List) {
- list = new Uint8List.fromList(data);
+ list = Uint8List.fromList(data);
} else {
- list = new Uint8List(data.length);
+ list = Uint8List(data.length);
for (int i = 0; i < data.length; i++) {
if (data[i] < 0 || 255 < data[i]) {
- throw new ArgumentError("List element is not a byte value "
+ throw ArgumentError("List element is not a byte value "
"(value ${data[i]} at index $i)");
}
list[i] = data[i];
@@ -823,9 +817,9 @@
for (int i = 3; i >= 0; i--) {
mask = (mask << 8) | maskBytes[i];
}
- Int32x4 blockMask = new Int32x4(mask, mask, mask, mask);
+ Int32x4 blockMask = Int32x4(mask, mask, mask, mask);
Int32x4List blockBuffer =
- new Int32x4List.view(list.buffer, list.offsetInBytes, blockCount);
+ Int32x4List.view(list.buffer, list.offsetInBytes, blockCount);
for (int i = 0; i < blockBuffer.length; i++) {
blockBuffer[i] ^= blockMask;
}
@@ -853,7 +847,7 @@
StreamSubscription? _subscription;
bool _issuedPause = false;
bool _closed = false;
- Completer _closeCompleter = new Completer<WebSocket>();
+ final Completer _closeCompleter = Completer<WebSocket>();
Completer? _completer;
_WebSocketConsumer(this.webSocket, this.socket);
@@ -891,13 +885,13 @@
StreamController _ensureController() {
var controller = _controller;
if (controller != null) return controller;
- controller = _controller = new StreamController(
+ controller = _controller = StreamController(
sync: true,
onPause: _onPause,
onResume: _onResume,
onCancel: _onListen);
- var stream = controller.stream
- .transform(new _WebSocketOutgoingTransformer(webSocket));
+ var stream =
+ controller.stream.transform(_WebSocketOutgoingTransformer(webSocket));
socket.addStream(stream).then((_) {
_done();
_closeCompleter.complete(webSocket);
@@ -931,10 +925,10 @@
Future addStream(Stream stream) {
if (_closed) {
stream.listen(null).cancel();
- return new Future.value(webSocket);
+ return Future.value(webSocket);
}
_ensureController();
- var completer = _completer = new Completer();
+ var completer = _completer = Completer();
var subscription = _subscription = stream.listen((data) {
_controller!.add(data);
}, onDone: _done, onError: _done, cancelOnError: true);
@@ -970,7 +964,7 @@
class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
// Use default Map so we keep order.
- static Map<int, _WebSocketImpl> _webSockets = new Map<int, _WebSocketImpl>();
+ static final Map<int, _WebSocketImpl> _webSockets = <int, _WebSocketImpl>{};
static const int DEFAULT_WINDOW_BITS = 15;
static const String PER_MESSAGE_DEFLATE = "permessage-deflate";
@@ -995,7 +989,7 @@
Timer? _closeTimer;
_WebSocketPerMessageDeflate? _deflate;
- static final HttpClient _httpClient = new HttpClient();
+ static final HttpClient _httpClient = HttpClient();
static Future<WebSocket> connect(
String url, Iterable<String>? protocols, Map<String, dynamic>? headers,
@@ -1003,12 +997,12 @@
HttpClient? customClient}) {
Uri uri = Uri.parse(url);
if (uri.scheme != "ws" && uri.scheme != "wss") {
- throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
+ throw WebSocketException("Unsupported URL scheme '${uri.scheme}'");
}
- Random random = new Random();
+ Random random = Random();
// Generate 16 random bytes.
- Uint8List nonceData = new Uint8List(16);
+ Uint8List nonceData = Uint8List(16);
for (int i = 0; i < 16; i++) {
nonceData[i] = random.nextInt(256);
}
@@ -1016,7 +1010,7 @@
final callerStackTrace = StackTrace.current;
- uri = new Uri(
+ uri = Uri(
scheme: uri.scheme == "wss" ? "https" : "http",
userInfo: uri.userInfo,
host: uri.host,
@@ -1025,7 +1019,7 @@
query: uri.query,
fragment: uri.fragment);
return (customClient ?? _httpClient).openUrl("GET", uri).then((request) {
- if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+ if (uri.userInfo != null && uri.userInfo.isNotEmpty) {
// If the URL contains user information use that for basic
// authorization.
String auth = _CryptoUtils.bytesToBase64(utf8.encode(uri.userInfo));
@@ -1058,7 +1052,7 @@
socket.destroy();
});
return Future<WebSocket>.error(
- new WebSocketException(message), callerStackTrace);
+ WebSocketException(message), callerStackTrace);
}
var connectionHeader = response.headers[HttpHeaders.connectionHeader];
@@ -1074,7 +1068,7 @@
return error(
"Response did not contain a 'Sec-WebSocket-Accept' header");
}
- _SHA1 sha1 = new _SHA1();
+ _SHA1 sha1 = _SHA1();
sha1.add("$nonce$_webSocketGUID".codeUnits);
List<int> expectedAccept = sha1.close();
List<int> receivedAccept = _CryptoUtils.base64StringToBytes(accept);
@@ -1093,7 +1087,7 @@
negotiateClientCompression(response, compression);
return response.detachSocket().then<WebSocket>((socket) =>
- new _WebSocketImpl._fromSocket(
+ _WebSocketImpl._fromSocket(
socket, protocol, compression, false, deflate));
});
}
@@ -1120,7 +1114,7 @@
return int.tryParse(o) ?? DEFAULT_WINDOW_BITS;
}
- return new _WebSocketPerMessageDeflate(
+ return _WebSocketPerMessageDeflate(
clientMaxWindowBits: getWindowBits(_clientMaxWindowBits),
serverMaxWindowBits: getWindowBits(_serverMaxWindowBits),
clientNoContextTakeover: clientNoContextTakeover,
@@ -1133,16 +1127,16 @@
_WebSocketImpl._fromSocket(
this._socket, this.protocol, CompressionOptions compression,
[this._serverSide = false, _WebSocketPerMessageDeflate? deflate])
- : _controller = new StreamController(sync: true) {
- _consumer = new _WebSocketConsumer(this, _socket);
- _sink = new _StreamSinkImpl(_consumer);
+ : _controller = StreamController(sync: true) {
+ _consumer = _WebSocketConsumer(this, _socket);
+ _sink = _StreamSinkImpl(_consumer);
_readyState = WebSocket.open;
_deflate = deflate;
- var transformer = new _WebSocketProtocolTransformer(_serverSide, deflate);
+ var transformer = _WebSocketProtocolTransformer(_serverSide, deflate);
var subscription = _subscription = transformer.bind(_socket).listen((data) {
if (data is _WebSocketPing) {
- if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload));
+ if (!_writeClosed) _consumer.add(_WebSocketPong(data.payload));
} else if (data is _WebSocketPong) {
// Simply set pingInterval, as it'll cancel any timers.
pingInterval = _pingInterval;
@@ -1190,7 +1184,7 @@
}
StreamSubscription listen(void onData(message)?,
- {Function? onError, void onDone()?, bool? cancelOnError}) {
+ {Function? onError, void Function()? onDone, bool? cancelOnError}) {
return _controller.stream.listen(onData,
onError: onError, onDone: onDone, cancelOnError: cancelOnError);
}
@@ -1204,10 +1198,10 @@
if (interval == null) return;
- _pingTimer = new Timer(interval, () {
+ _pingTimer = Timer(interval, () {
if (_writeClosed) return;
- _consumer.add(new _WebSocketPing());
- _pingTimer = new Timer(interval, () {
+ _consumer.add(_WebSocketPing());
+ _pingTimer = Timer(interval, () {
_closeTimer?.cancel();
// No pong received.
_close(WebSocketStatus.goingAway);
@@ -1231,7 +1225,7 @@
void addUtf8Text(List<int> bytes) {
// TODO(40614): Remove once non-nullability is sound.
ArgumentError.checkNotNull(bytes, "bytes");
- _sink.add(new _EncodedString(bytes));
+ _sink.add(_EncodedString(bytes));
}
void addError(Object error, [StackTrace? stackTrace]) {
@@ -1243,7 +1237,7 @@
Future close([int? code, String? reason]) {
if (_isReservedStatusCode(code)) {
- throw new WebSocketException("Reserved status code $code");
+ throw WebSocketException("Reserved status code $code");
}
if (_outCloseCode == null) {
_outCloseCode = code;
@@ -1258,17 +1252,14 @@
if (!_controller.hasListener && _subscription != null) {
_controller.stream.drain().catchError((_) => {});
}
- if (_closeTimer == null) {
- // When closing the web-socket, we no longer accept data.
- _closeTimer = new Timer(const Duration(seconds: 5), () {
- // Reuse code and reason from the local close.
- _closeCode = _outCloseCode;
- _closeReason = _outCloseReason;
- _subscription?.cancel();
- _controller.close();
- _webSockets.remove(_serviceId);
- });
- }
+ _closeTimer ??= Timer(const Duration(seconds: 5), () {
+ // Reuse code and reason from the local close.
+ _closeCode = _outCloseCode;
+ _closeReason = _outCloseReason;
+ _subscription?.cancel();
+ _controller.close();
+ _webSockets.remove(_serviceId);
+ });
}
return _sink.close();
}
diff --git a/tools/VERSION b/tools/VERSION
index 5f2e7ac..f7fff19 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
MAJOR 2
MINOR 15
PATCH 0
-PRERELEASE 242
+PRERELEASE 243
PRERELEASE_PATCH 0
\ No newline at end of file
diff --git a/tools/experimental_features.yaml b/tools/experimental_features.yaml
index bf28d56..3c3b0a7 100644
--- a/tools/experimental_features.yaml
+++ b/tools/experimental_features.yaml
@@ -128,6 +128,9 @@
named-arguments-anywhere:
help: "Named Arguments Anywhere"
+ super-parameters:
+ help: "Super-Initializer Parameters"
+
# Experiment flag only used for testing.
test-experiment:
help: >-