Reland "Add API to services package that overrides HTTP ban (#54243)" (#54522)

diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart
index d52c58c..9639ac1 100644
--- a/packages/flutter/lib/services.dart
+++ b/packages/flutter/lib/services.dart
@@ -22,6 +22,7 @@
 export 'src/services/message_codecs.dart';
 export 'src/services/platform_channel.dart';
 export 'src/services/platform_messages.dart';
+export 'src/services/platform_network.dart';
 export 'src/services/platform_views.dart';
 export 'src/services/raw_keyboard.dart';
 export 'src/services/raw_keyboard_android.dart';
diff --git a/packages/flutter/lib/src/services/platform_network.dart b/packages/flutter/lib/src/services/platform_network.dart
new file mode 100644
index 0000000..94a5d6d
--- /dev/null
+++ b/packages/flutter/lib/src/services/platform_network.dart
@@ -0,0 +1,29 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+
+/// Allows an operation executed via [action] to access insecure HTTP URLs.
+///
+/// On some platforms (notably iOS and Android), HTTP is disallowed by default.
+/// You should strive to access secure URLs from your app, but we recognize that
+/// sometimes that is not possible. In such cases, use the function below to
+/// allow access to HTTP URLs.
+///
+/// On Web, we delegate security to the browser policies (such as CORS).
+///
+/// Sample usage:
+///
+/// ```dart
+/// import 'package:flutter/services.dart' as services;
+///
+/// final Image image = services.allowHttp(() => Image.network('http://some_insecure_url');
+/// ```
+///
+/// Best Practices:
+/// * Do not wrap your entire app with [allowHttp]. Wrap *exactly* what you need and nothing more.
+/// * Avoid libraries that require accessing HTTP URLs.
+T allowHttp<T>(T action()) {
+  return runZoned<T>(action, zoneValues: <Symbol, bool>{#dart.library.io.allow_http: true});
+}
diff --git a/packages/flutter/test/services/platform_network_test.dart b/packages/flutter/test/services/platform_network_test.dart
new file mode 100644
index 0000000..e5b633b
--- /dev/null
+++ b/packages/flutter/test/services/platform_network_test.dart
@@ -0,0 +1,41 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/services.dart';
+
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+  const Symbol _symbol = #dart.library.io.allow_http;
+
+  test('AllowHTTP sets the correct zone variable', () async {
+    expect(Zone.current[_symbol], isNull);
+    allowHttp(() {
+      expect(Zone.current[_symbol], isTrue);
+    });
+  });
+
+  if (!kIsWeb) {
+    // This test ensures the zone variable used in Dart SDK does not change.
+    //
+    // If this symbol changes, then update [allowHttp] function as well.
+    test('Zone variable can override HTTP behavior', () async {
+      final HttpClient httpClient = HttpClient();
+      try {
+        await runZoned(
+          () async => await httpClient.getUrl(Uri.parse('http://${Platform.localHostname}')),
+          zoneValues: <Symbol, bool>{_symbol: false},
+        );
+        fail('This should have thrown a StateError. '
+             'Check if the symbol for setting allow_http behavior has changed');
+      } on StateError catch(e) {
+        expect(e.message, contains('Insecure HTTP is not allowed by the current platform'));
+      }
+    });
+  }
+}