diff --git a/api/client.ts b/api/client.ts index ba05f18fc..6053ac0a3 100644 --- a/api/client.ts +++ b/api/client.ts @@ -402,7 +402,7 @@ class RedirectionError { export class Transport { _ws: WebSocket; _counter: number; - _callbacks: { [k: number]: Function }; + _callbacks: { [k: number]: { resolve: Function; reject: Function } }; _closeCallback: Callback; _debug: boolean; @@ -447,7 +447,7 @@ export class Transport { this._counter += 1; // Include the current request id in the request. req["request-id"] = this._counter; - this._callbacks[this._counter] = resolve; + this._callbacks[this._counter] = { resolve, reject }; const msg = JSON.stringify(req); if (this._debug) { console.debug("-->", msg); @@ -482,12 +482,46 @@ export class Transport { string. */ _handle(data: string) { - const resp = JSON.parse(data); + let resp: unknown; + try { + resp = JSON.parse(data); + } catch (error) { + console.error("Unable to parse the raw response from Juju:", data, error); + return; + } + if ( + !( + resp && + typeof resp === "object" && + "request-id" in resp && + typeof resp["request-id"] === "number" && + ("error" in resp || "response" in resp) + ) + ) { + console.error( + "Parsed raw response from Juju is in incorrect format:", + resp + ); + return; + } const id = resp["request-id"]; const callback = this._callbacks[id]; delete this._callbacks[id]; - if (callback) { - callback(resp.error || resp.response); + if (!callback) { + console.error( + "Parsed raw response from Juju can't be handled. No callback available." + ); + return; + } + if ("error" in resp && resp.error) { + callback.reject(resp.error); + } else if ("response" in resp) { + callback.resolve(resp.response); + } else { + console.error( + "Parsed raw response from Juju doesn't contain response or error:", + resp + ); } } } diff --git a/api/tests/test-client.ts b/api/tests/test-client.ts index 2f6a25a06..204ced3f9 100644 --- a/api/tests/test-client.ts +++ b/api/tests/test-client.ts @@ -577,11 +577,19 @@ describe("connectAndLogin", () => { connectAndLogin(url, creds, options) .then(() => fail) .catch((error) => { - expect(error.message).toBe("cannot connect to model after redirection"); + console.log("error", error); + expect(error).toBe("redirection required"); requestEqual(ws.lastRequest, { type: "Admin", - request: "RedirectInfo", - params: null, + request: "Login", + params: { + "auth-tag": "", + "client-version": "3.3.2", + credentials: "", + macaroons: ["fake macaroon"], + nonce: "", + "user-data": "", + }, version: 3, }); done(); @@ -590,8 +598,6 @@ describe("connectAndLogin", () => { new Map([ // Reply to the login request. [1, { error: "redirection required" }], - // Reply to the redirectInfo request. - [2, { response: { servers: [], "ca-cert": null } }], ]) ); // Open the WebSocket connection.