isReachable method

Future<bool> isReachable(
  1. String url, {
  2. Duration? timeout,
  3. ClientType? clientType,
})

Checks if a URL is reachable.

First attempts a HEAD request, but if it fails with 403/405/501 (common for servers that block HEAD), falls back to a GET request.

A URL is considered reachable if we get ANY response from the server, even error responses like 404 or 500 - because that means the server is online and responding. Only connection failures return false.

Implementation

Future<bool> isReachable(String url,
    {Duration? timeout, ClientType? clientType}) async {
  final effectiveTimeout = timeout ?? const Duration(seconds: 10);

  // First try HEAD request (lighter weight)
  final (headResponse, headError) = await head(
    url: url,
    timeout: effectiveTimeout,
    clientType: clientType,
  );

  // If HEAD succeeded with any response, the server is reachable
  if (headError == null && headResponse != null) {
    return true;
  }

  // If HEAD returned 403, 405, or 501, the server blocked HEAD but is reachable
  // Try a GET request to confirm
  if (headResponse != null &&
      (headResponse.statusCode == 403 ||
          headResponse.statusCode == 405 ||
          headResponse.statusCode == 501)) {
    final (getResponse, getError) = await get(
      url: url,
      timeout: effectiveTimeout,
      clientType: clientType,
    );
    // Any response (even 404, 500) means the server is reachable
    return getError == null || getResponse != null;
  }

  // If HEAD had a connection error, try GET as fallback
  // Some servers don't handle HEAD properly
  if (headError != null &&
      (headError.type == ClientErrorType.unknown ||
          headError.type == ClientErrorType.badResponse)) {
    final (getResponse, getError) = await get(
      url: url,
      timeout: effectiveTimeout,
      clientType: clientType,
    );
    // Any response means server is reachable
    return getError == null || getResponse != null;
  }

  // Check if the error indicates a network/connection issue vs server response
  if (headError != null) {
    // These errors mean we couldn't connect at all
    if (headError.type == ClientErrorType.connectionError ||
        headError.type == ClientErrorType.connectionTimeout ||
        headError.type == ClientErrorType.sendTimeout) {
      return false;
    }
    // Other errors might mean the server responded but with an error
    // If we have a response object, the server IS reachable
    if (headResponse != null) {
      return true;
    }
  }

  return false;
}