http
http copied to clipboard
Request cancellation/abort through cancellation-token
This is an effort trying to tackle the problems discussed in #567, #424, and #204.
This solution introduces a CancellationToken, which is passed into the http-methods of Client e.g.:
http.get(url,
cancellationToken: http.CancellationToken(autoDispose: false));
This design is avoiding breaking changes.
Despite most cancellation-token-based solutions (working with a simple Completer), this approach is also trying to really abort()a request if necessary.
It is possible to add multiple requests to a CancellationToken, to cancel them at once when triggered (disposal of the token has to get done manually in that case).
A request can (if requested) get aborted at multiple points:
- Before the request is getting prepared
- When the request is getting registered at the
CancellationToken - When the request is running, through a
Stream-based invoke ofabort()
Status:
Cancellation is supported and implemented for both IOClient and BrowserClient.
Existing tests have been fixed, but no new tests written yet.
It will need some further reviews.
Problems:
The abort() on HttpRequest (for web) doesn't seem to have any effect during my tests (request time < 800ms) - although it's called during the according readyState (https://api.flutter.dev/flutter/dart-html/HttpRequest/abort.html)
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).
:memo: Please visit https://cla.developers.google.com/ to sign.
Once you've signed (or fixed any issues), please reply here with @googlebot I signed it! and we'll verify it.
What to do if you already signed the CLA
Individual signers
- It's possible we don't have your GitHub username or you're using a different email address on your commit. Check your existing CLA data and verify that your email is set on your git commits.
Corporate signers
- Your company has a Point of Contact who decides which employees are authorized to participate. Ask your POC to be added to the group of authorized contributors. If you don't know who your Point of Contact is, direct the Google project maintainer to go/cla#troubleshoot (Public version).
- The email used to register you as an authorized contributor must be the email used for the Git commit. Check your existing CLA data and verify that your email is set on your git commits.
- The email used to register you as an authorized contributor must also be attached to your GitHub account.
ℹ️ Googlers: Go here for more info.
@FaFre - did you have a chance to check out the CLA agreement linked above?
@natebosch - haven't had the time to read the agreement yet. Will do it today or tomorrow.
@googlebot I signed it!
@FaFre Thank you very much for this Draft and PR This is a very important feature. Just to make sure I have a question: if user is downloading a big file and they are at 50% when the download is cancelled, the rest of the file will not be downloaded right? Also does Firebase Storage give the necesaary server support out of the box? Thanks Fabian.
@aytunch I'm happy you find use in it :+1: Well, it depends. The provided API is pretty high level. The only option we have is to call abort, but the definition, what actually happens with the underlying TCP connection is not given and will probably also vary depending on the underlying platform libraries/native client.
You need to check that on your own with an package sniffer on each platform you want to provide your app for (unless somebody did already research on that...). I'm not familiar with Firebase storage but I assume you just have an basic HTTP-Download. In that case it should be enough to close the TCP connection to stop the download. And this is what you need to confirm with an package sniffer.
I understand @FaFre thanks Do you have an ETA for this PR?
@aytunch I use the feature already in my App's since a couple of months and didn't encounter any problems with it (not targeting web).
However, the implementation for web may need some further investigation. Although I implemented it like in the documentation described, my requests didn't get cancelled accordingly. I couldn't fix that on my own so far, but it's also possible that this effect is caused by the dart libraries and I do everything correctly here.
@aytunch While doing some research on the web request I actually discovered that also the dart:io implementation isn't able to abort requests once the server started sending data.
The implementation seems to see a request as done once the server starts sending data. Therefore they cannot get aborted anymore.
final bigFileUrl = Uri.https('ftp5.gwdg.de',
'/pub/misc/openstreetmap/planet.openstreetmap.org/pbf/planet-latest.osm.pbf'); //~60GB file
final req = await HttpClient().getUrl(bigFileUrl);
unawaited(Future.delayed(const Duration(seconds: 1)).whenComplete(() {
req.abort();
print('sent abort');
}));
final resp = await req.close();
print(await resp.toList());
The code above confirms with DevTools that the HTTP request is still running and memory is filling up.
@natebosch I saw you also did some research on abort() and opened some issues regarding it. Is this behavior already discussed somewhere?
@FaFre Have you seen this issue? https://github.com/dart-lang/http/issues/424
Is this behavior already discussed somewhere?
I think once we have a response, instead of request.abort() we need to use response.detachSocket().
https://github.com/dart-lang/http/pull/521/files#diff-7a772406b9a2c46d9b0b95da73d10dd5ad9807fa78c9519bac0d247690482957R83
I do have plans to revisit timeouts and request aborting soon. I suspect we won't be able to use this PR because it would be a breaking change that might be too hard to roll out, but I have not yet finished exploring the implications of various designs.
I've been working on a fork with cancellation using a CancellationToken, but I've taken a different approach with the tokens so they're not specific to HTTP requests: https://github.com/JonathanPeterCole/dart-cancellation-token-http I haven't had a chance to properly test this implementation in a Flutter app yet though.
Closing this, as it is stale and the files are out of date. Feel free to reopen in case you still want to land it!