fetchMetadataValue function

Future<String> fetchMetadataValue(
  1. String path, {
  2. Client? client,
  3. Duration timeout = const Duration(seconds: 3),
})

Retrieves a value from the GCE metadata server without caching the result.

If client is provided, it is used to make the request to the metadata server.

If the metadata server cannot be contacted or returns a non-200 status code, a MetadataServerException is thrown.

The request should complete almost immediately if the metadata server is available. If the metadata server is not available, the request will timeout after timeout.

timeout defaults to 1 second, which we set to a very low value to avoid waiting too long.

Implementation

Future<String> fetchMetadataValue(
  String path, {
  http.Client? client,
  Duration timeout = const Duration(seconds: 3),
}) async {
  final url = gceMetadataUrl(path);

  try {
    final abortTrigger = Completer<void>();
    final request = http.AbortableRequest(
      'GET',
      url,
      abortTrigger: abortTrigger.future,
    )..headers.addAll(metadataFlavorHeaders);

    final actualClient = client ?? http.Client();
    http.Response response;
    Timer? timeoutTimer;
    try {
      timeoutTimer = Timer(timeout, () {
        if (!abortTrigger.isCompleted) {
          abortTrigger.complete();
        }
      });

      final responseStream = await actualClient.send(request);
      response = await http.Response.fromStream(responseStream);
      timeoutTimer.cancel();
    } catch (e) {
      timeoutTimer?.cancel();
      if (abortTrigger.isCompleted) {
        throw TimeoutException('Metadata server check timed out');
      }
      rethrow;
    } finally {
      if (client == null) {
        actualClient.close();
      }
    }

    if (response.statusCode != 200) {
      throw MetadataServerException._(
        '${response.body} (${response.statusCode})',
      );
    }

    return response.body.trim();
  } on TimeoutException catch (e, stackTrace) {
    throw MetadataServerException._(
      'Metadata server check timed out.',
      innerException: e,
      innerStackTrace: stackTrace,
    );
  } on SocketException catch (e, stackTrace) {
    throw MetadataServerException._(
      'Could not connect to $gceMetadataHost.',
      innerException: e,
      innerStackTrace: stackTrace,
    );
  } on http.ClientException catch (e, stackTrace) {
    throw MetadataServerException._(
      'HTTP Client Exception when connecting to $gceMetadataHost.',
      innerException: e,
      innerStackTrace: stackTrace,
    );
  }
}