Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

web_socket_channel 2.3.0 - IOWebSocketChannel.connect Doesn't Handle SocketException #1519

Closed
knnkrt opened this issue Jan 22, 2023 · 6 comments · Fixed by dart-lang/web_socket_channel#272

Comments

@knnkrt
Copy link

knnkrt commented Jan 22, 2023

When I close the internet connection to test what happens in onDone method,

Trying to handle exception but it doesn't handle;

try {
  _channel = IOWebSocketChannel.connect(
   Uri.parse("wss://MY_URL.com/ws/"),
  );
} on SocketException {
  print("HANDLE");
}

This code works on web_socket_channel version 2.2.0 (even I don't need to use) but on version 2.3.0 I'm getting:

SocketException (SocketException: Failed host lookup: 'MY_URL.com' (OS Error: No address associated with hostname, errno = 7))
@michpolicht
Copy link

michpolicht commented Jan 23, 2023

Same issue with WebSocketChannel

  try {
    WebSocketChannel.connect(Uri.parse("ws://dupa.nowhere"));
  } catch (e) {
    debugPrint("exception $e");
  }

It's impossible to handle the exception. You'll get [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: SocketException: Failed host lookup

@nicoroy2561
Copy link

Having the same issue, to me being a Timeout whenever I hot restart.

@tmtong
Copy link

tmtong commented Feb 4, 2023

    Uri uri = Uri.parse('ws://$host:${port.toString()}');
    try {
      socket = WebSocketChannel.connect(uri);
    } catch (e) {
      retryReconnect();
      return;
    }
    socket.stream.listen((message) {
      handlePacket(message);
    }, onError: (error) async {
      // debugPrint("onerror $error.toString()");
      retryReconnect();
      return;
    }, onDone: () async {
      // debugPrint("ondone ");
      retryReconnect();
      return;
    });


[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: SocketException: Connection refused (OS Error: Connection refused, errno = 111), address = 127.0.0.1, port = 48482

#0 _NativeSocket.startConnect (dart:io-patch/socket_patch.dart:694:35)
#1604 _RawSocket.startConnect (dart:io-patch/socket_patch.dart:1855:26)
dart-lang/web_socket_channel#2 RawSocket.startConnect (dart:io-patch/socket_patch.dart:27:23)
dart-lang/web_socket_channel#3 Socket._startConnect (dart:io-patch/socket_patch.dart:2078:22)
dart-lang/web_socket_channel#4 Socket.startConnect (dart:io/socket.dart:763:21)
dart-lang/web_socket_channel#5 _ConnectionTarget.connect (dart:_http/http_impl.dart:2466:20)
dart-lang/web_socket_channel#6 _HttpClient._getConnection.connect (dart:_http/http_impl.dart:2906:12)
dart-lang/web_socket_channel#7 _HttpClient._getConnection (dart:_http/http_impl.dart:2911:12)
dart-lang/web_socket_channel#8 _HttpClient._openUrl (dart:_http/http_impl.dart:2766:12)
dart-lang/web_socket_channel#9 _HttpClient.openUrl (dart:_http/http_impl.dart:2604:7)
dart-lang/web_socket_channel#10 _WebSocketImpl.connect (dart:_http/websocket_impl.dart:1021:42)
dart-lang/web_socket_channel#11 WebSocket.connect (dart:_http/websocket.dart:320:22)
dart-lang/web_socket_channel#12 new IOWebSocketChannel.connect (package:web_socket_channel/io.dart:80:28)
dart-lang/web_socket_channel#13 connect (package:web_socket_channel/src/_connect_io.dart:15:24)
#1606 new WebSocketChannel.connect (package:web_socket_channel/src/channel.dart:115:16)

I deliberately have the server down, and have the client retry to tests things out. It should have handle it gracefully.
But an exception is thrown from connect.
I have been using this code for years, and it starts to have this problem for the past two weeks. The catch e didn't catch the exception

Platform I use is linux

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.7.1, on Arch Linux 5.15.90-1-lts, locale
en_US.UTF-8)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Chrome - develop for the web
[✓] Linux toolchain - develop for Linux desktop
[✓] Android Studio (version 2021.3)
[✓] Connected device (2 available)
[✓] HTTP Host Availability

I believe this is thrown from io.dart line 117
connect start a task, and the act of putting the task has no problem, no exception.
However, the task runs into a problem of not able to connect, and throw an exception. It is not surprising that the exception is not catched

  /// Creates a channel without a socket.
  ///
  /// This is used with `connect` to synchronously provide a channel that later
  /// has a socket added.
  IOWebSocketChannel._withoutSocket(Stream stream, this.sink)
      : _webSocket = null,
        stream = stream.handleError(
            (error) => throw WebSocketChannelException.from(error)),
        _readyCompleter = Completer();

But that part of the code from web_scoket_channel didn't change recently (but so am I)

@MousyBusiness
Copy link

I've just tested web_socket_channel 2.2.0 and 2.3.0 and both have the same issue. I believe why we're now seeing this bug is because of iOS16 (except maybe the linux guy), from what I can tell, it reclaims the port after ~1 minute in background (unless something has changed somewhere else in my setup which I've not accounted for).

Work around 1 (create websocket directly)

    try {
      await WebSocket.connect(_buildWebsocketUri("/usr").toString()).then((ws) {
        final _channel = IOWebSocketChannel(ws);
      });
    } catch (e) {
      print(e);
      return;
    }

Work around 2 (guarded zone)

    await runZonedGuarded<Future<void>>(() async {
      final _channel = WebSocketChannel.connect(
        _buildWebsocketUri("/usr"),
      );
    }, (err, stack) {
      print(err);
      print(stack);
    });

@tmtong
Copy link

tmtong commented Feb 11, 2023

Thanks @MousyBusiness

This is what I end up using

  void retryReconnect() {
    if (reconnectTimer != null) {
      return;
    }
    reconnectTimer = Timer(const Duration(seconds: 1), () {
      reconnectTimer = null;
      connect();
    });
  }

  void connect() async {
    // Uri uri = Uri.parse('ws://' + host + ':' + port.toString());
    Uri uri = Uri.parse('ws://$host:${port.toString()}');

    await runZonedGuarded<Future<void>>(() async {
      socket = WebSocketChannel.connect(uri);
    }, (err, stack) {
      retryReconnect();
    });

    socket.stream.listen((message) {
      handlePacket(message);
    }, onError: (error) async {
      // debugPrint("onerror $error.toString()");

      retryReconnect();
      return;
    }, onDone: () async {
      // debugPrint("ondone ");

      retryReconnect();
      return;
    });
  }

@brianquinlan
Copy link
Collaborator

As of 2.3.0, the correct error handling formulation is:

final channel = WebSocketChannel.connect(Uri.parse('ws://example.com'));
try {
  await channel.ready;
} on SocketException catch (e) {
  // Handle the exception.
} on WebSocketChannelException catch (e) {
  // Handle the exception.
}

I have a PR out to clarify this: dart-lang/web_socket_channel#272

And sorry for not managing this change better.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants