Skip to content

Commit

Permalink
Remove duplicated appPrefix from the SVS base name
Browse files Browse the repository at this point in the history
  • Loading branch information
zjkmxy committed Feb 9, 2024
1 parent a581c2b commit 054ee30
Show file tree
Hide file tree
Showing 10 changed files with 394 additions and 50 deletions.
2 changes: 1 addition & 1 deletion deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ucla-irl/ndnts-aux",
"version": "1.0.12",
"version": "1.1.0",
"description": "NDNts Auxiliary Package for Web and Deno",
"scripts": {
"test": "deno test --no-check",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 37 additions & 10 deletions src/namespace/base-node.ts
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
}
}
202 changes: 177 additions & 25 deletions src/namespace/expressing-point.ts
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;
}
}
Loading

0 comments on commit 054ee30

Please sign in to comment.