Skip to content

Commit 32783c7

Browse files
committed
Enhance SCTP, SctpReconfig, StreamManager, and SCTPTimerManager classes with DTO methods for improved data handling and organization
1 parent 4760db1 commit 32783c7

File tree

6 files changed

+313
-58
lines changed

6 files changed

+313
-58
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,5 @@
8383
],
8484
"typescript.tsdk": "node_modules/typescript/lib",
8585
"biome.lsp.bin": "node_modules/@biomejs/biome/bin/biome",
86-
"prettier.enable": false
86+
"prettier.enable": false,
8787
}

packages/sctp/src/reconfig.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ import { tsnMinusOne, tsnPlusOne } from "./util";
1818

1919
const log = debug("packages/sctp/src/reconfig.ts");
2020

21+
interface Deps {
22+
transmitter: SCTPTransmitter;
23+
timerManager: SCTPTimerManager;
24+
}
25+
2126
export class SctpReconfig {
2227
// # reconfiguration
2328

@@ -27,12 +32,16 @@ export class SctpReconfig {
2732
reconfigResponseSeq = 0;
2833
reconfigRequest?: OutgoingSSNResetRequestParam;
2934
reconfigQueue: number[] = [];
35+
36+
private transmitter: SCTPTransmitter;
37+
private timerManager: SCTPTimerManager;
38+
3039
readonly onReconfigStreams = new Event<[number[]]>();
3140

32-
constructor(
33-
private transmitter: SCTPTransmitter,
34-
private timerManager: SCTPTimerManager,
35-
) {
41+
constructor(deps: Deps) {
42+
this.transmitter = deps.transmitter;
43+
this.timerManager = deps.timerManager;
44+
3645
this.reconfigRequestSeq = this.transmitter.localTsn;
3746

3847
this.timerManager.onReconfigExpired.subscribe(async (reconfigFailures) => {
@@ -154,4 +163,35 @@ export class SctpReconfig {
154163
this.reconfigResponseSeq = add.requestSequence;
155164
await this.sendReconfigParam(res);
156165
}
166+
167+
toDto(): ReconfigDto {
168+
return {
169+
reconfigRequestSeq: this.reconfigRequestSeq,
170+
reconfigResponseSeq: this.reconfigResponseSeq,
171+
reconfigRequest: this.reconfigRequest?.bytes,
172+
reconfigQueue: this.reconfigQueue,
173+
};
174+
}
175+
176+
static fromDto(dto: ReconfigDto, deps: Deps): SctpReconfig {
177+
const reconfig = new SctpReconfig(deps);
178+
reconfig.reconfigRequestSeq = dto.reconfigRequestSeq;
179+
reconfig.reconfigResponseSeq = dto.reconfigResponseSeq;
180+
reconfig.reconfigQueue = dto.reconfigQueue;
181+
182+
if (dto.reconfigRequest) {
183+
reconfig.reconfigRequest = OutgoingSSNResetRequestParam.parse(
184+
dto.reconfigRequest,
185+
);
186+
}
187+
188+
return reconfig;
189+
}
190+
}
191+
192+
export interface ReconfigDto {
193+
reconfigRequestSeq: number;
194+
reconfigResponseSeq: number;
195+
reconfigRequest: Buffer<ArrayBuffer> | undefined;
196+
reconfigQueue: number[];
157197
}

packages/sctp/src/sctp.ts

Lines changed: 100 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,14 @@ import {
4747
StreamAddOutgoingParam,
4848
type StreamParam,
4949
} from "./param";
50-
import { SctpReconfig } from "./reconfig";
51-
import { StreamManager } from "./stream";
52-
import { SCTPTimerManager } from "./timer";
50+
import { type ReconfigDto, SctpReconfig } from "./reconfig";
51+
import { StreamManager, type StreamManagerStateDTO } from "./stream";
52+
import { type SCTPTimerDto, SCTPTimerManager } from "./timer";
5353
import {
5454
type SCTPConnectionState,
5555
SCTPConnectionStates,
5656
SCTPTransmitter,
57+
type SCTPTransmitterDto,
5758
} from "./transmitter";
5859
import type { Transport } from "./transport";
5960
import { tsnMinusOne, tsnPlusOne } from "./util";
@@ -63,27 +64,26 @@ const log = debug("werift/sctp/sctp");
6364
// SSN: Stream Sequence Number
6465

6566
export class SCTP {
66-
associationState = SCTP_STATE.CLOSED;
67-
started = false;
68-
isServer = true;
69-
7067
private readonly hmacKey = randomBytes(16);
7168
private readonly localPartialReliability = true;
7269
private readonly localVerificationTag = random32();
7370

71+
associationState = SCTP_STATE.CLOSED;
72+
started = false;
73+
isServer = true;
7474
remoteExtensions: number[] = [];
7575
remotePartialReliability = true;
76+
protected lastReceivedTsn?: number; // Transmission Sequence Number
77+
protected sackDuplicates: number[] = [];
78+
protected sackMisOrdered = new Set<number>();
79+
protected sackNeeded = false;
7680

77-
private lastReceivedTsn?: number; // Transmission Sequence Number
78-
private sackDuplicates: number[] = [];
79-
private sackMisOrdered = new Set<number>();
80-
private sackNeeded = false;
8181
private sackTimeout: NodeJS.Immediate | undefined;
8282

83-
readonly transmitter: SCTPTransmitter;
84-
readonly timerManager: SCTPTimerManager;
85-
readonly reconfig: SctpReconfig;
86-
readonly stream = new StreamManager();
83+
transmitter: SCTPTransmitter;
84+
timerManager: SCTPTimerManager;
85+
reconfig: SctpReconfig;
86+
stream: StreamManager;
8787

8888
readonly stateChanged: {
8989
[key in SCTPConnectionState]: Event<[]>;
@@ -98,16 +98,29 @@ export class SCTP {
9898
public transport: Transport,
9999
public port = 5000,
100100
) {
101-
this.transport.onData = (buf) => {
102-
this.handleData(buf);
103-
};
104101
this.timerManager = new SCTPTimerManager({
105102
sendChunk: async (chunk) => {
106103
await this.transmitter.sendChunk(chunk);
107104
},
108105
});
109-
this.transmitter = new SCTPTransmitter(transport, this.timerManager, port);
110-
this.reconfig = new SctpReconfig(this.transmitter, this.timerManager);
106+
this.transmitter = new SCTPTransmitter({
107+
transport,
108+
timerManager: this.timerManager,
109+
port,
110+
});
111+
this.reconfig = new SctpReconfig({
112+
transmitter: this.transmitter,
113+
timerManager: this.timerManager,
114+
});
115+
this.stream = new StreamManager();
116+
117+
this.setup();
118+
}
119+
120+
protected setup() {
121+
this.transport.onData = (buf) => {
122+
this.handleData(buf);
123+
};
111124

112125
this.timerManager.onT1Expired.subscribe(() => {
113126
this.setState(SCTP_STATE.CLOSED);
@@ -614,6 +627,73 @@ export class SCTP {
614627
this.reconfig.transmitReconfigRequest(this.associationState);
615628
}
616629
}
630+
631+
toDto(): SCTPDto {
632+
return {
633+
associationState: this.associationState,
634+
started: this.started,
635+
isServer: this.isServer,
636+
remoteExtensions: this.remoteExtensions,
637+
remotePartialReliability: this.remotePartialReliability,
638+
port: this.port,
639+
lastReceivedTsn: this.lastReceivedTsn,
640+
sackDuplicates: this.sackDuplicates,
641+
sackMisOrdered: Array.from(this.sackMisOrdered),
642+
sackNeeded: this.sackNeeded,
643+
transmitter: this.transmitter.toDto(),
644+
timerManager: this.timerManager.toDto(),
645+
reconfig: this.reconfig.toDto(),
646+
stream: this.stream.toDto(),
647+
};
648+
}
649+
650+
fromDto(dto: SCTPDto, transport: Transport) {
651+
const sctp = new SCTP(transport);
652+
sctp.associationState = dto.associationState;
653+
sctp.started = dto.started;
654+
sctp.isServer = dto.isServer;
655+
sctp.remoteExtensions = dto.remoteExtensions;
656+
sctp.remotePartialReliability = dto.remotePartialReliability;
657+
sctp.port = dto.port;
658+
sctp.lastReceivedTsn = dto.lastReceivedTsn;
659+
sctp.sackDuplicates = dto.sackDuplicates;
660+
sctp.sackMisOrdered = new Set(dto.sackMisOrdered);
661+
sctp.sackNeeded = dto.sackNeeded;
662+
663+
sctp.timerManager = SCTPTimerManager.fromDto(dto.timerManager, {
664+
sendChunk: async (chunk) => {
665+
await sctp.transmitter.sendChunk(chunk);
666+
},
667+
});
668+
sctp.transmitter = SCTPTransmitter.fromDto(dto.transmitter, {
669+
timerManager: sctp.timerManager,
670+
transport,
671+
});
672+
sctp.reconfig = SctpReconfig.fromDto(dto.reconfig, {
673+
timerManager: sctp.timerManager,
674+
transmitter: sctp.transmitter,
675+
});
676+
sctp.stream = StreamManager.fromDto(dto.stream);
677+
sctp.setup();
678+
return sctp;
679+
}
680+
}
681+
682+
export interface SCTPDto {
683+
associationState: SCTP_STATE;
684+
started: boolean;
685+
isServer: boolean;
686+
remoteExtensions: number[];
687+
remotePartialReliability: boolean;
688+
port: number;
689+
lastReceivedTsn?: number;
690+
sackDuplicates: number[];
691+
sackMisOrdered: number[];
692+
sackNeeded: boolean;
693+
transmitter: SCTPTransmitterDto;
694+
timerManager: SCTPTimerDto;
695+
reconfig: ReconfigDto;
696+
stream: StreamManagerStateDTO;
617697
}
618698

619699
export class RTCSctpCapabilities {

packages/sctp/src/stream.ts

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type {
2-
DataChunk,
3-
ForwardTsnChunk,
4-
InitAckChunk,
5-
InitChunk,
1+
import {
2+
type DataChunk,
3+
type ForwardTsnChunk,
4+
type InitAckChunk,
5+
type InitChunk,
6+
parseChunk,
67
} from "./chunk";
78
import {
89
MAX_STREAMS,
@@ -113,6 +114,44 @@ export class StreamManager {
113114
this.receive(...message);
114115
}
115116
}
117+
118+
toDto(): StreamManagerStateDTO {
119+
return {
120+
advertisedRwnd: this.advertisedRwnd,
121+
inboundStreams: Object.fromEntries(
122+
Object.entries(this.inboundStreams).map(([k, v]) => [k, v.toDto()]),
123+
),
124+
_inboundStreamsCount: this._inboundStreamsCount,
125+
outboundStreamSeq: this.outboundStreamSeq,
126+
_outboundStreamsCount: this._outboundStreamsCount,
127+
};
128+
}
129+
130+
static fromDto(dto: StreamManagerStateDTO): StreamManager {
131+
const streamManager = new StreamManager();
132+
streamManager.advertisedRwnd = dto.advertisedRwnd;
133+
streamManager._inboundStreamsCount = dto._inboundStreamsCount;
134+
streamManager.outboundStreamSeq = dto.outboundStreamSeq;
135+
streamManager._outboundStreamsCount = dto._outboundStreamsCount;
136+
137+
for (const [k, v] of Object.entries(dto.inboundStreams)) {
138+
streamManager.inboundStreams[Number(k)] = InboundStream.fromDto(v);
139+
}
140+
141+
return streamManager;
142+
}
143+
}
144+
145+
export interface StreamManagerStateDTO {
146+
advertisedRwnd: number;
147+
inboundStreams: {
148+
[k: string]: InboundStreamDTO;
149+
};
150+
_inboundStreamsCount: number;
151+
outboundStreamSeq: {
152+
[streamId: number]: number;
153+
};
154+
_outboundStreamsCount: number;
116155
}
117156

118157
export class InboundStream {
@@ -223,4 +262,25 @@ export class InboundStream {
223262
this.reassembly = this.reassembly.slice(pos + 1);
224263
return size;
225264
}
265+
266+
toDto(): InboundStreamDTO {
267+
return {
268+
reassembly: this.reassembly.map((chunk) => chunk.bytes),
269+
streamSequenceNumber: this.streamSequenceNumber,
270+
};
271+
}
272+
273+
static fromDto(dto: InboundStreamDTO): InboundStream {
274+
const stream = new InboundStream();
275+
stream.reassembly = dto.reassembly.map(
276+
(chunk) => parseChunk(chunk).chunk as DataChunk,
277+
);
278+
stream.streamSequenceNumber = dto.streamSequenceNumber;
279+
return stream;
280+
}
281+
}
282+
283+
interface InboundStreamDTO {
284+
reassembly: Buffer<ArrayBuffer>[];
285+
streamSequenceNumber: number;
226286
}

packages/sctp/src/timer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ export class SCTPTimerManager {
333333
}
334334
}
335335

336-
interface SCTPTimerDto {
336+
export interface SCTPTimerDto {
337337
t1Chunk: Buffer | undefined;
338338
t1Failures: number;
339339
t2Chunk: Buffer | undefined;

0 commit comments

Comments
 (0)