Skip to content
This repository has been archived by the owner on Apr 24, 2023. It is now read-only.

Commit

Permalink
fix: update export type (#479)
Browse files Browse the repository at this point in the history
Export factory functions for the transport and peer discovery separately
  • Loading branch information
achingbrain authored Oct 13, 2022
1 parent 82684bd commit 2b8a924
Show file tree
Hide file tree
Showing 15 changed files with 363 additions and 324 deletions.
14 changes: 7 additions & 7 deletions packages/webrtc-star-transport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ import wrtc from 'wrtc'
import electronWebRTC from 'electron-webrtc'

// Using wrtc in node
const transport = webRTCStar({ wrtc })
const star = webRTCStar({ wrtc })

// Using electron-webrtc in electron
const transport = webRTCStar({ wrtc: electronWebRTC() })
const star = webRTCStar({ wrtc: electronWebRTC() })

const node = await createLibp2pNode({
addresses: {
Expand All @@ -64,10 +64,10 @@ const node = await createLibp2pNode({
]
},
transports: [
transport
star.transport
],
peerDiscovery: [
transport.discovery
star.discovery
]
})
await node.start()
Expand All @@ -81,7 +81,7 @@ await node.dial('/ip4/188.166.203.82/tcp/20000/wss/p2p-webrtc-star/p2p/QmcgpsyWg
import { createLibp2pNode } from 'libp2p'
import { webRTCStar } from '@libp2p/webrtc-star'

const transport = webRTCStar()
const star = webRTCStar()

const node = await createLibp2pNode({
addresses: {
Expand All @@ -90,10 +90,10 @@ const node = await createLibp2pNode({
]
},
transports: [
transport
star.transport
],
peerDiscovery: [
transport.discovery
star.discovery
]
})
await node.start()
Expand Down
306 changes: 14 additions & 292 deletions packages/webrtc-star-transport/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,298 +1,20 @@
import { logger } from '@libp2p/logger'
import errcode from 'err-code'
import { AbortError } from 'abortable-iterator'
import type { Multiaddr } from '@multiformats/multiaddr'
import { multiaddr } from '@multiformats/multiaddr'
import * as mafmt from '@multiformats/mafmt'
import { CODE_CIRCUIT } from './constants.js'
import { createListener } from './listener.js'
import { toMultiaddrConnection } from './socket-to-conn.js'
import { cleanMultiaddr, cleanUrlSIO } from './utils.js'
import { WebRTCInitiator } from '@libp2p/webrtc-peer'
import randomBytes from 'iso-random-stream/src/random.js'
import { toString as uint8ArrayToString } from 'uint8arrays'
import { EventEmitter, CustomEvent } from '@libp2p/interfaces/events'
import type { Startable } from '@libp2p/interfaces/startable'
import { peerIdFromString } from '@libp2p/peer-id'
import { symbol } from '@libp2p/interface-transport'
import type { WRTC, WebRTCInitiatorInit, WebRTCReceiver, WebRTCReceiverInit } from '@libp2p/webrtc-peer'
import type { Connection, MultiaddrConnection } from '@libp2p/interface-connection'
import type { Transport, Listener, DialOptions, CreateListenerOptions } from '@libp2p/interface-transport'
import type { PeerDiscovery, PeerDiscoveryEvents } from '@libp2p/interface-peer-discovery'
import type { WebRTCStarSocket, HandshakeSignal } from '@libp2p/webrtc-star-protocol'
import { symbol as peerDiscoverySymbol } from '@libp2p/interface-peer-discovery'
import type { PeerId } from '@libp2p/interface-peer-id'
import type { PeerDiscovery } from '@libp2p/interface-peer-discovery'
import type { Transport } from '@libp2p/interface-transport'
import { WebRTCStar, WebRTCStarComponents, WebRTCStarInit } from './transport.js'

const webrtcSupport = 'RTCPeerConnection' in globalThis
const log = logger('libp2p:webrtc-star')

const noop = () => {}

class WebRTCStarDiscovery extends EventEmitter<PeerDiscoveryEvents> implements PeerDiscovery, Startable {
private started = false

get [peerDiscoverySymbol] (): true {
return true
}

get [Symbol.toStringTag] () {
return '@libp2p/webrtc-star-discovery'
}

isStarted () {
return this.started
}

async start () {
this.started = true
}

async stop () {
this.started = false
}

dispatchEvent (event: CustomEvent) {
if (!this.isStarted()) {
return false
}

return super.dispatchEvent(event)
}
}

export interface WebRTCStarInit {
wrtc?: WRTC
}

export interface WebRTCStarDialOptions extends DialOptions {
channelOptions?: WebRTCInitiatorInit
export interface WebRTCStarTuple {
transport: (components: WebRTCStarComponents) => Transport
discovery: (components?: WebRTCStarComponents) => PeerDiscovery
}

export interface WebRTCStarListenerOptions extends CreateListenerOptions, WebRTCInitiatorInit {
channelOptions?: WebRTCReceiverInit
}

export interface SignalServerServerEvents {
'error': CustomEvent<Error>
'listening': CustomEvent
'peer': CustomEvent<string>
'connection': CustomEvent<Connection>
'disconnect': CustomEvent
'reconnect': CustomEvent
}

export interface SignalServer extends EventEmitter<SignalServerServerEvents> {
signallingAddr: Multiaddr
socket: WebRTCStarSocket
connections: MultiaddrConnection[]
channels: Map<string, WebRTCReceiver>
pendingSignals: Map<string, HandshakeSignal[]>
close: () => Promise<void>
}

export interface WebRTCStarComponents {
peerId: PeerId
}

/**
* @class WebRTCStar
*/
export class WebRTCStar implements Transport {
public wrtc?: WRTC
public discovery: () => PeerDiscovery & Startable
public sigServers: Map<string, SignalServer>
private readonly components: WebRTCStarComponents
private readonly _discovery: WebRTCStarDiscovery

constructor (components: WebRTCStarComponents, init?: WebRTCStarInit) {
if (init?.wrtc != null) {
this.wrtc = init.wrtc
}

this.components = components
export function webRTCStar (init: WebRTCStarInit = {}): WebRTCStarTuple {
const transport = new WebRTCStar(init)

// Keep Signalling references
this.sigServers = new Map()

// Discovery
this._discovery = new WebRTCStarDiscovery()
this.discovery = () => this._discovery
this.peerDiscovered = this.peerDiscovered.bind(this)
}

get [symbol] (): true {
return true
}

get [Symbol.toStringTag] () {
return '@libp2p/webrtc-star'
return {
transport: (components: WebRTCStarComponents) => {
transport.peerId = components.peerId
return transport
},
discovery: transport.discovery
}

async dial (ma: Multiaddr, options: WebRTCStarDialOptions) {
const rawConn = await this._connect(ma, options)
const maConn = toMultiaddrConnection(rawConn, { remoteAddr: ma, signal: options.signal })
log('new outbound connection %s', maConn.remoteAddr)
const conn = await options.upgrader.upgradeOutbound(maConn)
log('outbound connection %s upgraded', maConn.remoteAddr)
return conn
}

async _connect (ma: Multiaddr, options: WebRTCStarDialOptions) {
if (options.signal?.aborted === true) {
throw new AbortError()
}

const channelOptions = {
...(options.channelOptions ?? {})
}

// Use custom WebRTC implementation
if (this.wrtc != null) {
channelOptions.wrtc = this.wrtc
}

const cOpts = ma.toOptions()
const intentId = uint8ArrayToString(randomBytes(36), 'hex')

return await new Promise<WebRTCInitiator>((resolve, reject) => {
const sio = this.sigServers.get(cleanUrlSIO(ma))

if (sio?.socket == null) {
return reject(errcode(new Error('unknown signal server to use'), 'ERR_UNKNOWN_SIGNAL_SERVER'))
}

let connected: boolean = false

log('dialing %s:%s', cOpts.host, cOpts.port)
const channel = new WebRTCInitiator(channelOptions)

const onError = (evt: CustomEvent<Error>) => {
const err = evt.detail

if (!connected) {
const msg = `connection error ${cOpts.host}:${cOpts.port}: ${err.message}`
log.error(msg)
done(err)
}
}

const onReady = () => {
connected = true

log('connection opened %s:%s', cOpts.host, cOpts.port)
done()
}

const onAbort = () => {
log.error('connection aborted %s:%s', cOpts.host, cOpts.port)
channel.close().finally(() => {
done(new AbortError())
})
}

const done = (err?: Error) => {
channel.removeEventListener('ready', onReady)
options.signal?.removeEventListener('abort', onAbort)

if (err == null) {
resolve(channel)
} else {
reject(err)
}
}

channel.addEventListener('ready', onReady, {
once: true
})
channel.addEventListener('close', () => {
channel.removeEventListener('error', onError)
})
options.signal?.addEventListener('abort', onAbort)

channel.addEventListener('signal', (evt) => {
const signal = evt.detail

sio.socket.emit('ss-handshake', {
intentId: intentId,
srcMultiaddr: sio.signallingAddr.toString(),
dstMultiaddr: ma.toString(),
signal: signal
})
})

sio.socket.on('ws-handshake', (offer) => {
if (offer.intentId === intentId && offer.err != null) {
channel.close().finally(() => {
reject(errcode(new Error(offer.err), 'ERR_SIGNALLING_FAILED'))
})
}

if (offer.intentId !== intentId || offer.answer == null || channel.closed) {
return
}

channel.handleSignal(offer.signal)
})
})
}

/**
* Creates a WebrtcStar listener. The provided `handler` function will be called
* anytime a new incoming Connection has been successfully upgraded via
* `upgrader.upgradeInbound`.
*/
createListener (options: WebRTCStarListenerOptions): Listener {
if (!webrtcSupport && this.wrtc == null) {
throw errcode(new Error('no WebRTC support'), 'ERR_NO_WEBRTC_SUPPORT')
}

options.channelOptions = options.channelOptions ?? {}

if (this.wrtc != null) {
options.channelOptions.wrtc = this.wrtc
}

return createListener(options.upgrader, options.handler ?? noop, this.components.peerId, this, options)
}

/**
* Takes a list of `Multiaddr`s and returns only valid TCP addresses
*/
filter (multiaddrs: Multiaddr[]) {
multiaddrs = Array.isArray(multiaddrs) ? multiaddrs : [multiaddrs]

return multiaddrs.filter((ma) => {
if (ma.protoCodes().includes(CODE_CIRCUIT)) {
return false
}

return mafmt.WebRTCStar.matches(ma)
})
}

peerDiscovered (maStr: string) {
log('peer discovered: %s', maStr)
maStr = cleanMultiaddr(maStr)

const ma = multiaddr(maStr)
const peerIdStr = ma.getPeerId()

if (peerIdStr == null) {
return
}

const peerId = peerIdFromString(peerIdStr)

this._discovery.dispatchEvent(new CustomEvent('peer', {
detail: {
id: peerId,
multiaddrs: [ma],
protocols: []
}
}))
}
}

export function webRTCStar (init: WebRTCStarInit = {}): (components: WebRTCStarComponents) => WebRTCStar {
return (components: WebRTCStarComponents) => new WebRTCStar(components, init)
}
2 changes: 1 addition & 1 deletion packages/webrtc-star-transport/src/listener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { PeerId } from '@libp2p/interface-peer-id'
import type { Multiaddr } from '@multiformats/multiaddr'
import type { MultiaddrConnection } from '@libp2p/interface-connection'
import type { Upgrader, ConnectionHandler, Listener, ListenerEvents } from '@libp2p/interface-transport'
import type { WebRTCStar, WebRTCStarListenerOptions, SignalServer, SignalServerServerEvents } from './index.js'
import type { WebRTCStar, WebRTCStarListenerOptions, SignalServer, SignalServerServerEvents } from './transport.js'
import type { WebRTCReceiverInit } from '@libp2p/webrtc-peer'
import type { WebRTCStarSocket, HandshakeSignal } from '@libp2p/webrtc-star-protocol'
import { EventEmitter, CustomEvent } from '@libp2p/interfaces/events'
Expand Down
Loading

0 comments on commit 2b8a924

Please sign in to comment.