Skip to content

Commit f70f6db

Browse files
authored
Merge pull request #2753 from matrix-org/kegan/http-code-on-non-json
Always send back an httpStatus property if one is known
2 parents c81d759 + 500601e commit f70f6db

File tree

5 files changed

+64
-22
lines changed

5 files changed

+64
-22
lines changed

spec/unit/http-api/utils.spec.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { mocked } from "jest-mock";
1919
import {
2020
anySignal,
2121
ConnectionError,
22+
HTTPError,
2223
MatrixError,
2324
parseErrorResponse,
2425
retryNetworkOperation,
@@ -113,6 +114,41 @@ describe("parseErrorResponse", () => {
113114
}, 500));
114115
});
115116

117+
it("should resolve Matrix Errors from XHR with urls", () => {
118+
expect(parseErrorResponse({
119+
responseURL: "https://example.com",
120+
getResponseHeader(name: string): string | null {
121+
return name === "Content-Type" ? "application/json" : null;
122+
},
123+
status: 500,
124+
} as XMLHttpRequest, '{"errcode": "TEST"}')).toStrictEqual(new MatrixError({
125+
errcode: "TEST",
126+
}, 500, "https://example.com"));
127+
});
128+
129+
it("should resolve Matrix Errors from fetch with urls", () => {
130+
expect(parseErrorResponse({
131+
url: "https://example.com",
132+
headers: {
133+
get(name: string): string | null {
134+
return name === "Content-Type" ? "application/json" : null;
135+
},
136+
},
137+
status: 500,
138+
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new MatrixError({
139+
errcode: "TEST",
140+
}, 500, "https://example.com"));
141+
});
142+
143+
it("should set a sensible default error message on MatrixError", () => {
144+
let err = new MatrixError();
145+
expect(err.message).toEqual("MatrixError: Unknown message");
146+
err = new MatrixError({
147+
error: "Oh no",
148+
});
149+
expect(err.message).toEqual("MatrixError: Oh no");
150+
});
151+
116152
it("should handle no type gracefully", () => {
117153
expect(parseErrorResponse({
118154
headers: {
@@ -121,7 +157,7 @@ describe("parseErrorResponse", () => {
121157
},
122158
},
123159
status: 500,
124-
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new Error("Server returned 500 error"));
160+
} as Response, '{"errcode": "TEST"}')).toStrictEqual(new HTTPError("Server returned 500 error", 500));
125161
});
126162

127163
it("should handle invalid type gracefully", () => {
@@ -144,7 +180,7 @@ describe("parseErrorResponse", () => {
144180
},
145181
},
146182
status: 418,
147-
} as Response, "I'm a teapot")).toStrictEqual(new Error("Server returned 418 error: I'm a teapot"));
183+
} as Response, "I'm a teapot")).toStrictEqual(new HTTPError("Server returned 418 error: I'm a teapot", 418));
148184
});
149185
});
150186

spec/unit/interactive-auth.spec.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ limitations under the License.
1818
import { MatrixClient } from "../../src/client";
1919
import { logger } from "../../src/logger";
2020
import { InteractiveAuth, AuthType } from "../../src/interactive-auth";
21-
import { MatrixError } from "../../src/http-api";
21+
import { HTTPError, MatrixError } from "../../src/http-api";
2222
import { sleep } from "../../src/utils";
2323
import { randomString } from "../../src/randomstring";
2424

@@ -219,8 +219,7 @@ describe("InteractiveAuth", () => {
219219
params: {
220220
[AuthType.Password]: { param: "aa" },
221221
},
222-
});
223-
err.httpStatus = 401;
222+
}, 401);
224223
throw err;
225224
});
226225

@@ -282,8 +281,7 @@ describe("InteractiveAuth", () => {
282281
params: {
283282
[AuthType.Password]: { param: "aa" },
284283
},
285-
});
286-
err.httpStatus = 401;
284+
}, 401);
287285
throw err;
288286
});
289287

@@ -338,8 +336,7 @@ describe("InteractiveAuth", () => {
338336
params: {
339337
[AuthType.Password]: { param: "aa" },
340338
},
341-
});
342-
err.httpStatus = 401;
339+
}, 401);
343340
throw err;
344341
});
345342

@@ -374,8 +371,7 @@ describe("InteractiveAuth", () => {
374371
},
375372
error: "Mock Error 1",
376373
errcode: "MOCKERR1",
377-
});
378-
err.httpStatus = 401;
374+
}, 401);
379375
throw err;
380376
});
381377

@@ -402,8 +398,7 @@ describe("InteractiveAuth", () => {
402398
doRequest.mockImplementation((authData) => {
403399
logger.log("request1", authData);
404400
expect(authData).toEqual({ "session": "sessionId" }); // has existing sessionId
405-
const err = new Error('myerror');
406-
(err as any).httpStatus = 401;
401+
const err = new HTTPError('myerror', 401);
407402
throw err;
408403
});
409404

src/http-api/errors.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ interface IErrorJson extends Partial<IUsageLimit> {
2222
error?: string;
2323
}
2424

25+
/**
26+
* Construct a generic HTTP error. This is a JavaScript Error with additional information
27+
* specific to HTTP responses.
28+
* @constructor
29+
* @param {string} msg The error message to include.
30+
* @param {number} httpStatus The HTTP response status code.
31+
*/
32+
export class HTTPError extends Error {
33+
constructor(msg: string, public readonly httpStatus?: number) {
34+
super(msg);
35+
}
36+
}
37+
2538
/**
2639
* Construct a Matrix error. This is a JavaScript Error with additional
2740
* information specific to the standard Matrix error response.
@@ -33,19 +46,19 @@ interface IErrorJson extends Partial<IUsageLimit> {
3346
* @prop {Object} data The raw Matrix error JSON used to construct this object.
3447
* @prop {number} httpStatus The numeric HTTP status code given
3548
*/
36-
export class MatrixError extends Error {
49+
export class MatrixError extends HTTPError {
3750
public readonly errcode?: string;
3851
public readonly data: IErrorJson;
3952

40-
constructor(errorJson: IErrorJson = {}, public httpStatus?: number, public url?: string) {
53+
constructor(errorJson: IErrorJson = {}, public readonly httpStatus?: number, public url?: string) {
4154
let message = errorJson.error || "Unknown message";
4255
if (httpStatus) {
4356
message = `[${httpStatus}] ${message}`;
4457
}
4558
if (url) {
4659
message = `${message} (${url})`;
4760
}
48-
super(`MatrixError: ${message}`);
61+
super(`MatrixError: ${message}`, httpStatus);
4962
this.errcode = errorJson.errcode;
5063
this.name = errorJson.errcode || "Unknown error code";
5164
this.data = errorJson;

src/http-api/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { MediaPrefix } from "./prefix";
2020
import * as utils from "../utils";
2121
import * as callbacks from "../realtime-callbacks";
2222
import { Method } from "./method";
23-
import { ConnectionError, MatrixError } from "./errors";
23+
import { ConnectionError } from "./errors";
2424
import { parseErrorResponse } from "./utils";
2525

2626
export * from "./interface";
@@ -116,8 +116,6 @@ export class MatrixHttpApi<O extends IHttpOpts> extends FetchHttpApi<O> {
116116
defer.reject(err);
117117
return;
118118
}
119-
120-
(<MatrixError>err).httpStatus = xhr.status;
121119
defer.reject(new ConnectionError("request failed", err));
122120
}
123121
break;

src/http-api/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { parse as parseContentType, ParsedMediaType } from "content-type";
1818

1919
import { logger } from "../logger";
2020
import { sleep } from "../utils";
21-
import { ConnectionError, MatrixError } from "./errors";
21+
import { ConnectionError, HTTPError, MatrixError } from "./errors";
2222

2323
// Ponyfill for https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
2424
export function timeoutSignal(ms: number): AbortSignal {
@@ -87,9 +87,9 @@ export function parseErrorResponse(response: XMLHttpRequest | Response, body?: s
8787
);
8888
}
8989
if (contentType?.type === "text/plain") {
90-
return new Error(`Server returned ${response.status} error: ${body}`);
90+
return new HTTPError(`Server returned ${response.status} error: ${body}`, response.status);
9191
}
92-
return new Error(`Server returned ${response.status} error`);
92+
return new HTTPError(`Server returned ${response.status} error`, response.status);
9393
}
9494

9595
function isXhr(response: XMLHttpRequest | Response): response is XMLHttpRequest {

0 commit comments

Comments
 (0)