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

fix: in the stream mode, request cannot be canceled #1947

Closed
wants to merge 3 commits into from

Conversation

lh900519
Copy link

@lh900519 lh900519 commented Aug 30, 2023

New Pull Request Checklist

  • I have read the Documentation
  • I have searched for a similar pull request in the project and found none
  • I have updated this branch with the latest main branch to avoid conflicts (via merge from master or rebase)
  • I have added the required tests to prove the fix/feature I'm adding
  • I have updated the documentation (if necessary)
  • I have run the tests without failures
  • I have updated the CHANGELOG.md in the corresponding package

Additional context and info (if any)

@lh900519 lh900519 requested a review from a team as a code owner August 30, 2023 11:07
@kuhnroyal
Copy link
Member

Thanks for your PR!

Do you have any additional information for the root problem?
The fix currently won't work when thinking about parallel requests, the closing of the HTTP client may kill other requests.
Please provide a test that reproduces the problem.

@lh900519
Copy link
Author

lh900519 commented Sep 4, 2023

import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:dio/dio.dart';

void main() async {
  final dio = Dio();
  clientStream(dio, '1');
  clientStream(dio, '2');
}

Future clientStream(Dio dio, String name) async {
  try {
    final token = CancelToken();
    Timer(const Duration(seconds: 3), () {
      token.cancel("----Canceled");
    });
    const url = "https://hacker-news.firebaseio.com/v0/topstories.json";
    Response<ResponseBody> rs = await dio.get<ResponseBody>(
      url,
      options: Options(
        headers: {
          "Authorization": 'vhdrjb token"',
          "Accept": "text/event-stream",
          "Cache-Control": "no-cache",
        },
        responseType: ResponseType.stream,
      ), // set responseType to `stream`
      cancelToken: token,
    );

    rs.data?.stream
        .transform(unit8Transformer)
        .transform(const Utf8Decoder())
        .transform(const LineSplitter())
        .listen((event) {
      if (event.startsWith("event:")) {
        print("$name $event isCancelled:${token.isCancelled}");
      }
      if (event.startsWith("data:")) {
        print(
            "$name ${event.substring(0, 5)} isCancelled:${token.isCancelled}");
      }
    });
  } catch (e) {
    print('error $e');
  }
}

StreamTransformer<Uint8List, List<int>> unit8Transformer =
    StreamTransformer.fromHandlers(
  handleData: (data, sink) {
    sink.add(List<int>.from(data));
  },
);

Run the code with dart test.dart
The request was canceled using CancelToken after 3 seconds, but the terminal did not stop and continued to output.

This PR does not reuse _cachedHttpClient for stream type requests, but creates a new httpclient, and closes the newly created httpclient when using CancelToken. The processing of stream types here is the same as in 5.1.2.

Copy link
Member

@AlexV525 AlexV525 left a comment

Choose a reason for hiding this comment

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

If you have a reproducible example, please make it a test, we need tests to avoid further breakages.

Comment on lines +241 to +242
HttpClient _configHttpClient(Duration? connectionTimeout,
{bool newClient = false}) {
Copy link
Member

Choose a reason for hiding this comment

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

These are not well-formatted (with trailing commas).

HttpClient _configHttpClient(Duration? connectionTimeout,
{bool newClient = false}) {
if (newClient) {
final client = _createHttpClient();
Copy link
Member

Choose a reason for hiding this comment

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

If we create new clients here, what about the closing after the client has been used?

@@ -227,7 +238,13 @@ class IOHttpClientAdapter implements HttpClientAdapter {
);
}

HttpClient _configHttpClient(Duration? connectionTimeout) {
HttpClient _configHttpClient(Duration? connectionTimeout,
{bool newClient = false}) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer forceNotCachedClient for better understanding, or someone else can have better namings.

@kuhnroyal
Copy link
Member

I am also wondering if there is an upstream issue for this problem. We abort() the request correctly, why does this not work?

@kuhnroyal
Copy link
Member

Maybe this is the source of the problem? dart-lang/sdk#51267

@AlexV525 AlexV525 linked an issue Sep 21, 2023 that may be closed by this pull request
@AlexV525
Copy link
Member

AlexV525 commented Oct 1, 2023

Converting this to a draft until we get progress with the implementation and tests.

@AlexV525 AlexV525 marked this pull request as draft October 1, 2023 10:18
@AlexV525
Copy link
Member

Should be covered by #2032.

@AlexV525 AlexV525 closed this Nov 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Files cannot be deleted if network exceptions are thrown
3 participants