Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Impressum probe.ts

  Sprache: JAVA
 

import { randomUUID } from "node:crypto";
import { formatErrorMessage } from "../infra/errors.js";
import type { SystemPresence } from "../infra/system-presence.js";
import { MAX_SAFE_TIMEOUT_DELAY_MS, resolveSafeTimeoutDelayMs } from "../utils/timer-delay.js";
import { GatewayClient, GatewayClientRequestError } from "./client.js";
import { READ_SCOPE } from "./method-scopes.js";
import { isLoopbackHost } from "./net.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "./protocol/client-info.js";

export type GatewayProbeAuth = {
  token?: string;
  password?: string;
};

export type GatewayProbeClose = {
  code: number;
  reason: string;
  hint?: string;
};

export type GatewayProbeCapability =
  | "unknown"
  | "pairing_pending"
  | "connected_no_operator_scope"
  | "read_only"
  | "write_capable"
  | "admin_capable";

export type GatewayProbeAuthSummary = {
  role: string | null;
  scopes: string[];
  capability: GatewayProbeCapability;
};

export type GatewayProbeResult = {
  ok: boolean;
  url: string;
  connectLatencyMs: number | null;
  error: string | null;
  connectErrorDetails?: unknown;
  close: GatewayProbeClose | null;
  auth: GatewayProbeAuthSummary;
  health: unknown;
  status: unknown;
  presence: SystemPresence[] | null;
  configSnapshot: unknown;
};

export const MIN_PROBE_TIMEOUT_MS = 250;
export const MAX_TIMER_DELAY_MS = MAX_SAFE_TIMEOUT_DELAY_MS;
const PAIRING_REQUIRED_PATTERN = /\bpairing required\b/i;
const OPERATOR_READ_SCOPE = "operator.read";
const OPERATOR_WRITE_SCOPE = "operator.write";
const OPERATOR_ADMIN_SCOPE = "operator.admin";

export function clampProbeTimeoutMs(timeoutMs: number): number {
  return resolveSafeTimeoutDelayMs(timeoutMs, { minMs: MIN_PROBE_TIMEOUT_MS });
}

function formatProbeCloseError(close: GatewayProbeClose): string {
  return `gateway closed (${close.code}): ${close.reason}`;
}

function emptyProbeAuth(): GatewayProbeAuthSummary {
  return {
    role: null,
    scopes: [],
    capability: "unknown",
  };
}

function resolveProbeAuthSummary(params: {
  role?: string | null;
  scopes?: string[];
  authMetadataPresent?: boolean;
  error?: string | null;
  close?: GatewayProbeClose | null;
  verifiedRead?: boolean;
  connectLatencyMs?: number | null;
}): GatewayProbeAuthSummary {
  const scopes = Array.isArray(params.scopes) ? params.scopes : [];
  return {
    role: params.role ?? null,
    scopes,
    capability: resolveGatewayProbeCapability({
      auth: { scopes },
      authMetadataPresent: params.authMetadataPresent,
      error: params.error,
      close: params.close,
      verifiedRead: params.verifiedRead,
      connectLatencyMs: params.connectLatencyMs,
    }),
  };
}

export function isPairingPendingProbeFailure(params: {
  error?: string | null;
  close?: GatewayProbeClose | null;
}): boolean {
  return PAIRING_REQUIRED_PATTERN.test(params.close?.reason ?? params.error ?? "");
}

export function resolveGatewayProbeCapability(params: {
  auth?: Pick<GatewayProbeAuthSummary, "scopes"> | null;
  authMetadataPresent?: boolean;
  error?: string | null;
  close?: GatewayProbeClose | null;
  verifiedRead?: boolean;
  connectLatencyMs?: number | null;
}): GatewayProbeCapability {
  if (isPairingPendingProbeFailure(params)) {
    return "pairing_pending";
  }
  const scopes = Array.isArray(params.auth?.scopes) ? params.auth.scopes : [];
  if (scopes.includes(OPERATOR_ADMIN_SCOPE)) {
    return "admin_capable";
  }
  if (scopes.includes(OPERATOR_WRITE_SCOPE)) {
    return "write_capable";
  }
  if (scopes.includes(OPERATOR_READ_SCOPE) || params.verifiedRead === true) {
    return "read_only";
  }
  if (params.connectLatencyMs != null && params.authMetadataPresent === true) {
    return "connected_no_operator_scope";
  }
  return "unknown";
}

export async function probeGateway(opts: {
  url: string;
  auth?: GatewayProbeAuth;
  timeoutMs: number;
  includeDetails?: boolean;
  detailLevel?: "none" | "presence" | "full";
  tlsFingerprint?: string;
}): Promise<GatewayProbeResult> {
  const startedAt = Date.now();
  const instanceId = randomUUID();
  let connectLatencyMs: number | null = null;
  let connectError: string | null = null;
  let connectErrorDetails: unknown = null;
  let close: GatewayProbeClose | null = null;
  let auth = emptyProbeAuth();
  let authMetadataPresent = false;

  const detailLevel = opts.includeDetails === false ? "none" : (opts.detailLevel ?? "full");

  const deviceIdentity = await (async () => {
    let hostname: string;
    try {
      hostname = new URL(opts.url).hostname;
    } catch {
      return null;
    }
    // Local authenticated probes should stay device-bound so read/detail RPCs
    // are not scope-limited by the shared-auth scope stripping hardening.
    if (isLoopbackHost(hostname) && !(opts.auth?.token || opts.auth?.password)) {
      return null;
    }
    try {
      const { loadOrCreateDeviceIdentity } = await import("../infra/device-identity.js");
      return loadOrCreateDeviceIdentity();
    } catch {
      // Read-only or restricted environments should still be able to run
      // token/password-auth detail probes without crashing on identity persistence.
      return null;
    }
  })();

  return await new Promise<GatewayProbeResult>((resolve) => {
    let settled = false;
    let timer: ReturnType<typeof setTimeout> | null = null;
    const clearProbeTimer = () => {
      if (timer) {
        clearTimeout(timer);
        timer = null;
      }
    };
    const armProbeTimer = (onTimeout: () => void) => {
      clearProbeTimer();
      timer = setTimeout(onTimeout, clampProbeTimeoutMs(opts.timeoutMs));
    };
    const settle = (
      result: Omit<GatewayProbeResult, "url" | "connectErrorDetails"> & {
        connectErrorDetails?: unknown;
      },
    ) => {
      if (settled) {
        return;
      }
      settled = true;
      clearProbeTimer();
      client.stop();
      const { connectErrorDetails: resultConnectErrorDetails, ...rest } = result;
      resolve({
        url: opts.url,
        ...rest,
        ...(resultConnectErrorDetails != null
          ? { connectErrorDetails: resultConnectErrorDetails }
          : {}),
      });
    };
    const settleProbe = (params: {
      ok: boolean;
      error: string | null;
      verifiedRead?: boolean;
      health: unknown;
      status: unknown;
      presence: SystemPresence[] | null;
      configSnapshot: unknown;
    }) => {
      settle({
        ok: params.ok,
        connectLatencyMs,
        error: params.error,
        connectErrorDetails,
        close,
        auth: resolveProbeAuthSummary({
          role: auth.role,
          scopes: auth.scopes,
          authMetadataPresent,
          error: params.error,
          close,
          verifiedRead: params.verifiedRead,
          connectLatencyMs,
        }),
        health: params.health,
        status: params.status,
        presence: params.presence,
        configSnapshot: params.configSnapshot,
      });
    };

    const client = new GatewayClient({
      url: opts.url,
      token: opts.auth?.token,
      password: opts.auth?.password,
      tlsFingerprint: opts.tlsFingerprint,
      scopes: [READ_SCOPE],
      clientName: GATEWAY_CLIENT_NAMES.CLI,
      clientVersion: "dev",
      mode: GATEWAY_CLIENT_MODES.PROBE,
      instanceId,
      deviceIdentity,
      onConnectError: (err) => {
        connectError = formatErrorMessage(err);
        connectErrorDetails = err instanceof GatewayClientRequestError ? err.details : null;
      },
      onClose: (code, reason) => {
        close = { code, reason };
        if (connectLatencyMs == null) {
          settleProbe({
            ok: false,
            error: connectError || formatProbeCloseError(close),
            health: null,
            status: null,
            presence: null,
            configSnapshot: null,
          });
        }
      },
      onHelloOk: async (hello) => {
        connectLatencyMs = Date.now() - startedAt;
        authMetadataPresent = typeof hello?.auth === "object" && hello.auth !== null;
        auth = resolveProbeAuthSummary({
          role: typeof hello?.auth?.role === "string" ? hello.auth.role : null,
          scopes: Array.isArray(hello?.auth?.scopes)
            ? hello.auth.scopes.filter((scope): scope is string => typeof scope === "string")
            : [],
          authMetadataPresent,
        });
        if (detailLevel === "none") {
          settleProbe({
            ok: true,
            error: null,
            verifiedRead: false,
            health: null,
            status: null,
            presence: null,
            configSnapshot: null,
          });
          return;
        }
        // Once the gateway has accepted the session, a slow follow-up RPC should no longer
        // downgrade the probe to "unreachable". Give detail fetching its own budget.
        armProbeTimer(() => {
          settleProbe({
            ok: false,
            error: "timeout",
            health: null,
            status: null,
            presence: null,
            configSnapshot: null,
          });
        });
        try {
          if (detailLevel === "presence") {
            const presence = await client.request("system-presence");
            settleProbe({
              ok: true,
              error: null,
              verifiedRead: true,
              health: null,
              status: null,
              presence: Array.isArray(presence) ? (presence as SystemPresence[]) : null,
              configSnapshot: null,
            });
            return;
          }
          const [health, status, presence, configSnapshot] = await Promise.all([
            client.request("health"),
            client.request("status"),
            client.request("system-presence"),
            client.request("config.get", {}),
          ]);
          settleProbe({
            ok: true,
            error: null,
            verifiedRead: true,
            health,
            status,
            presence: Array.isArray(presence) ? (presence as SystemPresence[]) : null,
            configSnapshot,
          });
        } catch (err) {
          const error = formatErrorMessage(err);
          settleProbe({
            ok: false,
            error,
            health: null,
            status: null,
            presence: null,
            configSnapshot: null,
          });
        }
      },
    });

    armProbeTimer(() => {
      const error = connectError ? `connect failed: ${connectError}` : "timeout";
      settleProbe({
        ok: false,
        error,
        health: null,
        status: null,
        presence: null,
        configSnapshot: null,
      });
    });

    client.start();
  });
}

Messung V0.5 in Prozent
C=99 H=100 G=99

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.13Bemerkung:  (vorverarbeitet am  2026-06-10) ¤

*Bot Zugriff






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

letze Version des Agenda Kalenders

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

letze Version der Autor Authoringsoftware

     letze Version des Demonstrationsprogramms Goedel
     letze Version des Bille Abgleichprogramms
     Bilder

Jenseits des Üblichen ....
    

Besucher

Besucher