fix(nextcloud-files): WR-03 hoechstens vier gleichzeitige Nextcloud-Aufrufe je Zugang
- die Aufrufsperre vergibt je Zugangsschluessel vier Plaetze bis zur Antwort (Kopfzeilen); weitere Aufrufe warten der Reihe nach und pruefen danach, ob der Zugang inzwischen tot oder der Ursprung gesperrt ist; ein widerrufener Zugang erzeugt so hoechstens vier 401 - Hochladen und Zusammenbau (Antwort erst nach dem ganzen Koerper) belegen keinen Platz - Weboberflaeche: Vorschaubilder laden weiter lazy; schlaegt eines fehl, prueft die Ansicht hoechstens alle 10 s die Verbindung und kehrt bei connectionExpired zur Anmeldung zurueck Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -212,6 +212,12 @@ export interface NcRequestOptions {
|
||||
authorization?: string;
|
||||
/** Gesetzt bei Aufrufen mit gespeichertem App-Passwort (siehe Aufrufsperre). */
|
||||
credentialKey?: string;
|
||||
/**
|
||||
* Nicht unter die Grenze gleichzeitiger Aufrufe je Schluessel (WR-03) fallen: nur fuer
|
||||
* Uebertragungen, deren Antwort erst nach dem ganzen Koerper kommt (PUT eines Stuecks,
|
||||
* Zusammenbau). Sie laufen ohnehin nacheinander je Datei.
|
||||
*/
|
||||
unthrottled?: boolean;
|
||||
/** OCS-Aufruf: setzt `OCS-APIRequest` und `Accept: application/json`. */
|
||||
ocs?: boolean;
|
||||
headersTimeoutMs?: number;
|
||||
@@ -319,6 +325,42 @@ export async function ncRequest(
|
||||
return { ok: false, kind: 'paused', retryAfterSeconds: pause.retryAfterSeconds };
|
||||
}
|
||||
|
||||
// WR-03: hoechstens 4 gleichzeitige Aufrufe je Zugangsschluessel bis zur Antwort. Ein
|
||||
// widerrufener Zugang erzeugt so hoechstens ein paar 401, bevor er als tot gilt.
|
||||
let releaseSlot: (() => void) | undefined;
|
||||
if (opts.credentialKey && !opts.unthrottled) {
|
||||
try {
|
||||
releaseSlot = await gate.acquireSlot(opts.credentialKey, opts.signal);
|
||||
} catch {
|
||||
return { ok: false, kind: 'aborted' };
|
||||
}
|
||||
// Waehrend des Wartens kann der Schluessel gestorben oder der Ursprung gesperrt worden sein.
|
||||
if (gate.isDead(opts.credentialKey)) {
|
||||
releaseSlot();
|
||||
return { ok: false, kind: 'credential-dead' };
|
||||
}
|
||||
const later = gate.isPaused(origin);
|
||||
if (later.paused) {
|
||||
releaseSlot();
|
||||
return { ok: false, kind: 'paused', retryAfterSeconds: later.retryAfterSeconds };
|
||||
}
|
||||
}
|
||||
try {
|
||||
return await sendNcRequest(transport, gate, opts, url, origin);
|
||||
} finally {
|
||||
// Erst NACH der Auswertung (401 -> tot, 429 -> Sperre) freigeben: der naechste Wartende
|
||||
// sieht den neuen Zustand, bevor er sendet.
|
||||
releaseSlot?.();
|
||||
}
|
||||
}
|
||||
|
||||
async function sendNcRequest(
|
||||
transport: NextcloudTransport,
|
||||
gate: NextcloudCallGate,
|
||||
opts: NcRequestOptions,
|
||||
url: string,
|
||||
origin: string,
|
||||
): Promise<NcResult> {
|
||||
const headers: Record<string, string> = { 'user-agent': NC_USER_AGENT };
|
||||
if (opts.ocs) {
|
||||
headers['ocs-apirequest'] = 'true';
|
||||
|
||||
Reference in New Issue
Block a user