Sdk: Add abort (or similar) to HttpClientRequest

Created on 4 Feb 2015  Â·  43Comments  Â·  Source: dart-lang/sdk

HttpRequest (in dart:html) has an abort() method

There is a request to add a feature to the http package to support timeout.

This can be done with dart:html, but not dart:io

P1 area-library library-io type-enhancement

Most helpful comment

All 43 comments

_Marked this as blocking #19831._

_Added C11 label._

When creating a HTTP request in dart:io we have the following steps:

HttpClient client = new HttpClient();
client.getUrl(Uri.parse("http://www.example.com/"))
   .then((HttpClientRequest request) {
     // Optionally set up headers...
     // Optionally write to the request object...
     // Then call close.
     ...
     return request.close();
   })
   .then((HttpClientResponse response) {
     // Process the response.
     ...
   });

The call to getUrl completes when the connection has been established. This can take time, so it should be possible to cancel that part. Currently there is no object to hook up any abort call to. A Future is returned, but futures cannot be canceled/aborted.

Then the future completes with a HttpClientRequest. That is closed to send the request and wait for the response. This can take time. In this step there is an object (the HttpClientRequest) on which you can actually call something (e.g. abort). However is seems wrong to call abort after calling close.

Finally the HttpClientResponse comes along and the response can be processed. Here there is also an object where an abort method can be added.

Having to add abort to both HttpClientRequest and HttpClientResponse seems wrong.

One option could be to add an additional argument to getUrl (and friends) where the called can pass an object where getUrl can place a callback for aborting the HTTP transaction, e.g.:

var c = new HttpClientConnectionController();
new Timer(new Duration(seconds: 2), () {
  c.cancel();
});
client.getUrl(Uri.parse("http://www.example.com/"), controller: c)
   .then((HttpClientRequest request) {
   ...
   .catchError((e) => ..., test: (e) => e is CancledException);

Straight-line code here ensures that the cancel member of HttpClientConnectionController has been set by getUrl before the timer callback can be called.

This is also related to issue #19120.


cc @lrhn.

_Added this to the 1.10 milestone._

_This comment was originally written by @kaendfinger_


In all honesty, it would be nice to have some sort of Cancelable Future. In fact, in theory, it would be easy to add cancellation to futures, since it's built upon the Timer, you could either do an if statement to check if it was canceled, but it's unclear how this would propagate to the other futures.

There are definitely cases where it would be nice to have some sort of channel to tell an asynchronous computation that its result is no longer needed.

I don't think adding a function to Future is the best way to do that. Futures can be safely shared with other code. If the future could also be used to cancel the computation for everybody, then sharing it would no longer be safe.
I'd rather have something like a CancelableComputation class with a future and a cancel method (and a shorter name) - since we can't just return a tuple of a future and cancel function.

_Removed this from the 1.10 milestone._
_Added this to the 1.11 milestone._

@sgjesse is anyone actually working on this? If not, should we close it as not planned?

At the moment we don't have a good solution to this. Using Future.timeout on in the various steps should work. That will not cancel the underlying operation, but that will fail in the background.

Wow, still no timeout support. We'll continue using our custom httpRead() method then. Here it is:

https://gist.github.com/davenotik/169cc3a00d428516b257

It uses a Completer to build and return a Future. We start a Timer that calls completeError() to throw an error if the Future still hasn't completed after a specified duration.

Just wondering if there are any plans to address it, or should we forget about it and use workarounds?

Now that Timeout has been added to Socket.connect can a similar timeout be added to HttpClientRequest. I have an issue where I make a connection to a remote host, the connection is established but then the remote host gets "busy" for 10 minutes and blocks the event loop entirely. After that period the remote server eventually closes the connection and I get an error "Remote host closed connection before full headers were received" or something like that.

If I try adding a .timeout to the future from the HttpClientRequest.close() the timeout exception (set for 10 seconds) still waits for the ten minutes for the connection to fail because once established it is blocking.

Likely blocked on some refactoring we're doing with dart:io in Dart 2.

But certainly possible.

Are there any workarounds that can be used to cancel a request?

I'm downloading large files from a server and sometimes users may want to cancel the request so that the download or upload is actually stopped.

+1

We are facing the same issue and we cannot release to prodution because of this. Any chance this will get fixed?

@zanderso @a-siva @mit-mit – thoughts on this?

The new static field HttpClient.connectionTimeout (https://api.dartlang.org/stable/2.0.0/dart-io/HttpClient/connectionTimeout.html) may help with some component of this, but I'm not sure it will cover everything described here.

This is still a critical shortcoming for any app that supports file downloads or other long-running http requests. A connection timeout is not really a solution.

@a-siva @mit-mit @zanderso – I'd love to push on this some more. We really should get on this.

cc @sortie who's looking into :io

Any news on this ?? I'm doing a MJPEG reader, if I can't abort the request does this mean once it start it will actually never stop ??

Or @jonasfj ?

I haven't tried it, but if you're streaming down data from a response, you can probably stop reading the stream and call HttpClientResponse.detachSocket which gives you a socket you can destroy.

But yes, there seems to be something missing here. Both on request and response.

cc @sortie

Any update on this?

cc @zichangg

I can see three infos regarding timeouts in this thread, how they are related?

  • @butlermatt : Now that Timeout has been added to Socket.connect... here
  • @zanderso : new static field HttpClient.connectionTimeout may help with some component here... What does it mean?
  • @jonasfj : call HttpClientResponse.detachSocket which gives you a socket you can destroy... here

package:http with timeout is blocking feature for me. As I understand, problem is with dart:io (dart:html has solution for it).

Some example with the best solution so far would be very helpful. Is https://github.com/dart-lang/http/issues/21#issuecomment-569247009 the correct one (using @zanderso's httpClient.connectionTimeout only, no @jonasfj's HttpClientResponse.detachSocket and @butlermatt's Socket.connect Timeout)?

zichang@ should be able to look at this once he is back from vacation.

Seems like CancellableOperation could be helpful here? A potential solution route?

@ZichangG, I am still waiting for explanation if https://github.com/dart-lang/sdk/issues/22265#issuecomment-571626796

@j0nscalet – sadly not. In many cases you want the network connection to terminate. The CancellableOperation won't do that – although it will give you back control flow

@kevmoo Ah right, makes sense. Thanks.

In case this is helpful to anyone...
As a workaround I'm using CancelableOperation to get control flow around fetching the response stream, and while I don't like it I'm ok with the underlying network operation failing in the background, later, for now.

connectionTimeout is probably the answer. Here is an example.

  HttpClient client = new HttpClient();
  // Set up a time limit.
  client.connectionTimeout = Duration(microseconds: 1);

  client.getUrl(Uri.parse("https://www.google.com/"))
   .then((HttpClientRequest request) {
     // Optionally set up headers...
     // Optionally write to the request object...
     // Then call close.
     return request.close();
   })
   .then((HttpClientResponse response) {
     // response may not come back, so detachSocket() is not reliable.
   });

For other methods like Socket.connect and detachSocket are not good solutions for this problem. Socket.destroy will close the connection but it will not be exposed to user unless detachSocket is called. Since everything here is connected with Future.then mechanism. The only place to call detachSocket to abort the connection is when response is received. (It doesn't make any sense to cancel after request.close().) In this case, HttpClientResponse may not come back and detachSocket will not be called.

Using Future.timeout or CancelableOperation is probably not closing the underlying connection.

For providing a timeout handler, perhaps a try catch block can be used to catch timeout exception.

try {
  // Http connections.
} catch (e) {
  // Check exception type and provide handler.
}

There is no good solution yet

I'm also looking for a way to do this. I have a project where I need to upload images to the server, which of course can take time depending on the size of the image. I want provide the user a way to cancel the operation in case it's taking too long without the upload request continuing in the background. Can anybody help with a workaround? Thanks.

it's almost five years and still looking for a standard solution, :(

Is the plan to reland the above CL?

Reland was ready! Because Lasse went on vacation. He will be back next week and I'll get it land next week.

Thanks @zichangg - marking this as part of Sept release for now.

@zichangg looks like this was relanded?

Yes. It has fixed the broken test and is ready to go.

Related issue for exposing this in package:http: https://github.com/dart-lang/http/issues/424

Was this page helpful?
0 / 5 - 0 ratings

Related issues

matanlurey picture matanlurey  Â·  3Comments

brooth picture brooth  Â·  3Comments

bergwerf picture bergwerf  Â·  3Comments

DartBot picture DartBot  Â·  3Comments

Hixie picture Hixie  Â·  3Comments