Skip to content

Commit aa68149

Browse files
committed
feat(filter): support handler cleanup
1 parent 26de2d1 commit aa68149

6 files changed

Lines changed: 72 additions & 9 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import type { Libp2p } from "@waku/interfaces";
2+
import { expect } from "chai";
3+
import sinon from "sinon";
4+
5+
import { FilterCodecs, FilterCore } from "./filter.js";
6+
7+
function mockLibp2p(unhandle: sinon.SinonStub): Libp2p {
8+
return {
9+
handle: sinon.stub().resolves(unhandle),
10+
components: {
11+
events: {
12+
addEventListener: sinon.stub(),
13+
removeEventListener: sinon.stub()
14+
},
15+
connectionManager: {
16+
getConnections: sinon.stub().returns([])
17+
}
18+
}
19+
} as unknown as Libp2p;
20+
}
21+
22+
describe("FilterCore", () => {
23+
it("registers handler and removes on stop", async () => {
24+
const unhandle = sinon.stub().resolves();
25+
const libp2p = mockLibp2p(unhandle);
26+
27+
const core = new FilterCore(async () => Promise.resolve(), libp2p);
28+
29+
await core.stop();
30+
31+
expect((libp2p.handle as any).calledOnceWith(FilterCodecs.PUSH)).to.be.true;
32+
expect(unhandle.calledOnce).to.be.true;
33+
});
34+
});

packages/core/src/lib/filter/filter.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ type IncomingMessageHandler = (
3737

3838
export class FilterCore {
3939
private streamManager: StreamManager;
40+
private unregister?: () => void | Promise<void>;
41+
private handlePromise: Promise<void | (() => void | Promise<void>)>;
4042

4143
public readonly multicodec = FilterCodecs.SUBSCRIBE;
4244

@@ -49,15 +51,30 @@ export class FilterCore {
4951
libp2p.components
5052
);
5153

52-
libp2p
53-
.handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
54+
this.handlePromise = (
55+
libp2p.handle(FilterCodecs.PUSH, this.onRequest.bind(this), {
5456
maxInboundStreams: 100
57+
}) as unknown as Promise<() => void | Promise<void>>
58+
)
59+
.then((unhandle) => {
60+
this.unregister = unhandle;
61+
return unhandle;
5562
})
5663
.catch((e) => {
5764
log.error("Failed to register ", FilterCodecs.PUSH, e);
5865
});
5966
}
6067

68+
public async stop(): Promise<void> {
69+
await this.handlePromise;
70+
71+
try {
72+
await this.unregister?.();
73+
} catch (e) {
74+
log.error("Failed to unregister ", FilterCodecs.PUSH, e);
75+
}
76+
}
77+
6178
public async subscribe(
6279
pubsubTopic: PubsubTopic,
6380
peerId: PeerId,

packages/interfaces/src/filter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ export type IFilter = {
7373
* await filter.subscribe(newDecoder, newCallback);
7474
*/
7575
unsubscribeAll(): void;
76+
77+
/**
78+
* Stops the Filter protocol and removes any registered handlers.
79+
*/
80+
stop(): Promise<void>;
7681
};
7782

7883
export type FilterProtocolOptions = {

packages/sdk/src/filter/filter.spec.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,26 +98,27 @@ describe("Filter SDK", () => {
9898
expect(subscriptionInvokeStub.firstCall.args[1]).to.equal(peerId);
9999
});
100100

101-
it("should successfully stop", async () => {
101+
it("should stop and remove handler", async () => {
102102
const contentTopic2 = "/test/1/waku-filter-2/utf8";
103103
const decoder2 = createDecoder(
104104
contentTopic2,
105105
createRoutingInfo(testNetworkconfig, { contentTopic: contentTopic2 })
106106
);
107-
const stopStub = sinon.stub(Subscription.prototype, "stop");
108-
107+
const subscriptionStopStub = sinon.stub(Subscription.prototype, "stop");
109108
sinon.stub(Subscription.prototype, "add").resolves(true);
110109
sinon.stub(Subscription.prototype, "start");
111110

112111
await filter.subscribe(decoder, callback);
113112
await filter.subscribe(decoder2, callback);
114113

115-
filter.unsubscribeAll();
114+
const protocolStopStub = sinon
115+
.stub((filter as any)["protocol"], "stop")
116+
.resolves();
116117

117-
expect(stopStub.calledOnce).to.be.true;
118+
await filter.stop();
118119

119-
const result = await filter.unsubscribe(decoder);
120-
expect(result).to.be.false;
120+
expect(subscriptionStopStub.calledOnce).to.be.true;
121+
expect(protocolStopStub.calledOnce).to.be.true;
121122
});
122123
});
123124

packages/sdk/src/filter/filter.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export class Filter implements IFilter {
4545
return this.protocol.multicodec;
4646
}
4747

48+
public async stop(): Promise<void> {
49+
this.unsubscribeAll();
50+
await this.protocol.stop();
51+
}
52+
4853
public unsubscribeAll(): void {
4954
for (const subscription of this.subscriptions.values()) {
5055
subscription.stop();

packages/sdk/src/waku/waku.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export class WakuNode implements IWaku {
231231
this._nodeStateLock = true;
232232

233233
this.lightPush?.stop();
234+
await this.filter?.stop();
234235
this.healthIndicator.stop();
235236
this.peerManager.stop();
236237
this.connectionManager.stop();

0 commit comments

Comments
 (0)