Skip to content

Commit

Permalink
node-transport: adopt TSDoc syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
yoursunny committed Feb 1, 2024
1 parent d9dd5d6 commit 41c5613
Show file tree
Hide file tree
Showing 16 changed files with 188 additions and 56 deletions.
4 changes: 3 additions & 1 deletion packages/fw/src/face.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ export namespace FwFace {
* @defaultValue true
* @see {@link \@ndn/endpoint!ProducerOptions.routeCapture}
*/
routeCapture?: boolean;
/* eslint-enable tsdoc/syntax */
routeCapture?: boolean;

[k: string]: unknown;
}

/**
Expand Down
13 changes: 13 additions & 0 deletions packages/l3face/src/l3face.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ type EventMap = {

/** Network layer face for sending and receiving L3 packets. */
export class L3Face extends TypedEventTarget<EventMap> implements FwFace.RxTx {
/**
* Constructor.
* @param transport - Initial transport. It may be replaced through reopen mechanism.
* @param attributes - Additional attributes.
* L3Face attributes consist of transport attributes overridden by these attributes.
* @param lpOptions - NDNLPv2 service options.
*/
constructor(
private transport: Transport,
attributes: L3Face.Attributes = {},
Expand All @@ -44,6 +51,12 @@ export class L3Face extends TypedEventTarget<EventMap> implements FwFace.RxTx {
this.rx = this.makeRx();
}

/**
* Attributes of a network layer face.
*
* @remarks
* When L3Face is added to a logical forwarder, this is copied to {@link FwFace.attributes}.
*/
public readonly attributes: L3Face.Attributes;
public readonly lp: LpService;
public readonly rx: AsyncIterable<FwPacket>;
Expand Down
1 change: 1 addition & 0 deletions packages/l3face/src/stream-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ export class StreamTransport extends Transport {
this.tx = txToStream(conn);
}

/** Report MTU as Infinity. */
public override get mtu() { return Infinity; }
}
13 changes: 12 additions & 1 deletion packages/l3face/src/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@ const DEFAULT_MTU = 1200;
* The transport understands NDN TLV structures, but does not otherwise concern with packet format.
*/
export abstract class Transport {
/** Iterable of outgoing packets. */
public abstract readonly rx: Transport.Rx;
/** Function to accept iterable of incoming packets. */
public abstract readonly tx: Transport.Tx;

/**
* Constructor.
* @param attributes - Attributes of the transport.
*/
protected constructor(public readonly attributes: Transport.Attributes) {}

/**
* Return the transport MTU, if known.
* Return the transport MTU.
*
* @remarks
* The transport should be able to send TLV structure of up to this size.
* If not overridden, return a conservative number.
*
* Note that this does not restrict incoming packet size.
*/
public get mtu() { return DEFAULT_MTU; }

Expand Down Expand Up @@ -58,6 +67,8 @@ export namespace Transport {
* @defaultValue `false`
*/
multicast?: boolean;

[k: string]: unknown;
}

/** RX iterable for incoming packets. */
Expand Down
5 changes: 4 additions & 1 deletion packages/l3face/test-fixture/mock-transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import { pushable } from "it-pushable";

import { Transport } from "..";

/** Mock transport that records outgoing packets and allows injecting incoming packets */
export class MockTransport extends Transport {
public override readonly rx = pushable<Decoder.Tlv>({ objectMode: true });
/** Packets transmitted by the transport. */
public sent: Uint8Array[] = [];
private readonly closing = new AbortController();

constructor(attributes: Transport.Attributes = {}) {
super(attributes);
}

/** Cause the transport to receive an incoming packet */
public recv(pkt: Encodable) {
const wire = Encoder.encode(pkt);
const decoder = new Decoder(wire);
Expand All @@ -21,7 +24,7 @@ export class MockTransport extends Transport {
decoder.throwUnlessEof();
}

public close(err?: Error) {
public close(err?: Error): void {
this.rx.end(err);
this.closing.abort();
}
Expand Down
15 changes: 14 additions & 1 deletion packages/l3face/test-fixture/transport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,15 @@ export interface TestRecord {
namesB: string[];
}

/**
* Test a pair of transports.
*
* @remarks
* Two transports should be paired: packets sent on one transport should be received on the other.
*/
export async function execute<T extends Transport>(
transportA: T, transportB: T): Promise<TestRecord> {
transportA: T, transportB: T,
): Promise<TestRecord> {
const faceA = new L3Face(transportA, { describe: "A" });
const faceB = new L3Face(transportB, { describe: "B" });

Expand Down Expand Up @@ -58,6 +65,12 @@ export async function execute<T extends Transport>(
return record;
}

/**
* Check test records.
* @param record - {@link execute} return value.
* @param threshold - Minimum ratio of successful delivered.
* 0.9 means 90% delivery, i.e. tolerate 10% loss.
*/
export function check(record: TestRecord, threshold = 0.9) {
expect(record.namesA.length).toBeGreaterThanOrEqual(Math.ceil(COUNT * threshold));
expect(record.namesB.length).toBeGreaterThanOrEqual(Math.ceil(COUNT * threshold));
Expand Down
2 changes: 1 addition & 1 deletion packages/node-transport/src/mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export * from "./hostport";
export * from "./tcp-transport";
export * from "./unix-transport";
export * as udp_helper from "./udp-helper";
export * from "./udp-transport";
export * from "./unix-transport";
31 changes: 22 additions & 9 deletions packages/node-transport/src/tcp-transport.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,42 @@
import net from "node:net";

import { L3Face, StreamTransport } from "@ndn/l3face";
import type { Except } from "type-fest";
import type { SetOptional } from "type-fest";

import { joinHostPort } from "./hostport";

const DEFAULT_PORT = 6363;

/** TCP socket transport. */
export class TcpTransport extends StreamTransport {
/**
* Constructor.
*
* @remarks
* {@link TcpTransport.connect} and {@link TcpTransport.createFace} are recommended.
*/
constructor(sock: net.Socket, private readonly connectOpts: net.TcpNetConnectOpts) {
super(sock, {
describe: `TCP(${joinHostPort(sock.remoteAddress!, sock.remotePort!)})`,
local: sock.localAddress === sock.remoteAddress,
});
}

/** Reopen the transport by connecting again with the same options. */
public override reopen(): Promise<TcpTransport> {
return TcpTransport.connect(this.connectOpts);
}
}

export namespace TcpTransport {
export type NetConnectOpts = Except<net.TcpNetConnectOpts, "port"> & Partial<Pick<net.TcpNetConnectOpts, "port">>;
type NetConnectOpts = SetOptional<net.TcpNetConnectOpts, "port">;

/** {@link connect} options. */
export interface Options {
/** Connect timeout (in milliseconds). */
/**
* Connect timeout (in milliseconds).
* @defaultValue 10 seconds
*/
connectTimeout?: number;

/** AbortSignal that allows canceling connection attempt via AbortController. */
Expand All @@ -34,24 +45,26 @@ export namespace TcpTransport {

/**
* Create a transport and connect to remote endpoint.
* @param host remote host, default is "localhost".
* @param port remote port, default is 6363.
* @param opts other options.
* @param host - Remote host. Default is localhost.
* @param port - Remote port. Default is 6363.
*/
export function connect(host?: string, port?: number, opts?: Options): Promise<TcpTransport>;

/**
* Create a transport and connect to remote endpoint.
* @param opts remote endpoint and other options.
* @param opts - Remote endpoint and other options.
*/
export function connect(opts: NetConnectOpts & Options): Promise<TcpTransport>;

export function connect(arg1?: string | (NetConnectOpts & Options), port?: number, opts?: Options) {
return connectImpl(arg1, port, opts);
}

function connectImpl(arg1?: string | (NetConnectOpts & Options), port = DEFAULT_PORT,
opts: Options = {}): Promise<TcpTransport> {
function connectImpl(
arg1?: string | (NetConnectOpts & Options),
port = DEFAULT_PORT,
opts: Options = {},
): Promise<TcpTransport> {
const connectOpts: net.TcpNetConnectOpts =
arg1 === undefined ? { port } :
typeof arg1 === "string" ? { host: arg1, port } :
Expand Down
42 changes: 36 additions & 6 deletions packages/node-transport/src/udp-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import dgram from "node:dgram";
import { once } from "node:events";
import os from "node:os";

export const DEFAULT_MTU = 65000;
const DEFAULT_UNICAST_PORT = 6363;
const DEFAULT_MULTICAST_GROUP = "224.0.23.170";
const DEFAULT_MULTICAST_PORT = 56363;
Expand All @@ -13,17 +12,19 @@ export type SocketBufferOptions = Pick<dgram.SocketOptions, "recvBufferSize" | "

export type AddressFamily = 4 | 6;

/** {@link openSocket} options. */
export interface OpenSocketOptions extends SocketBufferOptions {
/**
* IPv4 or IPv6.
* Default is IPv4, unless hostname is an IPv6 address (contains a colon).
* @defaultValue IPv4, unless hostname is a literal IPv6 address.
*/
family?: AddressFamily;

/** Bind options, such as local address and port. */
bind?: dgram.BindOptions;
}

/** Create a UDP socket and start listening on local endpoint. */
export async function openSocket({
family,
recvBufferSize,
Expand All @@ -47,13 +48,15 @@ export async function openSocket({
return sock;
}

/** {@link connect} options. */
export interface ConnectOptions {
/** Remote address. */
host: string;
/** Remote port. */
port?: number;
}

/** Connect a UDP socket to remote endpoint. */
export async function connect(sock: Socket, {
host,
port = DEFAULT_UNICAST_PORT,
Expand All @@ -68,8 +71,10 @@ export async function connect(sock: Socket, {
return sock;
}

/** {@link openUnicast} options. */
export interface UnicastOptions extends OpenSocketOptions, ConnectOptions {}

/** Create a UDP socket and connect to remote endpoint. */
export async function openUnicast(opts: UnicastOptions): Promise<Socket> {
if (!opts.family && opts.host.includes(":")) {
opts.family = 6;
Expand All @@ -93,19 +98,43 @@ export function listMulticastIntfs(): string[] {
});
}

/** {@link openMulticastRx} and {@link openMulticastTx} options. */
export interface MulticastOptions extends SocketBufferOptions {
/** IPv4 address of local network interface. */
intf: string;
/** Multicast group address. */

/**
* Multicast group address.
* @defaultValue 224.0.23.170
*/
group?: string;
/** Local and group port. */

/**
* Local and group port.
* @defaultValue 56363
*/
port?: number;
/** Multicast TTL (for unit testing). */

/**
* Multicast TTL.
* @defaultValue 1
*
* @remarks
* Changing this option is inadvisable except for unit testing.
*/
multicastTtl?: number;
/** MulticastLoopback flag (for unit testing). */

/**
* MulticastLoopback flag.
* @defaultValue false
*
* @remarks
* Changing this option is inadvisable except for unit testing.
*/
multicastLoopback?: boolean;
}

/** Create a UDP socket and prepare for receiving multicast datagrams. */
export async function openMulticastRx(opts: MulticastOptions): Promise<Socket> {
const {
intf,
Expand All @@ -128,6 +157,7 @@ export async function openMulticastRx(opts: MulticastOptions): Promise<Socket> {
return sock;
}

/** Create a UDP socket and prepare for transmitting multicast datagrams. */
export async function openMulticastTx(opts: MulticastOptions): Promise<Socket> {
const {
intf,
Expand Down

0 comments on commit 41c5613

Please sign in to comment.