Skip to content

Commit c137b96

Browse files
committed
fix: split webrtc transport into browser and node versions
Reduces bundle size by about 50KB
1 parent d3291de commit c137b96

File tree

4 files changed

+167
-114
lines changed

4 files changed

+167
-114
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @type {import('aegir').PartialOptions} */
22
export default {
33
build: {
4-
bundlesizeMax: '117KB'
4+
bundlesizeMax: '66KB'
55
}
66
}

packages/transport-webrtc/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@
9494
"browser": {
9595
"./dist/src/webrtc/index.js": "./dist/src/webrtc/index.browser.js",
9696
"./dist/src/private-to-public/listener.js": "./dist/src/private-to-public/listener.browser.js",
97+
"./dist/src/private-to-public/transport.js": "./dist/src/private-to-public/transport.browser.js",
9798
"./dist/src/private-to-public/utils/get-rtcpeerconnection.js": "./dist/src/private-to-public/utils/get-rtcpeerconnection.browser.js",
9899
"node:net": false,
99100
"node:os": false
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import { serviceCapabilities, transportSymbol } from '@libp2p/interface'
2+
import { peerIdFromString } from '@libp2p/peer-id'
3+
import { CODE_P2P } from '@multiformats/multiaddr'
4+
import { WebRTCDirect } from '@multiformats/multiaddr-matcher'
5+
import { genUfrag } from '../util.js'
6+
import { connect } from './utils/connect.js'
7+
import { createDialerRTCPeerConnection } from './utils/get-rtcpeerconnection.js'
8+
import type { DataChannelOptions } from '../index.js'
9+
import type { WebRTCDialEvents } from '../private-to-private/transport.js'
10+
import type { CreateListenerOptions, Transport, Listener, ComponentLogger, Logger, Connection, CounterGroup, Metrics, PeerId, DialTransportOptions, PrivateKey, Upgrader } from '@libp2p/interface'
11+
import type { TransportManager } from '@libp2p/interface-internal'
12+
import type { Keychain } from '@libp2p/keychain'
13+
import type { Multiaddr } from '@multiformats/multiaddr'
14+
import type { Datastore } from 'interface-datastore'
15+
import { UnimplementedError } from '../error.ts'
16+
17+
export interface WebRTCDirectTransportComponents {
18+
peerId: PeerId
19+
privateKey: PrivateKey
20+
metrics?: Metrics
21+
logger: ComponentLogger
22+
transportManager: TransportManager
23+
upgrader: Upgrader
24+
keychain?: Keychain
25+
datastore: Datastore
26+
}
27+
28+
export interface WebRTCMetrics {
29+
dialerEvents: CounterGroup
30+
}
31+
32+
export interface WebRTCTransportDirectInit {
33+
/**
34+
* The default configuration used by all created RTCPeerConnections
35+
*/
36+
rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise<RTCConfiguration>)
37+
38+
/**
39+
* The default configuration used by all created RTCDataChannels
40+
*/
41+
dataChannel?: DataChannelOptions
42+
}
43+
44+
export class WebRTCDirectTransport implements Transport {
45+
protected readonly log: Logger
46+
protected readonly metrics?: WebRTCMetrics
47+
protected readonly components: WebRTCDirectTransportComponents
48+
protected readonly init: WebRTCTransportDirectInit
49+
50+
constructor (components: WebRTCDirectTransportComponents, init: WebRTCTransportDirectInit = {}) {
51+
this.log = components.logger.forComponent('libp2p:webrtc-direct')
52+
this.components = components
53+
this.init = init
54+
55+
if (components.metrics != null) {
56+
this.metrics = {
57+
dialerEvents: components.metrics.registerCounterGroup('libp2p_webrtc-direct_dialer_events_total', {
58+
label: 'event',
59+
help: 'Total count of WebRTC-direct dial events by type'
60+
})
61+
}
62+
}
63+
}
64+
65+
readonly [transportSymbol] = true
66+
67+
readonly [Symbol.toStringTag] = '@libp2p/webrtc-direct'
68+
69+
readonly [serviceCapabilities]: string[] = [
70+
'@libp2p/transport'
71+
]
72+
73+
/**
74+
* Dial a given multiaddr
75+
*/
76+
async dial (ma: Multiaddr, options: DialTransportOptions<WebRTCDialEvents>): Promise<Connection> {
77+
this.log('dial %a', ma)
78+
// do not create RTCPeerConnection if the signal has already been aborted
79+
options.signal.throwIfAborted()
80+
81+
let theirPeerId: PeerId | undefined
82+
const remotePeerString = ma.getComponents().findLast(c => c.code === CODE_P2P)?.value
83+
if (remotePeerString != null) {
84+
theirPeerId = peerIdFromString(remotePeerString)
85+
}
86+
87+
const ufrag = genUfrag()
88+
89+
// https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md#browser-to-public-server
90+
const {
91+
peerConnection,
92+
muxerFactory
93+
} = await createDialerRTCPeerConnection('client', ufrag, {
94+
rtcConfiguration: typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration ?? {},
95+
dataChannel: this.init.dataChannel
96+
})
97+
98+
try {
99+
return await connect(peerConnection, muxerFactory, ufrag, {
100+
role: 'client',
101+
log: this.log,
102+
logger: this.components.logger,
103+
events: this.metrics?.dialerEvents,
104+
signal: options.signal,
105+
remoteAddr: ma,
106+
dataChannel: this.init.dataChannel,
107+
upgrader: options.upgrader,
108+
peerId: this.components.peerId,
109+
remotePeer: theirPeerId,
110+
privateKey: this.components.privateKey
111+
})
112+
} catch (err) {
113+
peerConnection.close()
114+
throw err
115+
}
116+
}
117+
118+
/**
119+
* Create a transport listener - this will throw in browsers
120+
*/
121+
createListener (options: CreateListenerOptions): Listener {
122+
throw new UnimplementedError('WebRTCDirectTransport.createListener')
123+
}
124+
125+
/**
126+
* Filter check for all Multiaddrs that this transport can listen on
127+
*/
128+
listenFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
129+
return []
130+
}
131+
132+
/**
133+
* Filter check for all Multiaddrs that this transport can dial
134+
*/
135+
dialFilter (multiaddrs: Multiaddr[]): Multiaddr[] {
136+
return multiaddrs.filter(WebRTCDirect.exactMatch)
137+
}
138+
}

packages/transport-webrtc/src/private-to-public/transport.ts

Lines changed: 27 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { generateKeyPair, privateKeyToCryptoKeyPair } from '@libp2p/crypto/keys'
2-
import { InvalidParametersError, NotFoundError, NotStartedError, serviceCapabilities, transportSymbol } from '@libp2p/interface'
3-
import { peerIdFromString } from '@libp2p/peer-id'
4-
import { CODE_P2P } from '@multiformats/multiaddr'
2+
import { InvalidParametersError, NotFoundError, NotStartedError } from '@libp2p/interface'
53
import { WebRTCDirect } from '@multiformats/multiaddr-matcher'
64
import { BasicConstraintsExtension, X509Certificate, X509CertificateGenerator } from '@peculiar/x509'
75
import { Key } from 'interface-datastore'
@@ -11,46 +9,19 @@ import { sha256 } from 'multiformats/hashes/sha2'
119
import { equals as uint8ArrayEquals } from 'uint8arrays/equals'
1210
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
1311
import { DEFAULT_CERTIFICATE_DATASTORE_KEY, DEFAULT_CERTIFICATE_LIFESPAN, DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME, DEFAULT_CERTIFICATE_RENEWAL_THRESHOLD } from '../constants.js'
14-
import { genUfrag } from '../util.js'
1512
import { WebRTCDirectListener } from './listener.js'
16-
import { connect } from './utils/connect.js'
17-
import { createDialerRTCPeerConnection } from './utils/get-rtcpeerconnection.js'
1813
import { formatAsPem } from './utils/pem.js'
19-
import type { DataChannelOptions, TransportCertificate } from '../index.js'
20-
import type { WebRTCDialEvents } from '../private-to-private/transport.js'
21-
import type { CreateListenerOptions, Transport, Listener, ComponentLogger, Logger, Connection, CounterGroup, Metrics, PeerId, DialTransportOptions, PrivateKey, Upgrader, Startable } from '@libp2p/interface'
22-
import type { TransportManager } from '@libp2p/interface-internal'
14+
import type { TransportCertificate } from '../index.js'
15+
import type { CreateListenerOptions, Transport, Listener, PrivateKey, Startable } from '@libp2p/interface'
2316
import type { Keychain } from '@libp2p/keychain'
2417
import type { Multiaddr } from '@multiformats/multiaddr'
25-
import type { Datastore } from 'interface-datastore'
2618
import type { TypedEventTarget } from 'main-event'
19+
import { WebRTCDirectTransport as WebRTCDirectBrowserTransport } from './transport.browser.js'
20+
import type { WebRTCTransportDirectInit as WebRTCTransportDirectBrowserInit, WebRTCMetrics, WebRTCDirectTransportComponents } from './transport.browser.ts'
2721

28-
export interface WebRTCDirectTransportComponents {
29-
peerId: PeerId
30-
privateKey: PrivateKey
31-
metrics?: Metrics
32-
logger: ComponentLogger
33-
transportManager: TransportManager
34-
upgrader: Upgrader
35-
keychain?: Keychain
36-
datastore: Datastore
37-
}
38-
39-
export interface WebRTCMetrics {
40-
dialerEvents: CounterGroup
41-
}
42-
43-
export interface WebRTCTransportDirectInit {
44-
/**
45-
* The default configuration used by all created RTCPeerConnections
46-
*/
47-
rtcConfiguration?: RTCConfiguration | (() => RTCConfiguration | Promise<RTCConfiguration>)
48-
49-
/**
50-
* The default configuration used by all created RTCDataChannels
51-
*/
52-
dataChannel?: DataChannelOptions
22+
export type { WebRTCMetrics, WebRTCDirectTransportComponents }
5323

24+
export interface WebRTCTransportDirectInit extends WebRTCTransportDirectBrowserInit {
5425
/**
5526
* Use an existing TLS certificate to secure incoming connections or supply
5627
* settings to generate one.
@@ -96,44 +67,32 @@ export interface WebRTCDirectTransportCertificateEvents {
9667
'certificate:renew': CustomEvent<TransportCertificate>
9768
}
9869

99-
export class WebRTCDirectTransport implements Transport, Startable {
100-
private readonly log: Logger
101-
private readonly metrics?: WebRTCMetrics
102-
private readonly components: WebRTCDirectTransportComponents
103-
private readonly init: WebRTCTransportDirectInit
70+
interface CertInit {
71+
certificate?: TransportCertificate
72+
certificateDatastoreKey?: string
73+
certificateKeychainName?: string
74+
certificateLifespan?: number
75+
certificateRenewalThreshold?: number
76+
}
77+
78+
export class WebRTCDirectTransport extends WebRTCDirectBrowserTransport implements Transport, Startable {
10479
private certificate?: TransportCertificate
10580
private privateKey?: PrivateKey
10681
private readonly emitter: TypedEventTarget<WebRTCDirectTransportCertificateEvents>
10782
private renewCertificateTask?: ReturnType<typeof setTimeout>
83+
private certInit: CertInit
10884

10985
constructor (components: WebRTCDirectTransportComponents, init: WebRTCTransportDirectInit = {}) {
110-
this.log = components.logger.forComponent('libp2p:webrtc-direct')
111-
this.components = components
112-
this.init = init
86+
super(components, init)
87+
11388
this.emitter = new TypedEventEmitter()
89+
this.certInit = init
11490

11591
if (init.certificateLifespan != null && init.certificateRenewalThreshold != null && init.certificateRenewalThreshold >= init.certificateLifespan) {
11692
throw new InvalidParametersError('Certificate renewal threshold must be less than certificate lifespan')
11793
}
118-
119-
if (components.metrics != null) {
120-
this.metrics = {
121-
dialerEvents: components.metrics.registerCounterGroup('libp2p_webrtc-direct_dialer_events_total', {
122-
label: 'event',
123-
help: 'Total count of WebRTC-direct dial events by type'
124-
})
125-
}
126-
}
12794
}
12895

129-
readonly [transportSymbol] = true
130-
131-
readonly [Symbol.toStringTag] = '@libp2p/webrtc-direct'
132-
133-
readonly [serviceCapabilities]: string[] = [
134-
'@libp2p/transport'
135-
]
136-
13796
async start (): Promise<void> {
13897
this.certificate = await this.getCertificate()
13998
}
@@ -146,51 +105,6 @@ export class WebRTCDirectTransport implements Transport, Startable {
146105
this.certificate = undefined
147106
}
148107

149-
/**
150-
* Dial a given multiaddr
151-
*/
152-
async dial (ma: Multiaddr, options: DialTransportOptions<WebRTCDialEvents>): Promise<Connection> {
153-
this.log('dial %a', ma)
154-
// do not create RTCPeerConnection if the signal has already been aborted
155-
options.signal.throwIfAborted()
156-
157-
let theirPeerId: PeerId | undefined
158-
const remotePeerString = ma.getComponents().findLast(c => c.code === CODE_P2P)?.value
159-
if (remotePeerString != null) {
160-
theirPeerId = peerIdFromString(remotePeerString)
161-
}
162-
163-
const ufrag = genUfrag()
164-
165-
// https://github.com/libp2p/specs/blob/master/webrtc/webrtc-direct.md#browser-to-public-server
166-
const {
167-
peerConnection,
168-
muxerFactory
169-
} = await createDialerRTCPeerConnection('client', ufrag, {
170-
rtcConfiguration: typeof this.init.rtcConfiguration === 'function' ? await this.init.rtcConfiguration() : this.init.rtcConfiguration ?? {},
171-
dataChannel: this.init.dataChannel
172-
})
173-
174-
try {
175-
return await connect(peerConnection, muxerFactory, ufrag, {
176-
role: 'client',
177-
log: this.log,
178-
logger: this.components.logger,
179-
events: this.metrics?.dialerEvents,
180-
signal: options.signal,
181-
remoteAddr: ma,
182-
dataChannel: this.init.dataChannel,
183-
upgrader: options.upgrader,
184-
peerId: this.components.peerId,
185-
remotePeer: theirPeerId,
186-
privateKey: this.components.privateKey
187-
})
188-
} catch (err) {
189-
peerConnection.close()
190-
throw err
191-
}
192-
}
193-
194108
/**
195109
* Create a transport listener - this will throw in browsers
196110
*/
@@ -222,9 +136,9 @@ export class WebRTCDirectTransport implements Transport, Startable {
222136
}
223137

224138
private async getCertificate (forceRenew?: boolean): Promise<TransportCertificate> {
225-
if (isTransportCertificate(this.init.certificate)) {
139+
if (isTransportCertificate(this.certInit.certificate)) {
226140
this.log('using provided TLS certificate')
227-
return this.init.certificate
141+
return this.certInit.certificate
228142
}
229143

230144
const privateKey = await this.loadOrCreatePrivateKey()
@@ -242,7 +156,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
242156
return this.privateKey
243157
}
244158

245-
const keychainName = this.init.certificateKeychainName ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME
159+
const keychainName = this.certInit.certificateKeychainName ?? DEFAULT_CERTIFICATE_PRIVATE_KEY_NAME
246160
const keychain = this.getKeychain()
247161

248162
try {
@@ -278,7 +192,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
278192
}
279193

280194
let cert: X509Certificate
281-
const dsKey = new Key(this.init.certificateDatastoreKey ?? DEFAULT_CERTIFICATE_DATASTORE_KEY)
195+
const dsKey = new Key(this.certInit.certificateDatastoreKey ?? DEFAULT_CERTIFICATE_DATASTORE_KEY)
282196
const keyPair = await privateKeyToCryptoKeyPair(privateKey)
283197

284198
try {
@@ -299,7 +213,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
299213
}
300214

301215
// set timeout to renew certificate
302-
let renewTime = (cert.notAfter.getTime() - (this.init.certificateRenewalThreshold ?? DEFAULT_CERTIFICATE_RENEWAL_THRESHOLD)) - Date.now()
216+
let renewTime = (cert.notAfter.getTime() - (this.certInit.certificateRenewalThreshold ?? DEFAULT_CERTIFICATE_RENEWAL_THRESHOLD)) - Date.now()
303217

304218
if (renewTime < 0) {
305219
renewTime = 100
@@ -332,7 +246,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
332246
const cert = new X509Certificate(buf)
333247

334248
// check expiry date
335-
const expiryTime = cert.notAfter.getTime() - (this.init.certificateRenewalThreshold ?? DEFAULT_CERTIFICATE_RENEWAL_THRESHOLD)
249+
const expiryTime = cert.notAfter.getTime() - (this.certInit.certificateRenewalThreshold ?? DEFAULT_CERTIFICATE_RENEWAL_THRESHOLD)
336250

337251
if (Date.now() > expiryTime) {
338252
this.log('stored TLS certificate has expired')
@@ -362,7 +276,7 @@ export class WebRTCDirectTransport implements Transport, Startable {
362276

363277
async createCertificate (dsKey: Key, keyPair: CryptoKeyPair): Promise<X509Certificate> {
364278
const notBefore = new Date()
365-
const notAfter = new Date(Date.now() + (this.init.certificateLifespan ?? DEFAULT_CERTIFICATE_LIFESPAN))
279+
const notAfter = new Date(Date.now() + (this.certInit.certificateLifespan ?? DEFAULT_CERTIFICATE_LIFESPAN))
366280

367281
// have to set ms to 0 to work around https://github.com/PeculiarVentures/x509/issues/73
368282
notBefore.setMilliseconds(0)

0 commit comments

Comments
 (0)