Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions src/transports/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,28 @@ export default class HTTPServerTransport extends ServerTransport {
}

private async httpRouterHandler(req: any, res: any): Promise<void> {
let result = null;
if (req.body instanceof Array) {
result = await Promise.all(req.body.map((r: JSONRPCRequest) => super.routerHandler(r)));
} else {
result = await super.routerHandler(req.body);
const result = (await Promise.all(req.body.map((r: JSONRPCRequest) => super.routerHandler(r))))
.filter((r) => r !== undefined);

if (result.length === 0) {
res.statusCode = 204;
res.end();
return;
}

res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(result));
return;
}

const result = await super.routerHandler(req.body);
if (result === undefined) {
res.statusCode = 204;
res.end();
return;
}

res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(result));
}
Expand Down
24 changes: 20 additions & 4 deletions src/transports/https.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,28 @@ export default class HTTPSServerTransport extends ServerTransport {
}

private async httpsRouterHandler(req: any, res: any): Promise<void> {
let result = null;
if (req.body instanceof Array) {
result = await Promise.all(req.body.map((r: JSONRPCRequest) => super.routerHandler(r)));
} else {
result = await super.routerHandler(req.body);
const result = (await Promise.all(req.body.map((r: JSONRPCRequest) => super.routerHandler(r))))
.filter((r) => r !== undefined);

if (result.length === 0) {
res.statusCode = 204;
res.end();
return;
}

res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(result));
return;
}

const result = await super.routerHandler(req.body);
if (result === undefined) {
res.statusCode = 204;
res.end();
return;
}

res.setHeader("Content-Type", "application/json");
res.end(JSON.stringify(result));
}
Expand Down
19 changes: 15 additions & 4 deletions src/transports/ipc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,23 @@ export default class IPCServerTransport extends ServerTransport {
}

private async ipcRouterHandler(req: any, respondWith: any) {
let result = null;
if (req instanceof Array) {
result = await Promise.all(req.map((jsonrpcReq: JSONRPCRequest) => super.routerHandler(jsonrpcReq)));
} else {
result = await super.routerHandler(req);
const result = (await Promise.all(req.map((jsonrpcReq: JSONRPCRequest) => super.routerHandler(jsonrpcReq))))
.filter((r) => r !== undefined);

if (result.length === 0) {
return;
}

respondWith(JSON.stringify(result));
return;
}

const result = await super.routerHandler(req);
if (result === undefined) {
return;
}

respondWith(JSON.stringify(result));
}
}
27 changes: 24 additions & 3 deletions src/transports/server-transport.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import ServerTransport from "./server-transport";
import ServerTransport, { JSONRPCResponse } from "./server-transport";

describe("Server transport test", () => {

Expand All @@ -21,7 +21,7 @@ describe("Server transport test", () => {
// Mock a router that doesn't implement any method
const fakeRouter = { isMethodImplemented: () => false, call: jest.fn() } as unknown as import("../router").Router;
t.addRouter(fakeRouter);
const result = await t['routerHandler']({ jsonrpc: "2.0", id: "1", method: "foo", params: [] });
const result = await t['routerHandler']({ jsonrpc: "2.0", id: "1", method: "foo", params: [] }) as JSONRPCResponse;
expect(result.error).toBeDefined();
expect(result.error?.code).toBe(-32601); // Method not found
});
Expand All @@ -33,10 +33,31 @@ describe("Server transport test", () => {
call: async () => ({ result: 42 }),
} as unknown as import("../router").Router;
t.addRouter(fakeRouter);
const result = await t['routerHandler']({ jsonrpc: "2.0", id: "1", method: "bar", params: [] });
const result = await t['routerHandler']({ jsonrpc: "2.0", id: "1", method: "bar", params: [] }) as JSONRPCResponse;
expect(result.result).toBe(42);
});

it("returns undefined for notifications", async () => {
const t = new DummyTransport();
const fakeRouter = {
isMethodImplemented: () => true,
call: jest.fn().mockResolvedValue({ result: 0 }),
} as unknown as import("../router").Router;
t.addRouter(fakeRouter);
const result = await t['routerHandler']({ jsonrpc: "2.0", method: "notify", params: [] });
expect(result).toBeUndefined();
expect(fakeRouter.call).toHaveBeenCalled();
});

it("returns undefined for notifications with unknown method", async () => {
const t = new DummyTransport();
const fakeRouter = { isMethodImplemented: () => false, call: jest.fn() } as unknown as import("../router").Router;
t.addRouter(fakeRouter);
const result = await t['routerHandler']({ jsonrpc: "2.0", method: "foo", params: [] });
expect(result).toBeUndefined();
expect(fakeRouter.call).not.toHaveBeenCalled();
});

it("covers the no router configured branch in routerHandler", async () => {
class DummyTransport extends ServerTransport {}
const t = new DummyTransport();
Expand Down
14 changes: 11 additions & 3 deletions src/transports/server-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export interface JSONRPCRequest {
export interface JSONRPCErrorObject {
code: number;
message: string;
data: any;
data?: any;
}

export interface JSONRPCResponse {
Expand Down Expand Up @@ -41,15 +41,23 @@ export abstract class ServerTransport {
throw new Error("Transport missing stop implementation");
}

protected async routerHandler({ id, method, params }: JSONRPCRequest): Promise<JSONRPCResponse> {
protected async routerHandler({ id, method, params }: JSONRPCRequest): Promise<JSONRPCResponse | void> {
if (this.routers.length === 0) {
console.warn("transport method called without a router configured."); // tslint:disable-line
throw new Error("No router configured");
}

const routerForMethod = this.routers.find((r) => r.isMethodImplemented(method));

let res = {
// Notifications (id is undefined) must not yield a response
if (id === undefined) {
if (routerForMethod !== undefined) {
await routerForMethod.call(method, params);
}
return;
}

let res: JSONRPCResponse = {
id,
jsonrpc: "2.0",
};
Expand Down
19 changes: 15 additions & 4 deletions src/transports/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,23 @@ export default class WebSocketServerTransport extends ServerTransport {
}

private async webSocketRouterHandler(req: any, respondWith: any) {
let result = null;
if (req instanceof Array) {
result = await Promise.all(req.map((r: JSONRPCRequest) => super.routerHandler(r)));
} else {
result = await super.routerHandler(req);
const result = (await Promise.all(req.map((r: JSONRPCRequest) => super.routerHandler(r))))
.filter((r) => r !== undefined);

if (result.length === 0) {
return;
}

respondWith(JSON.stringify(result));
return;
}

const result = await super.routerHandler(req);
if (result === undefined) {
return;
}

respondWith(JSON.stringify(result));
}
}
Loading