/
response.ts
100 lines (97 loc) · 2.76 KB
/
response.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { handleHttpRequest } from "./http.ts";
import { handleWs } from "./ws.ts";
import { internalMethods } from "./ws_internal_methods.ts";
import type { JsonValue } from "../json_rpc_types.ts";
export type Methods = {
[method: string]: (...arg: any[]) => JsonValue | Promise<JsonValue>;
};
export type Options = {
// Add headers to the default header '{"content-type" : "application/json"}':
headers?: Headers;
// include or don't include server side error messages in response:
publicErrorStack?: boolean;
// enable 'subscribe', 'emit' and 'unsubscribe' (only ws):
enableInternalMethods?: boolean;
// defaults to 'both':
proto?: "ws" | "http" | "both";
// Enable CORS via the "Access-Control-Allow-Origin" header (only http):
cors?: boolean;
// The server can pass additional arguments to the rpc methods:
additionalArguments?: {
// e.g. `args: {db : new Db()}`, where 'db' will be the named parameter:
args: Record<string, any>;
methods?: (keyof Methods)[];
allMethods?: boolean;
}[];
// for jwt verification:
auth?: {
key?: CryptoKey;
methods?: (keyof Methods)[];
allMethods?: boolean;
jwt?: string | null;
};
};
export async function respond(
methods: Methods,
req: Request,
{
headers = new Headers(),
publicErrorStack = false,
enableInternalMethods = false,
proto = "both",
cors = false,
additionalArguments = [],
auth = {},
}: Options = {},
): Promise<Response> {
const realProto = req.headers.get("upgrade") === "websocket" ? "ws" : "http";
if (
(proto === "http" || proto === "both") && realProto === "http"
) {
return await handleHttpRequest(
req,
methods,
{
headers,
publicErrorStack,
enableInternalMethods,
additionalArguments,
proto,
cors,
auth,
},
req.headers.get("Authorization"),
);
} else if ((proto === "ws" || proto === "both") && realProto === "ws") {
const { socket, response } = Deno.upgradeWebSocket(req);
const methodsAndIdsStore = new Map();
handleWs(
{
socket,
methods: enableInternalMethods
? { ...methods, ...internalMethods }
: methods,
options: {
headers,
publicErrorStack,
enableInternalMethods,
additionalArguments: enableInternalMethods
? [...additionalArguments, {
args: { methodsAndIdsStore },
methods: ["subscribe", "unsubscribe"],
}]
: additionalArguments,
proto,
cors,
auth,
},
},
auth.jwt,
);
return response;
} else {
throw new TypeError(
`The received protocol '${realProto}' doesn't match the expected protocol '${proto}'.`,
);
}
}