[io] When (SecureSocket|Socket).startConnect fails due to timeout, include a `OSError("Connection timed out", 110)` in the SocketException.

This allows the developer to determine the reason for the failure without parsing the exception message.

Bug:https://github.com/dart-lang/sdk/issues/60161
Change-Id: I27a6a81be7a83fe2fa0cbbdaf040af3f7fa38f13
Tested: unit tests
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/412184
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Brian Quinlan <bquinlan@google.com>
diff --git a/sdk/lib/_internal/vm/bin/socket_patch.dart b/sdk/lib/_internal/vm/bin/socket_patch.dart
index 18c3c86..a5ed255 100644
--- a/sdk/lib/_internal/vm/bin/socket_patch.dart
+++ b/sdk/lib/_internal/vm/bin/socket_patch.dart
@@ -1156,7 +1156,12 @@
           onTimeout: () {
             task.cancel();
             throw createError(
-              null,
+              OSError(
+                "Connection timed out",
+                Platform.isWindows
+                    ? 10060 // WSAETIMEDOUT
+                    : 110, // ETIMEDOUT
+              ),
               "Connection timed out, host: ${host}, port: ${port}",
             );
           },
diff --git a/tests/standalone/io/secure_socket_test.dart b/tests/standalone/io/secure_socket_test.dart
index 4bef311..687f436 100644
--- a/tests/standalone/io/secure_socket_test.dart
+++ b/tests/standalone/io/secure_socket_test.dart
@@ -23,30 +23,39 @@
 
 SecurityContext serverContext(String certType, String password) =>
     new SecurityContext()
-      ..useCertificateChain(localFile('certificates/server_chain.$certType'),
-          password: password)
-      ..usePrivateKey(localFile('certificates/server_key.$certType'),
-          password: password);
+      ..useCertificateChain(
+        localFile('certificates/server_chain.$certType'),
+        password: password,
+      )
+      ..usePrivateKey(
+        localFile('certificates/server_key.$certType'),
+        password: password,
+      );
 
 SecurityContext clientContext(String certType, String password) =>
-    new SecurityContext()
-      ..setTrustedCertificates(
-          localFile('certificates/trusted_certs.$certType'),
-          password: password);
+    new SecurityContext()..setTrustedCertificates(
+      localFile('certificates/trusted_certs.$certType'),
+      password: password,
+    );
 
 Future<HttpServer> startServer(String certType, String password) {
   return HttpServer.bindSecure(
-          "localhost", 0, serverContext(certType, password),
-          backlog: 5)
-      .then((server) {
+    "localhost",
+    0,
+    serverContext(certType, password),
+    backlog: 5,
+  ).then((server) {
     server.listen((HttpRequest request) {
-      request.listen((_) {}, onDone: () {
-        request.response.contentLength = 100;
-        for (int i = 0; i < 10; i++) {
-          request.response.add([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
-        }
-        request.response.close();
-      });
+      request.listen(
+        (_) {},
+        onDone: () {
+          request.response.contentLength = 100;
+          for (int i = 0; i < 10; i++) {
+            request.response.add([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+          }
+          request.response.close();
+        },
+      );
     });
     return server;
   });
@@ -56,25 +65,31 @@
   List<int> body = <int>[];
   Completer completer = new Completer();
   startServer(certType, password).then((server) {
-    SecureSocket.connect("localhost", server.port,
-            context: clientContext(certType, password))
-        .then((socket) {
+    SecureSocket.connect(
+      "localhost",
+      server.port,
+      context: clientContext(certType, password),
+    ).then((socket) {
       socket.write("GET / HTTP/1.0\r\nHost: localhost\r\n\r\n");
       socket.close();
-      socket.listen((List<int> data) {
-        body.addAll(data);
-      }, onDone: () {
-        Expect.isTrue(body.length > 100, "$body\n${body.length}");
-        Expect.equals(72, body[0]);
-        Expect.equals(9, body[body.length - 1]);
-        server.close();
-        completer.complete(null);
-      }, onError: (e, trace) {
-        String msg = "Unexpected error $e";
-        if (trace != null) msg += "\nStackTrace: $trace";
-        Expect.fail(msg);
-        completer.complete(null);
-      });
+      socket.listen(
+        (List<int> data) {
+          body.addAll(data);
+        },
+        onDone: () {
+          Expect.isTrue(body.length > 100, "$body\n${body.length}");
+          Expect.equals(72, body[0]);
+          Expect.equals(9, body[body.length - 1]);
+          server.close();
+          completer.complete(null);
+        },
+        onError: (e, trace) {
+          String msg = "Unexpected error $e";
+          if (trace != null) msg += "\nStackTrace: $trace";
+          Expect.fail(msg);
+          completer.complete(null);
+        },
+      );
     });
   });
   return completer.future;
@@ -82,14 +97,17 @@
 
 void testConnectTimeout() {
   asyncStart();
-  Duration timeout = new Duration(milliseconds: 20);
-  SecureSocket.connect("8.8.8.7", 80, timeout: timeout).then((socket) {
-    Expect.fail("Unexpected connection made.");
-    asyncEnd();
-  }).catchError((e) {
-    Expect.isTrue(e is SocketException);
-    asyncEnd();
-  });
+  Duration timeout = new Duration(milliseconds: 0);
+  SecureSocket.connect("8.8.8.7", 80, timeout: timeout)
+      .then((socket) {
+        Expect.fail("Unexpected connection made.");
+        asyncEnd();
+      })
+      .catchError((e) {
+        Expect.isTrue(e is SocketException);
+        Expect.equals(Platform.isWindows? 10060 : 110, (e as SocketException).osError?.errorCode);
+        asyncEnd();
+      });
 }
 
 main() async {
diff --git a/tests/standalone/io/socket_connect_timeout_test.dart b/tests/standalone/io/socket_connect_timeout_test.dart
index 2b3fe97..b2f7175 100644
--- a/tests/standalone/io/socket_connect_timeout_test.dart
+++ b/tests/standalone/io/socket_connect_timeout_test.dart
@@ -15,12 +15,18 @@
 
 void main() {
   asyncStart();
-  Duration timeout = new Duration(milliseconds: 20);
-  Socket.connect("8.8.8.7", 80, timeout: timeout).then((socket) {
-    Expect.fail("Unexpected connection made.");
-    asyncEnd();
-  }).catchError((e) {
-    Expect.isTrue(e is SocketException);
-    asyncEnd();
-  });
+  Duration timeout = new Duration(milliseconds: 0);
+  Socket.connect("8.8.8.7", 80, timeout: timeout)
+      .then((socket) {
+        Expect.fail("Unexpected connection made.");
+        asyncEnd();
+      })
+      .catchError((e) {
+        Expect.isTrue(e is SocketException);
+        Expect.equals(
+          Platform.isWindows ? 10060 : 110,
+          (e as SocketException).osError?.errorCode,
+        );
+        asyncEnd();
+      });
 }