Skip to content

Make go-libp2p compile to WASM (including WebRTC transport) #3280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 3 additions & 105 deletions p2p/transport/webrtc/listener.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build !js
// +build !js

package libp2pwebrtc

import (
Expand All @@ -8,8 +11,6 @@ import (
"fmt"
"net"
"strings"
"sync"
"time"

"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
Expand All @@ -23,47 +24,6 @@ import (
"github.com/pion/webrtc/v4"
)

type connMultiaddrs struct {
local, remote ma.Multiaddr
}

var _ network.ConnMultiaddrs = &connMultiaddrs{}

func (c *connMultiaddrs) LocalMultiaddr() ma.Multiaddr { return c.local }
func (c *connMultiaddrs) RemoteMultiaddr() ma.Multiaddr { return c.remote }

const (
candidateSetupTimeout = 10 * time.Second
// This is higher than other transports(64) as there's no way to detect a peer that has gone away after
// sending the initial connection request message(STUN Binding request). Such peers take up a goroutine
// till connection timeout. As the number of handshakes in parallel is still guarded by the resource
// manager, this higher number is okay.
DefaultMaxInFlightConnections = 128
)

type listener struct {
transport *WebRTCTransport

mux *udpmux.UDPMux

config webrtc.Configuration
localFingerprint webrtc.DTLSFingerprint
localFingerprintMultibase string

localAddr net.Addr
localMultiaddr ma.Multiaddr

// buffered incoming connections
acceptQueue chan tpt.CapableConn

// used to control the lifecycle of the listener
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
}

var _ tpt.Listener = &listener{}

func newListener(transport *WebRTCTransport, laddr ma.Multiaddr, socket net.PacketConn, config webrtc.Configuration) (*listener, error) {
localFingerprints, err := config.Certificates[0].GetFingerprints()
if err != nil {
Expand Down Expand Up @@ -288,65 +248,3 @@ func (l *listener) setupConnection(

return conn, err
}

func (l *listener) Accept() (tpt.CapableConn, error) {
select {
case <-l.ctx.Done():
return nil, tpt.ErrListenerClosed
case conn := <-l.acceptQueue:
return conn, nil
}
}

func (l *listener) Close() error {
select {
case <-l.ctx.Done():
default:
}
l.cancel()
l.mux.Close()
l.wg.Wait()
loop:
for {
select {
case conn := <-l.acceptQueue:
conn.Close()
default:
break loop
}
}
return nil
}

func (l *listener) Addr() net.Addr {
return l.localAddr
}

func (l *listener) Multiaddr() ma.Multiaddr {
return l.localMultiaddr
}

// addOnConnectionStateChangeCallback adds the OnConnectionStateChange to the PeerConnection.
// The channel returned here:
// * is closed when the state changes to Connection
// * receives an error when the state changes to Failed or Closed or Disconnected
func addOnConnectionStateChangeCallback(pc *webrtc.PeerConnection) <-chan error {
errC := make(chan error, 1)
var once sync.Once
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
switch pc.ConnectionState() {
case webrtc.PeerConnectionStateConnected:
once.Do(func() { close(errC) })
// PeerConnectionStateFailed happens when we fail to negotiate the connection.
// PeerConnectionStateDisconnected happens when we disconnect immediately after connecting.
// PeerConnectionStateClosed happens when we close the peer connection locally, not when remote closes. We don't need
// to error in this case, but it's a no-op, so it doesn't hurt.
case webrtc.PeerConnectionStateFailed, webrtc.PeerConnectionStateClosed, webrtc.PeerConnectionStateDisconnected:
once.Do(func() {
errC <- errors.New("peerconnection failed")
close(errC)
})
}
})
return errC
}
118 changes: 118 additions & 0 deletions p2p/transport/webrtc/listener_shared.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package libp2pwebrtc

import (
"context"
"errors"
"net"
"sync"
"time"

"github.com/libp2p/go-libp2p/core/network"
tpt "github.com/libp2p/go-libp2p/core/transport"
"github.com/libp2p/go-libp2p/p2p/transport/webrtc/udpmux"
ma "github.com/multiformats/go-multiaddr"
"github.com/pion/webrtc/v4"
)

const (
candidateSetupTimeout = 10 * time.Second
// This is higher than other transports(64) as there's no way to detect a peer that has gone away after
// sending the initial connection request message(STUN Binding request). Such peers take up a goroutine
// till connection timeout. As the number of handshakes in parallel is still guarded by the resource
// manager, this higher number is okay.
DefaultMaxInFlightConnections = 128
)

type connMultiaddrs struct {
local, remote ma.Multiaddr
}

var _ network.ConnMultiaddrs = &connMultiaddrs{}

func (c *connMultiaddrs) LocalMultiaddr() ma.Multiaddr { return c.local }
func (c *connMultiaddrs) RemoteMultiaddr() ma.Multiaddr { return c.remote }

type listener struct {
transport *WebRTCTransport

mux *udpmux.UDPMux

config webrtc.Configuration
localFingerprint webrtc.DTLSFingerprint
localFingerprintMultibase string

localAddr net.Addr
localMultiaddr ma.Multiaddr

// buffered incoming connections
acceptQueue chan tpt.CapableConn

// used to control the lifecycle of the listener
ctx context.Context
cancel context.CancelFunc
wg sync.WaitGroup
}

var _ tpt.Listener = &listener{}

func (l *listener) Accept() (tpt.CapableConn, error) {
select {
case <-l.ctx.Done():
return nil, tpt.ErrListenerClosed
case conn := <-l.acceptQueue:
return conn, nil
}
}

func (l *listener) Addr() net.Addr {
return l.localAddr
}

func (l *listener) Multiaddr() ma.Multiaddr {
return l.localMultiaddr
}

func (l *listener) Close() error {
select {
case <-l.ctx.Done():
default:
}
l.cancel()
l.mux.Close()
l.wg.Wait()
loop:
for {
select {
case conn := <-l.acceptQueue:
conn.Close()
default:
break loop
}
}
return nil
}

// addOnConnectionStateChangeCallback adds the OnConnectionStateChange to the PeerConnection.
// The channel returned here:
// * is closed when the state changes to Connection
// * receives an error when the state changes to Failed or Closed or Disconnected
func addOnConnectionStateChangeCallback(pc *webrtc.PeerConnection) <-chan error {
errC := make(chan error, 1)
var once sync.Once
pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) {
switch pc.ConnectionState() {
case webrtc.PeerConnectionStateConnected:
once.Do(func() { close(errC) })
// PeerConnectionStateFailed happens when we fail to negotiate the connection.
// PeerConnectionStateDisconnected happens when we disconnect immediately after connecting.
// PeerConnectionStateClosed happens when we close the peer connection locally, not when remote closes. We don't need
// to error in this case, but it's a no-op, so it doesn't hurt.
case webrtc.PeerConnectionStateFailed, webrtc.PeerConnectionStateClosed, webrtc.PeerConnectionStateDisconnected:
once.Do(func() {
errC <- errors.New("peerconnection failed")
close(errC)
})
}
})
return errC
}
3 changes: 3 additions & 0 deletions p2p/transport/webrtc/stream_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//go:build !js
// +build !js

package libp2pwebrtc

import (
Expand Down
Loading