http icon indicating copy to clipboard operation
http copied to clipboard

Make SocketException catchable

Open jonasbadstuebner opened this issue 4 years ago • 9 comments

You can check if that fixes everything, but for me it worked as expected. Before I had the annoying issue that my app would crash completely without any try-catch etc. helping in any way if my phone was simply offline. Now I can catch the SocketException with the try-catch around my http.get.

Future<User> getMe() async {
    try {
      final http.Response response = await http.get(
        baseUrl + "user/",
        headers: await defaultHeaders,
      );
      print("getMe: ${json.decode(utf8.decode(response.bodyBytes))}");
      if (response.statusCode == 200) {
        return User.fromJson(json.decode(utf8.decode(response.bodyBytes)));
      } else {
        String errorMsg =
            Error.fromJson(json.decode(utf8.decode(response.bodyBytes)))
                .message;
        return Future.error(errorMsg);
      }
    } catch (e) {
      print("getMe: $e");
      // THIS IS WHERE THE SOCKETEXCEPTION IS CATCHED
      return Future.error(_prepareError(e));
    }
  }

jonasbadstuebner avatar Dec 15 '20 18:12 jonasbadstuebner

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
Corporate signers

ℹ️ Googlers: Go here for more info.

google-cla[bot] avatar Dec 15 '20 18:12 google-cla[bot]

@googlebot I singed it!

jonasbadstuebner avatar Dec 15 '20 20:12 jonasbadstuebner

I would not expect any of these changes to make a meaningful difference to the exceptions you are able to catch.

Can you give a specific narrow reproduction case where this changes behavior?

natebosch avatar Dec 23 '20 21:12 natebosch

Sure, I think I already did. But it should be an issue with every http-request sent. If I put my phone offline via firewall (not by disabling WiFi) and use the non-forked version of http, the whole app crashes with a SocketException that was uncatched. I tried a lot in my own code until I looked into http and changed the things I changed for that PR. :D The SocketException was thrown deeper in http and not correctly pushed up to where I could catch it with the provided try-catch I put in the PR as an example.

jonasbadstuebner avatar Dec 23 '20 22:12 jonasbadstuebner

The SocketException was thrown deeper in http and not correctly pushed up to where I could catch it

I'm not able to see that happen. Here is what I tried:

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

void main() async {
  try {
    await http.get(Uri.http('www.google.com', ''));
  } catch (e) {
    print('Caught: $e');
  }
  print('Done');
}

I disabled wifi and ran this with the VM. The output was:

Caught: SocketException: Failed host lookup: 'www.google.com' (OS Error: nodename nor servname provided, or not known, errno = 8)
Done

I am able to catch the exception without applying the diff in this PR.

I don't know how to reproduce the behavior you are seeing. And I would not expect that the changes in this diff would have any impact on the behavior of this package.

natebosch avatar Dec 23 '20 23:12 natebosch

@natebosch I am having the same issue. http on MacOS desktop. You can reproduce by trying to do a http.post while wifi is off on the laptop. I am unable to catch the error even though I try.

kenthinson avatar Mar 30 '21 19:03 kenthinson

I am experiencing the issue this pull request aims to solve.

This is related to issue #551. In my case I am connecting directly to an ip address over https on the local network. My wifi is on, but the ip in question doesn't exist and thus isn't routable. The socket exception gets reported but never manages to hit the catch block.

0xNF avatar May 20 '21 05:05 0xNF

@0xNF - do you have a minimal reproduction you can share? I have not been able to see locally a SocketException which escapes a catch

natebosch avatar May 20 '21 17:05 natebosch

@natebosch I came across the same issue today. I havent tested the solution provided with this MR,

but here's a sample Code which causes the exception:

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<void> _doLoadStuff() async {
    try {
      String resBody = (await http
              .get(Uri.parse("http://192.168.2.111/test"))
              .timeout(Duration(seconds: 3)))
          .body;
      print(resBody);
    } catch (ex) {
      print(ex);
    }
  }

  @override
  void initState() {
    _doLoadStuff();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[Text("Hello World")],
        ),
      ),
    );
  }
}

The Exception will throw even after the timeout exception has been raised (just wait a few moments..). I used the latest http version:

name: http_issue
description: A new Flutter project.
publish_to: "none" 
version: 1.0.0+1
environment:
  sdk: ">=2.14.0 <3.0.0"
dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.4
  # ...

Flutter doctor output:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 2.5.2, on Ubuntu 20.04.3 LTS 5.11.0-37-generic, locale de_DE.UTF-8)
[!] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    ✗ cmdline-tools component is missing
      Run `path/to/sdkmanager --install "cmdline-tools;latest"`
      See https://developer.android.com/studio/command-line for more details.
    ✗ Android license status unknown.
      Run `flutter doctor --android-licenses` to accept the SDK licenses.
      See https://flutter.dev/docs/get-started/install/linux#android-setup for more details.
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.5)
[✓] Android Studio (version 2020.3)
[✓] VS Code (version 1.60.2)
[✓] Connected device (2 available)

I used the Android Emulator to debug this.

Marcel2508 avatar Oct 08 '21 09:10 Marcel2508

See 484274785 which includes a code sample putting a .catchError before the .timeout to catch the SocketException.

I'll close this PR for now since it does not make a meaningful difference about what exceptions can be caught.

natebosch avatar Oct 28 '22 01:10 natebosch