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

Cannot catch SocketException when using .timeout() #329

Closed
Gujie-Novade opened this issue Oct 9, 2019 · 9 comments
Closed

Cannot catch SocketException when using .timeout() #329

Gujie-Novade opened this issue Oct 9, 2019 · 9 comments

Comments

@Gujie-Novade
Copy link

I want to create a login request and set a timeout of 5s.

If I use this code:

Future<void> login(String email, String password) async {
  final body = { 'email': email, 'password': password };
  final client = http.Client();
  http.Response res;
  try {
    res = await client
      .post(
        '$url/login',
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(body))
      .timeout(const Duration(seconds: 5));
  } on TimeoutException catch (e) {
    // Display an alert, no internet
  } catch (err) {
    print(err);
    return null;
  }

  // Do something with the response...
}

In case there is no internet, I will catch the Timeout Exception first. But 15-20s later, there will be another SocketException thrown asynchronously which I cannot catch...

However, if I only use dart.io with this code:

Future<void> login(String email, String password) async {
  final client = HttpClient();
  client.connectionTimeout = const Duration(seconds: 5);
  
  try {
    final res = await client.postUrl(Uri.parse('$url/login'));
    res.close();
  } on SocketException catch (_) {
    // If there is a timeout, you will catch the exception here
    print('caught socket exception');
  } catch (e) {
    print('caught another exception');
    throw e;
  } finally {
    client.close();
  }
}

In case of a timeout, the exception will be thrown as a SocketException so I don't have this issue.
BTW, someone already created a question on SO here

So there is a problem with the .timeout() function.

@natebosch
Copy link
Member

natebosch commented Oct 11, 2019

The equivalent approach using this package is to set a timeout on the IO HttpClient like you would when using purely dart:io, then construct an IOClient to wrap it as a client from this package.

import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;

Future<void> login(String email, String password) async {
  final ioClient = HttpClient();
  client.connectionTimeout = const Duration(seconds: 5);
  final body = { 'email': email, 'password': password };
  final client = http.IOClient(ioClient);
  http.Response res;
  try {
    res = await client
      .post(
        '$url/login',
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(body));
  } on SocketException catch (e) {
    // Display an alert, no internet
  } catch (err) {
    print(err);
    return null;
  }

  // Do something with the response...
}

The .timeout function on Future is unrelated to this package.

If you do want to use future.timeout you can install a separate error handler for the original future.

Edit: The place where I thought the exception could occur after a .timeout was incorrect.

@ayazemre
Copy link

The equivalent approach using this package is to set a timeout on the IO HttpClient like you would when using purely dart:io, then construct an IOClient to wrap it as a client from this package.

import 'dart:io';

import 'package:http/http.dart' as http;
import 'package:http/io_client.dart' as http;

Future<void> login(String email, String password) async {
  final ioClient = HttpClient();
  client.connectionTimeout = const Duration(seconds: 5);
  final body = { 'email': email, 'password': password };
  final client = http.IOClient(ioClient);
  http.Response res;
  try {
    res = await client
      .post(
        '$url/login',
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(body));
  } on SocketException catch (e) {
    // Display an alert, no internet
  } catch (err) {
    print(err);
    return null;
  }

  // Do something with the response...
}

The .timeout function on Future is unrelated to this package.

If you do want to use future.timeout you can install a separate error handler for the original future.

Future<void> login(String email, String password) async {
  final body = { 'email': email, 'password': password };
  final client = http.Client();
  http.Response res;
  try {
    res = await client
      .post(
        '$url/login',
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode(body))
      .catchError((e) {
        // SocketException would show up here, potentially after the timeout.
      })
      .timeout(const Duration(seconds: 5));
  } on TimeoutException catch (e) {
    // Display an alert, no internet
  } catch (err) {
    print(err);
    return null;
  }

  // Do something with the response...
}

Is this solution still viable? I am having same error. With or without timeout it throws SocketException when there is no internet and I can not catch it with try catch block. When I try to construct a client it does not accept "ioClient" as a parameter.

My code;

try {
      final checkEmail = await http
          .get(credentials, headers: {headers}).catchError((error) {
        print(error);
      }).timeout(Duration(seconds: 5));

      print(checkEmail.body);
      print(checkEmail.statusCode);
      return checkEmail.statusCode == 200 ? "ok" : "Could not validate email.";
    } on Exception catch (f) {
      print(f.toString());
      return "Could not validate email.";
    } on Error catch (e) {
      print(e.toString());
      return "Could not validate email.";
    }

@natebosch
Copy link
Member

With or without timeout it throws SocketException when there is no internet and I can not catch it with try catch block.

Do you have a reliable and minimal reproduction case for this? This isn't the first time I've seen a report of an uncatchable exception but I have never been able to set it up on the VM such that I get an unhandled async exception. Whenever I try this I am able to catch the SocketException in a try/catch block.
#508

@ayazemre
Copy link

ayazemre commented Dec 29, 2020

With or without timeout it throws SocketException when there is no internet and I can not catch it with try catch block.

Do you have a reliable and minimal reproduction case for this? This isn't the first time I've seen a report of an uncatchable exception but I have never been able to set it up on the VM such that I get an unhandled async exception. Whenever I try this I am able to catch the SocketException in a try/catch block.
#508

I recorded my issue. In first the device is on airplane mode so it throws directly. Second part I cut and sped up the video. Server is offline and I successfully catch timeout exception. After close to a minute it throws the exception.
https://youtu.be/dHUFB6jyE48

@natebosch
Copy link
Member

After close to a minute it throws the exception.

It looks like the exception can be caught because it's showing up in a try block in your debugger.

I'm looking for help reproducing the case where a SocketException surfaces and cannot be caught at all.

@ayazemre
Copy link

ayazemre commented Dec 30, 2020

After close to a minute it throws the exception.

It looks like the exception can be caught because it's showing up in a try block in your debugger.

I'm looking for help reproducing the case where a SocketException surfaces and cannot be caught at all.

Sorry if this is a noob question, I am a bit inexperienced. Can you tell me how I can catch it inside of current block?

@Binozo
Copy link

Binozo commented Oct 31, 2021

Still having same issue.

This is my Code:

  Future<ComputerModel?> checkIfComputerHasRufSohn(String ip) async {
    try {
      var response = await http
          .get(Uri.http(ip, '/test?test=hi'))
          .timeout(const Duration(seconds: 5))
          .catchError((error, stacktrace) {
        print("catched error");
      });
      //TODO parse daten
      return ComputerModel("Name", ip);
    } on SocketException catch (e) {
      //print("Caught socketexception: $e");
    } on TimeoutException catch (e) {
      //print('Caught: $e');
    } catch (e) {}
    //print('Done');
    return null;
  }

All exceptions get successfully caught.
Only Socket Timeout Exception gets triggered after ~1 Minute and crashes App without being catchable.

@Desync-o-tron
Copy link

@natebosch I too am having this issue that @Binozo et. al. outlined.

@Desync-o-tron
Copy link

I suppose this isn't a huge issue because the first response gives a way around using a client object. That (sans the typo in the code) fixed it for me.

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

No branches or pull requests

5 participants