Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove package:http from Flutter #15416

Merged
merged 20 commits into from Mar 22, 2018

Conversation

jonahwilliams
Copy link
Member

@jonahwilliams jonahwilliams commented Mar 11, 2018

Flutter will no longer automatically include package:http, nor will it support setting createHttpClient to override the default client.

NetworkAssetBundle and ImageProvider now use dart:io HttpClient directly.
NetworkAssetBundle and ImageProvider use the content-length header if provided to allocate directly into a Uint8List.
Moved some tests which used package:http MockClient to a Mockito based test.
Tests that need mock http requests should use HttpOverrides.runZoned to provide a mock HttpClient instance. For an example, see dev/manual_tests/test/card_collection_test.dart

flutter_test now uses HttpOverrides.global to set a default mocked client which always returns empty 400 responses. This is completely private, clients should author their own overrides if they want specific behavior.

This is a breaking change.

Fixes #13456

This saves a solid 1.4 Kb from the Gallery app on Android.

@Hixie
Copy link
Contributor

Hixie commented Mar 12, 2018

One thing we lose with this is that we used to use only a single HTTP client for all network requests done by the app. That seems unfortunate (e.g. you have to configure the UA string multiple times). Would it make sense to keep an equivalent to createHttpClient around?

@xqwzts
Copy link
Contributor

xqwzts commented Mar 12, 2018


No it isn't, sorry I misread that.

Ah it is in the flutter for react-native docs though: https://flutter.io/flutter-for-react-native/#making-http-networking-requests

@gspencergoog
Copy link
Contributor

gspencergoog commented Mar 12, 2018

Also, there are some scripts that imported http that will probably fail now. You should make sure that they have http in their pubspecs. It looks like you fixed some of them, but not all:

./packages/flutter_tools/test/crash_reporting_test.dart:12:import 'package:http/http.dart';
./packages/flutter_tools/lib/src/crash_reporting.dart:7:import 'package:http/http.dart' as http;
./dev/bots/prepare_package.dart:11:import 'package:http/http.dart' as http;
./dev/tools/java_and_objc_doc.dart:9:import 'package:http/http.dart' as http;

@jonahwilliams
Copy link
Member Author

@Hixie

One thing we lose with this is that we used to use only a single HTTP client for all network requests done by the app. That seems unfortunate (e.g. you have to configure the UA string multiple times). Would it make sense to keep an equivalent to createHttpClient around?

I don't think that is true (at least at master). createHttpClient always returns a new client and package:http's IoClient always created a new HttpClient. We can actually get this feature back using HttpOverrides. For example, the following snippet will make new HttpClient() into a singleton.

class FlutterHttpOverrides extends HttpOverrides {
  HttpClient _httpClient;

  HttpClient createHttpClient(SecurityContext context) {
    return _httpClient ??= new HttpClient();
  }
}

@xqwzts It looks like there are references to createHttpClient in those docs, as well as the udacity course. I will file follow up bugs to fix the documentation and note them in this PR.

@gspencergoog Thanks, I've added the missing dependency to the tools/ folder.

@jonahwilliams
Copy link
Member Author

Filed #15447 for updating the website

@jonahwilliams
Copy link
Member Author

Filed https://github.com/flutter/udacity-course/issues/72 for the udacity course. I can open PRs to update the code in each after this PR is resolved

http_multi_server: 2.0.4 # TRANSITIVE DEPENDENCY
http_parser: 3.1.1 # TRANSITIVE DEPENDENCY
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please make sure to run flutter update-packages --force-upgrade to update the pubspec.yaml files.

@jonahwilliams
Copy link
Member Author

@Hixie I've updated the dependencies and the documentation that refers to createHttpClient, is there anything else I need to do for this?

final Uint8List bytes = new Uint8List(response.contentLength);
int offset = 0;
response.listen((List<int> chunk) {
for (int i = 0; i < chunk.length; i++, offset++) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: += 1 rather than ++

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but forget that. Instead of this loop, just use bytes.addAll(chunk).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or rather, bytes.setRange

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

cancelOnError: true,
);
}
final Uint8List bytes = await completer.future;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @rmacnak-google who may have a better idea of how to do this without copying bytes multiple times.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the case where the total length is known is optimal: copy from the OS socket into the chunk, copy from chunks into the linear buffer. The unknown length case has additional copies from growing the buffer and trimming the final result. I believe the chunks are not recycled, so it could be improved by holding onto the chunks and building the linear buffer after receiving the last chunk.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well the "optimal" case wouldn't copy at all, right, we'd just re-use the OS buffer unmodified...

completer.complete(bytes.buffer.asByteData());
},
cancelOnError: true);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should definitely not duplicate this code. see if you can factor it out into something in the foundation library, so that we only have to optimize it once.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to response_converter.dart in foundation

);
return response.body;
final ByteData bytes = await load(key);
return const Utf8Decoder().convert(bytes.buffer.asUint8List());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we really shouldn't do the UTF8 conversion on the main thread if the data size is big. Look for uses of compute which show how to move this kind of thing off the main thread.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this logic is shared by all of the Bundle classes I moved it into the base class.

@Hixie
Copy link
Contributor

Hixie commented Mar 21, 2018

LGTM modulo comments above.

@@ -4,4 +4,6 @@

import 'package:flutter/widgets.dart';

void main() => runApp(const Center(child: const Text('Hello, world!', textDirection: TextDirection.ltr)));
void main() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fat arrow syntax for main() => is preferred in examples, so new users don't write any statements other than runApp there - which wouldn't be picked up by the hot reload.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woops, shouldn't have committed this

@jonahwilliams
Copy link
Member Author

@rmacnak-google
Copy link
Contributor

lgtm

@@ -0,0 +1,45 @@
import 'dart:async';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need license block (copy it from another file verbatim then change the year to 2018) with no other changes

import 'dart:io';
import 'dart:typed_data';


Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove a blank line here

/// Efficiently converts the response body of an [HttpClientResponse] into a [Uint8List].
///
/// The future returned by [convert] will forward all errors emitted by [response].
Future<Uint8List> convertResponse(HttpClientResponse response) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be an HttpClientResponse, or will it work with any Stream<List<int>>?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i guess you need contentLength, oh well.

/// Efficiently converts the response body of an [HttpClientResponse] into a [Uint8List].
///
/// The future returned by [convert] will forward all errors emitted by [response].
Future<Uint8List> convertResponse(HttpClientResponse response) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a global method, let's call it something more specific, like "consolidateHttpClientResponseBytes" or something.

@Hixie
Copy link
Contributor

Hixie commented Mar 22, 2018

LGTM!

@jonahwilliams jonahwilliams merged commit 88cc977 into flutter:master Mar 22, 2018
@jonahwilliams jonahwilliams deleted the remove_http_2 branch March 22, 2018 15:21
DaveShuckerow pushed a commit to DaveShuckerow/flutter that referenced this pull request May 14, 2018
* use HttpOverrides and dart:io HttpClient in flutter

* add missing package:http dependency

* update flutter packages and remove comment about createHttpClient from flutter_test

* move byte loading logic to common class, move string parsing logic to base class

* addAll doesn't work for a Uint8List

* use bytes.setRange

* undo addition to hello_world

* add newline to end of binding.dart

* and a newline for hello world

* refactor to function and add tests

* address comments on unknown length case

* alignment

* sort alaphabetically

* rename convertResponse to consolidateClientHttpClientResponseBytes.  Add header

* fix alignment in test
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Switch from services.createHttpClient to dart:io.createHttpClient and use dart:io's HttpClient
6 participants