1
- import { type DataChannelOpts , WebRTCStream } from './stream.js'
1
+ import { createStream } from './stream.js'
2
2
import { nopSink , nopSource } from './util.js'
3
+ import type { DataChannelOpts } from './stream.js'
3
4
import type { Stream } from '@libp2p/interface-connection'
4
5
import type { CounterGroup } from '@libp2p/interface-metrics'
5
6
import type { StreamMuxer , StreamMuxerFactory , StreamMuxerInit } from '@libp2p/interface-stream-muxer'
6
7
import type { Source , Sink } from 'it-stream-types'
7
8
import type { Uint8ArrayList } from 'uint8arraylist'
8
9
10
+ const PROTOCOL = '/webrtc'
11
+
9
12
export interface DataChannelMuxerFactoryInit {
10
13
/**
11
14
* WebRTC Peer Connection
@@ -21,50 +24,73 @@ export interface DataChannelMuxerFactoryInit {
21
24
* Data channel options
22
25
*/
23
26
dataChannelOptions ?: Partial < DataChannelOpts >
27
+
28
+ /**
29
+ * The protocol to use
30
+ */
31
+ protocol ?: string
24
32
}
25
33
26
34
export class DataChannelMuxerFactory implements StreamMuxerFactory {
35
+ public readonly protocol : string
36
+
27
37
/**
28
38
* WebRTC Peer Connection
29
39
*/
30
- private streamBuffer : WebRTCStream [ ] = [ ]
40
+ private readonly peerConnection : RTCPeerConnection
41
+ private streamBuffer : Stream [ ] = [ ]
42
+ private readonly metrics ?: CounterGroup
43
+ private readonly dataChannelOptions ?: Partial < DataChannelOpts >
44
+
45
+ constructor ( init : DataChannelMuxerFactoryInit ) {
46
+ this . peerConnection = init . peerConnection
47
+ this . metrics = init . metrics
48
+ this . protocol = init . protocol ?? PROTOCOL
49
+ this . dataChannelOptions = init . dataChannelOptions
31
50
32
- constructor ( readonly init : DataChannelMuxerFactoryInit , readonly protocol = '/webrtc' ) {
33
51
// store any datachannels opened before upgrade has been completed
34
- this . init . peerConnection . ondatachannel = ( { channel } ) => {
35
- const stream = new WebRTCStream ( {
52
+ this . peerConnection . ondatachannel = ( { channel } ) => {
53
+ const stream = createStream ( {
36
54
channel,
37
- stat : {
38
- direction : 'inbound' ,
39
- timeline : { open : 0 }
40
- } ,
55
+ direction : 'inbound' ,
41
56
dataChannelOptions : init . dataChannelOptions ,
42
- closeCb : ( _stream ) => {
43
- this . streamBuffer = this . streamBuffer . filter ( s => ! _stream . eq ( s ) )
57
+ onEnd : ( ) => {
58
+ this . streamBuffer = this . streamBuffer . filter ( s => s . id !== stream . id )
44
59
}
45
60
} )
46
61
this . streamBuffer . push ( stream )
47
62
}
48
63
}
49
64
50
- createStreamMuxer ( init ?: StreamMuxerInit | undefined ) : StreamMuxer {
51
- return new DataChannelMuxer ( this . init , this . streamBuffer , this . protocol , init )
65
+ createStreamMuxer ( init ?: StreamMuxerInit ) : StreamMuxer {
66
+ return new DataChannelMuxer ( {
67
+ ...init ,
68
+ peerConnection : this . peerConnection ,
69
+ dataChannelOptions : this . dataChannelOptions ,
70
+ metrics : this . metrics ,
71
+ streams : this . streamBuffer ,
72
+ protocol : this . protocol
73
+ } )
52
74
}
53
75
}
54
76
77
+ export interface DataChannelMuxerInit extends DataChannelMuxerFactoryInit , StreamMuxerInit {
78
+ streams : Stream [ ]
79
+ }
80
+
55
81
/**
56
82
* A libp2p data channel stream muxer
57
83
*/
58
84
export class DataChannelMuxer implements StreamMuxer {
59
85
/**
60
86
* Array of streams in the data channel
61
87
*/
62
- streams : Stream [ ] = [ ]
88
+ public streams : Stream [ ]
89
+ public protocol : string
63
90
64
- /**
65
- * Initialized stream muxer
66
- */
67
- init ?: StreamMuxerInit
91
+ private readonly peerConnection : RTCPeerConnection
92
+ private readonly dataChannelOptions ?: DataChannelOpts
93
+ private readonly metrics ?: CounterGroup
68
94
69
95
/**
70
96
* Close or abort all tracked streams and stop the muxer
@@ -81,45 +107,37 @@ export class DataChannelMuxer implements StreamMuxer {
81
107
*/
82
108
sink : Sink < Source < Uint8Array | Uint8ArrayList > , Promise < void > > = nopSink
83
109
84
- constructor ( readonly dataChannelMuxer : DataChannelMuxerFactoryInit , streams : Stream [ ] , readonly protocol : string = '/webrtc' , init ?: StreamMuxerInit ) {
85
- /**
86
- * Initialized stream muxer
87
- */
88
- this . init = init
110
+ constructor ( readonly init : DataChannelMuxerInit ) {
111
+ this . streams = init . streams
112
+ this . peerConnection = init . peerConnection
113
+ this . protocol = init . protocol ?? PROTOCOL
114
+ this . metrics = init . metrics
89
115
90
116
/**
91
117
* Fired when a data channel has been added to the connection has been
92
118
* added by the remote peer.
93
119
*
94
120
* {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event}
95
121
*/
96
- this . dataChannelMuxer . peerConnection . ondatachannel = ( { channel } ) => {
97
- const stream = new WebRTCStream ( {
122
+ this . peerConnection . ondatachannel = ( { channel } ) => {
123
+ const stream = createStream ( {
98
124
channel,
99
- stat : {
100
- direction : 'inbound' ,
101
- timeline : {
102
- open : 0
103
- }
104
- } ,
105
- dataChannelOptions : dataChannelMuxer . dataChannelOptions ,
106
- closeCb : this . wrapStreamEnd ( init ?. onIncomingStream )
125
+ direction : 'inbound' ,
126
+ dataChannelOptions : this . dataChannelOptions ,
127
+ onEnd : ( ) => {
128
+ this . streams = this . streams . filter ( s => s . id !== stream . id )
129
+ this . metrics ?. increment ( { stream_end : true } )
130
+ init ?. onStreamEnd ?.( stream )
131
+ }
107
132
} )
108
133
109
134
this . streams . push ( stream )
110
135
if ( ( init ?. onIncomingStream ) != null ) {
111
- this . dataChannelMuxer . metrics ?. increment ( { incoming_stream : true } )
136
+ this . metrics ?. increment ( { incoming_stream : true } )
112
137
init . onIncomingStream ( stream )
113
138
}
114
139
}
115
140
116
- // wrap open streams with the onStreamEnd callback
117
- this . streams = streams
118
- . filter ( stream => stream . stat . timeline . close == null )
119
- . map ( stream => {
120
- ( stream as WebRTCStream ) . closeCb = this . wrapStreamEnd ( init ?. onStreamEnd )
121
- return stream
122
- } )
123
141
const onIncomingStream = init ?. onIncomingStream
124
142
if ( onIncomingStream != null ) {
125
143
this . streams . forEach ( s => { onIncomingStream ( s ) } )
@@ -128,35 +146,20 @@ export class DataChannelMuxer implements StreamMuxer {
128
146
129
147
newStream ( ) : Stream {
130
148
// The spec says the label SHOULD be an empty string: https://github.com/libp2p/specs/blob/master/webrtc/README.md#rtcdatachannel-label
131
- const channel = this . dataChannelMuxer . peerConnection . createDataChannel ( '' )
132
- const closeCb = ( stream : Stream ) : void => {
133
- this . dataChannelMuxer . metrics ?. increment ( { stream_end : true } )
134
- this . init ?. onStreamEnd ?.( stream )
135
- }
136
- const stream = new WebRTCStream ( {
149
+ const channel = this . peerConnection . createDataChannel ( '' )
150
+ const stream = createStream ( {
137
151
channel,
138
- stat : {
139
- direction : 'outbound' ,
140
- timeline : {
141
- open : 0
142
- }
143
- } ,
144
- dataChannelOptions : this . dataChannelMuxer . dataChannelOptions ,
145
- closeCb : this . wrapStreamEnd ( closeCb )
152
+ direction : 'outbound' ,
153
+ dataChannelOptions : this . dataChannelOptions ,
154
+ onEnd : ( ) => {
155
+ this . streams = this . streams . filter ( s => s . id !== stream . id )
156
+ this . metrics ?. increment ( { stream_end : true } )
157
+ this . init ?. onStreamEnd ?.( stream )
158
+ }
146
159
} )
147
160
this . streams . push ( stream )
148
- this . dataChannelMuxer . metrics ?. increment ( { outgoing_stream : true } )
161
+ this . metrics ?. increment ( { outgoing_stream : true } )
149
162
150
163
return stream
151
164
}
152
-
153
- private wrapStreamEnd ( onStreamEnd ?: ( s : Stream ) => void ) : ( stream : Stream ) => void {
154
- const self = this
155
- return ( _stream ) => {
156
- self . streams = self . streams . filter ( s => ! ( _stream instanceof WebRTCStream && ( _stream ) . eq ( s ) ) )
157
- if ( onStreamEnd != null ) {
158
- onStreamEnd ( _stream )
159
- }
160
- }
161
- }
162
165
}
0 commit comments