Skip to content

Commit

Permalink
transport/webtransport: add support for WASM
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorropo committed May 22, 2023
1 parent 6544501 commit a1b84d0
Show file tree
Hide file tree
Showing 14 changed files with 725 additions and 220 deletions.
4 changes: 3 additions & 1 deletion defaults_browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ package libp2p

import (
ws "github.com/libp2p/go-libp2p/p2p/transport/websocket"
webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport"
)

// DefaultTransport has been trimmed down to what works in the browser.
var DefaultTransports = ChainOptions(
// TODO(@Jorropo): If the wasm experiment is doing good, write shims for webtransport and webrtc.
// TODO(@Jorropo): If the wasm experiment is doing good, write shims for webrtc.
Transport(ws.New),
Transport(webtransport.New),
)

// DefaultPrivateTransports has been trimmed down to what works in the browser.
Expand Down
28 changes: 21 additions & 7 deletions examples/chat/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func main() {

sourcePort := flag.Int("sp", 0, "Source port number")
websocket := flag.Bool("ws", false, "Use websocket transport for wasm browser example.")
webtransport := flag.Bool("wt", false, "Use WebTransport transport for wasm browser example.")
dest := flag.String("d", "", "Destination multiaddr string")
help := flag.Bool("help", false, "Display help")
debug := flag.Bool("debug", false, "Debug generates the same node ID on every execution")
Expand All @@ -124,14 +125,14 @@ func main() {
r = rand.Reader
}

h, err := makeHost(*sourcePort, r, *websocket)
h, err := makeHost(*sourcePort, r, *websocket, *webtransport)
if err != nil {
log.Println(err)
return
}

if *dest == "" {
startPeer(ctx, h, handleStream, *websocket)
startPeer(ctx, h, handleStream, *websocket, *webtransport)
} else {
rw, err := startPeerAndConnect(ctx, h, *dest)
if err != nil {
Expand All @@ -149,7 +150,7 @@ func main() {
select {}
}

func makeHost(port int, randomness io.Reader, websocket bool) (host.Host, error) {
func makeHost(port int, randomness io.Reader, websocket, webtransport bool) (host.Host, error) {
// Creates a new RSA key pair for this host.
prvKey, _, err := crypto.GenerateKeyPairWithReader(crypto.RSA, 2048, randomness)
if err != nil {
Expand All @@ -162,6 +163,9 @@ func makeHost(port int, randomness io.Reader, websocket bool) (host.Host, error)
if websocket {
format += "/ws"
}
if webtransport {
format = "/ip4/0.0.0.0/udp/%d/quic-v1/webtransport"
}
sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf(format, port))

// libp2p.New constructs a new libp2p Host.
Expand All @@ -172,7 +176,7 @@ func makeHost(port int, randomness io.Reader, websocket bool) (host.Host, error)
)
}

func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler, websocket bool) {
func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler, websocket, webtransport bool) {
// Set a function as stream handler.
// This function is called when a peer connects, and starts a stream with this protocol.
// Only applies on the receiving side.
Expand All @@ -181,9 +185,16 @@ func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHan
// Let's get the actual TCP port from our listen multiaddr, in case we're using 0 (default; random available port).
var port string
for _, la := range h.Network().ListenAddresses() {
if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil {
port = p
break
if webtransport {
if _, err := la.ValueForProtocol(multiaddr.P_WEBTRANSPORT); err == nil {
port = la.String()
break
}
} else {
if p, err := la.ValueForProtocol(multiaddr.P_TCP); err == nil {
port = p
break
}
}
}

Expand All @@ -196,6 +207,9 @@ func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHan
if websocket {
format = "Use address '/ip4/127.0.0.1/tcp/%v/ws/p2p/%s' to connect from the browser.\n"
}
if webtransport {
format = "Use address '%s/p2p/%s' to connect from the browser.\n"
}
log.Printf(format, port, h.ID().Pretty())
log.Println("You can replace 127.0.0.1 with public IP as well.")
log.Println("Waiting for incoming connection")
Expand Down
6 changes: 3 additions & 3 deletions examples/chat/chat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestMain(t *testing.T) {
return
}

h1, err := makeHost(port1, rand.Reader, false)
h1, err := makeHost(port1, rand.Reader, false, false)
if err != nil {
log.Println(err)
return
Expand All @@ -47,11 +47,11 @@ func TestMain(t *testing.T) {
// Sleep a bit to let h2 print the logs we're waiting for
time.Sleep(500 * time.Millisecond)
cancel() // end the test
}, false)
}, false, false)

dest := fmt.Sprintf("/ip4/127.0.0.1/tcp/%v/p2p/%s", port1, h1.ID().Pretty())

h2, err := makeHost(port2, rand.Reader, false)
h2, err := makeHost(port2, rand.Reader, false, false)
if err != nil {
log.Println(err)
return
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ require (
github.com/stretchr/testify v1.8.1
go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924
go.uber.org/goleak v1.1.12
go.uber.org/multierr v1.11.0
golang.org/x/crypto v0.7.0
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/sync v0.1.0
Expand Down Expand Up @@ -118,7 +119,6 @@ require (
github.com/syndtr/goleveldb v1.0.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/dig v1.16.1 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/net v0.8.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions p2p/transport/webtransport/cert_manager.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package libp2pwebtransport

import (
Expand Down
2 changes: 2 additions & 0 deletions p2p/transport/webtransport/cert_manager_test.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package libp2pwebtransport

import (
Expand Down
115 changes: 115 additions & 0 deletions p2p/transport/webtransport/conn_browser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//go:build js

package libp2pwebtransport

import (
"context"
"fmt"
"io"
"net"
"sync/atomic"
"syscall/js"

ic "github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
tpt "github.com/libp2p/go-libp2p/core/transport"

ma "github.com/multiformats/go-multiaddr"
)

var _ net.Addr = (*addr)(nil)

type addr struct {
url string
}

func (addr *addr) Network() string {
return "webtransport"
}

func (addr *addr) String() string {
return addr.url
}

type conn struct {
scope network.ConnScope
transport *transport

wt js.Value
incoming js.Value

rpid peer.ID
rpk ic.PubKey

rmaddr ma.Multiaddr
raddr addr

isClosed atomic.Bool
done bool
}

func newConn(scope network.ConnScope, t *transport, wt js.Value, rmaddr ma.Multiaddr, p peer.ID, raddr addr) *conn {
return &conn{
scope: scope,
transport: t,
wt: wt,
incoming: wt.Get("incomingBidirectionalStreams").Call("getReader"),
rpid: p,
rmaddr: rmaddr,
raddr: raddr,
}
}

func (c *conn) OpenStream(ctx context.Context) (network.MuxedStream, error) {
return c.openStream(ctx)
}

func (c *conn) openStream(ctx context.Context) (*stream, error) {
r, err := await(ctx, c.wt.Call("createBidirectionalStream"))
if err != nil {
return nil, fmt.Errorf("createBidirectionalStream: %w", err)
}

return newStream(r[0], c), nil
}

func (c *conn) AcceptStream() (network.MuxedStream, error) {
if c.done {
return nil, io.EOF
}

r, err := await(context.Background(), c.incoming.Call("read"))
if err != nil {
return nil, err
}
o := r[0]
s := o.Get("value")
c.done = o.Get("done").Bool()

return newStream(s, c), nil
}

func (c *conn) Close() error {
c.wt.Call("close")
_, err := await(context.Background(), c.wt.Get("closed"))
c.isClosed.Store(true)
return err
}

var noAddr = addr{"https://0.0.0.0" + webtransportHTTPEndpoint + "?type=noise"}

func (c *conn) RemotePeer() peer.ID { return c.rpid }
func (c *conn) LocalPeer() peer.ID { return c.transport.pid }
func (c *conn) RemoteMultiaddr() ma.Multiaddr { return c.rmaddr }
func (c *conn) LocalMultiaddr() ma.Multiaddr { return webtransportMA }
func (c *conn) RemoteAddr() net.Addr { return &c.raddr }
func (c *conn) LocalAddr() net.Addr { return &noAddr }
func (c *conn) IsClosed() bool { return c.isClosed.Load() }
func (c *conn) Scope() network.ConnScope { return c.scope }
func (c *conn) Transport() tpt.Transport { return c.transport }
func (c *conn) RemotePublicKey() ic.PubKey { return c.rpk }

func (c *conn) ConnState() network.ConnectionState {
return network.ConnectionState{Transport: "webtransport"}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package libp2pwebtransport

import (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !js

package libp2pwebtransport

import (
Expand Down
Loading

0 comments on commit a1b84d0

Please sign in to comment.