Skip to content

SocketException: Reading from a closed socket #52210

@bagedevimo

Description

@bagedevimo

We've seen multiple instances of errors, with the following exception being thrown when using WebSocket from dart:io:

SocketException: SocketException: Reading from a closed socket
  File "secure_socket.dart", line 770, in _RawSecureSocket.read
  File "socket_patch.dart", line 2323, in _Socket._onData
  File "zone.dart", line 1399, in _rootRunUnary
  File "zone.dart", line 1300, in _CustomZone.runUnary
  File "zone.dart", line 1209, in _CustomZone.runUnaryGuarded
  File "stream_impl.dart", line 339, in _BufferingStreamSubscription._sendData
  File "stream_impl.dart", line 271, in _BufferingStreamSubscription._add
  File "stream_controller.dart", line 774, in _SyncStreamControllerDispatch._sendData
  File "stream_controller.dart", line 648, in _StreamController._add
  File "stream_controller.dart", line 596, in _StreamController.add
  File "secure_socket.dart", line 1107, in _RawSecureSocket._sendReadEvent
  File "zone.dart", line 1383, in _rootRun
  File "zone.dart", line 1293, in _CustomZone.run
  File "zone.dart", line 1201, in _CustomZone.runGuarded
  File "zone.dart", line 1241, in _CustomZone.bindCallbackGuarded.<fn>
  File "zone.dart", line 1391, in _rootRun
  File "zone.dart", line 1293, in _CustomZone.run
  File "zone.dart", line 1225, in _CustomZone.bindCallback.<fn>
  File "timer_patch.dart", line 18, in Timer._createTimer.<fn>
  File "timer_impl.dart", line 398, in _Timer._runTimers
  File "timer_impl.dart", line 429, in _Timer._handleMessage
  File "isolate_patch.dart", line 192, in _RawReceivePortImpl._handleMessage

We did some investigation, and managed to create a small(ish) reproduction which uses a tiny nodejs server to echo back messages, but you could probably reproduce with other websocket servers.

Versions:

Dart SDK version: 2.19.5 (stable) (Mon Mar 20 17:09:37 2023 +0000) on "macos_arm64"

The following code seems to produce the issue fairly reliably, though we're pretty sure it's a race condition so your mileage my vary. Please note we haven't managed to reproduce this on a non-secure websocket channel, and we don't think it will reproducable. The exception seems to be raised here

if (_closedRead) {
throw new SocketException("Reading from a closed socket");
and it seems like maybe that conditional should be returning nil in this case.

Repro code:

import 'dart:async';
import 'dart:io';

class MyHttpOverrides extends HttpOverrides {
  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)
      ..badCertificateCallback =
          (X509Certificate cert, String host, int port) => true;
  }
}

void main() async {
  HttpOverrides.global = MyHttpOverrides();

  await connect();
}

Future<void> connect() async {
  StreamSubscription<dynamic>? sub;

  var sock = await WebSocket.connect(
    "wss://localhost:8080",
    protocols: ["echo-protocol"],
  );

  sub = sock.listen((event) {
    print("got event: $event");
    sub?.cancel();
  });

  sock.add("Hello!");
}

and here is the small echo server we wrote to test against.

var WebSocketServer = require('websocket').server;
var https = require('https');
const fs = require('fs');

const options = {
  key: fs.readFileSync('domain.key'),
  cert: fs.readFileSync('domain.crt'),
  passphrase: 'password'
};

var server = https.createServer(options, function(request, response) {
    console.log((new Date()) + ' Received request for ' + request.url);
    response.writeHead(404);
    response.end();
});
server.listen(8080, function() {
    console.log((new Date()) + ' Server is listening on port 8080');
});

var wsServer = new WebSocketServer({
    httpServer: server,
    // You should not use autoAcceptConnections for production
    // applications, as it defeats all standard cross-origin protection
    // facilities built into the protocol and the browser.  You should
    // *always* verify the connection's origin and decide whether or not
    // to accept it.
    autoAcceptConnections: false
});

wsServer.on('request', function(request) {
    var connection = request.accept('echo-protocol', request.origin);
    console.log((new Date()) + ' Connection accepted.');
    connection.on('message', function(message) {
        if (message.type === 'utf8') {
            console.log('Received Message: ' + message.utf8Data);
            connection.sendUTF(message.utf8Data);
            connection.sendUTF(message.utf8Data);
            connection.sendUTF(message.utf8Data);
            connection.sendUTF(message.utf8Data);
            connection.close();
        }
    });
    connection.on('close', function(reasonCode, description) {
        console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
    });
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-core-librarySDK core library issues (core, async, ...); use area-vm or area-web for platform specific libraries.closed-duplicateClosed in favor of an existing reportlibrary-io

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions