Repository: flutter
Issue: [Windows] Image.network images seem to be swallowing root cert errors
Labels: framework,platform-windows,a: images,a: error message,P2,team-framework,triaged-framework

I was having a problem on a Windows 10 test machine where network images never seemed to be loading. The expected images never rendered, nor were there any reported errors. It turns out the problem was that the root certificates on that machine had expired, in the case of the test app to LetsEncrypt. [This link by LetsEncrypt](https://docs.certifytheweb.com/docs/kb/kb-202109-letsencrypt/) discusses how to fix that. The larger issue I'm documenting here is the fact that there were no errors, exceptions, timeouts etc. The request just never returned. This is unexpected behavior and not consistent with the way other network errors fail compared to a direct http request. Below is code which goes to the link via http.get as well as the Image.network constructor. On a machine with a root certificate problem for the corresponding server the http path throws an exception while the Image.network path does not, nor ever returns. In instances of bad domains, 404 errors, etc., the behavior of http and the Image.network code is consistent. 

## Steps to Reproduce

1. Change the site/path data in the example code to point to a server whose root certificate your system does not trust
2. Execute the code. Whether it calls the info (thus inducing the network resolution on text update rather than the UI to definitively show the hang) is configurable in the code or by the first CLI parameter.

**Expected results:** <!-- what did you want to see? -->
The call should report an exception and return immediately consistent with what a direct http request returns to the same URL. For example if the domain is wrong:

```bash
flutter: SocketException: Failed host lookup: 'nequalsonelifestyle1.com' (OS Error: No such host is known.

, errno = 11001)

======== Exception caught by image resource service ================================================
The following SocketException was thrown resolving an image codec:
Failed host lookup: 'nequalsonelifestyle1.com' (OS Error: No such host is known.
, errno = 11001)

When the exception was thrown, this was the stack: 
#0      _NativeSocket.startConnect (dart:io-patch/socket_patch.dart:677:35)
...
#44     _dispatchPointerDataPacket (dart:ui/hooks.dart:98:31)
(elided 6 frames from dart:async)
Image provider: NetworkImage("https://nequalsonelifestyle1.com/assets/avatars/current_150x150.jpg", scale: 1.0)
Image key: NetworkImage("https://nequalsonelifestyle1.com/assets/avatars/current_150x150.jpg", scale: 1.0)
====================================================================================================
```

**Actual results:** <!-- what did you see? -->
While the direct http request correctly returns the certificate error the Image network request throws no errors and never returns:

```bash
flutter: HandshakeException: Handshake error in client (OS Error:
        CERTIFICATE_VERIFY_FAILED: unable to get local issuer certificate(../../third_party/boringssl/src/ssl/handshake.cc:359))
```

There should be a comparable exception from the `await _getImageData()` or the direct image load from attempting to load the image to draw the widget but there is nothing.

<details>
<summary>Code sample</summary>

```dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:ui' as ui;

bool useImageInfoQuery = true;
final siteName = 'nequalsonelifestyle.com';
final path = '/assets/avatars/current_150x1150.jpg';
final url = 'https://$siteName$path';

void main(List<String> args) async {
  useImageInfoQuery = args.isEmpty
      ? true
      : args[0].toLowerCase() == 'true'
          ? true
          : false;
  print('Use Image Info Query? $useImageInfoQuery');
  try {
    final response = await http.get(Uri.https(siteName, path));
    print('${response.statusCode} code w/${response.bodyBytes.length} bytes');
  } catch (e) {
    print(e);
  }
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Image.network Cert Bug',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(
          title: 'Image.network Cert Bug'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Image? image;

  Future<void> _loadImage() async {
    image = Image.network(url);
    if (useImageInfoQuery) {
      final info = await _getImageData();
      print('Info = ${info?.width}, ${info?.height}');
    }

    setState(() {
      print('SetState');
    });
  }

  Future<ui.Image?> _getImageData() {
    final completer = Completer<ui.Image>();
    final listener = ImageStreamListener(
        (ImageInfo info, bool _) => completer.complete(info.image));
    image!.image.resolve(const ImageConfiguration()).addListener(listener);
    return completer.future;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            image == null
                ? const Text(
                    'Push Button to load image',
                  )
                : Image(
                    image: image!.image,
                    fit: BoxFit.fill,
                  ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _loadImage,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

</details>