Skip to content

Commit

Permalink
chat/chat-browser-wasm: Add an example of running the browser.
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorropo committed May 20, 2023
1 parent 9f9f8a5 commit 94ef3b6
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 63 deletions.
2 changes: 2 additions & 0 deletions examples/chat-wasm-browser/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
main.wasm
wasm_exec.js
9 changes: 9 additions & 0 deletions examples/chat-wasm-browser/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
set -e +x

cd "$(dirname "$0")"

set -x

GOOS=js GOARCH=wasm go build -o main.wasm .
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" wasm_exec.js
18 changes: 18 additions & 0 deletions examples/chat-wasm-browser/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<html>
<head>
<meta charset="utf-8"/>
<script src="wasm_exec.js"></script>
<script defer>
const go = new Go();
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
go.run(result.instance);
});
</script>
</head>
<body>
<input type="text" id="maddr" placeholder="/ip4/1.2.3.4/tcp/1337/ws/p2p/Qmfoo"/>
<button type="button" id="connect">Connect</button>
<p id="self"></p>
<div id="chats"></div>
<p id="output"></p>
</html>
128 changes: 128 additions & 0 deletions examples/chat-wasm-browser/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
//go:build js

package main

import (
"bufio"
"context"
"fmt"
"strings"
"syscall/js"

"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/peerstore"
)

const chatProtocol = "/chat/1.0.0"

func main() {
g := js.Global()
out := g.Get("output")
maddr := g.Get("maddr")
connect := g.Get("connect")
document := g.Get("document")
selfp := g.Get("self")
chats := g.Get("chats")

h, err := libp2p.New()
if err != nil {
out.Set("innerText", "Error starting libp2p, check the console !")
panic(err)
}
self := h.ID().String()
selfp.Set("innerText", self)

connect.Set("onclick", js.FuncOf(func(_ js.Value, _ []js.Value) any {
m := maddr.Get("value").String()
maddr.Set("value", "")
out.Set("innerText", "Contacting "+m)
// Start a new goroutine because are about to do IO and we don't want to block JS's event loop.
go func() {
err := func() error {
info, err := peer.AddrInfoFromString(m)
if err != nil {
return fmt.Errorf("parsing maddr: %w", err)
}

h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL)
s, err := h.NewStream(context.Background(), info.ID, chatProtocol)
if err != nil {
return fmt.Errorf("NewStream: %w", err)
}

anchor := document.Call("createElement", "div")
peerid := document.Call("createElement", "p")
them := info.ID.Pretty()
peerid.Set("innerText", "Connected with: "+them)
anchor.Call("appendChild", peerid)

text := document.Call("createElement", "div")
addLine := func(strs ...string) {
line := document.Call("createElement", "p")
var textBuf strings.Builder
for _, s := range strs {
textBuf.WriteString(s)
}
line.Set("innerText", textBuf.String())
text.Call("appendChild", line)
}
anchor.Call("appendChild", text)

entry := document.Call("createElement", "input")
entry.Set("onkeyup", js.FuncOf(func(_ js.Value, args []js.Value) any {
if len(args) != 1 {
panic("expected 1 argument callback from onkeyup")
}
event := args[0]

if event.Get("key").String() != "Enter" {
return nil
}
toSend := entry.Get("value").String()
entry.Set("value", "")
go func() {
_, err := strings.NewReader(toSend).WriteTo(s)
if err != nil {
addLine("Error sending: ", err.Error())
return
}
_, err = strings.NewReader("\n").WriteTo(s)
if err != nil {
addLine("Error sending: ", err.Error())
return
}
addLine(self, "> ", toSend)
}()
return nil
}))
anchor.Call("appendChild", entry)

chats.Call("appendChild", anchor)

scanner := bufio.NewScanner(s)
for scanner.Scan() {
message := strings.Trim(scanner.Text(), " \n")
if message == "" {
continue
}
addLine(them, "> ", message)
}
if err := scanner.Err(); err != nil {
addLine("Error receiving: ", err.Error())
}

return nil
}()
if err != nil {
s := "error contacting " + m + ": " + err.Error()
fmt.Println(s)
out.Set("innerText", s)
}
}()

return nil
}))

select {}
}
8 changes: 8 additions & 0 deletions examples/chat-wasm-browser/serve.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/sh
set -e +x

cd "$(dirname "$0")"

. ./build.sh

go run github.com/Jorropo/jhttp
7 changes: 7 additions & 0 deletions examples/chat-wasm-browser/tool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build tools

package tools

import (
_ "github.com/Jorropo/jhttp"
)
21 changes: 15 additions & 6 deletions examples/chat/chat.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ func main() {
defer cancel()

sourcePort := flag.Int("sp", 0, "Source port number")
websocket := flag.Bool("ws", false, "Use websocket 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 @@ -123,14 +124,14 @@ func main() {
r = rand.Reader
}

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

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

func makeHost(port int, randomness io.Reader) (host.Host, error) {
func makeHost(port int, randomness io.Reader, websocket 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 @@ -157,7 +158,11 @@ func makeHost(port int, randomness io.Reader) (host.Host, error) {
}

// 0.0.0.0 will listen on any interface device.
sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", port))
format := "/ip4/0.0.0.0/tcp/%d"
if websocket {
format += "/ws"
}
sourceMultiAddr, _ := multiaddr.NewMultiaddr(fmt.Sprintf(format, port))

// libp2p.New constructs a new libp2p Host.
// Other options can be added here.
Expand All @@ -167,7 +172,7 @@ func makeHost(port int, randomness io.Reader) (host.Host, error) {
)
}

func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler) {
func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHandler, websocket 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 @@ -187,7 +192,11 @@ func startPeer(ctx context.Context, h host.Host, streamHandler network.StreamHan
return
}

log.Printf("Run './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s' on another console.\n", port, h.ID().Pretty())
format := "Run './chat -d /ip4/127.0.0.1/tcp/%v/p2p/%s' on another console.\n"
if websocket {
format = "Use address '/ip4/127.0.0.1/tcp/%v/ws/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")
log.Println()
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)
h1, err := makeHost(port1, rand.Reader, 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)

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

h2, err := makeHost(port2, rand.Reader)
h2, err := makeHost(port2, rand.Reader, false)
if err != nil {
log.Println(err)
return
Expand Down
21 changes: 17 additions & 4 deletions examples/go.mod
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
module github.com/libp2p/go-libp2p/examples

replace github.com/libp2p/go-libp2p => ../

go 1.19

require (
github.com/Jorropo/jhttp v1.0.0
github.com/gogo/protobuf v1.3.2
github.com/google/uuid v1.3.0
github.com/ipfs/go-datastore v0.6.0
github.com/ipfs/go-log/v2 v2.5.1
github.com/libp2p/go-libp2p v0.27.1
github.com/libp2p/go-libp2p v0.26.4
github.com/libp2p/go-libp2p-kad-dht v0.20.1-0.20230209220319-6c2045abad23
github.com/multiformats/go-multiaddr v0.9.0
)

require (
github.com/agnivade/wasmbrowsertest v0.7.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab // indirect
github.com/chromedp/chromedp v0.8.6 // indirect
github.com/chromedp/sysutil v1.0.0 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
Expand All @@ -24,12 +31,17 @@ require (
github.com/elastic/gosigar v0.14.2 // indirect
github.com/flynn/noise v1.0.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/go-interpreter/wagon v0.6.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.1.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
Expand All @@ -42,6 +54,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/jbenet/goprocess v0.1.4 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.16.4 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
Expand All @@ -57,6 +70,7 @@ require (
github.com/libp2p/go-reuseport v0.2.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
Expand Down Expand Up @@ -88,14 +102,14 @@ require (
github.com/quic-go/qtls-go1-19 v0.3.2 // indirect
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/quic-go/quic-go v0.33.0 // indirect
github.com/quic-go/webtransport-go v0.5.2 // indirect
github.com/quic-go/webtransport-go v0.5.3 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect
go.opencensus.io v0.24.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/dig v1.16.1 // indirect
go.uber.org/fx v1.19.2 // indirect
go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
Expand All @@ -108,5 +122,4 @@ require (
golang.org/x/tools v0.7.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
lukechampine.com/blake3 v1.1.7 // indirect
nhooyr.io/websocket v1.8.7 // indirect
)
Loading

0 comments on commit 94ef3b6

Please sign in to comment.