1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-09 10:22:46 +03:00

Merge pull request #2753 from matrix-org/kegan/http-code-on-non-json

Always send back an httpStatus property if one is known
This commit is contained in:
kegsay
2022-10-14 10:31:12 +01:00
committed by GitHub
5 changed files with 64 additions and 22 deletions

View File

@@ -19,6 +19,7 @@ import { mocked } from "jest-mock";
import {
anySignal,
ConnectionError,
HTTPError,
MatrixError,
parseErrorResponse,
retryNetworkOperation,
@@ -113,6 +114,41 @@ describe("parseErrorResponse", () => {
}, 500));
});
it("should resolve Matrix Errors from XHR with urls", () => {
expect(parseErrorResponse({
responseURL: "https://example.com",
getResponseHeader(name: string): string | null {
return name === "Content-Type" ? "application/json" : null;
},
status: 500,
} as XMLHttpRequest, '{"errcode": "TEST"}')).toStrictEqual(new MatrixError({
errcode: "TEST",
}, 500, "https://example.com"));
});
it("should resolve Matrix Errors from fetch with urls", () => {
expect(parseErrorResponse({
url: "https://example.com",
headers: {
get(name: string): string | null {
return name === "Content-Type" ? "application/json" : null;
},
},
status: 500,
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new MatrixError({
errcode: "TEST",
}, 500, "https://example.com"));
});
it("should set a sensible default error message on MatrixError", () => {
let err = new MatrixError();
expect(err.message).toEqual("MatrixError: Unknown message");
err = new MatrixError({
error: "Oh no",
});
expect(err.message).toEqual("MatrixError: Oh no");
});
it("should handle no type gracefully", () => {
expect(parseErrorResponse({
headers: {
@@ -121,7 +157,7 @@ describe("parseErrorResponse", () => {
},
},
status: 500,
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new Error("Server returned 500 error"));
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new HTTPError("Server returned 500 error", 500));
});
it("should handle invalid type gracefully", () => {
@@ -144,7 +180,7 @@ describe("parseErrorResponse", () => {
},
},
status: 418,
} as Response, "I'm a teapot")).toStrictEqual(new Error("Server returned 418 error: I'm a teapot"));
} as Response, "I'm a teapot")).toStrictEqual(new HTTPError("Server returned 418 error: I'm a teapot", 418));
});
});

View File

@@ -18,7 +18,7 @@ limitations under the License.
import { MatrixClient } from "../../src/client";
import { logger } from "../../src/logger";
import { InteractiveAuth, AuthType } from "../../src/interactive-auth";
import { MatrixError } from "../../src/http-api";
import { HTTPError, MatrixError } from "../../src/http-api";
import { sleep } from "../../src/utils";
import { randomString } from "../../src/randomstring";
@@ -219,8 +219,7 @@ describe("InteractiveAuth", () => {
params: {
[AuthType.Password]: { param: "aa" },
},
});
err.httpStatus = 401;
}, 401);
throw err;
});
@@ -282,8 +281,7 @@ describe("InteractiveAuth", () => {
params: {
[AuthType.Password]: { param: "aa" },
},
});
err.httpStatus = 401;
}, 401);
throw err;
});
@@ -338,8 +336,7 @@ describe("InteractiveAuth", () => {
params: {
[AuthType.Password]: { param: "aa" },
},
});
err.httpStatus = 401;
}, 401);
throw err;
});
@@ -374,8 +371,7 @@ describe("InteractiveAuth", () => {
},
error: "Mock Error 1",
errcode: "MOCKERR1",
});
err.httpStatus = 401;
}, 401);
throw err;
});
@@ -402,8 +398,7 @@ describe("InteractiveAuth", () => {
doRequest.mockImplementation((authData) => {
logger.log("request1", authData);
expect(authData).toEqual({ "session": "sessionId" }); // has existing sessionId
const err = new Error('myerror');
(err as any).httpStatus = 401;
const err = new HTTPError('myerror', 401);
throw err;
});

View File

@@ -22,6 +22,19 @@ interface IErrorJson extends Partial<IUsageLimit> {
error?: string;
}
/**
* Construct a generic HTTP error. This is a JavaScript Error with additional information
* specific to HTTP responses.
* @constructor
* @param {string} msg The error message to include.
* @param {number} httpStatus The HTTP response status code.
*/
export class HTTPError extends Error {
constructor(msg: string, public readonly httpStatus?: number) {
super(msg);
}
}
/**
* Construct a Matrix error. This is a JavaScript Error with additional
* information specific to the standard Matrix error response.
@@ -33,11 +46,11 @@ interface IErrorJson extends Partial<IUsageLimit> {
* @prop {Object} data The raw Matrix error JSON used to construct this object.
* @prop {number} httpStatus The numeric HTTP status code given
*/
export class MatrixError extends Error {
export class MatrixError extends HTTPError {
public readonly errcode?: string;
public readonly data: IErrorJson;
constructor(errorJson: IErrorJson = {}, public httpStatus?: number, public url?: string) {
constructor(errorJson: IErrorJson = {}, public readonly httpStatus?: number, public url?: string) {
let message = errorJson.error || "Unknown message";
if (httpStatus) {
message = `[${httpStatus}] ${message}`;
@@ -45,7 +58,7 @@ export class MatrixError extends Error {
if (url) {
message = `${message} (${url})`;
}
super(`MatrixError: ${message}`);
super(`MatrixError: ${message}`, httpStatus);
this.errcode = errorJson.errcode;
this.name = errorJson.errcode || "Unknown error code";
this.data = errorJson;

View File

@@ -20,7 +20,7 @@ import { MediaPrefix } from "./prefix";
import * as utils from "../utils";
import * as callbacks from "../realtime-callbacks";
import { Method } from "./method";
import { ConnectionError, MatrixError } from "./errors";
import { ConnectionError } from "./errors";
import { parseErrorResponse } from "./utils";
export * from "./interface";
@@ -116,8 +116,6 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
defer.reject(err);
return;
}
(<MatrixError>err).httpStatus = xhr.status;
defer.reject(new ConnectionError("request failed", err));
}
break;

View File

@@ -18,7 +18,7 @@ import { parse as parseContentType, ParsedMediaType } from "content-type";
import { logger } from "../logger";
import { sleep } from "../utils";
import { ConnectionError, MatrixError } from "./errors";
import { ConnectionError, HTTPError, MatrixError } from "./errors";
// Ponyfill for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
export function timeoutSignal(ms: number): AbortSignal {
@@ -87,9 +87,9 @@ export function parseErrorResponse(response: XMLHttpRequest | Response, body?: s
);
}
if (contentType?.type === "text/plain") {
return new Error(`Server returned ${response.status} error: ${body}`);
return new HTTPError(`Server returned ${response.status} error: ${body}`, response.status);
}
return new Error(`Server returned ${response.status} error`);
return new HTTPError(`Server returned ${response.status} error`, response.status);
}
function isXhr(response: XMLHttpRequest | Response): response is XMLHttpRequest {