-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove duplicated appPrefix from the SVS base name
- Loading branch information
Showing
10 changed files
with
394 additions
and
50 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,63 @@ | ||
import { Endpoint } from '@ndn/endpoint'; | ||
import type { Data, Interest } from '@ndn/packet'; | ||
import type { Data, Interest, Verifier } from '@ndn/packet'; | ||
import * as namePattern from './name-pattern.ts'; | ||
import * as schemaTree from './schema-tree.ts'; | ||
import { EventChain } from '../utils/event-chain.ts'; | ||
import { NamespaceHandler } from './nt-schema.ts'; | ||
|
||
export interface BaseNodeEvents { | ||
attach(path: namePattern.Pattern, endpoint: Endpoint): Promise<void>; | ||
detach(): Promise<void>; | ||
detach(endpoint: Endpoint): Promise<void>; | ||
} | ||
|
||
export class BaseNode { | ||
public readonly onAttach = new EventChain<BaseNodeEvents['attach']>(); | ||
public readonly onDetach = new EventChain<BaseNodeEvents['detach']>(); | ||
protected endpoint: Endpoint | undefined = undefined; | ||
protected handler: NamespaceHandler | undefined = undefined; | ||
|
||
public get namespaceHandler() { | ||
return this.handler; | ||
} | ||
|
||
public processInterest( | ||
matched: schemaTree.MatchedObject<BaseNode>, | ||
matched: schemaTree.StrictMatch<BaseNode>, | ||
interest: Interest, | ||
deadline: number, | ||
): Promise<Data | undefined> { | ||
console.warn(`Silently drop unprocessable Interest ${matched.name}: ${interest.appParameters}`); | ||
console.warn(`Silently drop unprocessable Interest ${matched.name.toString()}: ${interest.appParameters}`); | ||
deadline; // Silence warning | ||
return Promise.resolve(undefined); | ||
} | ||
|
||
public async processAttach(path: namePattern.Pattern, endpoint: Endpoint) { | ||
public verifyPacket( | ||
matched: schemaTree.StrictMatch<BaseNode>, | ||
pkt: Verifier.Verifiable, | ||
deadline: number, | ||
) { | ||
console.warn(`Silently drop unverified packet ${matched.name.toString()}`); | ||
pkt; | ||
deadline; | ||
return Promise.resolve(false); | ||
} | ||
|
||
public storeData( | ||
matched: schemaTree.StrictMatch<BaseNode>, | ||
data: Data, | ||
) { | ||
console.warn(`Not store unexpected Data ${matched.name.toString()}`); | ||
data; | ||
return Promise.resolve(); | ||
} | ||
|
||
public async processAttach(path: namePattern.Pattern, handler: NamespaceHandler) { | ||
// All children's attach events are called | ||
this.endpoint = endpoint; | ||
await this.onAttach.emit(path, endpoint); | ||
this.handler = handler; | ||
await this.onAttach.emit(path, handler.endpoint); | ||
} | ||
|
||
public async processDetach() { | ||
await this.onDetach.emit(); | ||
this.endpoint = undefined; | ||
await this.onDetach.emit(this.handler!.endpoint); | ||
this.handler = undefined; | ||
// Then call children's detach | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,44 +1,196 @@ | ||
import { Endpoint, RetxPolicy } from '@ndn/endpoint'; | ||
import { Data, Interest, Name, Signer, type Verifier } from '@ndn/packet'; | ||
import * as namePattern from './name-pattern.ts'; | ||
import { RetxPolicy } from '@ndn/endpoint'; | ||
import { Data, Interest, Signer, type Verifier } from '@ndn/packet'; | ||
import * as schemaTree from './schema-tree.ts'; | ||
import { BaseNode, BaseNodeEvents } from './base-node.ts'; | ||
import { EventChain } from '../utils/event-chain.ts'; | ||
|
||
export enum VerifyResult { | ||
Fail = -1, | ||
Unknown = 0, | ||
Pass = 1, | ||
Bypass = 2, | ||
} | ||
import { EventChain, Stop } from '../utils/event-chain.ts'; | ||
import { VerifyResult } from './nt-schema.ts'; | ||
|
||
export interface ExpressingPointEvents extends BaseNodeEvents { | ||
interest(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<Data | undefined>; | ||
verify(target: schemaTree.StrictMatch<ExpressingPoint>, pkt: Verifier.Verifiable): Promise<VerifyResult>; | ||
searchStorage(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<Data | undefined>; | ||
saveStorage(target: schemaTree.StrictMatch<ExpressingPoint>): Promise<void>; | ||
interest( | ||
args: { | ||
target: schemaTree.StrictMatch<ExpressingPoint>; | ||
interest: Interest; | ||
deadline: number; | ||
}, | ||
): Promise<Data | undefined>; | ||
|
||
verify( | ||
args: { | ||
target: schemaTree.StrictMatch<ExpressingPoint>; | ||
deadline: number; | ||
pkt: Verifier.Verifiable; | ||
}, | ||
): Promise<VerifyResult>; | ||
|
||
searchStorage( | ||
args: { | ||
target: schemaTree.StrictMatch<ExpressingPoint>; | ||
interest: Interest; | ||
deadline: number; | ||
}, | ||
): Promise<Data | undefined>; | ||
} | ||
|
||
export type ExpressingPointOpts = { | ||
lifetimeMs: number; | ||
signer: Signer; | ||
interestSigner?: Signer; | ||
canBePrefix?: boolean; | ||
mustBeFresh?: boolean; | ||
supressInterest?: boolean; | ||
abortSignal?: AbortSignal; | ||
modifyInterest?: Interest.Modify; | ||
retx?: RetxPolicy; | ||
}; | ||
|
||
export class ExpressingPoint extends BaseNode { | ||
/** Called when Interest received */ | ||
public readonly onInterest = new EventChain<ExpressingPointEvents['interest']>(); | ||
|
||
/** Verify Interest event. Also verifies Data if this is a LeafNode */ | ||
public readonly onVerify = new EventChain<ExpressingPointEvents['verify']>(); | ||
|
||
/** Searching stored data from the storage */ | ||
public readonly onSearchStorage = new EventChain<ExpressingPointEvents['searchStorage']>(); | ||
public readonly onSaveStorage = new EventChain<ExpressingPointEvents['saveStorage']>(); | ||
|
||
// public async need(matched: schemaTree.MatchedObject<ExpressingPoint>): Promise<Data | undefined> {} | ||
constructor( | ||
public readonly config: ExpressingPointOpts, | ||
) { | ||
super(); | ||
} | ||
|
||
public searchCache(target: schemaTree.StrictMatch<ExpressingPoint>, interest: Interest, deadline: number) { | ||
return this.onSearchStorage.chain( | ||
undefined, | ||
(ret) => Promise.resolve(ret ? Stop : [{ target, interest, deadline }]), | ||
{ target, interest, deadline }, | ||
); | ||
} | ||
|
||
public override async verifyPacket( | ||
matched: schemaTree.StrictMatch<ExpressingPoint>, | ||
pkt: Verifier.Verifiable, | ||
deadline: number, | ||
) { | ||
const verifyResult = await this.onVerify.chain( | ||
VerifyResult.Unknown, | ||
(ret, args) => Promise.resolve((ret < VerifyResult.Unknown || ret >= VerifyResult.Bypass) ? Stop : [args]), | ||
{ target: matched, pkt, deadline }, | ||
); | ||
return verifyResult >= VerifyResult.Pass; | ||
} | ||
|
||
public override async processInterest( | ||
matched: schemaTree.StrictMatch<ExpressingPoint>, | ||
interest: Interest, | ||
deadline: number, | ||
): Promise<Data | undefined> { | ||
// Search storage | ||
// Reply if there is data (including AppNack). No further callback will be called if hit. | ||
// This is the same behavior as a forwarder. | ||
const cachedData = await this.searchCache(matched, interest, deadline); | ||
if (cachedData) { | ||
return cachedData; | ||
} | ||
|
||
// Validate Interest | ||
// Only done when there is a sigInfo or appParam. | ||
// Signed Interests are required to carry AppParam, but may be zero length. | ||
// To guarantee everything is good in case the underlying library returns `undefined` when zero length, check both. | ||
if (interest.appParameters || interest.sigInfo) { | ||
if (!await this.verifyPacket(matched, interest, deadline)) { | ||
// Unverified Interest. Drop | ||
return; | ||
} | ||
} | ||
|
||
// PreRecvInt | ||
// Used to decrypt AppParam or handle before onInterest hits, if applicable. | ||
// Do we need them? Hold for now. | ||
|
||
// OnInt | ||
const result = await this.onInterest.chain( | ||
undefined, | ||
(ret, args) => Promise.resolve(ret ? Stop : [args]), | ||
{ target: matched, interest, deadline }, | ||
); | ||
|
||
// PreSendData | ||
// Used to encrypt Data or handle after onInterest hits, if applicable. | ||
// Do we need them? Hold for now. | ||
return result; | ||
} | ||
|
||
public async need( | ||
matched: schemaTree.StrictMatch<ExpressingPoint>, | ||
opts: { | ||
appParam?: Uint8Array | string; | ||
supressInterest?: boolean; | ||
abortSignal?: AbortSignal; | ||
signer?: Signer; | ||
lifetimeMs?: number; | ||
deadline?: number; | ||
} = {}, | ||
): Promise<Data | undefined> { | ||
// Construct Interest, but without signing, so the parameter digest is not there | ||
const interestArgs = [matched.name] as Array<Interest.CtorArg>; | ||
if (this.config.canBePrefix) { | ||
// Be aware that if CanBePrefix is set, you may need to also validate the data against the LeafNode's validator. | ||
interestArgs.push(Interest.CanBePrefix); | ||
} | ||
if (this.config.mustBeFresh ?? true) { | ||
interestArgs.push(Interest.MustBeFresh); | ||
} | ||
const appParam = opts.appParam instanceof Uint8Array | ||
? opts.appParam | ||
: typeof opts.appParam === 'string' | ||
? new TextEncoder().encode(opts.appParam) | ||
: undefined; | ||
if (appParam) { | ||
interestArgs.push(appParam); | ||
} | ||
// TODO: FwHint is not supported for now. Who should provide this info? | ||
const lifetimeMs = opts.lifetimeMs ?? this.config.lifetimeMs; | ||
interestArgs.push(Interest.Lifetime(lifetimeMs)); | ||
const interest = new Interest(...interestArgs); | ||
|
||
// Compute deadline | ||
const deadline = opts.deadline ?? (Date.now() + lifetimeMs); | ||
|
||
// Get a signer for this interest | ||
const signer = opts.signer ?? this.config.interestSigner; | ||
|
||
// If appParam is empty and not signed, the Interest name is final. | ||
// Otherwise, we have to construct the Interest first before searching storage. | ||
// Get a signer for Interest. | ||
let cachedData: Data | undefined = undefined; | ||
if (!appParam && !signer) { | ||
cachedData = await this.searchCache(matched, interest, deadline); | ||
if (cachedData) { | ||
return cachedData; | ||
} | ||
} | ||
|
||
// After signing the digest is there | ||
if (signer) { | ||
await signer.sign(interest); | ||
} | ||
// We may search the storage if not yet. However, it seems not useful for now. | ||
|
||
// Express the Interest if not surpressed | ||
const supressInterest = opts.supressInterest ?? this.config.supressInterest; | ||
if (supressInterest) { | ||
return undefined; | ||
} | ||
|
||
const data = await this.handler!.endpoint.consume(interest, { | ||
// deno-lint-ignore no-explicit-any | ||
signal: opts.abortSignal as any, | ||
retx: this.config.retx, | ||
// Note: the verifier is at the LeafNode if CanBePrefix is set | ||
verifier: this.handler!.getVerifier(deadline), | ||
}); | ||
|
||
// (no await) Save (cache) the data in the storage | ||
this.handler!.storeData(data); | ||
|
||
// public override async processInterest( | ||
// matched: schemaTree.MatchedObject<ExpressingPoint>, | ||
// interest: Interest, | ||
// ): Promise<Data | undefined> { | ||
// } | ||
return data; | ||
} | ||
} |
Oops, something went wrong.