-
Notifications
You must be signed in to change notification settings - Fork 17
/
in-memory-relay-server.ts
88 lines (86 loc) · 3.28 KB
/
in-memory-relay-server.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
import {type Event, type Filter, matchFilters, matchFilter} from "nostr-tools";
import {WebSocket, WebSocketServer} from "isomorphic-ws";
const _ = WebSocket; // Importing WebSocket is needed for WebSocketServer to work
export class InMemoryRelayServer {
events: (Event & {id: string})[] = [];
auth?: string;
wss: WebSocketServer;
subs: Map<string, Filter[]> = new Map();
connections: Set<WebSocket> = new Set();
totalSubscriptions = 0;
constructor(port = 8081, host = "localhost") {
this.wss = new WebSocketServer({port, host});
this.wss.on("connection", (ws) => {
this.connections.add(ws);
// console.log('connected')
ws.on("message", (message) => {
const data = JSON.parse(message.toString());
// console.log('received: %s', JSON.stringify(data))
if (data && data[0] === "REQ") {
const sub = data[1];
const filters = data.slice(2);
this.totalSubscriptions++;
this.subs.set(sub, filters);
// Go through events in reverse order, look at limits
const counts = filters.map(() => 0);
// console.log("data", data, "events", this.events)
for (let i = this.events.length - 1; i >= 0; i--) {
const event = this.events[i];
// console.log("event", event)
let matched = false;
for (let j = 0; j < filters.length; j++) {
let filter = filters[j];
// console.log("filter", filter, "event", event)
if (matchFilter(filter, event)) {
counts[j]++;
// console.log("j", j, "count", counts[j], "limit", filter.limit)
if (!filter.limit || counts[j] <= filter.limit) {
// console.log("matched j", j, "count", counts[j], "limit", filter.limit)
matched = true;
}
}
}
if (matched) {
// console.log('sending event to sub %s', sub, JSON.stringify(['EVENT', sub, event]))
ws.send(JSON.stringify(["EVENT", sub, event]));
}
}
// console.log('sending eose to sub %s', sub, JSON.stringify(['EOSE', sub]))
ws.send(JSON.stringify(["EOSE", sub]));
} else if (data && data[0] === "EVENT") {
// console.log('received event', data[1], data[2])
const event = data[1];
this.events.push(event);
// Reply with OK
ws.send(JSON.stringify(["OK", event.id, true, ""]));
for (const [sub, filters] of this.subs) {
if (matchFilters(filters, event)) {
// console.log('sending event to sub %s', sub, JSON.stringify(['EVENT', sub, event]))
ws.send(JSON.stringify(["EVENT", sub, event]));
}
}
} else if (data && data[0] === "CLOSE") {
const sub = data[1];
this.subs.delete(sub);
}
});
if (this.auth) {
ws.send(JSON.stringify(["AUTH", this.auth]));
}
});
}
async close(): Promise<void> {
new Promise((resolve) => this.wss.close(resolve));
}
clear() {
this.events = [];
this.subs = new Map();
this.totalSubscriptions = 0;
this.auth = undefined;
}
disconnectAll() {
for (const ws of this.connections) {
ws.close();
}
}
}