Skip to content

Commit 054ee30

Browse files
committed
Remove duplicated appPrefix from the SVS base name
1 parent a581c2b commit 054ee30

File tree

10 files changed

+394
-50
lines changed

10 files changed

+394
-50
lines changed

deno.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@ucla-irl/ndnts-aux",
3-
"version": "1.0.12",
3+
"version": "1.1.0",
44
"description": "NDNts Auxiliary Package for Web and Deno",
55
"scripts": {
66
"test": "deno test --no-check",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/namespace/base-node.ts

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,63 @@
11
import { Endpoint } from '@ndn/endpoint';
2-
import type { Data, Interest } from '@ndn/packet';
2+
import type { Data, Interest, Verifier } from '@ndn/packet';
33
import * as namePattern from './name-pattern.ts';
44
import * as schemaTree from './schema-tree.ts';
55
import { EventChain } from '../utils/event-chain.ts';
6+
import { NamespaceHandler } from './nt-schema.ts';
67

78
export interface BaseNodeEvents {
89
attach(path: namePattern.Pattern, endpoint: Endpoint): Promise<void>;
9-
detach(): Promise<void>;
10+
detach(endpoint: Endpoint): Promise<void>;
1011
}
1112

1213
export class BaseNode {
1314
public readonly onAttach = new EventChain<BaseNodeEvents['attach']>();
1415
public readonly onDetach = new EventChain<BaseNodeEvents['detach']>();
15-
protected endpoint: Endpoint | undefined = undefined;
16+
protected handler: NamespaceHandler | undefined = undefined;
17+
18+
public get namespaceHandler() {
19+
return this.handler;
20+
}
1621

1722
public processInterest(
18-
matched: schemaTree.MatchedObject<BaseNode>,
23+
matched: schemaTree.StrictMatch<BaseNode>,
1924
interest: Interest,
25+
deadline: number,
2026
): Promise<Data | undefined> {
21-
console.warn(`Silently drop unprocessable Interest ${matched.name}: ${interest.appParameters}`);
27+
console.warn(`Silently drop unprocessable Interest ${matched.name.toString()}: ${interest.appParameters}`);
28+
deadline; // Silence warning
2229
return Promise.resolve(undefined);
2330
}
2431

25-
public async processAttach(path: namePattern.Pattern, endpoint: Endpoint) {
32+
public verifyPacket(
33+
matched: schemaTree.StrictMatch<BaseNode>,
34+
pkt: Verifier.Verifiable,
35+
deadline: number,
36+
) {
37+
console.warn(`Silently drop unverified packet ${matched.name.toString()}`);
38+
pkt;
39+
deadline;
40+
return Promise.resolve(false);
41+
}
42+
43+
public storeData(
44+
matched: schemaTree.StrictMatch<BaseNode>,
45+
data: Data,
46+
) {
47+
console.warn(`Not store unexpected Data ${matched.name.toString()}`);
48+
data;
49+
return Promise.resolve();
50+
}
51+
52+
public async processAttach(path: namePattern.Pattern, handler: NamespaceHandler) {
2653
// All children's attach events are called
27-
this.endpoint = endpoint;
28-
await this.onAttach.emit(path, endpoint);
54+
this.handler = handler;
55+
await this.onAttach.emit(path, handler.endpoint);
2956
}
3057

3158
public async processDetach() {
32-
await this.onDetach.emit();
33-
this.endpoint = undefined;
59+
await this.onDetach.emit(this.handler!.endpoint);
60+
this.handler = undefined;
3461
// Then call children's detach
3562
}
3663
}

src/namespace/expressing-point.ts

Lines changed: 177 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,196 @@
1-
import { Endpoint, RetxPolicy } from '@ndn/endpoint';
2-
import { Data, Interest, Name, Signer, type Verifier } from '@ndn/packet';
3-
import * as namePattern from './name-pattern.ts';
1+
import { RetxPolicy } from '@ndn/endpoint';
2+
import { Data, Interest, Signer, type Verifier } from '@ndn/packet';
43
import * as schemaTree from './schema-tree.ts';
54
import { BaseNode, BaseNodeEvents } from './base-node.ts';
6-
import { EventChain } from '../utils/event-chain.ts';
7-
8-
export enum VerifyResult {
9-
Fail = -1,
10-
Unknown = 0,
11-
Pass = 1,
12-
Bypass = 2,
13-
}
5+
import { EventChain, Stop } from '../utils/event-chain.ts';
6+
import { VerifyResult } from './nt-schema.ts';
147

158
export interface ExpressingPointEvents extends BaseNodeEvents {
16-
interest(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<Data | undefined>;
17-
verify(target: schemaTree.StrictMatch<ExpressingPoint>, pkt: Verifier.Verifiable): Promise<VerifyResult>;
18-
searchStorage(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<Data | undefined>;
19-
saveStorage(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<void>;
9+
interest(
10+
args: {
11+
target: schemaTree.StrictMatch<ExpressingPoint>;
12+
interest: Interest;
13+
deadline: number;
14+
},
15+
): Promise<Data | undefined>;
16+
17+
verify(
18+
args: {
19+
target: schemaTree.StrictMatch<ExpressingPoint>;
20+
deadline: number;
21+
pkt: Verifier.Verifiable;
22+
},
23+
): Promise<VerifyResult>;
24+
25+
searchStorage(
26+
args: {
27+
target: schemaTree.StrictMatch<ExpressingPoint>;
28+
interest: Interest;
29+
deadline: number;
30+
},
31+
): Promise<Data | undefined>;
2032
}
2133

2234
export type ExpressingPointOpts = {
2335
lifetimeMs: number;
24-
signer: Signer;
36+
interestSigner?: Signer;
37+
canBePrefix?: boolean;
38+
mustBeFresh?: boolean;
2539
supressInterest?: boolean;
26-
abortSignal?: AbortSignal;
27-
modifyInterest?: Interest.Modify;
2840
retx?: RetxPolicy;
2941
};
3042

3143
export class ExpressingPoint extends BaseNode {
44+
/** Called when Interest received */
3245
public readonly onInterest = new EventChain<ExpressingPointEvents['interest']>();
46+
47+
/** Verify Interest event. Also verifies Data if this is a LeafNode */
3348
public readonly onVerify = new EventChain<ExpressingPointEvents['verify']>();
49+
50+
/** Searching stored data from the storage */
3451
public readonly onSearchStorage = new EventChain<ExpressingPointEvents['searchStorage']>();
35-
public readonly onSaveStorage = new EventChain<ExpressingPointEvents['saveStorage']>();
3652

37-
// public async need(matched: schemaTree.MatchedObject<ExpressingPoint>): Promise<Data | undefined> {}
53+
constructor(
54+
public readonly config: ExpressingPointOpts,
55+
) {
56+
super();
57+
}
58+
59+
public searchCache(target: schemaTree.StrictMatch<ExpressingPoint>, interest: Interest, deadline: number) {
60+
return this.onSearchStorage.chain(
61+
undefined,
62+
(ret) => Promise.resolve(ret ? Stop : [{ target, interest, deadline }]),
63+
{ target, interest, deadline },
64+
);
65+
}
66+
67+
public override async verifyPacket(
68+
matched: schemaTree.StrictMatch<ExpressingPoint>,
69+
pkt: Verifier.Verifiable,
70+
deadline: number,
71+
) {
72+
const verifyResult = await this.onVerify.chain(
73+
VerifyResult.Unknown,
74+
(ret, args) => Promise.resolve((ret < VerifyResult.Unknown || ret >= VerifyResult.Bypass) ? Stop : [args]),
75+
{ target: matched, pkt, deadline },
76+
);
77+
return verifyResult >= VerifyResult.Pass;
78+
}
79+
80+
public override async processInterest(
81+
matched: schemaTree.StrictMatch<ExpressingPoint>,
82+
interest: Interest,
83+
deadline: number,
84+
): Promise<Data | undefined> {
85+
// Search storage
86+
// Reply if there is data (including AppNack). No further callback will be called if hit.
87+
// This is the same behavior as a forwarder.
88+
const cachedData = await this.searchCache(matched, interest, deadline);
89+
if (cachedData) {
90+
return cachedData;
91+
}
92+
93+
// Validate Interest
94+
// Only done when there is a sigInfo or appParam.
95+
// Signed Interests are required to carry AppParam, but may be zero length.
96+
// To guarantee everything is good in case the underlying library returns `undefined` when zero length, check both.
97+
if (interest.appParameters || interest.sigInfo) {
98+
if (!await this.verifyPacket(matched, interest, deadline)) {
99+
// Unverified Interest. Drop
100+
return;
101+
}
102+
}
103+
104+
// PreRecvInt
105+
// Used to decrypt AppParam or handle before onInterest hits, if applicable.
106+
// Do we need them? Hold for now.
107+
108+
// OnInt
109+
const result = await this.onInterest.chain(
110+
undefined,
111+
(ret, args) => Promise.resolve(ret ? Stop : [args]),
112+
{ target: matched, interest, deadline },
113+
);
114+
115+
// PreSendData
116+
// Used to encrypt Data or handle after onInterest hits, if applicable.
117+
// Do we need them? Hold for now.
118+
return result;
119+
}
120+
121+
public async need(
122+
matched: schemaTree.StrictMatch<ExpressingPoint>,
123+
opts: {
124+
appParam?: Uint8Array | string;
125+
supressInterest?: boolean;
126+
abortSignal?: AbortSignal;
127+
signer?: Signer;
128+
lifetimeMs?: number;
129+
deadline?: number;
130+
} = {},
131+
): Promise<Data | undefined> {
132+
// Construct Interest, but without signing, so the parameter digest is not there
133+
const interestArgs = [matched.name] as Array<Interest.CtorArg>;
134+
if (this.config.canBePrefix) {
135+
// Be aware that if CanBePrefix is set, you may need to also validate the data against the LeafNode's validator.
136+
interestArgs.push(Interest.CanBePrefix);
137+
}
138+
if (this.config.mustBeFresh ?? true) {
139+
interestArgs.push(Interest.MustBeFresh);
140+
}
141+
const appParam = opts.appParam instanceof Uint8Array
142+
? opts.appParam
143+
: typeof opts.appParam === 'string'
144+
? new TextEncoder().encode(opts.appParam)
145+
: undefined;
146+
if (appParam) {
147+
interestArgs.push(appParam);
148+
}
149+
// TODO: FwHint is not supported for now. Who should provide this info?
150+
const lifetimeMs = opts.lifetimeMs ?? this.config.lifetimeMs;
151+
interestArgs.push(Interest.Lifetime(lifetimeMs));
152+
const interest = new Interest(...interestArgs);
153+
154+
// Compute deadline
155+
const deadline = opts.deadline ?? (Date.now() + lifetimeMs);
156+
157+
// Get a signer for this interest
158+
const signer = opts.signer ?? this.config.interestSigner;
159+
160+
// If appParam is empty and not signed, the Interest name is final.
161+
// Otherwise, we have to construct the Interest first before searching storage.
162+
// Get a signer for Interest.
163+
let cachedData: Data | undefined = undefined;
164+
if (!appParam && !signer) {
165+
cachedData = await this.searchCache(matched, interest, deadline);
166+
if (cachedData) {
167+
return cachedData;
168+
}
169+
}
170+
171+
// After signing the digest is there
172+
if (signer) {
173+
await signer.sign(interest);
174+
}
175+
// We may search the storage if not yet. However, it seems not useful for now.
176+
177+
// Express the Interest if not surpressed
178+
const supressInterest = opts.supressInterest ?? this.config.supressInterest;
179+
if (supressInterest) {
180+
return undefined;
181+
}
182+
183+
const data = await this.handler!.endpoint.consume(interest, {
184+
// deno-lint-ignore no-explicit-any
185+
signal: opts.abortSignal as any,
186+
retx: this.config.retx,
187+
// Note: the verifier is at the LeafNode if CanBePrefix is set
188+
verifier: this.handler!.getVerifier(deadline),
189+
});
190+
191+
// (no await) Save (cache) the data in the storage
192+
this.handler!.storeData(data);
38193

39-
// public override async processInterest(
40-
// matched: schemaTree.MatchedObject<ExpressingPoint>,
41-
// interest: Interest,
42-
// ): Promise<Data | undefined> {
43-
// }
194+
return data;
195+
}
44196
}

0 commit comments

Comments
 (0)