diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index d52c58c1862..9639ac1cb2a 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -22,6 +22,7 @@ export 'src/services/message_codec.dart'; 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 00000000000..94a5d6d4fbf --- /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 action()) { + return runZoned(action, zoneValues: {#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 00000000000..e5b633b6aa8 --- /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: 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')); + } + }); + } +}