nextcloud: repair the account on a throttle too, since a throttle follows refusals (issue 1253)
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 67b13c42d32a on f27f4c8cd58c
mesh/delivery closed: a newer head of the same pull request
mesh/check checking 67b13c42d32a on f27f4c8cd58c
Right after the delivery the first answer may be Nextcloud's 429, not a 401; without a repair then the account would never be made and the back-off would wait out a throttle that cannot end.
This commit is contained in:
@@ -289,13 +289,15 @@ export class NextcloudClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* List every share, over the OCS Sharing API as the mesh's own account. A refused login (401)
|
||||
* makes the account right once (ensureAdminAccount) and asks again; a refusal after that, or a
|
||||
* throttle (429), throws LoginRefusedError, so the caller backs off (novox/hq issue 1253).
|
||||
* List every share, over the OCS Sharing API as the mesh's own account. A refused login (401) or
|
||||
* a throttle (429) makes the account right once (ensureAdminAccount) and asks again; a refusal
|
||||
* or a throttle after that throws LoginRefusedError, so the caller backs off (novox/hq issue 1253).
|
||||
*/
|
||||
async listShares(): Promise<NextcloudShare[]> {
|
||||
let res = await this.fetchShares();
|
||||
if (res.status === 401 && !this.accountMadeRight) {
|
||||
// A throttle (429) follows refusals, so it gets the same one repair: the repair goes by occ,
|
||||
// never by a login, and the throttle then wears off under the caller's back-off.
|
||||
if ((res.status === 401 || res.status === 429) && !this.accountMadeRight) {
|
||||
await res.text();
|
||||
this.accountMadeRight = true;
|
||||
this.ensureAdminAccount();
|
||||
|
||||
@@ -113,15 +113,17 @@ test("a refusal after the repair throws LoginRefusedError once-repaired, short a
|
||||
}
|
||||
});
|
||||
|
||||
// 1253/AC3b: a throttle (429) is a refusal too, and is not met by a repair.
|
||||
test("a throttled login (429) throws LoginRefusedError without touching the account", async () => {
|
||||
// 1253/AC3b: a throttle (429) follows refusals: it gets the same one repair, and a throttle
|
||||
// that stays throws LoginRefusedError.
|
||||
test("a throttled login (429) is repaired once and then throws LoginRefusedError", async () => {
|
||||
const c = client();
|
||||
const occ = standInOcc(true);
|
||||
mock.method(c, "occ", occ.impl);
|
||||
const f = mock.method(globalThis, "fetch", standInFetch([429]).impl);
|
||||
try {
|
||||
await assert.rejects(c.listShares(), (err: any) => err instanceof LoginRefusedError && err.status === 429);
|
||||
assert.equal(occ.calls.length, 0);
|
||||
await assert.rejects(c.listShares(), LoginRefusedError);
|
||||
assert.equal(occ.calls.filter((a) => a[0] === "user:resetpassword").length, 1);
|
||||
} finally {
|
||||
f.mock.restore();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user