From 68fb3e4868fb8183cbcae39bcbbafc90cce98129 Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Tue, 7 Mar 2017 18:08:36 -0800 Subject: [PATCH] Farewell, local fork of the http package. (#8642) --- examples/stocks/lib/stock_data.dart | 16 +- examples/stocks/pubspec.yaml | 1 + packages/flutter/lib/http.dart | 20 -- packages/flutter/lib/services.dart | 5 +- packages/flutter/lib/src/http/.dartignore | 0 packages/flutter/lib/src/http/README.md | 7 - .../flutter/lib/src/http/base_client.dart | 199 ------------------ .../flutter/lib/src/http/base_request.dart | 141 ------------- .../flutter/lib/src/http/base_response.dart | 53 ----- .../flutter/lib/src/http/byte_stream.dart | 36 ---- packages/flutter/lib/src/http/client.dart | 148 ------------- packages/flutter/lib/src/http/exception.dart | 16 -- packages/flutter/lib/src/http/http.dart | 171 --------------- packages/flutter/lib/src/http/io.dart | 26 --- packages/flutter/lib/src/http/io_client.dart | 90 -------- .../flutter/lib/src/http/mock_client.dart | 88 -------- .../flutter/lib/src/http/multipart_file.dart | 111 ---------- .../lib/src/http/multipart_request.dart | 177 ---------------- packages/flutter/lib/src/http/request.dart | 164 --------------- packages/flutter/lib/src/http/response.dart | 95 --------- .../lib/src/http/streamed_request.dart | 42 ---- .../lib/src/http/streamed_response.dart | 39 ---- packages/flutter/lib/src/http/utils.dart | 143 ------------- .../lib/src/services/asset_bundle.dart | 12 +- .../flutter/lib/src/services/http_client.dart | 16 ++ .../lib/src/services/image_provider.dart | 7 +- packages/flutter/pubspec.yaml | 6 +- packages/flutter_test/lib/src/binding.dart | 6 +- 28 files changed, 48 insertions(+), 1787 deletions(-) delete mode 100644 packages/flutter/lib/http.dart delete mode 100644 packages/flutter/lib/src/http/.dartignore delete mode 100644 packages/flutter/lib/src/http/README.md delete mode 100644 packages/flutter/lib/src/http/base_client.dart delete mode 100644 packages/flutter/lib/src/http/base_request.dart delete mode 100644 packages/flutter/lib/src/http/base_response.dart delete mode 100644 packages/flutter/lib/src/http/byte_stream.dart delete mode 100644 packages/flutter/lib/src/http/client.dart delete mode 100644 packages/flutter/lib/src/http/exception.dart delete mode 100644 packages/flutter/lib/src/http/http.dart delete mode 100644 packages/flutter/lib/src/http/io.dart delete mode 100644 packages/flutter/lib/src/http/io_client.dart delete mode 100644 packages/flutter/lib/src/http/mock_client.dart delete mode 100644 packages/flutter/lib/src/http/multipart_file.dart delete mode 100644 packages/flutter/lib/src/http/multipart_request.dart delete mode 100644 packages/flutter/lib/src/http/request.dart delete mode 100644 packages/flutter/lib/src/http/response.dart delete mode 100644 packages/flutter/lib/src/http/streamed_request.dart delete mode 100644 packages/flutter/lib/src/http/streamed_response.dart delete mode 100644 packages/flutter/lib/src/http/utils.dart create mode 100644 packages/flutter/lib/src/services/http_client.dart diff --git a/examples/stocks/lib/stock_data.dart b/examples/stocks/lib/stock_data.dart index 7ab4c6584af..cd82d8b2ff1 100644 --- a/examples/stocks/lib/stock_data.dart +++ b/examples/stocks/lib/stock_data.dart @@ -10,7 +10,8 @@ import 'dart:convert'; import 'dart:math' as math; -import 'package:flutter/http.dart' as http; +import 'package:flutter/services.dart'; +import 'package:http/http.dart' as http; final math.Random _rng = new math.Random(); @@ -60,19 +61,22 @@ String _urlToFetch(int chunk) { } class StockDataFetcher { - int _nextChunk = 0; + StockDataFetcher(this.callback) { + _httpClient = createHttpClient(); + _fetchNextChunk(); + } + final StockDataCallback callback; + http.Client _httpClient; static bool actuallyFetchData = true; - StockDataFetcher(this.callback) { - _fetchNextChunk(); - } + int _nextChunk = 0; void _fetchNextChunk() { if (!actuallyFetchData) return; - http.get(_urlToFetch(_nextChunk++)).then((http.Response response) { + _httpClient.get(_urlToFetch(_nextChunk++)).then((http.Response response) { final String json = response.body; if (json == null) { print("Failed to load stock data chunk ${_nextChunk - 1}"); diff --git a/examples/stocks/pubspec.yaml b/examples/stocks/pubspec.yaml index 07fb30f4fa2..f15f87d457e 100644 --- a/examples/stocks/pubspec.yaml +++ b/examples/stocks/pubspec.yaml @@ -3,6 +3,7 @@ dependencies: flutter: sdk: flutter intl: '>=0.14.0 <0.15.0' + http: '>=0.11.3+11' dev_dependencies: flutter_test: diff --git a/packages/flutter/lib/http.dart b/packages/flutter/lib/http.dart deleted file mode 100644 index b0240bc63f9..00000000000 --- a/packages/flutter/lib/http.dart +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/// A [Future]-based library for making HTTP requests. -/// -/// To use, import `package:flutter/http.dart`. -/// -/// This library is based on Dart's `http` package, but differs in that it does -/// not have a dependency on mirrors. -/// -// TODO(chinmaygarde): The contents of `lib/src/http` will become redundant -// once https://github.com/dart-lang/http/issues/1 is fixed (removes the use -// of mirrors). Once that issue is addressed, we should get rid this directory -// and use `dart-lang/http` directly. -library http; - -export 'src/http/http.dart'; -export 'src/http/mock_client.dart'; -export 'src/http/response.dart'; diff --git a/packages/flutter/lib/services.dart b/packages/flutter/lib/services.dart index 4c342104b5a..cbbbefe2b23 100644 --- a/packages/flutter/lib/services.dart +++ b/packages/flutter/lib/services.dart @@ -13,14 +13,15 @@ library services; export 'src/services/asset_bundle.dart'; export 'src/services/binding.dart'; export 'src/services/clipboard.dart'; -export 'src/services/message_codec.dart'; -export 'src/services/message_codecs.dart'; export 'src/services/haptic_feedback.dart'; +export 'src/services/http_client.dart'; export 'src/services/image_cache.dart'; export 'src/services/image_decoder.dart'; export 'src/services/image_provider.dart'; export 'src/services/image_resolution.dart'; export 'src/services/image_stream.dart'; +export 'src/services/message_codec.dart'; +export 'src/services/message_codecs.dart'; export 'src/services/path_provider.dart'; export 'src/services/platform_channel.dart'; export 'src/services/platform_messages.dart'; diff --git a/packages/flutter/lib/src/http/.dartignore b/packages/flutter/lib/src/http/.dartignore deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/flutter/lib/src/http/README.md b/packages/flutter/lib/src/http/README.md deleted file mode 100644 index 1db8904ce10..00000000000 --- a/packages/flutter/lib/src/http/README.md +++ /dev/null @@ -1,7 +0,0 @@ -`dart-lang/http` without Mirrors -================================ - -The contents of this will become redundant once -https://github.com/dart-lang/http/issues/1 is fixed (removes the use -of mirrors). Once that issue is addressed, we should get rid this directory -and use `dart-lang/http` directly. diff --git a/packages/flutter/lib/src/http/base_client.dart b/packages/flutter/lib/src/http/base_client.dart deleted file mode 100644 index 1b16c257551..00000000000 --- a/packages/flutter/lib/src/http/base_client.dart +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -import 'package:collection/collection.dart'; - -import 'base_request.dart'; -import 'client.dart'; -import 'exception.dart'; -import 'request.dart'; -import 'response.dart'; -import 'streamed_response.dart'; - -/// The abstract base class for an HTTP client. This is a mixin-style class; -/// subclasses only need to implement [send] and maybe [close], and then they -/// get various convenience methods for free. -abstract class BaseClient implements Client { - /// Sends an HTTP HEAD request with the given headers to the given URL, which - /// can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future head(dynamic url, {Map headers}) => - _sendUnstreamed("HEAD", url, headers); - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future get(dynamic url, {Map headers}) => - _sendUnstreamed("GET", url, headers); - - /// Sends an HTTP POST request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to UTF-8. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future post(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _sendUnstreamed("POST", url, headers, body, encoding); - - /// Sends an HTTP PUT request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to UTF-8. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future put(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _sendUnstreamed("PUT", url, headers, body, encoding); - - /// Sends an HTTP PATCH request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to UTF-8. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future patch(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _sendUnstreamed("PATCH", url, headers, body, encoding); - - /// Sends an HTTP DELETE request with the given headers to the given URL, - /// which can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - @override - Future delete(dynamic url, {Map headers}) => - _sendUnstreamed("DELETE", url, headers); - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String], and returns a Future that completes to the - /// body of the response as a String. - /// - /// The Future will emit a [ClientException] if the response doesn't have a - /// success status code. - /// - /// For more fine-grained control over the request and response, use [send] or - /// [get] instead. - @override - Future read(dynamic url, {Map headers}) { - return get(url, headers: headers).then((Response response) { - _checkResponseSuccess(url, response); - return response.body; - }); - } - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String], and returns a Future that completes to the - /// body of the response as a list of bytes. - /// - /// The Future will emit an [ClientException] if the response doesn't have a - /// success status code. - /// - /// For more fine-grained control over the request and response, use [send] or - /// [get] instead. - @override - Future readBytes(dynamic url, {Map headers}) { - return get(url, headers: headers).then((Response response) { - _checkResponseSuccess(url, response); - return response.bodyBytes; - }); - } - - /// Sends an HTTP request and asynchronously returns the response. - /// - /// Implementers should call [BaseRequest.finalize] to get the body of the - /// request as a [ByteStream]. They shouldn't make any assumptions about the - /// state of the stream; it could have data written to it asynchronously at a - /// later point, or it could already be closed when it's returned. Any - /// internal HTTP errors should be wrapped as [ClientException]s. - @override - Future send(BaseRequest request); - - /// Sends a non-streaming [Request] and returns a non-streaming [Response]. - Future _sendUnstreamed(String method, dynamic url, - Map headers, [dynamic body, Encoding encoding]) async { - - if (url is String) url = Uri.parse(url); - Request request = new Request(method, url); - - if (headers != null) request.headers.addAll(headers); - if (encoding != null) request.encoding = encoding; - if (body != null) { - if (body is String) { - request.body = body; - } else if (body is List) { - request.bodyBytes = DelegatingList.typed(body); - } else if (body is Map) { - request.bodyFields = DelegatingMap.typed(body); - } else { - throw new ArgumentError('Invalid request body "$body".'); - } - } - - return Response.fromStream(await send(request)); - } - - /// Throws an error if [response] is not successful. - void _checkResponseSuccess(dynamic url, Response response) { - if (response.statusCode < 400) return; - String message = "Request to $url failed with status ${response.statusCode}"; - if (response.reasonPhrase != null) { - message = "$message: ${response.reasonPhrase}"; - } - if (url is String) url = Uri.parse(url); - throw new ClientException("$message.", url); - } - - /// Closes the client and cleans up any resources associated with it. It's - /// important to close each client when it's done being used; failing to do so - /// can cause the Dart process to hang. - @override - void close() {} -} diff --git a/packages/flutter/lib/src/http/base_request.dart b/packages/flutter/lib/src/http/base_request.dart deleted file mode 100644 index 4af1673f7e1..00000000000 --- a/packages/flutter/lib/src/http/base_request.dart +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:collection'; - -import 'byte_stream.dart'; -import 'client.dart'; -import 'streamed_response.dart'; -import 'utils.dart'; - -/// The base class for HTTP requests. -/// -/// Subclasses of [BaseRequest] can be constructed manually and passed to -/// [BaseClient.send], which allows the user to provide fine-grained control -/// over the request properties. However, usually it's easier to use convenience -/// methods like [get] or [BaseClient.get]. -abstract class BaseRequest { - /// The HTTP method of the request. Most commonly "GET" or "POST", less - /// commonly "HEAD", "PUT", or "DELETE". Non-standard method names are also - /// supported. - final String method; - - /// The URL to which the request will be sent. - final Uri url; - - /// Creates a new HTTP request. - BaseRequest(this.method, this.url) - : headers = new LinkedHashMap( - equals: (String key1, String key2) => key1.toLowerCase() == key2.toLowerCase(), - hashCode: (String key) => key.toLowerCase().hashCode); - - /// The size of the request body, in bytes. - /// - /// This defaults to `null`, which indicates that the size of the request is - /// not known in advance. - int get contentLength => _contentLength; - int _contentLength; - - set contentLength(int value) { - if (value != null && value < 0) { - throw new ArgumentError("Invalid content length $value."); - } - _checkFinalized(); - _contentLength = value; - } - - /// Whether a persistent connection should be maintained with the server. - /// Defaults to true. - bool get persistentConnection => _persistentConnection; - bool _persistentConnection = true; - - set persistentConnection(bool value) { - _checkFinalized(); - _persistentConnection = value; - } - - /// Whether the client should follow redirects while resolving this request. - /// Defaults to true. - bool get followRedirects => _followRedirects; - bool _followRedirects = true; - - set followRedirects(bool value) { - _checkFinalized(); - _followRedirects = value; - } - - /// The maximum number of redirects to follow when [followRedirects] is true. - /// If this number is exceeded the [BaseResponse] future will signal a - /// [RedirectException]. Defaults to 5. - int get maxRedirects => _maxRedirects; - int _maxRedirects = 5; - - set maxRedirects(int value) { - _checkFinalized(); - _maxRedirects = value; - } - - // TODO(nweiz): automatically parse cookies from headers - - // TODO(nweiz): make this a HttpHeaders object - /// The headers for this request. - final Map headers; - - /// Whether the request has been finalized. - bool get finalized => _finalized; - bool _finalized = false; - - /// Finalizes the HTTP request in preparation for it being sent. This freezes - /// all mutable fields and returns a single-subscription [ByteStream] that - /// emits the body of the request. - /// - /// The base implementation of this returns null rather than a [ByteStream]; - /// subclasses are responsible for creating the return value, which should be - /// single-subscription to ensure that no data is dropped. They should also - /// freeze any additional mutable fields they add that don't make sense to - /// change after the request headers are sent. - ByteStream finalize() { - // TODO(nweiz): freeze headers - if (finalized) throw new StateError("Can't finalize a finalized Request."); - _finalized = true; - return null; - } - - /// Sends this request. - /// - /// This automatically initializes a new [Client] and closes that client once - /// the request is complete. If you're planning on making multiple requests to - /// the same server, you should use a single [Client] for all of those - /// requests. - Future send() async { - Client client = new Client(); - - try { - StreamedResponse response = await client.send(this); - Stream stream = onDone(response.stream, client.close); - return new StreamedResponse( - new ByteStream(stream), - response.statusCode, - contentLength: response.contentLength, - request: response.request, - headers: response.headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase); - } catch (ex) { - client.close(); - rethrow; - } - } - - // Throws an error if this request has been finalized. - void _checkFinalized() { - if (!finalized) return; - throw new StateError("Can't modify a finalized Request."); - } - - @override - String toString() => "$method $url"; -} diff --git a/packages/flutter/lib/src/http/base_response.dart b/packages/flutter/lib/src/http/base_response.dart deleted file mode 100644 index 148b75165f0..00000000000 --- a/packages/flutter/lib/src/http/base_response.dart +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'base_request.dart'; - -/// The base class for HTTP responses. -/// -/// Subclasses of [BaseResponse] are usually not constructed manually; instead, -/// they're returned by [BaseClient.send] or other HTTP client methods. -abstract class BaseResponse { - /// The (frozen) request that triggered this response. - final BaseRequest request; - - /// The status code of the response. - final int statusCode; - - /// The reason phrase associated with the status code. - final String reasonPhrase; - - /// The size of the response body, in bytes. - /// - /// If the size of the request is not known in advance, this is `null`. - final int contentLength; - - // TODO(nweiz): automatically parse cookies from headers - - // TODO(nweiz): make this a HttpHeaders object. - /// The headers for this response. - final Map headers; - - /// Whether this response is a redirect. - final bool isRedirect; - - /// Whether the server requested that a persistent connection be maintained. - final bool persistentConnection; - - /// Creates a new HTTP response. - BaseResponse( - this.statusCode, - {this.contentLength, - this.request, - this.headers: const {}, - this.isRedirect: false, - this.persistentConnection: true, - this.reasonPhrase}) { - if (statusCode < 100) { - throw new ArgumentError("Invalid status code $statusCode."); - } else if (contentLength != null && contentLength < 0) { - throw new ArgumentError("Invalid content length $contentLength."); - } - } -} diff --git a/packages/flutter/lib/src/http/byte_stream.dart b/packages/flutter/lib/src/http/byte_stream.dart deleted file mode 100644 index 825d419e722..00000000000 --- a/packages/flutter/lib/src/http/byte_stream.dart +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -/// A stream of chunks of bytes representing a single piece of data. -class ByteStream extends StreamView> { - ByteStream(Stream> stream) - : super(stream); - - /// Returns a single-subscription byte stream that will emit the given bytes - /// in a single chunk. - factory ByteStream.fromBytes(List bytes) => - new ByteStream(new Stream.fromIterable(>[bytes])); - - /// Collects the data of this stream in a [Uint8List]. - Future toBytes() { - Completer completer = new Completer(); - dynamic sink = new ByteConversionSink.withCallback((dynamic bytes) => - completer.complete(new Uint8List.fromList(bytes))); - listen(sink.add, onError: completer.completeError, onDone: sink.close, - cancelOnError: true); - return completer.future; - } - - /// Collect the data of this stream in a [String], decoded according to - /// [encoding], which defaults to `UTF8`. - Future bytesToString([Encoding encoding=UTF8]) => - encoding.decodeStream(this); - - Stream toStringStream([Encoding encoding=UTF8]) => - encoding.decoder.bind(this); -} diff --git a/packages/flutter/lib/src/http/client.dart b/packages/flutter/lib/src/http/client.dart deleted file mode 100644 index 32807778044..00000000000 --- a/packages/flutter/lib/src/http/client.dart +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -import 'base_client.dart'; -import 'base_request.dart'; -import 'io_client.dart'; -import 'response.dart'; -import 'streamed_response.dart'; - -typedef Client ClientOverride(); - -/// The interface for HTTP clients that take care of maintaining persistent -/// connections across multiple requests to the same server. If you only need to -/// send a single request, it's usually easier to use [head], [get], [post], -/// [put], [patch], or [delete] instead. -/// -/// When creating an HTTP client class with additional functionality, you must -/// extend [BaseClient] rather than [Client]. In most cases, you can wrap -/// another instance of [Client] and add functionality on top of that. This -/// allows all classes implementing [Client] to be mutually composable. -abstract class Client { - /// Creates a new client. - /// - /// Currently this will create an [IOClient] if `dart:io` is available and - /// throw an [UnsupportedError] otherwise. In the future, it will create a - /// [BrowserClient] if `dart:html` is available. - factory Client() { - return clientOverride == null ? new IOClient() : clientOverride(); - } - - static ClientOverride clientOverride; - - /// Sends an HTTP HEAD request with the given headers to the given URL, which - /// can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future head(dynamic url, {Map headers}); - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future get(dynamic url, {Map headers}); - - /// Sends an HTTP POST request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to [UTF8]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future post(dynamic url, {Map headers, dynamic body, - Encoding encoding}); - - /// Sends an HTTP PUT request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to [UTF8]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future put(dynamic url, {Map headers, dynamic body, - Encoding encoding}); - - /// Sends an HTTP PATCH request with the given headers and body to the given - /// URL, which can be a [Uri] or a [String]. - /// - /// [body] sets the body of the request. It can be a [String], a [List] - /// or a [Map]. If it's a String, it's encoded using - /// [encoding] and used as the body of the request. The content-type of the - /// request will default to "text/plain". - /// - /// If [body] is a List, it's used as a list of bytes for the body of the - /// request. - /// - /// If [body] is a Map, it's encoded as form fields using [encoding]. The - /// content-type of the request will be set to - /// `"application/x-www-form-urlencoded"`; this cannot be overridden. - /// - /// [encoding] defaults to [UTF8]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future patch(dynamic url, {Map headers, dynamic body, - Encoding encoding}); - - /// Sends an HTTP DELETE request with the given headers to the given URL, - /// which can be a [Uri] or a [String]. - /// - /// For more fine-grained control over the request, use [send] instead. - Future delete(dynamic url, {Map headers}); - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String], and returns a Future that completes to the - /// body of the response as a String. - /// - /// The Future will emit a [ClientException] if the response doesn't have a - /// success status code. - /// - /// For more fine-grained control over the request and response, use [send] or - /// [get] instead. - Future read(dynamic url, {Map headers}); - - /// Sends an HTTP GET request with the given headers to the given URL, which - /// can be a [Uri] or a [String], and returns a Future that completes to the - /// body of the response as a list of bytes. - /// - /// The Future will emit a [ClientException] if the response doesn't have a - /// success status code. - /// - /// For more fine-grained control over the request and response, use [send] or - /// [get] instead. - Future readBytes(dynamic url, {Map headers}); - - /// Sends an HTTP request and asynchronously returns the response. - Future send(BaseRequest request); - - /// Closes the client and cleans up any resources associated with it. It's - /// important to close each client when it's done being used; failing to do so - /// can cause the Dart process to hang. - void close(); -} diff --git a/packages/flutter/lib/src/http/exception.dart b/packages/flutter/lib/src/http/exception.dart deleted file mode 100644 index c92a861a5b0..00000000000 --- a/packages/flutter/lib/src/http/exception.dart +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/// An exception caused by an error in a pkg/http client. -class ClientException implements Exception { - final String message; - - /// The URL of the HTTP request or response that failed. - final Uri uri; - - ClientException(this.message, [this.uri]); - - @override - String toString() => message; -} diff --git a/packages/flutter/lib/src/http/http.dart b/packages/flutter/lib/src/http/http.dart deleted file mode 100644 index 6806075dd13..00000000000 --- a/packages/flutter/lib/src/http/http.dart +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -/// A composable, [Future]-based library for making HTTP requests. -import 'dart:async'; -import 'dart:convert'; -import 'dart:typed_data'; - -import 'client.dart'; -import 'response.dart'; - -export 'base_client.dart'; -export 'base_request.dart'; -export 'base_response.dart'; -export 'byte_stream.dart'; -export 'client.dart'; -export 'exception.dart'; -export 'io_client.dart'; -export 'multipart_file.dart'; -export 'multipart_request.dart'; -export 'request.dart'; -export 'response.dart'; -export 'streamed_request.dart'; -export 'streamed_response.dart'; - -/// Sends an HTTP HEAD request with the given headers to the given URL, which -/// can be a [Uri] or a [String]. -/// -/// This automatically initializes a new [Client] and closes that client once -/// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request, use [Request] instead. -Future head(dynamic url, {Map headers}) => - _withClient((Client client) => client.head(url, headers: headers)); - -/// Sends an HTTP GET request with the given headers to the given URL, which can -/// be a [Uri] or a [String]. -/// -/// This automatically initializes a new [Client] and closes that client once -/// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request, use [Request] instead. -Future get(dynamic url, {Map headers}) => - _withClient((Client client) => client.get(url, headers: headers)); - -/// Sends an HTTP POST request with the given headers and body to the given URL, -/// which can be a [Uri] or a [String]. -/// -/// [body] sets the body of the request. It can be a [String], a [List] or -/// a [Map]. If it's a String, it's encoded using [encoding] and -/// used as the body of the request. The content-type of the request will -/// default to "text/plain". -/// -/// If [body] is a List, it's used as a list of bytes for the body of the -/// request. -/// -/// If [body] is a Map, it's encoded as form fields using [encoding]. The -/// content-type of the request will be set to -/// `"application/x-www-form-urlencoded"`; this cannot be overridden. -/// -/// [encoding] defaults to [UTF8]. -/// -/// For more fine-grained control over the request, use [Request] or -/// [StreamedRequest] instead. -Future post(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _withClient((Client client) => client.post(url, - headers: headers, body: body, encoding: encoding)); - -/// Sends an HTTP PUT request with the given headers and body to the given URL, -/// which can be a [Uri] or a [String]. -/// -/// [body] sets the body of the request. It can be a [String], a [List] or -/// a [Map]. If it's a String, it's encoded using [encoding] and -/// used as the body of the request. The content-type of the request will -/// default to "text/plain". -/// -/// If [body] is a List, it's used as a list of bytes for the body of the -/// request. -/// -/// If [body] is a Map, it's encoded as form fields using [encoding]. The -/// content-type of the request will be set to -/// `"application/x-www-form-urlencoded"`; this cannot be overridden. -/// -/// [encoding] defaults to [UTF8]. -/// -/// For more fine-grained control over the request, use [Request] or -/// [StreamedRequest] instead. -Future put(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _withClient((Client client) => client.put(url, - headers: headers, body: body, encoding: encoding)); - -/// Sends an HTTP PATCH request with the given headers and body to the given -/// URL, which can be a [Uri] or a [String]. -/// -/// [body] sets the body of the request. It can be a [String], a [List] or -/// a [Map]. If it's a String, it's encoded using [encoding] and -/// used as the body of the request. The content-type of the request will -/// default to "text/plain". -/// -/// If [body] is a List, it's used as a list of bytes for the body of the -/// request. -/// -/// If [body] is a Map, it's encoded as form fields using [encoding]. The -/// content-type of the request will be set to -/// `"application/x-www-form-urlencoded"`; this cannot be overridden. -/// -/// [encoding] defaults to [UTF8]. -/// -/// For more fine-grained control over the request, use [Request] or -/// [StreamedRequest] instead. -Future patch(dynamic url, {Map headers, dynamic body, - Encoding encoding}) => - _withClient((Client client) => client.patch(url, - headers: headers, body: body, encoding: encoding)); - -/// Sends an HTTP DELETE request with the given headers to the given URL, which -/// can be a [Uri] or a [String]. -/// -/// This automatically initializes a new [Client] and closes that client once -/// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request, use [Request] instead. -Future delete(dynamic url, {Map headers}) => - _withClient((Client client) => client.delete(url, headers: headers)); - -/// Sends an HTTP GET request with the given headers to the given URL, which can -/// be a [Uri] or a [String], and returns a Future that completes to the body of -/// the response as a [String]. -/// -/// The Future will emit a [ClientException] if the response doesn't have a -/// success status code. -/// -/// This automatically initializes a new [Client] and closes that client once -/// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request and response, use [Request] -/// instead. -Future read(dynamic url, {Map headers}) => - _withClient((Client client) => client.read(url, headers: headers)); - -/// Sends an HTTP GET request with the given headers to the given URL, which can -/// be a [Uri] or a [String], and returns a Future that completes to the body of -/// the response as a list of bytes. -/// -/// The Future will emit a [ClientException] if the response doesn't have a -/// success status code. -/// -/// This automatically initializes a new [Client] and closes that client once -/// the request is complete. If you're planning on making multiple requests to -/// the same server, you should use a single [Client] for all of those requests. -/// -/// For more fine-grained control over the request and response, use [Request] -/// instead. -Future readBytes(dynamic url, {Map headers}) => - _withClient((Client client) => client.readBytes(url, headers: headers)); - -Future/**/ _withClient/**/(Future/**/ fn(Client client)) async { - Client client = new Client(); - try { - return await fn(client); - } finally { - client.close(); - } -} diff --git a/packages/flutter/lib/src/http/io.dart b/packages/flutter/lib/src/http/io.dart deleted file mode 100644 index 7748a6d2a06..00000000000 --- a/packages/flutter/lib/src/http/io.dart +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file -// for details. 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:io' as io; - -/// Whether `dart:io` is supported on this platform. -bool get supported => true; - -/// Asserts that the [name]d `dart:io` feature is supported on this platform. -/// -/// If `dart:io` doesn't work on this platform, this throws an -/// [UnsupportedError]. -void assertSupported(String name) {} - -/// Creates a new `dart:io` HttpClient instance. -io.HttpClient newHttpClient() => new io.HttpClient(); - -/// Creates a new `dart:io` File instance with the given [path]. -io.File newFile(String path) => new io.File(path); - -/// Returns whether [error] is a `dart:io` HttpException. -bool isHttpException(dynamic error) => error is io.HttpException; - -/// Returns whether [client] is a `dart:io` HttpClient. -bool isHttpClient(dynamic client) => client is io.HttpClient; diff --git a/packages/flutter/lib/src/http/io_client.dart b/packages/flutter/lib/src/http/io_client.dart deleted file mode 100644 index 5a3ae797248..00000000000 --- a/packages/flutter/lib/src/http/io_client.dart +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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 'package:async/async.dart'; - -import 'base_client.dart'; -import 'base_request.dart'; -import 'exception.dart'; -import 'io.dart' as io; -import 'streamed_response.dart'; - -/// A `dart:io`-based HTTP client. -/// -/// This is the default client when running on the command line. -class IOClient extends BaseClient { - /// The underlying `dart:io` HTTP client. - dynamic _inner; - - /// Creates a new HTTP client. - /// - /// [innerClient] must be a `dart:io` HTTP client. If it's not passed, a - /// default one will be instantiated. - IOClient([dynamic innerClient]) { - io.assertSupported("IOClient"); - if (innerClient != null) { - // TODO(nweiz): remove this assert when we can type [innerClient] - // properly. - assert(io.isHttpClient(innerClient)); - _inner = innerClient; - } else { - _inner = io.newHttpClient(); - } - } - - /// Sends an HTTP request and asynchronously returns the response. - @override - Future send(BaseRequest request) async { - dynamic stream = request.finalize(); - - try { - dynamic ioRequest = await _inner.openUrl(request.method, request.url); - - ioRequest - ..followRedirects = request.followRedirects - ..maxRedirects = request.maxRedirects - ..contentLength = request.contentLength == null - ? -1 - : request.contentLength - ..persistentConnection = request.persistentConnection; - request.headers.forEach((String name, String value) { - ioRequest.headers.set(name, value); - }); - - dynamic response = await stream.pipe( - DelegatingStreamConsumer.typed(ioRequest)); - Map headers = {}; - response.headers.forEach((String key, dynamic values) { - headers[key] = values.join(','); - }); - - return new StreamedResponse( - DelegatingStream.typed/*>*/(response).handleError((dynamic error) => - throw new ClientException(error.message, error.uri), - test: (dynamic error) => io.isHttpException(error)), - response.statusCode, - contentLength: response.contentLength == -1 - ? null - : response.contentLength, - request: request, - headers: headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase); - } catch (error) { - if (!io.isHttpException(error)) rethrow; - throw new ClientException(error.message, error.uri); - } - } - - /// Closes the client. This terminates all active connections. If a client - /// remains unclosed, the Dart process may not terminate. - @override - void close() { - if (_inner != null) _inner.close(force: true); - _inner = null; - } -} diff --git a/packages/flutter/lib/src/http/mock_client.dart b/packages/flutter/lib/src/http/mock_client.dart deleted file mode 100644 index 9b44e849563..00000000000 --- a/packages/flutter/lib/src/http/mock_client.dart +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:typed_data'; - -import 'base_client.dart'; -import 'base_request.dart'; -import 'byte_stream.dart'; -import 'request.dart'; -import 'response.dart'; -import 'streamed_response.dart'; - -// TODO(nweiz): once Dart has some sort of Rack- or WSGI-like standard for -// server APIs, MockClient should conform to it. - -/// A mock HTTP client designed for use when testing code that uses -/// [BaseClient]. This client allows you to define a handler callback for all -/// requests that are made through it so that you can mock a server without -/// having to send real HTTP requests. -class MockClient extends BaseClient { - /// The handler for receiving [StreamedRequest]s and sending - /// [StreamedResponse]s. - final MockClientStreamHandler _handler; - - /// Creates a [MockClient] with a handler that receives [Request]s and sends - /// [Response]s. - MockClient(MockClientHandler fn) - : this._((BaseRequest baseRequest, ByteStream bodyStream) { - return bodyStream.toBytes().then((Uint8List bodyBytes) { - Request request = new Request(baseRequest.method, baseRequest.url) - ..persistentConnection = baseRequest.persistentConnection - ..followRedirects = baseRequest.followRedirects - ..maxRedirects = baseRequest.maxRedirects - ..headers.addAll(baseRequest.headers) - ..bodyBytes = bodyBytes - ..finalize(); - - return fn(request); - }).then((Response response) { - return new StreamedResponse( - new ByteStream.fromBytes(response.bodyBytes), - response.statusCode, - contentLength: response.contentLength, - request: baseRequest, - headers: response.headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase); - }); - }); - - MockClient._(this._handler); - - /// Creates a [MockClient] with a handler that receives [StreamedRequest]s and - /// sends [StreamedResponse]s. - MockClient.streaming(MockClientStreamHandler fn) - : this._((BaseRequest request, ByteStream bodyStream) { - return fn(request, bodyStream).then((StreamedResponse response) { - return new StreamedResponse( - response.stream, - response.statusCode, - contentLength: response.contentLength, - request: request, - headers: response.headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase); - }); - }); - - /// Sends a request. - @override - Future send(BaseRequest request) async { - ByteStream bodyStream = request.finalize(); - return await _handler(request, bodyStream); - } -} - -/// A handler function that receives [StreamedRequest]s and sends -/// [StreamedResponse]s. Note that [request] will be finalized. -typedef Future MockClientStreamHandler( - BaseRequest request, ByteStream bodyStream); - -/// A handler function that receives [Request]s and sends [Response]s. Note that -/// [request] will be finalized. -typedef Future MockClientHandler(BaseRequest request); diff --git a/packages/flutter/lib/src/http/multipart_file.dart b/packages/flutter/lib/src/http/multipart_file.dart deleted file mode 100644 index a21e6249e14..00000000000 --- a/packages/flutter/lib/src/http/multipart_file.dart +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; - -import 'package:async/async.dart'; -import 'package:http_parser/http_parser.dart'; -import 'package:path/path.dart' as path; - -import 'byte_stream.dart'; -import 'io.dart' as io; -import 'utils.dart'; - -/// A file to be uploaded as part of a [MultipartRequest]. This doesn't need to -/// correspond to a physical file. -class MultipartFile { - /// The name of the form field for the file. - final String field; - - /// The size of the file in bytes. This must be known in advance, even if this - /// file is created from a [ByteStream]. - final int length; - - /// The basename of the file. May be null. - final String filename; - - /// The content-type of the file. Defaults to `application/octet-stream`. - final MediaType contentType; - - /// The stream that will emit the file's contents. - final ByteStream _stream; - - /// Creates a new [MultipartFile] from a chunked [Stream] of bytes. The length - /// of the file in bytes must be known in advance. If it's not, read the data - /// from the stream and use [MultipartFile.fromBytes] instead. - /// - /// [contentType] currently defaults to `application/octet-stream`, but in the - /// future may be inferred from [filename]. - MultipartFile(this.field, Stream> stream, this.length, - {this.filename, MediaType contentType}) - : this._stream = toByteStream(stream), - this.contentType = contentType != null ? contentType : - new MediaType("application", "octet-stream"); - - /// Creates a new [MultipartFile] from a byte array. - /// - /// [contentType] currently defaults to `application/octet-stream`, but in the - /// future may be inferred from [filename]. - factory MultipartFile.fromBytes(String field, List value, - {String filename, MediaType contentType}) { - ByteStream stream = new ByteStream.fromBytes(value); - return new MultipartFile(field, stream, value.length, - filename: filename, - contentType: contentType); - } - - /// Creates a new [MultipartFile] from a string. - /// - /// The encoding to use when translating [value] into bytes is taken from - /// [contentType] if it has a charset set. Otherwise, it defaults to UTF-8. - /// [contentType] currently defaults to `text/plain; charset=utf-8`, but in - /// the future may be inferred from [filename]. - factory MultipartFile.fromString(String field, String value, - {String filename, MediaType contentType}) { - contentType = contentType == null ? new MediaType("text", "plain") - : contentType; - Encoding encoding = encodingForCharset(contentType.parameters['charset'], UTF8); - contentType = contentType.change(parameters: {'charset': encoding.name}); - - return new MultipartFile.fromBytes(field, encoding.encode(value), - filename: filename, - contentType: contentType); - } - - /// Whether [finalize] has been called. - bool get isFinalized => _isFinalized; - bool _isFinalized = false; - - // TODO(nweiz): Infer the content-type from the filename. - /// Creates a new [MultipartFile] from a path to a file on disk. - /// - /// [filename] defaults to the basename of [filePath]. [contentType] currently - /// defaults to `application/octet-stream`, but in the future may be inferred - /// from [filename]. - /// - /// This can only be used in an environment that supports "dart:io". - static Future fromPath(String field, String filePath, - {String filename, MediaType contentType}) async { - io.assertSupported("MultipartFile.fromPath"); - if (filename == null) filename = path.basename(filePath); - dynamic file = io.newFile(filePath); - int length = await file.length(); - ByteStream stream = new ByteStream(DelegatingStream.typed(file.openRead())); - return new MultipartFile(field, stream, length, - filename: filename, - contentType: contentType); - } - - // Finalizes the file in preparation for it being sent as part of a - // [MultipartRequest]. This returns a [ByteStream] that should emit the body - // of the file. The stream may be closed to indicate an empty file. - ByteStream finalize() { - if (isFinalized) { - throw new StateError("Can't finalize a finalized MultipartFile."); - } - _isFinalized = true; - return _stream; - } -} diff --git a/packages/flutter/lib/src/http/multipart_request.dart b/packages/flutter/lib/src/http/multipart_request.dart deleted file mode 100644 index c108eda5c92..00000000000 --- a/packages/flutter/lib/src/http/multipart_request.dart +++ /dev/null @@ -1,177 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:math'; - -import 'base_request.dart'; -import 'byte_stream.dart'; -import 'multipart_file.dart'; -import 'utils.dart'; - -final RegExp _newlineRegExp = new RegExp(r"\r\n|\r|\n"); - -/// A `multipart/form-data` request. Such a request has both string [fields], -/// which function as normal form fields, and (potentially streamed) binary -/// [files]. -/// -/// This request automatically sets the Content-Type header to -/// `multipart/form-data`. This value will override any value set by the user. -/// -/// var uri = Uri.parse("http://pub.dartlang.org/packages/create"); -/// var request = new http.MultipartRequest("POST", url); -/// request.fields['user'] = 'nweiz@google.com'; -/// request.files.add(new http.MultipartFile.fromFile( -/// 'package', -/// new File('build/package.tar.gz'), -/// contentType: new MediaType('application', 'x-tar')); -/// request.send().then((response) { -/// if (response.statusCode == 200) print("Uploaded!"); -/// }); -class MultipartRequest extends BaseRequest { - /// The total length of the multipart boundaries used when building the - /// request body. According to http://tools.ietf.org/html/rfc1341.html, this - /// can't be longer than 70. - static const int _BOUNDARY_LENGTH = 70; - - static final Random _random = new Random(); - - /// The form fields to send for this request. - final Map fields; - - /// The private version of [files]. - final List _files; - - /// Creates a new [MultipartRequest]. - MultipartRequest(String method, Uri url) - : fields = {}, - _files = [], - super(method, url); - - /// The list of files to upload for this request. - List get files => _files; - - /// The total length of the request body, in bytes. This is calculated from - /// [fields] and [files] and cannot be set manually. - @override - int get contentLength { - int length = 0; - - fields.forEach((String name, String value) { - length += "--".length + _BOUNDARY_LENGTH + "\r\n".length + - UTF8.encode(_headerForField(name, value)).length + - UTF8.encode(value).length + "\r\n".length; - }); - - for (MultipartFile file in _files) { - length += "--".length + _BOUNDARY_LENGTH + "\r\n".length + - UTF8.encode(_headerForFile(file)).length + - file.length + "\r\n".length; - } - - return length + "--".length + _BOUNDARY_LENGTH + "--\r\n".length; - } - - @override - set contentLength(int value) { - throw new UnsupportedError("Cannot set the contentLength property of " - "multipart requests."); - } - - /// Freezes all mutable fields and returns a single-subscription [ByteStream] - /// that will emit the request body. - @override - ByteStream finalize() { - // TODO(nweiz): freeze fields and files - String boundary = _boundaryString(); - headers['content-type'] = 'multipart/form-data; boundary="$boundary"'; - super.finalize(); - - StreamController> controller = new StreamController>(sync: true); - - void writeAscii(String string) { - controller.add(UTF8.encode(string)); - } - - dynamic writeUtf8(String string) => controller.add(UTF8.encode(string)); - dynamic writeLine() => controller.add([13, 10]); // \r\n - - fields.forEach((String name, String value) { - writeAscii('--$boundary\r\n'); - writeAscii(_headerForField(name, value)); - writeUtf8(value); - writeLine(); - }); - - Future.forEach(_files, (MultipartFile file) { - writeAscii('--$boundary\r\n'); - writeAscii(_headerForFile(file)); - return writeStreamToSink(file.finalize(), controller) - .then((_) => writeLine()); - }).then((_) { - // TODO(nweiz): pass any errors propagated through this future on to - // the stream. See issue 3657. - writeAscii('--$boundary--\r\n'); - controller.close(); - }); - - return new ByteStream(controller.stream); - } - - /// All character codes that are valid in multipart boundaries. From - /// http://tools.ietf.org/html/rfc2046#section-5.1.1. - static const List _BOUNDARY_CHARACTERS = const [ - 39, 40, 41, 43, 95, 44, 45, 46, 47, 58, 61, 63, 48, 49, 50, 51, 52, 53, 54, - 55, 56, 57, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, - 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, - 119, 120, 121, 122 - ]; - - /// Returns the header string for a field. The return value is guaranteed to - /// contain only ASCII characters. - String _headerForField(String name, String value) { - String header = - 'content-disposition: form-data; name="${_browserEncode(name)}"'; - if (!isPlainAscii(value)) { - header = '$header\r\n' - 'content-type: text/plain; charset=utf-8\r\n' - 'content-transfer-encoding: binary'; - } - return '$header\r\n\r\n'; - } - - /// Returns the header string for a file. The return value is guaranteed to - /// contain only ASCII characters. - String _headerForFile(MultipartFile file) { - String header = 'content-type: ${file.contentType}\r\n' - 'content-disposition: form-data; name="${_browserEncode(file.field)}"'; - - if (file.filename != null) { - header = '$header; filename="${_browserEncode(file.filename)}"'; - } - return '$header\r\n\r\n'; - } - - /// Encode [value] in the same way browsers do. - String _browserEncode(String value) { - // http://tools.ietf.org/html/rfc2388 mandates some complex encodings for - // field names and file names, but in practice user agents seem not to - // follow this at all. Instead, they URL-encode `\r`, `\n`, and `\r\n` as - // `\r\n`; URL-encode `"`; and do nothing else (even for `%` or non-ASCII - // characters). We follow their behavior. - return value.replaceAll(_newlineRegExp, "%0D%0A").replaceAll('"', "%22"); - } - - /// Returns a randomly-generated multipart boundary string - String _boundaryString() { - String prefix = "dart-http-boundary-"; - List list = new List.generate(_BOUNDARY_LENGTH - prefix.length, - (int index) => - _BOUNDARY_CHARACTERS[_random.nextInt(_BOUNDARY_CHARACTERS.length)], - growable: false); - return "$prefix${new String.fromCharCodes(list)}"; - } -} diff --git a/packages/flutter/lib/src/http/request.dart b/packages/flutter/lib/src/http/request.dart deleted file mode 100644 index 495e663d2c5..00000000000 --- a/packages/flutter/lib/src/http/request.dart +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -import 'package:http_parser/http_parser.dart'; - -import 'base_request.dart'; -import 'byte_stream.dart'; -import 'utils.dart'; - -/// An HTTP request where the entire request body is known in advance. -class Request extends BaseRequest { - /// Creates a new HTTP request. - Request(String method, Uri url) - : _defaultEncoding = UTF8, - _bodyBytes = new Uint8List(0), - super(method, url); - - /// The size of the request body, in bytes. This is calculated from - /// [bodyBytes]. - /// - /// The content length cannot be set for [Request], since it's automatically - /// calculated from [bodyBytes]. - @override - int get contentLength => bodyBytes.length; - - @override - set contentLength(int value) { - throw new UnsupportedError("Cannot set the contentLength property of " - "non-streaming Request objects."); - } - - /// The default encoding to use when converting between [bodyBytes] and - /// [body]. This is only used if [encoding] hasn't been manually set and if - /// the content-type header has no encoding information. - Encoding _defaultEncoding; - - /// The encoding used for the request. This encoding is used when converting - /// between [bodyBytes] and [body]. - /// - /// If the request has a `Content-Type` header and that header has a `charset` - /// parameter, that parameter's value is used as the encoding. Otherwise, if - /// [encoding] has been set manually, that encoding is used. If that hasn't - /// been set either, this defaults to [UTF8]. - /// - /// If the `charset` parameter's value is not a known [Encoding], reading this - /// will throw a [FormatException]. - /// - /// If the request has a `Content-Type` header, setting this will set the - /// charset parameter on that header. - Encoding get encoding { - if (_contentType == null || - !_contentType.parameters.containsKey('charset')) { - return _defaultEncoding; - } - return requiredEncodingForCharset(_contentType.parameters['charset']); - } - - set encoding(Encoding value) { - _checkFinalized(); - _defaultEncoding = value; - MediaType contentType = _contentType; - if (contentType == null) return; - _contentType = contentType.change(parameters: {'charset': value.name}); - } - - // TODO(nweiz): make this return a read-only view - /// The bytes comprising the body of the request. This is converted to and - /// from [body] using [encoding]. - /// - /// This list should only be set, not be modified in place. - Uint8List get bodyBytes => _bodyBytes; - Uint8List _bodyBytes; - - set bodyBytes(List value) { - _checkFinalized(); - _bodyBytes = toUint8List(value); - } - - /// The body of the request as a string. This is converted to and from - /// [bodyBytes] using [encoding]. - /// - /// When this is set, if the request does not yet have a `Content-Type` - /// header, one will be added with the type `text/plain`. Then the `charset` - /// parameter of the `Content-Type` header (whether new or pre-existing) will - /// be set to [encoding] if it wasn't already set. - String get body => encoding.decode(bodyBytes); - - set body(String value) { - bodyBytes = encoding.encode(value); - MediaType contentType = _contentType; - if (contentType == null) { - _contentType = new MediaType("text", "plain", {'charset': encoding.name}); - } else if (!contentType.parameters.containsKey('charset')) { - _contentType = contentType.change(parameters: {'charset': encoding.name}); - } - } - - /// The form-encoded fields in the body of the request as a map from field - /// names to values. The form-encoded body is converted to and from - /// [bodyBytes] using [encoding] (in the same way as [body]). - /// - /// If the request doesn't have a `Content-Type` header of - /// `application/x-www-form-urlencoded`, reading this will throw a - /// [StateError]. - /// - /// If the request has a `Content-Type` header with a type other than - /// `application/x-www-form-urlencoded`, setting this will throw a - /// [StateError]. Otherwise, the content type will be set to - /// `application/x-www-form-urlencoded`. - /// - /// This map should only be set, not modified in place. - Map get bodyFields { - MediaType contentType = _contentType; - if (contentType == null || - contentType.mimeType != "application/x-www-form-urlencoded") { - throw new StateError('Cannot access the body fields of a Request without ' - 'content-type "application/x-www-form-urlencoded".'); - } - - return Uri.splitQueryString(body, encoding: encoding); - } - - set bodyFields(Map fields) { - MediaType contentType = _contentType; - if (contentType == null) { - _contentType = new MediaType("application", "x-www-form-urlencoded"); - } else if (contentType.mimeType != "application/x-www-form-urlencoded") { - throw new StateError('Cannot set the body fields of a Request with ' - 'content-type "${contentType.mimeType}".'); - } - - this.body = mapToQuery(fields, encoding: encoding); - } - - /// Freezes all mutable fields and returns a single-subscription [ByteStream] - /// containing the request body. - @override - ByteStream finalize() { - super.finalize(); - return new ByteStream.fromBytes(bodyBytes); - } - - /// The `Content-Type` header of the request (if it exists) as a - /// [MediaType]. - MediaType get _contentType { - String contentType = headers['content-type']; - if (contentType == null) return null; - return new MediaType.parse(contentType); - } - - set _contentType(MediaType value) { - headers['content-type'] = value.toString(); - } - - /// Throw an error if this request has been finalized. - void _checkFinalized() { - if (!finalized) return; - throw new StateError("Can't modify a finalized Request."); - } -} diff --git a/packages/flutter/lib/src/http/response.dart b/packages/flutter/lib/src/http/response.dart deleted file mode 100644 index caba63b2a26..00000000000 --- a/packages/flutter/lib/src/http/response.dart +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -import 'package:http_parser/http_parser.dart'; - -import 'base_request.dart'; -import 'base_response.dart'; -import 'streamed_response.dart'; -import 'utils.dart'; - -/// An HTTP response where the entire response body is known in advance. -class Response extends BaseResponse { - /// The bytes comprising the body of this response. - final Uint8List bodyBytes; - - /// Creates a new HTTP response with a string body. - Response( - String body, - int statusCode, - {BaseRequest request, - Map headers: const {}, - bool isRedirect: false, - bool persistentConnection: true, - String reasonPhrase}) - : this.bytes( - _encodingForHeaders(headers).encode(body), - statusCode, - request: request, - headers: headers, - isRedirect: isRedirect, - persistentConnection: persistentConnection, - reasonPhrase: reasonPhrase); - - /// Create a new HTTP response with a byte array body. - Response.bytes( - List bodyBytes, - int statusCode, - {BaseRequest request, - Map headers: const {}, - bool isRedirect: false, - bool persistentConnection: true, - String reasonPhrase}) - : bodyBytes = toUint8List(bodyBytes), - super( - statusCode, - contentLength: bodyBytes.length, - request: request, - headers: headers, - isRedirect: isRedirect, - persistentConnection: persistentConnection, - reasonPhrase: reasonPhrase); - - /// The body of the response as a string. This is converted from [bodyBytes] - /// using the `charset` parameter of the `Content-Type` header field, if - /// available. If it's unavailable or if the encoding name is unknown, - /// [LATIN1] is used by default, as per [RFC 2616][]. - /// - /// [RFC 2616]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html - String get body => _encodingForHeaders(headers).decode(bodyBytes); - - /// Creates a new HTTP response by waiting for the full body to become - /// available from a [StreamedResponse]. - static Future fromStream(StreamedResponse response) { - return response.stream.toBytes().then((List body) { - return new Response.bytes( - body, - response.statusCode, - request: response.request, - headers: response.headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase); - }); - } -} - -/// Returns the encoding to use for a response with the given headers. This -/// defaults to [LATIN1] if the headers don't specify a charset or -/// if that charset is unknown. -Encoding _encodingForHeaders(Map headers) => - encodingForCharset(_contentTypeForHeaders(headers).parameters['charset']); - -/// Returns the [MediaType] object for the given headers's content-type. -/// -/// Defaults to `application/octet-stream`. -MediaType _contentTypeForHeaders(Map headers) { - String contentType = headers['content-type']; - if (contentType != null) return new MediaType.parse(contentType); - return new MediaType("application", "octet-stream"); -} diff --git a/packages/flutter/lib/src/http/streamed_request.dart b/packages/flutter/lib/src/http/streamed_request.dart deleted file mode 100644 index 8d1b408bc95..00000000000 --- a/packages/flutter/lib/src/http/streamed_request.dart +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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 'byte_stream.dart'; -import 'base_request.dart'; - -/// An HTTP request where the request body is sent asynchronously after the -/// connection has been established and the headers have been sent. -/// -/// When the request is sent via [BaseClient.send], only the headers and -/// whatever data has already been written to [StreamedRequest.stream] will be -/// sent immediately. More data will be sent as soon as it's written to -/// [StreamedRequest.sink], and when the sink is closed the request will end. -class StreamedRequest extends BaseRequest { - /// Creates a new streaming request. - StreamedRequest(String method, Uri url) - : _controller = new StreamController>(sync: true), - super(method, url); - - /// The sink to which to write data that will be sent as the request body. - /// This may be safely written to before the request is sent; the data will be - /// buffered. - /// - /// Closing this signals the end of the request. - EventSink> get sink => _controller.sink; - - /// The controller for [sink], from which [BaseRequest] will read data for - /// [finalize]. - final StreamController> _controller; - - /// Freezes all mutable fields other than [stream] and returns a - /// single-subscription [ByteStream] that emits the data being written to - /// [sink]. - @override - ByteStream finalize() { - super.finalize(); - return new ByteStream(_controller.stream); - } -} diff --git a/packages/flutter/lib/src/http/streamed_response.dart b/packages/flutter/lib/src/http/streamed_response.dart deleted file mode 100644 index a23a9520eb8..00000000000 --- a/packages/flutter/lib/src/http/streamed_response.dart +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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 'byte_stream.dart'; -import 'base_response.dart'; -import 'base_request.dart'; -import 'utils.dart'; - -/// An HTTP response where the response body is received asynchronously after -/// the headers have been received. -class StreamedResponse extends BaseResponse { - /// The stream from which the response body data can be read. This should - /// always be a single-subscription stream. - final ByteStream stream; - - /// Creates a new streaming response. [stream] should be a single-subscription - /// stream. - StreamedResponse( - Stream> stream, - int statusCode, - {int contentLength, - BaseRequest request, - Map headers: const {}, - bool isRedirect: false, - bool persistentConnection: true, - String reasonPhrase}) - : this.stream = toByteStream(stream), - super( - statusCode, - contentLength: contentLength, - request: request, - headers: headers, - isRedirect: isRedirect, - persistentConnection: persistentConnection, - reasonPhrase: reasonPhrase); -} diff --git a/packages/flutter/lib/src/http/utils.dart b/packages/flutter/lib/src/http/utils.dart deleted file mode 100644 index 4617269dd0c..00000000000 --- a/packages/flutter/lib/src/http/utils.dart +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file -// for details. 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:convert'; -import 'dart:typed_data'; - -import 'byte_stream.dart'; - -/// Converts a [Map] from parameter names to values to a URL query string. -/// -/// mapToQuery({"foo": "bar", "baz": "bang"}); -/// //=> "foo=bar&baz=bang" -String mapToQuery(Map map, {Encoding encoding}) { - List> pairs = >[]; - map.forEach((String key, String value) => - pairs.add([Uri.encodeQueryComponent(key, encoding: encoding), - Uri.encodeQueryComponent(value, encoding: encoding)])); - return pairs.map((List pair) => "${pair[0]}=${pair[1]}").join("&"); -} - -/// Like [String.split], but only splits on the first occurrence of the pattern. -/// This will always return an array of two elements or fewer. -/// -/// split1("foo,bar,baz", ","); //=> ["foo", "bar,baz"] -/// split1("foo", ","); //=> ["foo"] -/// split1("", ","); //=> [] -List split1(String toSplit, String pattern) { - if (toSplit.isEmpty) return []; - - int index = toSplit.indexOf(pattern); - if (index == -1) return [toSplit]; - return [ - toSplit.substring(0, index), - toSplit.substring(index + pattern.length) - ]; -} - -/// Returns the [Encoding] that corresponds to [charset]. Returns [fallback] if -/// [charset] is null or if no [Encoding] was found that corresponds to -/// [charset]. -Encoding encodingForCharset(String charset, [Encoding fallback = LATIN1]) { - if (charset == null) return fallback; - Encoding encoding = Encoding.getByName(charset); - return encoding == null ? fallback : encoding; -} - - -/// Returns the [Encoding] that corresponds to [charset]. Throws a -/// [FormatException] if no [Encoding] was found that corresponds to [charset]. -/// [charset] may not be null. -Encoding requiredEncodingForCharset(String charset) { - Encoding encoding = Encoding.getByName(charset); - if (encoding != null) return encoding; - throw new FormatException('Unsupported encoding "$charset".'); -} - -/// A regular expression that matches strings that are composed entirely of -/// ASCII-compatible characters. -final RegExp _kAsciiOnly = new RegExp(r"^[\x00-\x7F]+$"); - -/// Returns whether [string] is composed entirely of ASCII-compatible -/// characters. -bool isPlainAscii(String string) => _kAsciiOnly.hasMatch(string); - -/// Converts [input] into a [Uint8List]. -/// -/// If [input] is a [TypedData], this just returns a view on [input]. -Uint8List toUint8List(dynamic input) { - if (input is Uint8List) - return input; - if (input is TypedData) - return new Uint8List.view(input.buffer); - return new Uint8List.fromList(input); -} - -/// If [stream] is already a [ByteStream], returns it. Otherwise, wraps it in a -/// [ByteStream]. -ByteStream toByteStream(Stream> stream) { - if (stream is ByteStream) return stream; - return new ByteStream(stream); -} - -/// Calls [onDone] once [stream] (a single-subscription [Stream]) is finished. -/// The return value, also a single-subscription [Stream] should be used in -/// place of [stream] after calling this method. -Stream/**/ onDone/**/(Stream/**/ stream, void onDone()) => - stream.transform(new StreamTransformer.fromHandlers(handleDone: (EventSink sink) { // ignore: always_specify_types - sink.close(); - onDone(); - })); - -// TODO(nweiz): remove this when issue 7786 is fixed. -/// Pipes all data and errors from [stream] into [sink]. When [stream] is done, -/// [sink] is closed and the returned [Future] is completed. -Future store(Stream stream, EventSink sink) { - Completer completer = new Completer(); - stream.listen(sink.add, - onError: sink.addError, - onDone: () { - sink.close(); - completer.complete(); - }); - return completer.future; -} - -/// Pipes all data and errors from [stream] into [sink]. Completes [Future] once -/// [stream] is done. Unlike [store], [sink] remains open after [stream] is -/// done. -Future writeStreamToSink(Stream stream, EventSink sink) { - Completer completer = new Completer(); - stream.listen(sink.add, - onError: sink.addError, - onDone: () => completer.complete()); - return completer.future; -} - -/// A pair of values. -class Pair { - E first; - F last; - - Pair(this.first, this.last); - - @override - String toString() => '($first, $last)'; - - @override - bool operator==(dynamic other) { - if (other is! Pair) return false; - return other.first == first && other.last == last; - } - - @override - int get hashCode => first.hashCode ^ last.hashCode; -} - -/// Configures [future] so that its result (success or exception) is passed on -/// to [completer]. -void chainToCompleter(Future future, Completer completer) { - future.then(completer.complete, onError: completer.completeError); -} diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index 373aa840dad..d64995207d3 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -7,9 +7,10 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; -import 'package:flutter/http.dart' as http; +import 'package:http/http.dart' as http; import 'platform_messages.dart'; +import 'http_client.dart'; /// A collection of resources used by the application. /// @@ -84,15 +85,18 @@ abstract class AssetBundle { class NetworkAssetBundle extends AssetBundle { /// Creates an network asset bundle that resolves asset keys as URLs relative /// to the given base URL. - NetworkAssetBundle(Uri baseUrl) : _baseUrl = baseUrl; + NetworkAssetBundle(Uri baseUrl) + : _baseUrl = baseUrl, + _httpClient = createHttpClient(); final Uri _baseUrl; + final http.Client _httpClient; String _urlFromKey(String key) => _baseUrl.resolve(key).toString(); @override Future load(String key) async { - final http.Response response = await http.get(_urlFromKey(key)); + final http.Response response = await _httpClient.get(_urlFromKey(key)); if (response.statusCode == 200) return null; return response.bodyBytes.buffer.asByteData(); @@ -100,7 +104,7 @@ class NetworkAssetBundle extends AssetBundle { @override Future loadString(String key, { bool cache: true }) async { - final http.Response response = await http.get(_urlFromKey(key)); + final http.Response response = await _httpClient.get(_urlFromKey(key)); return response.statusCode == 200 ? response.body : null; } diff --git a/packages/flutter/lib/src/services/http_client.dart b/packages/flutter/lib/src/services/http_client.dart new file mode 100644 index 00000000000..2a058d83137 --- /dev/null +++ b/packages/flutter/lib/src/services/http_client.dart @@ -0,0 +1,16 @@ +// Copyright 2017 The Chromium 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 'package:flutter/foundation.dart'; +import 'package:http/http.dart' as http; + +/// Create a new [http.Client] object. +/// +/// This can be set to a new function to override the default logic for creating +/// HTTP clients, for example so that all logic in the framework that triggers +/// HTTP requests will use the same `UserAgent` header, or so that tests can +/// provide an [http.MockClient]. +ValueGetter createHttpClient = () { + return new http.Client(); +}; diff --git a/packages/flutter/lib/src/services/image_provider.dart b/packages/flutter/lib/src/services/image_provider.dart index 1d59abcf972..ab837124f5f 100644 --- a/packages/flutter/lib/src/services/image_provider.dart +++ b/packages/flutter/lib/src/services/image_provider.dart @@ -9,9 +9,10 @@ import 'dart:ui' as ui show Image; import 'dart:ui' show Size, Locale, hashValues; import 'package:flutter/foundation.dart'; -import 'package:flutter/http.dart' as http; +import 'package:http/http.dart' as http; import 'asset_bundle.dart'; +import 'http_client.dart'; import 'image_cache.dart'; import 'image_decoder.dart'; import 'image_stream.dart'; @@ -332,11 +333,13 @@ class NetworkImage extends ImageProvider { ); } + static final http.Client _httpClient = createHttpClient(); + Future _loadAsync(NetworkImage key) async { assert(key == this); final Uri resolved = Uri.base.resolve(key.url); - final http.Response response = await http.get(resolved); + final http.Response response = await _httpClient.get(resolved); if (response == null || response.statusCode != 200) return null; diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 7a3651b75fe..4a8eae1630a 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -6,15 +6,11 @@ homepage: http://flutter.io dependencies: collection: '>=1.9.1 <2.0.0' + http: '>=0.11.3+11' intl: '>=0.14.0 <0.15.0' meta: ^1.0.4 vector_math: '>=2.0.3 <3.0.0' - # async and http_parser can be removed when we move to using dart-lang/http - # directly. - async: "^1.10.0" - http_parser: ">=0.0.1 <4.0.0" - sky_engine: path: ../../bin/cache/pkg/sky_engine diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart index 38498e79bb5..e1f8f31c88e 100644 --- a/packages/flutter_test/lib/src/binding.dart +++ b/packages/flutter_test/lib/src/binding.dart @@ -8,10 +8,12 @@ import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; -import 'package:flutter/http.dart' as http; import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; +import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; +import 'package:http/testing.dart' as http; import 'package:meta/meta.dart'; import 'package:quiver/testing/async.dart'; import 'package:quiver/time.dart'; @@ -119,7 +121,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase @override void initInstances() { timeDilation = 1.0; // just in case the developer has artificially changed it for development - http.Client.clientOverride = () { + createHttpClient = () { return new http.MockClient((http.BaseRequest request) { return new Future.value( new http.Response("Mocked: Unavailable.", 404, request: request)