waitUntilReady method

  1. @override
Future<void> waitUntilReady(
  1. WaitStrategyTarget target
)
override

Polls the HTTP endpoint until an acceptable response is received.

The URL is {http|https}://{host}:{mappedPort}{path}. Throws TimeoutException when startupTimeout elapses without success.

Implementation

@override
Future<void> waitUntilReady(WaitStrategyTarget target) async {
  final host = await target.containerHostIp();
  final mappedPort = await target.exposedPort(port);
  final scheme = _tls ? 'https' : 'http';
  final uri = Uri.parse('$scheme://$host:$mappedPort$path');

  // Created once and reused across all poll attempts. Must be closed
  // after polling regardless of outcome to avoid a connection leak.
  HttpClient? insecureClient;
  if (_insecureTls) {
    insecureClient = HttpClient()
      ..badCertificateCallback = (_, __, ___) => true;
  }

  final bool ready;
  try {
    ready = await poll(
      () async {
        try {
          http.Response response;
          if (insecureClient != null) {
            final req = await insecureClient.openUrl(_method, uri);
            for (final entry in _headers.entries) {
              req.headers.set(entry.key, entry.value);
            }
            if (_body != null) {
              req.write(_body);
            }
            final ioResp = await req.close().timeout(
                  const Duration(seconds: 1),
                );
            final bodyBytes = await ioResp.fold<List<int>>(
              [],
              (prev, elem) => prev..addAll(elem),
            );
            response = http.Response.bytes(
              Uint8List.fromList(bodyBytes),
              ioResp.statusCode,
            );
          } else {
            final client = http.Client();
            try {
              final request = http.Request(_method, uri);
              request.headers.addAll(_headers);
              final body = _body;
              if (body != null) {
                request.body = body;
              }
              final streamed = await client
                  .send(request)
                  .timeout(const Duration(seconds: 1));
              response = await http.Response.fromStream(streamed);
            } finally {
              client.close();
            }
          }

          final statusOk = _statusCodeMatcher != null
              ? _statusCodeMatcher!(response.statusCode)
              : _statusCodes.contains(response.statusCode);

          if (!statusOk) {
            return false;
          }
          if (_responsePredicate != null) {
            return _responsePredicate!(response.body);
          }
          return true;
        } on TimeoutException {
          return false;
        } on SocketException {
          return false;
        } on http.ClientException {
          return false;
        }
      },
      transientExceptions: [SocketException, TimeoutException],
    );
  } finally {
    // Release the insecure HttpClient regardless of poll outcome or any
    // unexpected exception thrown by a non-transient error in the loop.
    insecureClient?.close(force: true);
  }

  if (!ready) {
    throw TimeoutException(
      'HTTP endpoint not ready within $startupTimeout',
    );
  }
}