From 16162fe2be3d68065babac86c11295e8a8858fc2 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Sat, 20 May 2023 18:48:41 +0200 Subject: [PATCH 1/4] transport/webtransport: close the challenge stream --- p2p/transport/webtransport/transport.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/p2p/transport/webtransport/transport.go b/p2p/transport/webtransport/transport.go index f9c68ddf30..9e8ebcaca3 100644 --- a/p2p/transport/webtransport/transport.go +++ b/p2p/transport/webtransport/transport.go @@ -233,6 +233,7 @@ func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p p if err != nil { return nil, err } + defer str.Close() // Now run a Noise handshake (using early data) and get all the certificate hashes from the server. // We will verify that the certhashes we used to dial is a subset of the certhashes we received from the server. @@ -264,6 +265,9 @@ func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p p if err != nil { return nil, err } + if err = c.Close(); err != nil { + return nil, err + } // The Noise handshake _should_ guarantee that our verification callback is called. // Double-check just in case. if !verified { From 30d2111c7209b1cb21e2deee5ae952480149bc7d Mon Sep 17 00:00:00 2001 From: Jorropo Date: Sat, 20 May 2023 09:12:57 +0200 Subject: [PATCH 2/4] p2p/transport/websocket: add GOOS=js support back in This was surpsingly easy there is a high chance I messed something up. This is a revert of 2d53c8f4ebb7aa401a4e855f4668fb121d3fe7b1 and then a bunch of fixes to bring it up to speed with the latest implementation. The fx change is related to this: uber-go/fx#1022 --- default_not_browser.go | 42 +++ defaults.go | 63 +--- defaults_browser.go | 21 ++ go.mod | 14 +- go.sum | 46 ++- p2p/transport/websocket/.gitignore | 1 + p2p/transport/websocket/LICENSE-APACHE | 5 - p2p/transport/websocket/LICENSE-MIT | 19 -- p2p/transport/websocket/addrs.go | 12 + p2p/transport/websocket/addrs_test.go | 14 - .../browser_integration_browser_test.go | 60 ++++ .../browser_integration_native_test.go | 95 ++++++ p2p/transport/websocket/conn.go | 139 -------- p2p/transport/websocket/conn_browser.go | 304 ++++++++++++++++++ p2p/transport/websocket/conn_native.go | 149 +++++++++ p2p/transport/websocket/listener.go | 14 +- p2p/transport/websocket/listener_test.go | 22 ++ p2p/transport/websocket/muxer_test.go | 27 ++ p2p/transport/websocket/tools.go | 7 + p2p/transport/websocket/websocket.go | 115 +------ p2p/transport/websocket/websocket_browser.go | 47 +++ p2p/transport/websocket/websocket_native.go | 123 +++++++ p2p/transport/websocket/websocket_test.go | 18 +- test-plans/go.mod | 10 + test-plans/go.sum | 38 +++ 25 files changed, 1035 insertions(+), 370 deletions(-) create mode 100644 default_not_browser.go create mode 100644 defaults_browser.go create mode 100644 p2p/transport/websocket/.gitignore delete mode 100644 p2p/transport/websocket/LICENSE-APACHE delete mode 100644 p2p/transport/websocket/LICENSE-MIT create mode 100644 p2p/transport/websocket/browser_integration_browser_test.go create mode 100644 p2p/transport/websocket/browser_integration_native_test.go create mode 100644 p2p/transport/websocket/conn_browser.go create mode 100644 p2p/transport/websocket/conn_native.go create mode 100644 p2p/transport/websocket/listener_test.go create mode 100644 p2p/transport/websocket/muxer_test.go create mode 100644 p2p/transport/websocket/tools.go create mode 100644 p2p/transport/websocket/websocket_browser.go create mode 100644 p2p/transport/websocket/websocket_native.go diff --git a/default_not_browser.go b/default_not_browser.go new file mode 100644 index 0000000000..080660f36d --- /dev/null +++ b/default_not_browser.go @@ -0,0 +1,42 @@ +//go:build !js + +package libp2p + +import ( + quic "github.com/libp2p/go-libp2p/p2p/transport/quic" + "github.com/libp2p/go-libp2p/p2p/transport/tcp" + ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" + webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" +) + +// DefaultTransports are the default libp2p transports. +// +// Use this option when you want to *extend* the set of transports used by +// libp2p instead of replacing them. +var DefaultTransports = ChainOptions( + Transport(tcp.NewTCPTransport), + Transport(quic.NewTransport), + Transport(ws.New), + Transport(webtransport.New), +) + +// DefaultPrivateTransports are the default libp2p transports when a PSK is supplied. +// +// Use this option when you want to *extend* the set of transports used by +// libp2p instead of replacing them. +var DefaultPrivateTransports = ChainOptions( + Transport(tcp.NewTCPTransport), + Transport(ws.New), +) + +// DefaultListenAddrs configures libp2p to use default listen address. +var DefaultListenAddrs = makeDefaultListenAddrs( + "/ip4/0.0.0.0/tcp/0", + "/ip4/0.0.0.0/udp/0/quic", + "/ip4/0.0.0.0/udp/0/quic-v1", + "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", + "/ip6/::/tcp/0", + "/ip6/::/udp/0/quic", + "/ip6/::/udp/0/quic-v1", + "/ip6/::/udp/0/quic-v1/webtransport", +) diff --git a/defaults.go b/defaults.go index c0ed6698a1..6e944df9e1 100644 --- a/defaults.go +++ b/defaults.go @@ -1,6 +1,6 @@ package libp2p -// This file contains all the default configuration options. +// This file contains all the platform independant default configuration options. import ( "crypto/rand" @@ -12,10 +12,6 @@ import ( "github.com/libp2p/go-libp2p/p2p/net/connmgr" "github.com/libp2p/go-libp2p/p2p/security/noise" tls "github.com/libp2p/go-libp2p/p2p/security/tls" - quic "github.com/libp2p/go-libp2p/p2p/transport/quic" - "github.com/libp2p/go-libp2p/p2p/transport/tcp" - ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" - webtransport "github.com/libp2p/go-libp2p/p2p/transport/webtransport" "github.com/prometheus/client_golang/prometheus" "github.com/multiformats/go-multiaddr" @@ -37,26 +33,6 @@ var DefaultSecurity = ChainOptions( // libp2p instead of replacing them. var DefaultMuxers = Muxer(yamux.ID, yamux.DefaultTransport) -// DefaultTransports are the default libp2p transports. -// -// Use this option when you want to *extend* the set of transports used by -// libp2p instead of replacing them. -var DefaultTransports = ChainOptions( - Transport(tcp.NewTCPTransport), - Transport(quic.NewTransport), - Transport(ws.New), - Transport(webtransport.New), -) - -// DefaultPrivateTransports are the default libp2p transports when a PSK is supplied. -// -// Use this option when you want to *extend* the set of transports used by -// libp2p instead of replacing them. -var DefaultPrivateTransports = ChainOptions( - Transport(tcp.NewTCPTransport), - Transport(ws.New), -) - // DefaultPeerstore configures libp2p to use the default peerstore. var DefaultPeerstore Option = func(cfg *Config) error { ps, err := pstoremem.NewPeerstore() @@ -75,29 +51,6 @@ var RandomIdentity = func(cfg *Config) error { return cfg.Apply(Identity(priv)) } -// DefaultListenAddrs configures libp2p to use default listen address. -var DefaultListenAddrs = func(cfg *Config) error { - addrs := []string{ - "/ip4/0.0.0.0/tcp/0", - "/ip4/0.0.0.0/udp/0/quic", - "/ip4/0.0.0.0/udp/0/quic-v1", - "/ip4/0.0.0.0/udp/0/quic-v1/webtransport", - "/ip6/::/tcp/0", - "/ip6/::/udp/0/quic", - "/ip6/::/udp/0/quic-v1", - "/ip6/::/udp/0/quic-v1/webtransport", - } - listenAddrs := make([]multiaddr.Multiaddr, 0, len(addrs)) - for _, s := range addrs { - addr, err := multiaddr.NewMultiaddr(s) - if err != nil { - return err - } - listenAddrs = append(listenAddrs, addr) - } - return cfg.Apply(ListenAddrs(listenAddrs...)) -} - // DefaultEnableRelay enables relay dialing and listening by default. var DefaultEnableRelay = func(cfg *Config) error { return cfg.Apply(EnableRelay()) @@ -218,3 +171,17 @@ var FallbackDefaults Option = func(cfg *Config) error { } return nil } + +func makeDefaultListenAddrs(addrs ...string) func(*Config) error { + return func(cfg *Config) error { + listenAddrs := make([]multiaddr.Multiaddr, 0, len(addrs)) + for _, s := range addrs { + addr, err := multiaddr.NewMultiaddr(s) + if err != nil { + return err + } + listenAddrs = append(listenAddrs, addr) + } + return cfg.Apply(ListenAddrs(listenAddrs...)) + } +} diff --git a/defaults_browser.go b/defaults_browser.go new file mode 100644 index 0000000000..468c7240da --- /dev/null +++ b/defaults_browser.go @@ -0,0 +1,21 @@ +//go:build js + +package libp2p + +import ( + ws "github.com/libp2p/go-libp2p/p2p/transport/websocket" +) + +// 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. + Transport(ws.New), +) + +// DefaultPrivateTransports has been trimmed down to what works in the browser. +var DefaultPrivateTransports = ChainOptions( + Transport(ws.New), +) + +// DefaultListenAddrs is sadly empty, we could maybe prop it up if webrtc in browser support is added. +var DefaultListenAddrs = makeDefaultListenAddrs() diff --git a/go.mod b/go.mod index 8530037f60..b0b320504d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 retract v0.26.1 // Tag was applied incorrectly due to a bug in the release workflow. require ( + github.com/agnivade/wasmbrowsertest v0.7.0 github.com/benbjohnson/clock v1.3.0 github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 @@ -49,8 +50,8 @@ require ( github.com/quic-go/quic-go v0.33.0 github.com/quic-go/webtransport-go v0.5.3 github.com/raulk/go-watchdog v1.3.0 - github.com/stretchr/testify v1.8.2 - go.uber.org/fx v1.19.2 + github.com/stretchr/testify v1.8.1 + go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924 go.uber.org/goleak v1.1.12 golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 @@ -65,6 +66,9 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash v1.1.0 // 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/davecgh/go-spew v1.1.1 // indirect @@ -75,7 +79,11 @@ require ( github.com/elastic/gosigar v0.14.2 // indirect github.com/francoispqt/gojay v1.2.13 // indirect github.com/fsnotify/fsnotify v1.5.4 // 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/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.3 // indirect @@ -85,9 +93,11 @@ require ( github.com/huin/goupnp v1.1.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/goprocess v0.1.4 // indirect + github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/koron/go-ssdp v0.0.4 // indirect github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/miekg/dns v1.1.53 // indirect diff --git a/go.sum b/go.sum index 2b27173403..7241ec2b67 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOv github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/agnivade/wasmbrowsertest v0.7.0 h1:vgi7PKcYHBI13PBpefjtObJl3xQKYmcu3JbAQd4z79s= +github.com/agnivade/wasmbrowsertest v0.7.0/go.mod h1:kvkdPoZxkikAxwXLc0uW2c5iKhjP8//lmC9LA5hl8rU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -26,6 +28,16 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab h1:qzilDZlsMUMBpXySlekH7rgrrH/feo11uQoQxTY7Uk0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.8.6 h1:KobeeqR2dpfKSG1prS3Y6+FbffMmGC6xmAobRXA9QEQ= +github.com/chromedp/chromedp v0.8.6/go.mod h1:nBYHoD6YSNzrr82cIeuOzhw1Jo/s2o0QQ+ifTeoCZ+c= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -60,6 +72,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -75,10 +89,18 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-interpreter/wagon v0.6.0 h1:BBxDxjiJiHgw9EdkYXAWs8NHhwnazZ5P2EWBW5hFNWw= +github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -122,6 +144,7 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -143,6 +166,7 @@ github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7 github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= @@ -167,6 +191,8 @@ github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPw github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -189,6 +215,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= @@ -218,6 +246,8 @@ github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0 github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -291,6 +321,8 @@ github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -373,11 +405,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= @@ -393,8 +427,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924 h1:zEdVPfgkrAbJVV9srzXqAI18xUOSNl0DzlXrN7LM0BY= +go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= @@ -480,6 +514,7 @@ golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -493,6 +528,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -500,8 +536,10 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/p2p/transport/websocket/.gitignore b/p2p/transport/websocket/.gitignore new file mode 100644 index 0000000000..ef1aab0d0e --- /dev/null +++ b/p2p/transport/websocket/.gitignore @@ -0,0 +1 @@ +tools diff --git a/p2p/transport/websocket/LICENSE-APACHE b/p2p/transport/websocket/LICENSE-APACHE deleted file mode 100644 index 14478a3b60..0000000000 --- a/p2p/transport/websocket/LICENSE-APACHE +++ /dev/null @@ -1,5 +0,0 @@ -Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/p2p/transport/websocket/LICENSE-MIT b/p2p/transport/websocket/LICENSE-MIT deleted file mode 100644 index 72dc60d84b..0000000000 --- a/p2p/transport/websocket/LICENSE-MIT +++ /dev/null @@ -1,19 +0,0 @@ -The MIT License (MIT) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/p2p/transport/websocket/addrs.go b/p2p/transport/websocket/addrs.go index 5fea8567b5..9538a65e11 100644 --- a/p2p/transport/websocket/addrs.go +++ b/p2p/transport/websocket/addrs.go @@ -173,3 +173,15 @@ func parseWebsocketMultiaddr(a ma.Multiaddr) (parsedWebsocketMultiaddr, error) { return out, nil } + +func (pwma *parsedWebsocketMultiaddr) toMultiaddr() ma.Multiaddr { + if !pwma.isWSS { + return pwma.restMultiaddr.Encapsulate(wsComponent) + } + + if pwma.sni == nil { + return pwma.restMultiaddr.Encapsulate(tlsComponent).Encapsulate(wsComponent) + } + + return pwma.restMultiaddr.Encapsulate(tlsComponent).Encapsulate(pwma.sni).Encapsulate(wsComponent) +} diff --git a/p2p/transport/websocket/addrs_test.go b/p2p/transport/websocket/addrs_test.go index e2779fd462..1a73c28762 100644 --- a/p2p/transport/websocket/addrs_test.go +++ b/p2p/transport/websocket/addrs_test.go @@ -4,8 +4,6 @@ import ( "net/url" "testing" - "github.com/stretchr/testify/require" - ma "github.com/multiformats/go-multiaddr" ) @@ -67,15 +65,3 @@ func TestConvertWebsocketMultiaddrToNetAddr(t *testing.T) { t.Fatalf("expected network: \"websocket\", got \"%s\"", wsaddr.Network()) } } - -func TestListeningOnDNSAddr(t *testing.T) { - ln, err := newListener(ma.StringCast("/dns/localhost/tcp/0/ws"), nil) - require.NoError(t, err) - addr := ln.Multiaddr() - first, rest := ma.SplitFirst(addr) - require.Equal(t, first.Protocol().Code, ma.P_DNS) - require.Equal(t, first.Value(), "localhost") - next, _ := ma.SplitFirst(rest) - require.Equal(t, next.Protocol().Code, ma.P_TCP) - require.NotEqual(t, next.Value(), "0") -} diff --git a/p2p/transport/websocket/browser_integration_browser_test.go b/p2p/transport/websocket/browser_integration_browser_test.go new file mode 100644 index 0000000000..ce256a4a70 --- /dev/null +++ b/p2p/transport/websocket/browser_integration_browser_test.go @@ -0,0 +1,60 @@ +//go:build js + +package websocket + +import ( + "bufio" + "context" + "testing" + "time" + + "github.com/libp2p/go-libp2p/p2p/muxer/yamux" + tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" + ma "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" +) + +func TestInBrowser(t *testing.T) { + id, mux := newSecureMuxer(t) + u, err := tptu.New(mux, []tptu.StreamMuxer{{ID: "/yamux", Muxer: yamux.DefaultTransport}}, nil, nil, nil) + require.NoError(t, err) + tpt, err := New(u, nil) + require.NoError(t, err) + addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555/ws") + if err != nil { + t.Fatal("could not parse multiaddress:" + err.Error()) + } + conn, err := tpt.Dial(context.Background(), addr, id) + if err != nil { + t.Fatal("could not dial server:" + err.Error()) + } + defer conn.Close() + + stream, err := conn.AcceptStream() + if err != nil { + t.Fatal("could not accept stream:" + err.Error()) + } + defer stream.Close() + + buf := bufio.NewReader(stream) + msg, err := buf.ReadString('\n') + if err != nil { + t.Fatal("could not read ping message:" + err.Error()) + } + expected := "ping\n" + if msg != expected { + t.Fatalf("Received wrong message. Expected %q but got %q", expected, msg) + } + + _, err = stream.Write([]byte("pong\n")) + if err != nil { + t.Fatal("could not write pong message:" + err.Error()) + } + + // TODO(albrow): This hack is necessary in order to give the reader time to + // finish. As soon as this test function returns, the browser window is + // closed, which means there is no time for the other end of the connection to + // read the "pong" message. We should find some way to remove this hack if + // possible. + time.Sleep(1 * time.Second) +} diff --git a/p2p/transport/websocket/browser_integration_native_test.go b/p2p/transport/websocket/browser_integration_native_test.go new file mode 100644 index 0000000000..677625890a --- /dev/null +++ b/p2p/transport/websocket/browser_integration_native_test.go @@ -0,0 +1,95 @@ +//go:build !js && !windows + +package websocket + +import ( + "bufio" + "context" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + + "github.com/libp2p/go-libp2p/p2p/muxer/yamux" + tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" + ma "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" +) + +var ( + wasmBrowserTestBin = "wasmbrowsertest" + wasmBrowserTestDir = filepath.Join("tools", "bin") + wasmBrowserTestPackage = "github.com/agnivade/wasmbrowsertest" +) + +// TestInBrowser is a harness that allows us to use `go test` in order to run +// WebAssembly tests in a headless browser. +func TestInBrowser(t *testing.T) { + // ensure we have the right tools. + err := os.MkdirAll(wasmBrowserTestDir, 0755) + + t.Logf("building %s", wasmBrowserTestPackage) + if err != nil && !os.IsExist(err) { + t.Fatal(err) + } + + cmd := exec.Command( + "go", "build", + "-o", wasmBrowserTestBin, + "github.com/agnivade/wasmbrowsertest", + ) + cmd.Dir = wasmBrowserTestDir + err = cmd.Run() + if err != nil { + t.Fatal(err) + } + t.Log("starting server") + + // Start a transport which the browser peer will dial. + serverDoneSignal := make(chan struct{}) + go func() { + defer func() { + close(serverDoneSignal) + }() + _, mux := newSecureMuxer(t) + u, err := tptu.New(mux, []tptu.StreamMuxer{{ID: "/yamux", Muxer: yamux.DefaultTransport}}, nil, nil, nil) + require.NoError(t, err, "SERVER") + tpt, err := New(u, nil) + require.NoError(t, err, "SERVER") + addr, err := ma.NewMultiaddr("/ip4/127.0.0.1/tcp/5555/ws") + require.NoError(t, err, "SERVER") + listener, err := tpt.Listen(addr) + require.NoError(t, err, "SERVER") + conn, err := listener.Accept() + require.NoError(t, err, "SERVER") + defer conn.Close() + stream, err := conn.OpenStream(context.Background()) + require.NoError(t, err, "SERVER, could not open stream") + defer stream.Close() + buf := bufio.NewReader(stream) + _, err = stream.Write([]byte("ping\n")) + require.NoError(t, err, "SERVER") + msg, err := buf.ReadString('\n') + require.NoError(t, err, "SERVER, could not read pong message") + require.Equal(t, "pong\n", msg) + }() + + t.Log("starting browser") + + cmd = exec.Command( + "go", "test", "-v", + "-exec", filepath.Join(wasmBrowserTestDir, wasmBrowserTestBin), + "-run", "TestInBrowser", + ".", + ) + cmd.Env = append(os.Environ(), []string{"GOOS=js", "GOARCH=wasm"}...) + output, err := cmd.CombinedOutput() + if err != nil { + formattedOutput := "\t" + strings.Join(strings.Split(string(output), "\n"), "\n\t") + t.Log("BROWSER OUTPUT:\n", formattedOutput) + t.Fatal("BROWSER:", err) + } + + <-serverDoneSignal +} diff --git a/p2p/transport/websocket/conn.go b/p2p/transport/websocket/conn.go index 30b70055d0..76fa1e11ca 100644 --- a/p2p/transport/websocket/conn.go +++ b/p2p/transport/websocket/conn.go @@ -1,158 +1,19 @@ package websocket import ( - "io" "net" - "sync" "time" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/transport" - - ws "github.com/gorilla/websocket" ) // GracefulCloseTimeout is the time to wait trying to gracefully close a // connection before simply cutting it. var GracefulCloseTimeout = 100 * time.Millisecond -// Conn implements net.Conn interface for gorilla/websocket. -type Conn struct { - *ws.Conn - secure bool - DefaultMessageType int - reader io.Reader - closeOnce sync.Once - - readLock, writeLock sync.Mutex -} - var _ net.Conn = (*Conn)(nil) -// NewConn creates a Conn given a regular gorilla/websocket Conn. -func NewConn(raw *ws.Conn, secure bool) *Conn { - return &Conn{ - Conn: raw, - secure: secure, - DefaultMessageType: ws.BinaryMessage, - } -} - -func (c *Conn) Read(b []byte) (int, error) { - c.readLock.Lock() - defer c.readLock.Unlock() - - if c.reader == nil { - if err := c.prepNextReader(); err != nil { - return 0, err - } - } - - for { - n, err := c.reader.Read(b) - switch err { - case io.EOF: - c.reader = nil - - if n > 0 { - return n, nil - } - - if err := c.prepNextReader(); err != nil { - return 0, err - } - - // explicitly looping - default: - return n, err - } - } -} - -func (c *Conn) prepNextReader() error { - t, r, err := c.Conn.NextReader() - if err != nil { - if wserr, ok := err.(*ws.CloseError); ok { - if wserr.Code == 1000 || wserr.Code == 1005 { - return io.EOF - } - } - return err - } - - if t == ws.CloseMessage { - return io.EOF - } - - c.reader = r - return nil -} - -func (c *Conn) Write(b []byte) (n int, err error) { - c.writeLock.Lock() - defer c.writeLock.Unlock() - - if err := c.Conn.WriteMessage(c.DefaultMessageType, b); err != nil { - return 0, err - } - - return len(b), nil -} - -// Close closes the connection. Only the first call to Close will receive the -// close error, subsequent and concurrent calls will return nil. -// This method is thread-safe. -func (c *Conn) Close() error { - var err error - c.closeOnce.Do(func() { - err1 := c.Conn.WriteControl( - ws.CloseMessage, - ws.FormatCloseMessage(ws.CloseNormalClosure, "closed"), - time.Now().Add(GracefulCloseTimeout), - ) - err2 := c.Conn.Close() - switch { - case err1 != nil: - err = err1 - case err2 != nil: - err = err2 - } - }) - return err -} - -func (c *Conn) LocalAddr() net.Addr { - return NewAddrWithScheme(c.Conn.LocalAddr().String(), c.secure) -} - -func (c *Conn) RemoteAddr() net.Addr { - return NewAddrWithScheme(c.Conn.RemoteAddr().String(), c.secure) -} - -func (c *Conn) SetDeadline(t time.Time) error { - if err := c.SetReadDeadline(t); err != nil { - return err - } - - return c.SetWriteDeadline(t) -} - -func (c *Conn) SetReadDeadline(t time.Time) error { - // Don't lock when setting the read deadline. That would prevent us from - // interrupting an in-progress read. - return c.Conn.SetReadDeadline(t) -} - -func (c *Conn) SetWriteDeadline(t time.Time) error { - // Unlike the read deadline, we need to lock when setting the write - // deadline. - - c.writeLock.Lock() - defer c.writeLock.Unlock() - - return c.Conn.SetWriteDeadline(t) -} - type capableConn struct { transport.CapableConn } diff --git a/p2p/transport/websocket/conn_browser.go b/p2p/transport/websocket/conn_browser.go new file mode 100644 index 0000000000..e2a434845f --- /dev/null +++ b/p2p/transport/websocket/conn_browser.go @@ -0,0 +1,304 @@ +//go:build js + +package websocket + +import ( + "bytes" + "errors" + "fmt" + "net" + "strings" + "sync" + "syscall/js" + "time" +) + +const ( + webSocketStateConnecting = 0 + webSocketStateOpen = 1 + webSocketStateClosing = 2 + webSocketStateClosed = 3 +) + +var errConnectionClosed = errors.New("connection is closed") + +// Conn implements net.Conn interface for WebSockets in js/wasm. +type Conn struct { + js.Value + messageHandler *js.Func + closeHandler *js.Func + errorHandler *js.Func + mut sync.Mutex + currDataMut sync.RWMutex + currData bytes.Buffer + closeOnce sync.Once + closeSignalOnce sync.Once + closeSignal chan struct{} + dataSignal chan struct{} + localAddr net.Addr + remoteAddr net.Addr + firstErr error // only read this _after_ observing that closeSignal has been closed. +} + +// NewConn creates a Conn given a regular js/wasm WebSocket Conn. +func NewConn(raw js.Value) *Conn { + conn := &Conn{ + Value: raw, + closeSignal: make(chan struct{}), + dataSignal: make(chan struct{}, 1), + localAddr: NewAddr("0.0.0.0:0"), + remoteAddr: getRemoteAddr(raw), + } + // Force the JavaScript WebSockets API to use the ArrayBuffer type for + // incoming messages instead of the Blob type. This is better for us because + // ArrayBuffer can be converted to []byte synchronously but Blob cannot. + conn.Set("binaryType", "arraybuffer") + conn.setUpHandlers() + return conn +} + +func (c *Conn) Read(b []byte) (int, error) { + select { + case <-c.closeSignal: + c.readAfterErr(b) + default: + } + + for { + c.currDataMut.RLock() + n, _ := c.currData.Read(b) + c.currDataMut.RUnlock() + + if n != 0 { + // Data was ready. Return the number of bytes read. + return n, nil + } + + // There is no data ready to be read. Wait for more data or for the + // connection to be closed. + select { + case <-c.dataSignal: + case <-c.closeSignal: + return c.readAfterErr(b) + } + } +} + +// readAfterError reads from c.currData. If there is no more data left it +// returns c.firstErr if non-nil and otherwise returns io.EOF. +func (c *Conn) readAfterErr(b []byte) (int, error) { + if c.firstErr != nil { + return 0, c.firstErr + } + c.currDataMut.RLock() + n, err := c.currData.Read(b) + c.currDataMut.RUnlock() + return n, err +} + +// checkOpen returns an error if the connection is not open. Otherwise, it +// returns nil. +func (c *Conn) checkOpen() error { + state := c.Get("readyState").Int() + switch state { + case webSocketStateClosed, webSocketStateClosing: + return errConnectionClosed + } + return nil +} + +func (c *Conn) Write(b []byte) (n int, err error) { + defer func() { + if e := recover(); e != nil { + err = recoveredValueToError(e) + } + }() + if err := c.checkOpen(); err != nil { + return 0, err + } + uint8Array := js.Global().Get("Uint8Array").New(len(b)) + if js.CopyBytesToJS(uint8Array, b) != len(b) { + panic("expected to copy all bytes") + } + c.Call("send", uint8Array.Get("buffer")) + return len(b), nil +} + +// Close closes the connection. Only the first call to Close will receive the +// close error, subsequent and concurrent calls will return nil. +// This method is thread-safe. +func (c *Conn) Close() error { + c.closeOnce.Do(func() { + c.Call("close") + c.signalClose(nil) + c.releaseHandlers() + }) + return nil +} + +func (c *Conn) signalClose(err error) { + c.closeSignalOnce.Do(func() { + c.firstErr = err + close(c.closeSignal) + }) +} + +func (c *Conn) releaseHandlers() { + c.mut.Lock() + defer c.mut.Unlock() + if c.messageHandler != nil { + c.Call("removeEventListener", "message", *c.messageHandler) + c.messageHandler.Release() + c.messageHandler = nil + } + if c.closeHandler != nil { + c.Call("removeEventListener", "close", *c.closeHandler) + c.closeHandler.Release() + c.closeHandler = nil + } + if c.errorHandler != nil { + c.Call("removeEventListener", "error", *c.errorHandler) + c.errorHandler.Release() + c.errorHandler = nil + } +} + +func (c *Conn) LocalAddr() net.Addr { + return c.localAddr +} + +func getRemoteAddr(val js.Value) net.Addr { + rawURL := val.Get("url").String() + withoutPrefix := strings.TrimPrefix(rawURL, "ws://") + withoutSuffix := strings.TrimSuffix(withoutPrefix, "/") + return NewAddr(withoutSuffix) +} + +func (c *Conn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +// TODO: Return os.ErrNoDeadline. For now we return nil because multiplexers +// don't handle the error correctly. +func (c *Conn) SetDeadline(t time.Time) error { + return nil +} + +func (c *Conn) SetReadDeadline(t time.Time) error { + return nil +} + +func (c *Conn) SetWriteDeadline(t time.Time) error { + return nil +} + +func (c *Conn) setUpHandlers() { + c.mut.Lock() + defer c.mut.Unlock() + if c.messageHandler != nil { + // Message handlers already created. Nothing to do. + return + } + messageHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + arrayBuffer := args[0].Get("data") + data := arrayBufferToBytes(arrayBuffer) + c.currDataMut.Lock() + if _, err := c.currData.Write(data); err != nil { + c.currDataMut.Unlock() + return err + } + c.currDataMut.Unlock() + + // Non-blocking signal + select { + case c.dataSignal <- struct{}{}: + default: + } + + return nil + }) + c.messageHandler = &messageHandler + c.Call("addEventListener", "message", messageHandler) + + closeHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + go func() { + c.signalClose(errorEventToError(args[0])) + c.releaseHandlers() + }() + return nil + }) + c.closeHandler = &closeHandler + c.Call("addEventListener", "close", closeHandler) + + errorHandler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + // Unfortunately, the "error" event doesn't appear to give us any useful + // information. All we can do is close the connection. + c.Close() + return nil + }) + c.errorHandler = &errorHandler + c.Call("addEventListener", "error", errorHandler) +} + +func (c *Conn) waitForOpen() error { + openSignal := make(chan struct{}) + handler := js.FuncOf(func(this js.Value, args []js.Value) interface{} { + close(openSignal) + return nil + }) + defer c.Call("removeEventListener", "open", handler) + defer handler.Release() + c.Call("addEventListener", "open", handler) + select { + case <-openSignal: + return nil + case <-c.closeSignal: + // c.closeSignal means there was an error when trying to open the + // connection. + return c.firstErr + } +} + +// arrayBufferToBytes converts a JavaScript ArrayBuffer to a slice of bytes. +func arrayBufferToBytes(buffer js.Value) []byte { + view := js.Global().Get("Uint8Array").New(buffer) + dataLen := view.Length() + data := make([]byte, dataLen) + if js.CopyBytesToGo(data, view) != dataLen { + panic("expected to copy all bytes") + } + return data +} + +func errorEventToError(val js.Value) error { + var typ string + if gotType := val.Get("type"); !gotType.Equal(js.Undefined()) { + typ = gotType.String() + } else { + typ = val.Type().String() + } + var reason string + if gotReason := val.Get("reason"); !gotReason.Equal(js.Undefined()) && gotReason.String() != "" { + reason = gotReason.String() + } else { + code := val.Get("code") + if !code.Equal(js.Undefined()) { + switch code := code.Int(); code { + case 1006: + reason = "code 1006: connection unexpectedly closed" + default: + reason = fmt.Sprintf("unexpected code: %d", code) + } + } + } + return fmt.Errorf("JavaScript error: (%s) %s", typ, reason) +} + +func recoveredValueToError(e interface{}) error { + switch e := e.(type) { + case error: + return e + default: + return fmt.Errorf("recovered from unexpected panic: %T %s", e, e) + } +} diff --git a/p2p/transport/websocket/conn_native.go b/p2p/transport/websocket/conn_native.go new file mode 100644 index 0000000000..e4d90233d3 --- /dev/null +++ b/p2p/transport/websocket/conn_native.go @@ -0,0 +1,149 @@ +//go:build !js + +package websocket + +import ( + "io" + "net" + "sync" + "time" + + ws "github.com/gorilla/websocket" +) + +// Conn implements net.Conn interface for gorilla/websocket. +type Conn struct { + *ws.Conn + secure bool + DefaultMessageType int + reader io.Reader + closeOnce sync.Once + + readLock, writeLock sync.Mutex +} + +var _ net.Conn = (*Conn)(nil) + +// NewConn creates a Conn given a regular gorilla/websocket Conn. +func NewConn(raw *ws.Conn, secure bool) *Conn { + return &Conn{ + Conn: raw, + secure: secure, + DefaultMessageType: ws.BinaryMessage, + } +} + +func (c *Conn) Read(b []byte) (int, error) { + c.readLock.Lock() + defer c.readLock.Unlock() + + if c.reader == nil { + if err := c.prepNextReader(); err != nil { + return 0, err + } + } + + for { + n, err := c.reader.Read(b) + switch err { + case io.EOF: + c.reader = nil + + if n > 0 { + return n, nil + } + + if err := c.prepNextReader(); err != nil { + return 0, err + } + + // explicitly looping + default: + return n, err + } + } +} + +func (c *Conn) prepNextReader() error { + t, r, err := c.Conn.NextReader() + if err != nil { + if wserr, ok := err.(*ws.CloseError); ok { + if wserr.Code == 1000 || wserr.Code == 1005 { + return io.EOF + } + } + return err + } + + if t == ws.CloseMessage { + return io.EOF + } + + c.reader = r + return nil +} + +func (c *Conn) Write(b []byte) (n int, err error) { + c.writeLock.Lock() + defer c.writeLock.Unlock() + + if err := c.Conn.WriteMessage(c.DefaultMessageType, b); err != nil { + return 0, err + } + + return len(b), nil +} + +// Close closes the connection. Only the first call to Close will receive the +// close error, subsequent and concurrent calls will return nil. +// This method is thread-safe. +func (c *Conn) Close() error { + var err error + c.closeOnce.Do(func() { + err1 := c.Conn.WriteControl( + ws.CloseMessage, + ws.FormatCloseMessage(ws.CloseNormalClosure, "closed"), + time.Now().Add(GracefulCloseTimeout), + ) + err2 := c.Conn.Close() + switch { + case err1 != nil: + err = err1 + case err2 != nil: + err = err2 + } + }) + return err +} + +func (c *Conn) LocalAddr() net.Addr { + return NewAddrWithScheme(c.Conn.LocalAddr().String(), c.secure) +} + +func (c *Conn) RemoteAddr() net.Addr { + return NewAddrWithScheme(c.Conn.RemoteAddr().String(), c.secure) +} + +func (c *Conn) SetDeadline(t time.Time) error { + if err := c.SetReadDeadline(t); err != nil { + return err + } + + return c.SetWriteDeadline(t) +} + +func (c *Conn) SetReadDeadline(t time.Time) error { + // Don't lock when setting the read deadline. That would prevent us from + // interrupting an in-progress read. + return c.Conn.SetReadDeadline(t) +} + +func (c *Conn) SetWriteDeadline(t time.Time) error { + // Unlike the read deadline, we need to lock when setting the write + // deadline. + + c.writeLock.Lock() + defer c.writeLock.Unlock() + + return c.Conn.SetWriteDeadline(t) +} diff --git a/p2p/transport/websocket/listener.go b/p2p/transport/websocket/listener.go index 514cfb1b54..42f3c43934 100644 --- a/p2p/transport/websocket/listener.go +++ b/p2p/transport/websocket/listener.go @@ -1,3 +1,5 @@ +//go:build !js + package websocket import ( @@ -25,18 +27,6 @@ type listener struct { incoming chan *Conn } -func (pwma *parsedWebsocketMultiaddr) toMultiaddr() ma.Multiaddr { - if !pwma.isWSS { - return pwma.restMultiaddr.Encapsulate(wsComponent) - } - - if pwma.sni == nil { - return pwma.restMultiaddr.Encapsulate(tlsComponent).Encapsulate(wsComponent) - } - - return pwma.restMultiaddr.Encapsulate(tlsComponent).Encapsulate(pwma.sni).Encapsulate(wsComponent) -} - // newListener creates a new listener from a raw net.Listener. // tlsConf may be nil (for unencrypted websockets). func newListener(a ma.Multiaddr, tlsConf *tls.Config) (*listener, error) { diff --git a/p2p/transport/websocket/listener_test.go b/p2p/transport/websocket/listener_test.go new file mode 100644 index 0000000000..04165154af --- /dev/null +++ b/p2p/transport/websocket/listener_test.go @@ -0,0 +1,22 @@ +//go:build !js + +package websocket + +import ( + "testing" + + ma "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" +) + +func TestListeningOnDNSAddr(t *testing.T) { + ln, err := newListener(ma.StringCast("/dns/localhost/tcp/0/ws"), nil) + require.NoError(t, err) + addr := ln.Multiaddr() + first, rest := ma.SplitFirst(addr) + require.Equal(t, first.Protocol().Code, ma.P_DNS) + require.Equal(t, first.Value(), "localhost") + next, _ := ma.SplitFirst(rest) + require.Equal(t, next.Protocol().Code, ma.P_TCP) + require.NotEqual(t, next.Value(), "0") +} diff --git a/p2p/transport/websocket/muxer_test.go b/p2p/transport/websocket/muxer_test.go new file mode 100644 index 0000000000..2ddadc05ae --- /dev/null +++ b/p2p/transport/websocket/muxer_test.go @@ -0,0 +1,27 @@ +package websocket + +import ( + "testing" + + "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/sec" + "github.com/libp2p/go-libp2p/core/test" + "github.com/libp2p/go-libp2p/p2p/security/noise" + "github.com/stretchr/testify/require" +) + +func newSecureMuxer(t *testing.T) (peer.ID, []sec.SecureTransport) { + t.Helper() + priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256) + if err != nil { + t.Fatal(err) + } + id, err := peer.IDFromPrivateKey(priv) + if err != nil { + t.Fatal(err) + } + noiseTpt, err := noise.New(noise.ID, priv, nil) + require.NoError(t, err) + return id, []sec.SecureTransport{noiseTpt} +} diff --git a/p2p/transport/websocket/tools.go b/p2p/transport/websocket/tools.go new file mode 100644 index 0000000000..be78c76a11 --- /dev/null +++ b/p2p/transport/websocket/tools.go @@ -0,0 +1,7 @@ +//go:build tools + +package websocket + +import ( + _ "github.com/agnivade/wasmbrowsertest" +) diff --git a/p2p/transport/websocket/websocket.go b/p2p/transport/websocket/websocket.go index e1965123d9..e471053da4 100644 --- a/p2p/transport/websocket/websocket.go +++ b/p2p/transport/websocket/websocket.go @@ -3,10 +3,6 @@ package websocket import ( "context" - "crypto/tls" - "net" - "net/http" - "time" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" @@ -15,8 +11,6 @@ import ( ma "github.com/multiformats/go-multiaddr" mafmt "github.com/multiformats/go-multiaddr-fmt" manet "github.com/multiformats/go-multiaddr/net" - - ws "github.com/gorilla/websocket" ) // WsFmt is multiaddr formatter for WsProtocol @@ -50,56 +44,19 @@ func init() { manet.RegisterToNetAddr(ConvertWebsocketMultiaddrToNetAddr, "wss") } -// Default gorilla upgrader -var upgrader = ws.Upgrader{ - // Allow requests from *all* origins. - CheckOrigin: func(r *http.Request) bool { - return true - }, -} +var _ transport.Transport = (*WebsocketTransport)(nil) type Option func(*WebsocketTransport) error -// WithTLSClientConfig sets a TLS client configuration on the WebSocket Dialer. Only -// relevant for non-browser usages. -// -// Some useful use cases include setting InsecureSkipVerify to `true`, or -// setting user-defined trusted CA certificates. -func WithTLSClientConfig(c *tls.Config) Option { - return func(t *WebsocketTransport) error { - t.tlsClientConf = c - return nil - } -} - -// WithTLSConfig sets a TLS configuration for the WebSocket listener. -func WithTLSConfig(conf *tls.Config) Option { - return func(t *WebsocketTransport) error { - t.tlsConf = conf - return nil - } -} - -// WebsocketTransport is the actual go-libp2p transport -type WebsocketTransport struct { - upgrader transport.Upgrader - rcmgr network.ResourceManager - - tlsClientConf *tls.Config - tlsConf *tls.Config -} - -var _ transport.Transport = (*WebsocketTransport)(nil) - func New(u transport.Upgrader, rcmgr network.ResourceManager, opts ...Option) (*WebsocketTransport, error) { if rcmgr == nil { rcmgr = &network.NullResourceManager{} } t := &WebsocketTransport{ - upgrader: u, - rcmgr: rcmgr, - tlsClientConf: &tls.Config{}, + upgrader: u, + rcmgr: rcmgr, } + t.osNew() for _, opt := range opts { if err := opt(t); err != nil { return nil, err @@ -180,67 +137,3 @@ func (t *WebsocketTransport) dialWithScope(ctx context.Context, raddr ma.Multiad } return &capableConn{CapableConn: conn}, nil } - -func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) { - wsurl, err := parseMultiaddr(raddr) - if err != nil { - return nil, err - } - isWss := wsurl.Scheme == "wss" - dialer := ws.Dialer{HandshakeTimeout: 30 * time.Second} - if isWss { - sni := "" - sni, err = raddr.ValueForProtocol(ma.P_SNI) - if err != nil { - sni = "" - } - - if sni != "" { - copytlsClientConf := t.tlsClientConf.Clone() - copytlsClientConf.ServerName = sni - dialer.TLSClientConfig = copytlsClientConf - ipAddr := wsurl.Host - // Setting the NetDial because we already have the resolved IP address, so we don't want to do another resolution. - // We set the `.Host` to the sni field so that the host header gets properly set. - dialer.NetDial = func(network, address string) (net.Conn, error) { - tcpAddr, err := net.ResolveTCPAddr(network, ipAddr) - if err != nil { - return nil, err - } - return net.DialTCP("tcp", nil, tcpAddr) - } - wsurl.Host = sni + ":" + wsurl.Port() - } else { - dialer.TLSClientConfig = t.tlsClientConf - } - } - - wscon, _, err := dialer.DialContext(ctx, wsurl.String(), nil) - if err != nil { - return nil, err - } - - mnc, err := manet.WrapNetConn(NewConn(wscon, isWss)) - if err != nil { - wscon.Close() - return nil, err - } - return mnc, nil -} - -func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) { - l, err := newListener(a, t.tlsConf) - if err != nil { - return nil, err - } - go l.serve() - return l, nil -} - -func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) { - malist, err := t.maListen(a) - if err != nil { - return nil, err - } - return &transportListener{Listener: t.upgrader.UpgradeListener(t, malist)}, nil -} diff --git a/p2p/transport/websocket/websocket_browser.go b/p2p/transport/websocket/websocket_browser.go new file mode 100644 index 0000000000..8403875986 --- /dev/null +++ b/p2p/transport/websocket/websocket_browser.go @@ -0,0 +1,47 @@ +//go:build js + +package websocket + +import ( + "context" + "errors" + "syscall/js" + + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/transport" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" +) + +// WebsocketTransport is the actual go-libp2p transport +type WebsocketTransport struct { + upgrader transport.Upgrader + rcmgr network.ResourceManager +} + +func (t *WebsocketTransport) osNew() {} + +func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) { + wsurl, err := parseMultiaddr(raddr) + if err != nil { + return nil, err + } + + rawConn := js.Global().Get("WebSocket").New(wsurl.String()) + conn := NewConn(rawConn) + if err := conn.waitForOpen(); err != nil { + conn.Close() + return nil, err + } + mnc, err := manet.WrapNetConn(conn) + if err != nil { + conn.Close() + return nil, err + } + + return mnc, nil +} + +func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) { + return nil, errors.New("Listen not implemented when GOOS=js.") +} diff --git a/p2p/transport/websocket/websocket_native.go b/p2p/transport/websocket/websocket_native.go new file mode 100644 index 0000000000..b63a7dca38 --- /dev/null +++ b/p2p/transport/websocket/websocket_native.go @@ -0,0 +1,123 @@ +//go:build !js + +package websocket + +import ( + "context" + "crypto/tls" + "net" + "net/http" + "time" + + ws "github.com/gorilla/websocket" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/transport" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" +) + +// Default gorilla upgrader +var upgrader = ws.Upgrader{ + // Allow requests from *all* origins. + CheckOrigin: func(r *http.Request) bool { + return true + }, +} + +// WithTLSClientConfig sets a TLS client configuration on the WebSocket Dialer. Only +// relevant for non-browser usages. +// +// Some useful use cases include setting InsecureSkipVerify to `true`, or +// setting user-defined trusted CA certificates. +func WithTLSClientConfig(c *tls.Config) Option { + return func(t *WebsocketTransport) error { + t.tlsClientConf = c + return nil + } +} + +// WithTLSConfig sets a TLS configuration for the WebSocket listener. +func WithTLSConfig(conf *tls.Config) Option { + return func(t *WebsocketTransport) error { + t.tlsConf = conf + return nil + } +} + +// WebsocketTransport is the actual go-libp2p transport +type WebsocketTransport struct { + upgrader transport.Upgrader + rcmgr network.ResourceManager + + tlsClientConf *tls.Config + tlsConf *tls.Config +} + +// osNew is for initialisation code that os specific. +func (t *WebsocketTransport) osNew() { + t.tlsClientConf = &tls.Config{} +} + +func (t *WebsocketTransport) maDial(ctx context.Context, raddr ma.Multiaddr) (manet.Conn, error) { + wsurl, err := parseMultiaddr(raddr) + if err != nil { + return nil, err + } + isWss := wsurl.Scheme == "wss" + dialer := ws.Dialer{HandshakeTimeout: 30 * time.Second} + if isWss { + sni := "" + sni, err = raddr.ValueForProtocol(ma.P_SNI) + if err != nil { + sni = "" + } + + if sni != "" { + copytlsClientConf := t.tlsClientConf.Clone() + copytlsClientConf.ServerName = sni + dialer.TLSClientConfig = copytlsClientConf + ipAddr := wsurl.Host + // Setting the NetDial because we already have the resolved IP address, so we don't want to do another resolution. + // We set the `.Host` to the sni field so that the host header gets properly set. + dialer.NetDial = func(network, address string) (net.Conn, error) { + tcpAddr, err := net.ResolveTCPAddr(network, ipAddr) + if err != nil { + return nil, err + } + return net.DialTCP("tcp", nil, tcpAddr) + } + wsurl.Host = sni + ":" + wsurl.Port() + } else { + dialer.TLSClientConfig = t.tlsClientConf + } + } + + wscon, _, err := dialer.DialContext(ctx, wsurl.String(), nil) + if err != nil { + return nil, err + } + + mnc, err := manet.WrapNetConn(NewConn(wscon, isWss)) + if err != nil { + wscon.Close() + return nil, err + } + return mnc, nil +} + +func (t *WebsocketTransport) maListen(a ma.Multiaddr) (manet.Listener, error) { + l, err := newListener(a, t.tlsConf) + if err != nil { + return nil, err + } + go l.serve() + return l, nil +} + +func (t *WebsocketTransport) Listen(a ma.Multiaddr) (transport.Listener, error) { + malist, err := t.maListen(a) + if err != nil { + return nil, err + } + return &transportListener{Listener: t.upgrader.UpgradeListener(t, malist)}, nil +} diff --git a/p2p/transport/websocket/websocket_test.go b/p2p/transport/websocket/websocket_test.go index aa06c62825..816daa6ce6 100644 --- a/p2p/transport/websocket/websocket_test.go +++ b/p2p/transport/websocket/websocket_test.go @@ -1,3 +1,5 @@ +//go:build !js + package websocket import ( @@ -28,7 +30,6 @@ import ( "github.com/libp2p/go-libp2p/core/transport" "github.com/libp2p/go-libp2p/p2p/muxer/yamux" tptu "github.com/libp2p/go-libp2p/p2p/net/upgrader" - "github.com/libp2p/go-libp2p/p2p/security/noise" ttransport "github.com/libp2p/go-libp2p/p2p/transport/testsuite" ma "github.com/multiformats/go-multiaddr" @@ -64,21 +65,6 @@ func newInsecureMuxer(t *testing.T) (peer.ID, []sec.SecureTransport) { return id, []sec.SecureTransport{insecure.NewWithIdentity(insecure.ID, id, priv)} } -func newSecureMuxer(t *testing.T) (peer.ID, []sec.SecureTransport) { - t.Helper() - priv, _, err := test.RandTestKeyPair(crypto.Ed25519, 256) - if err != nil { - t.Fatal(err) - } - id, err := peer.IDFromPrivateKey(priv) - if err != nil { - t.Fatal(err) - } - noiseTpt, err := noise.New(noise.ID, priv, nil) - require.NoError(t, err) - return id, []sec.SecureTransport{noiseTpt} -} - func lastComponent(t *testing.T, a ma.Multiaddr) ma.Multiaddr { t.Helper() _, wscomponent := ma.SplitLast(a) diff --git a/test-plans/go.mod b/test-plans/go.mod index c2db9bbaab..b7bc4e90b8 100644 --- a/test-plans/go.mod +++ b/test-plans/go.mod @@ -9,9 +9,13 @@ require ( ) 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 @@ -21,7 +25,11 @@ 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/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect @@ -34,6 +42,7 @@ require ( github.com/ipfs/go-log/v2 v2.5.1 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // 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 @@ -47,6 +56,7 @@ require ( github.com/libp2p/go-netroute v0.2.1 // indirect github.com/libp2p/go-reuseport v0.2.0 // indirect github.com/libp2p/go-yamux/v4 v4.0.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 diff --git a/test-plans/go.sum b/test-plans/go.sum index 07725bcf07..11236628c3 100644 --- a/test-plans/go.sum +++ b/test-plans/go.sum @@ -8,6 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/agnivade/wasmbrowsertest v0.7.0 h1:vgi7PKcYHBI13PBpefjtObJl3xQKYmcu3JbAQd4z79s= +github.com/agnivade/wasmbrowsertest v0.7.0/go.mod h1:kvkdPoZxkikAxwXLc0uW2c5iKhjP8//lmC9LA5hl8rU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= @@ -19,6 +21,16 @@ github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBT github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab h1:qzilDZlsMUMBpXySlekH7rgrrH/feo11uQoQxTY7Uk0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.8.6 h1:KobeeqR2dpfKSG1prS3Y6+FbffMmGC6xmAobRXA9QEQ= +github.com/chromedp/chromedp v0.8.6/go.mod h1:nBYHoD6YSNzrr82cIeuOzhw1Jo/s2o0QQ+ifTeoCZ+c= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= @@ -44,6 +56,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -57,11 +71,19 @@ github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwV github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-interpreter/wagon v0.6.0 h1:BBxDxjiJiHgw9EdkYXAWs8NHhwnazZ5P2EWBW5hFNWw= +github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -94,6 +116,7 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= @@ -107,6 +130,7 @@ github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7 github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -118,6 +142,8 @@ github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -139,6 +165,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= @@ -164,6 +192,8 @@ github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rB github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= @@ -225,6 +255,8 @@ github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -295,6 +327,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= @@ -383,6 +417,7 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -391,11 +426,14 @@ golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From 6544501ce10e88d0c8402259cefaee8ff8cd00eb Mon Sep 17 00:00:00 2001 From: Jorropo Date: Thu, 16 Mar 2023 16:27:33 +0100 Subject: [PATCH 3/4] chat/chat-browser-wasm: Add an example of running the browser. --- examples/chat-wasm-browser/.gitignore | 2 + examples/chat-wasm-browser/build.sh | 9 ++ examples/chat-wasm-browser/index.html | 18 ++++ examples/chat-wasm-browser/main.go | 128 ++++++++++++++++++++++++++ examples/chat-wasm-browser/serve.sh | 8 ++ examples/chat-wasm-browser/tool.go | 7 ++ examples/chat/chat.go | 21 +++-- examples/chat/chat_test.go | 6 +- examples/go.mod | 21 ++++- examples/go.sum | 92 +++++++++--------- test-plans/go.mod | 2 +- test-plans/go.sum | 6 +- 12 files changed, 257 insertions(+), 63 deletions(-) create mode 100644 examples/chat-wasm-browser/.gitignore create mode 100755 examples/chat-wasm-browser/build.sh create mode 100644 examples/chat-wasm-browser/index.html create mode 100644 examples/chat-wasm-browser/main.go create mode 100755 examples/chat-wasm-browser/serve.sh create mode 100644 examples/chat-wasm-browser/tool.go diff --git a/examples/chat-wasm-browser/.gitignore b/examples/chat-wasm-browser/.gitignore new file mode 100644 index 0000000000..d6f0eaff55 --- /dev/null +++ b/examples/chat-wasm-browser/.gitignore @@ -0,0 +1,2 @@ +main.wasm +wasm_exec.js diff --git a/examples/chat-wasm-browser/build.sh b/examples/chat-wasm-browser/build.sh new file mode 100755 index 0000000000..6502c28950 --- /dev/null +++ b/examples/chat-wasm-browser/build.sh @@ -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 diff --git a/examples/chat-wasm-browser/index.html b/examples/chat-wasm-browser/index.html new file mode 100644 index 0000000000..8d594c7092 --- /dev/null +++ b/examples/chat-wasm-browser/index.html @@ -0,0 +1,18 @@ + + + + + + + + + +

+
+

+ diff --git a/examples/chat-wasm-browser/main.go b/examples/chat-wasm-browser/main.go new file mode 100644 index 0000000000..77ab2b4bb9 --- /dev/null +++ b/examples/chat-wasm-browser/main.go @@ -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 {} +} diff --git a/examples/chat-wasm-browser/serve.sh b/examples/chat-wasm-browser/serve.sh new file mode 100755 index 0000000000..df469aab63 --- /dev/null +++ b/examples/chat-wasm-browser/serve.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e +x + +cd "$(dirname "$0")" + +. ./build.sh + +go run github.com/Jorropo/jhttp diff --git a/examples/chat-wasm-browser/tool.go b/examples/chat-wasm-browser/tool.go new file mode 100644 index 0000000000..3b7343a24f --- /dev/null +++ b/examples/chat-wasm-browser/tool.go @@ -0,0 +1,7 @@ +//go:build tools + +package tools + +import ( + _ "github.com/Jorropo/jhttp" +) diff --git a/examples/chat/chat.go b/examples/chat/chat.go index 7276dfddae..c5139d40cc 100644 --- a/examples/chat/chat.go +++ b/examples/chat/chat.go @@ -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") @@ -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 { @@ -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 { @@ -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. @@ -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. @@ -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() diff --git a/examples/chat/chat_test.go b/examples/chat/chat_test.go index 1a1cea3c7e..0bf78824ee 100644 --- a/examples/chat/chat_test.go +++ b/examples/chat/chat_test.go @@ -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 @@ -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 diff --git a/examples/go.mod b/examples/go.mod index c38f9711cd..a6abc7b822 100644 --- a/examples/go.mod +++ b/examples/go.mod @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 ) diff --git a/examples/go.sum b/examples/go.sum index 6016ca1341..ade3672f2f 100644 --- a/examples/go.sum +++ b/examples/go.sum @@ -8,6 +8,10 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Jorropo/jhttp v1.0.0 h1:jL811wlH64Jx+Qyjav5+GMuxyqgD7ZqaQPC/HlB/LFo= +github.com/Jorropo/jhttp v1.0.0/go.mod h1:jnz/jn9HRMEZmeMxA4q1xqV6tlm4QK1ONex7fQ+58FU= +github.com/agnivade/wasmbrowsertest v0.7.0 h1:vgi7PKcYHBI13PBpefjtObJl3xQKYmcu3JbAQd4z79s= +github.com/agnivade/wasmbrowsertest v0.7.0/go.mod h1:kvkdPoZxkikAxwXLc0uW2c5iKhjP8//lmC9LA5hl8rU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= @@ -20,6 +24,16 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chromedp/cdproto v0.0.0-20220924210414-0e3390be1777/go.mod h1:5Y4sD/eXpwrChIuxhSr/G20n9CdbCmoerOHnuAf0Zr0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab h1:qzilDZlsMUMBpXySlekH7rgrrH/feo11uQoQxTY7Uk0= +github.com/chromedp/cdproto v0.0.0-20221108233440-fad8339618ab/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.8.6 h1:KobeeqR2dpfKSG1prS3Y6+FbffMmGC6xmAobRXA9QEQ= +github.com/chromedp/chromedp v0.8.6/go.mod h1:nBYHoD6YSNzrr82cIeuOzhw1Jo/s2o0QQ+ifTeoCZ+c= +github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY= +github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic= +github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= @@ -45,6 +59,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= +github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -61,28 +77,19 @@ github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnX github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= -github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-interpreter/wagon v0.6.0 h1:BBxDxjiJiHgw9EdkYXAWs8NHhwnazZ5P2EWBW5hFNWw= +github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= -github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= -github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= -github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= -github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.1.0 h1:7RFti/xnNkMJnrK7D1yQ/iCIB5OrrY/54/H930kIbHA= +github.com/gobwas/ws v1.1.0/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -102,7 +109,6 @@ github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+Licev github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -127,12 +133,12 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b h1:Qcx5LM0fSiks9uCyFZwDBUasd3lxd1RM0GYpL+Li5o4= github.com/google/pprof v0.0.0-20230405160723-4a4c7d95572b/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= @@ -143,8 +149,8 @@ github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -158,6 +164,7 @@ github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7 github.com/huin/goupnp v1.1.0 h1:gEe0Dp/lZmPZiDFzJJaOfUpOvv2MKUkoBX8lDrn9vKU= github.com/huin/goupnp v1.1.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= @@ -184,16 +191,15 @@ github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPw github.com/jbenet/goprocess v0.1.4 h1:DRGOFReOMqqDNXwW70QkacFW0YN9QnwLV0Vqk+3oU0o= github.com/jbenet/goprocess v0.1.4/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -212,16 +218,14 @@ github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80 h1:6Yzfa6GP0rIo/kULo2bwGEkFvCePZ3qHDDTC3/J9Swo= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= -github.com/libp2p/go-libp2p v0.27.1 h1:k1u6RHsX3hqKnslDjsSgLNURxJ3O1atIZCY4gpMbbus= -github.com/libp2p/go-libp2p v0.27.1/go.mod h1:FAvvfQa/YOShUYdiSS03IR9OXzkcJXwcNA2FUCh9ImE= github.com/libp2p/go-libp2p-asn-util v0.3.0 h1:gMDcMyYiZKkocGXDQ5nsUQyquC9+H+iLEQHwOCZ7s8s= github.com/libp2p/go-libp2p-asn-util v0.3.0/go.mod h1:B1mcOrKUE35Xq/ASTmQ4tN3LNzVVaMNmq2NACuqyB9w= github.com/libp2p/go-libp2p-kad-dht v0.20.1-0.20230209220319-6c2045abad23 h1:VGjUg3I0gLugtjZKI4kzvPiptmfYhWHK6YGBI4ZI2Cs= @@ -247,9 +251,10 @@ github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= @@ -271,12 +276,8 @@ github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8Rv github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -324,6 +325,8 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/ github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde h1:x0TT0RDC7UhAVbbWWBzr41ElhJx5tXPWkIHA2HWPRuw= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -354,8 +357,8 @@ github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8G github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA= -github.com/quic-go/webtransport-go v0.5.2 h1:GA6Bl6oZY+g/flt00Pnu0XtivSD8vukOu3lYhJjnGEk= -github.com/quic-go/webtransport-go v0.5.2/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= +github.com/quic-go/webtransport-go v0.5.3 h1:5XMlzemqB4qmOlgIus5zB45AcZ2kCgCy2EptUrfOPWU= +github.com/quic-go/webtransport-go v0.5.3/go.mod h1:OhmmgJIzTTqXK5xvtuX0oBpLV2GkLWNDA+UeTGJXErU= github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -406,13 +409,11 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= -github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI= +github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= @@ -433,8 +434,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924 h1:zEdVPfgkrAbJVV9srzXqAI18xUOSNl0DzlXrN7LM0BY= +go.uber.org/fx v1.19.2-0.20230112190306-5e90c3828924/go.mod h1:bGK+AEy7XUwTBkqCsK/vDyFF0JJOA6X5KWpNC0e6qTA= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= @@ -518,15 +519,16 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190306220234-b354f8bf4d9e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -534,7 +536,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -542,14 +546,12 @@ golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXR golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -633,7 +635,5 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/test-plans/go.mod b/test-plans/go.mod index b7bc4e90b8..22929c0677 100644 --- a/test-plans/go.mod +++ b/test-plans/go.mod @@ -91,7 +91,7 @@ require ( github.com/spaolacci/murmur3 v1.1.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.20230313180341-24e8b1a9eda8 // 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 diff --git a/test-plans/go.sum b/test-plans/go.sum index 11236628c3..010ca7b1d4 100644 --- a/test-plans/go.sum +++ b/test-plans/go.sum @@ -325,7 +325,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI= github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A= @@ -341,8 +341,8 @@ go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/dig v1.16.1 h1:+alNIBsl0qfY0j6epRubp/9obgtrObRAc5aD+6jbWY8= go.uber.org/dig v1.16.1/go.mod h1:557JTAUZT5bUK0SvCwikmLPPtdQhfvLYtO5tJgQSbnk= -go.uber.org/fx v1.19.2 h1:SyFgYQFr1Wl0AYstE8vyYIzP4bFz2URrScjwC4cwUvY= -go.uber.org/fx v1.19.2/go.mod h1:43G1VcqSzbIv77y00p1DRAsyZS8WdzuYdhZXmEUkMyQ= +go.uber.org/fx v1.19.2-0.20230313180341-24e8b1a9eda8 h1:Jns98MHiAGFjsO4OiHzmGQv/W6bnwx+nBILUlKWlr9U= +go.uber.org/fx v1.19.2-0.20230313180341-24e8b1a9eda8/go.mod h1:w2HrQg26ql9fLK7hlBiZ6JsRUKV+Lj/atT1KCjT8YhM= go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= From a1b84d0b90dccfdef23f5e0c5bd8ced7cb14d6d5 Mon Sep 17 00:00:00 2001 From: Jorropo Date: Sun, 21 May 2023 02:18:51 +0200 Subject: [PATCH 4/4] transport/webtransport: add support for WASM --- defaults_browser.go | 4 +- examples/chat/chat.go | 28 ++- examples/chat/chat_test.go | 6 +- go.mod | 2 +- p2p/transport/webtransport/cert_manager.go | 2 + .../webtransport/cert_manager_test.go | 2 + p2p/transport/webtransport/conn_browser.go | 115 +++++++++ .../webtransport/{conn.go => conn_native.go} | 2 + .../{listener.go => listener_native.go} | 2 + p2p/transport/webtransport/stream_browser.go | 169 +++++++++++++ .../{stream.go => stream_native.go} | 2 + p2p/transport/webtransport/transport.go | 224 ++--------------- .../webtransport/transport_browser.go | 156 ++++++++++++ .../webtransport/transport_native.go | 231 ++++++++++++++++++ 14 files changed, 725 insertions(+), 220 deletions(-) create mode 100644 p2p/transport/webtransport/conn_browser.go rename p2p/transport/webtransport/{conn.go => conn_native.go} (99%) rename p2p/transport/webtransport/{listener.go => listener_native.go} (99%) create mode 100644 p2p/transport/webtransport/stream_browser.go rename p2p/transport/webtransport/{stream.go => stream_native.go} (98%) create mode 100644 p2p/transport/webtransport/transport_browser.go create mode 100644 p2p/transport/webtransport/transport_native.go diff --git a/defaults_browser.go b/defaults_browser.go index 468c7240da..760001f8ea 100644 --- a/defaults_browser.go +++ b/defaults_browser.go @@ -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. diff --git a/examples/chat/chat.go b/examples/chat/chat.go index c5139d40cc..21c3c509ad 100644 --- a/examples/chat/chat.go +++ b/examples/chat/chat.go @@ -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") @@ -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 { @@ -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 { @@ -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. @@ -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. @@ -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 + } } } @@ -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") diff --git a/examples/chat/chat_test.go b/examples/chat/chat_test.go index 0bf78824ee..d621f84a28 100644 --- a/examples/chat/chat_test.go +++ b/examples/chat/chat_test.go @@ -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 @@ -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 diff --git a/go.mod b/go.mod index b0b320504d..2f9a631831 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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 diff --git a/p2p/transport/webtransport/cert_manager.go b/p2p/transport/webtransport/cert_manager.go index d48a0aa537..524c67e4a5 100644 --- a/p2p/transport/webtransport/cert_manager.go +++ b/p2p/transport/webtransport/cert_manager.go @@ -1,3 +1,5 @@ +//go:build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/cert_manager_test.go b/p2p/transport/webtransport/cert_manager_test.go index 29999a8fe0..2c4f8c620d 100644 --- a/p2p/transport/webtransport/cert_manager_test.go +++ b/p2p/transport/webtransport/cert_manager_test.go @@ -1,3 +1,5 @@ +//go:build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/conn_browser.go b/p2p/transport/webtransport/conn_browser.go new file mode 100644 index 0000000000..1ed18acf1b --- /dev/null +++ b/p2p/transport/webtransport/conn_browser.go @@ -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"} +} diff --git a/p2p/transport/webtransport/conn.go b/p2p/transport/webtransport/conn_native.go similarity index 99% rename from p2p/transport/webtransport/conn.go rename to p2p/transport/webtransport/conn_native.go index 5815d9b6a4..c951c7c56c 100644 --- a/p2p/transport/webtransport/conn.go +++ b/p2p/transport/webtransport/conn_native.go @@ -1,3 +1,5 @@ +//go:build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/listener.go b/p2p/transport/webtransport/listener_native.go similarity index 99% rename from p2p/transport/webtransport/listener.go rename to p2p/transport/webtransport/listener_native.go index 337239fa8a..cdb202008f 100644 --- a/p2p/transport/webtransport/listener.go +++ b/p2p/transport/webtransport/listener_native.go @@ -1,3 +1,5 @@ +//go:build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/stream_browser.go b/p2p/transport/webtransport/stream_browser.go new file mode 100644 index 0000000000..cef9f12c71 --- /dev/null +++ b/p2p/transport/webtransport/stream_browser.go @@ -0,0 +1,169 @@ +//go:build js + +package libp2pwebtransport + +import ( + "context" + "io" + "net" + "sync" + "sync/atomic" + "syscall/js" + "time" + + "go.uber.org/multierr" +) + +type stream struct { + conn *conn + read, write js.Value + readMux sync.Mutex + readBuf js.Value + done bool + + deadline, readDeadline, writeDeadline atomic.Pointer[time.Time] +} + +func newStream(s js.Value, c *conn) *stream { + return &stream{ + read: s.Get("readable").Call("getReader"), + write: s.Get("writable").Call("getWriter"), + readBuf: js.Global().Get("Uint8Array").New(0), + conn: c, + } +} + +func (s *stream) Read(b []byte) (n int, err error) { + if len(b) == 0 { + // We use a zero copy as dectection of an empty array bellow, so we need a + // a non empty destination. + return + } + + s.readMux.Lock() + defer s.readMux.Unlock() + if s.done { + return 0, io.EOF + } + n = js.CopyBytesToGo(b, s.readBuf) + if n != 0 { + s.readBuf = s.readBuf.Call("slice", n) + return + } + + ctx := context.Background() + var deadline, readDeadline time.Time + if t := s.deadline.Load(); t != nil { + deadline = *t + } + if t := s.readDeadline.Load(); t != nil { + readDeadline = *t + } + switch { + case deadline.IsZero() && !readDeadline.IsZero(): + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, readDeadline) + defer cancel() + case !deadline.IsZero(): + max := deadline + if deadline.Before(readDeadline) { + max = readDeadline + } + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, max) + defer cancel() + } + + r, err := await(ctx, s.read.Call("read")) + if err != nil { + return 0, err + } + o := r[0] + s.readBuf = o.Get("value") + s.done = o.Get("done").Bool() + + if s.done && s.readBuf.IsUndefined() { + return 0, io.EOF + } + + n = js.CopyBytesToGo(b, s.readBuf) + s.readBuf = s.readBuf.Call("slice", n) + return +} + +func (s *stream) Write(b []byte) (n int, err error) { + if len(b) == 0 { + return + } + + ctx := context.Background() + var deadline, writeDeadline time.Time + if t := s.deadline.Load(); t != nil { + deadline = *t + } + if t := s.writeDeadline.Load(); t != nil { + writeDeadline = *t + } + switch { + case deadline.IsZero() && !writeDeadline.IsZero(): + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, writeDeadline) + defer cancel() + case !deadline.IsZero(): + max := deadline + if deadline.Before(writeDeadline) { + max = writeDeadline + } + var cancel context.CancelFunc + ctx, cancel = context.WithDeadline(ctx, max) + defer cancel() + } + + _, err = await(ctx, s.write.Call("write", byteSliceToJS(b))) + if err != nil { + return 0, err + } + + return len(b), nil +} + +func (s *stream) Reset() error { + return multierr.Combine(s.resetWrite(), s.CloseRead()) +} + +func (s *stream) Close() error { + return multierr.Combine(s.CloseWrite(), s.CloseRead()) +} + +func (s *stream) CloseWrite() error { + _, err := await(context.Background(), s.write.Call("close")) + return err +} + +func (s *stream) resetWrite() error { + _, err := await(context.Background(), s.write.Call("abort")) + return err +} + +func (s *stream) CloseRead() error { + _, err := await(context.Background(), s.read.Call("cancel")) + return err +} + +func (s *stream) RemoteAddr() net.Addr { return s.conn.RemoteAddr() } +func (s *stream) LocalAddr() net.Addr { return s.conn.LocalAddr() } + +func (s *stream) SetDeadline(t time.Time) error { + s.deadline.Store(&t) + return nil +} + +func (s *stream) SetReadDeadline(t time.Time) error { + s.readDeadline.Store(&t) + return nil +} + +func (s *stream) SetWriteDeadline(t time.Time) error { + s.writeDeadline.Store(&t) + return nil +} diff --git a/p2p/transport/webtransport/stream.go b/p2p/transport/webtransport/stream_native.go similarity index 98% rename from p2p/transport/webtransport/stream.go rename to p2p/transport/webtransport/stream_native.go index 0849fc9f38..54ecbc3fb5 100644 --- a/p2p/transport/webtransport/stream.go +++ b/p2p/transport/webtransport/stream_native.go @@ -1,3 +1,5 @@ +//go:build !js + package libp2pwebtransport import ( diff --git a/p2p/transport/webtransport/transport.go b/p2p/transport/webtransport/transport.go index 9e8ebcaca3..0916a1ea90 100644 --- a/p2p/transport/webtransport/transport.go +++ b/p2p/transport/webtransport/transport.go @@ -3,13 +3,9 @@ package libp2pwebtransport import ( "bytes" "context" - "crypto/tls" - "crypto/x509" "errors" "fmt" - "io" - "sync" - "sync/atomic" + "net" "time" "github.com/libp2p/go-libp2p/core/connmgr" @@ -17,19 +13,15 @@ import ( "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/pnet" + "github.com/libp2p/go-libp2p/core/sec" tpt "github.com/libp2p/go-libp2p/core/transport" "github.com/libp2p/go-libp2p/p2p/security/noise" "github.com/libp2p/go-libp2p/p2p/security/noise/pb" - "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" "github.com/benbjohnson/clock" logging "github.com/ipfs/go-log/v2" ma "github.com/multiformats/go-multiaddr" - manet "github.com/multiformats/go-multiaddr/net" "github.com/multiformats/go-multihash" - "github.com/quic-go/quic-go" - "github.com/quic-go/quic-go/http3" - "github.com/quic-go/webtransport-go" ) var log = logging.Logger("webtransport") @@ -49,44 +41,9 @@ func WithClock(cl clock.Clock) Option { } } -// WithTLSClientConfig sets a custom tls.Config used for dialing. -// This option is most useful for setting a custom tls.Config.RootCAs certificate pool. -// When dialing a multiaddr that contains a /certhash component, this library will set InsecureSkipVerify and -// overwrite the VerifyPeerCertificate callback. -func WithTLSClientConfig(c *tls.Config) Option { - return func(t *transport) error { - t.tlsClientConf = c - return nil - } -} - -type transport struct { - privKey ic.PrivKey - pid peer.ID - clock clock.Clock - - connManager *quicreuse.ConnManager - rcmgr network.ResourceManager - gater connmgr.ConnectionGater - - listenOnce sync.Once - listenOnceErr error - certManager *certManager - hasCertManager atomic.Bool // set to true once the certManager is initialized - staticTLSConf *tls.Config - tlsClientConf *tls.Config - - noise *noise.Transport - - connMx sync.Mutex - conns map[uint64]*conn // using quic-go's ConnectionTracingKey as map key -} - var _ tpt.Transport = &transport{} -var _ tpt.Resolver = &transport{} -var _ io.Closer = &transport{} -func New(key ic.PrivKey, psk pnet.PSK, connManager *quicreuse.ConnManager, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, opts ...Option) (tpt.Transport, error) { +func new(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, beforeOpt func(*transport), opts ...Option) (*transport, error) { if len(psk) > 0 { log.Error("WebTransport doesn't support private networks yet.") return nil, errors.New("WebTransport doesn't support private networks yet") @@ -99,13 +56,14 @@ func New(key ic.PrivKey, psk pnet.PSK, connManager *quicreuse.ConnManager, gater return nil, err } t := &transport{ - pid: id, - privKey: key, - rcmgr: rcmgr, - gater: gater, - clock: clock.New(), - connManager: connManager, - conns: map[uint64]*conn{}, + pid: id, + privKey: key, + rcmgr: rcmgr, + gater: gater, + clock: clock.New(), + } + if beforeOpt != nil { + beforeOpt(t) } for _, opt := range opts { if err := opt(t); err != nil { @@ -137,11 +95,6 @@ func (t *transport) Dial(ctx context.Context, raddr ma.Multiaddr, p peer.ID) (tp } func (t *transport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p peer.ID, scope network.ConnManagementScope) (tpt.CapableConn, error) { - _, addr, err := manet.DialArgs(raddr) - if err != nil { - return nil, err - } - url := fmt.Sprintf("https://%s%s?type=noise", addr, webtransportHTTPEndpoint) certHashes, err := extractCertHashes(raddr) if err != nil { return nil, err @@ -159,82 +112,10 @@ func (t *transport) dialWithScope(ctx context.Context, raddr ma.Multiaddr, p pee } maddr, _ := ma.SplitFunc(raddr, func(c ma.Component) bool { return c.Protocol().Code == ma.P_WEBTRANSPORT }) - sess, err := t.dial(ctx, maddr, url, sni, certHashes) - if err != nil { - return nil, err - } - sconn, err := t.upgrade(ctx, sess, p, certHashes) - if err != nil { - sess.CloseWithError(1, "") - return nil, err - } - if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, sconn) { - sess.CloseWithError(errorCodeConnectionGating, "") - return nil, fmt.Errorf("secured connection gated") - } - conn := newConn(t, sess, sconn, scope) - t.addConn(sess, conn) - return conn, nil + return t.dial(ctx, maddr, p, sni, certHashes, scope) } -func (t *transport) dial(ctx context.Context, addr ma.Multiaddr, url, sni string, certHashes []multihash.DecodedMultihash) (*webtransport.Session, error) { - var tlsConf *tls.Config - if t.tlsClientConf != nil { - tlsConf = t.tlsClientConf.Clone() - } else { - tlsConf = &tls.Config{} - } - tlsConf.NextProtos = append(tlsConf.NextProtos, http3.NextProtoH3) - - if sni != "" { - tlsConf.ServerName = sni - } - - if len(certHashes) > 0 { - // This is not insecure. We verify the certificate ourselves. - // See https://www.w3.org/TR/webtransport/#certificate-hashes. - tlsConf.InsecureSkipVerify = true - tlsConf.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error { - return verifyRawCerts(rawCerts, certHashes) - } - } - conn, err := t.connManager.DialQUIC(ctx, addr, tlsConf, t.allowWindowIncrease) - if err != nil { - return nil, err - } - dialer := webtransport.Dialer{ - RoundTripper: &http3.RoundTripper{ - Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { - return conn.(quic.EarlyConnection), nil - }, - }, - } - rsp, sess, err := dialer.Dial(ctx, url, nil) - if err != nil { - return nil, err - } - if rsp.StatusCode < 200 || rsp.StatusCode > 299 { - return nil, fmt.Errorf("invalid response status code: %d", rsp.StatusCode) - } - return sess, err -} - -func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p peer.ID, certHashes []multihash.DecodedMultihash) (*connSecurityMultiaddrs, error) { - local, err := toWebtransportMultiaddr(sess.LocalAddr()) - if err != nil { - return nil, fmt.Errorf("error determining local addr: %w", err) - } - remote, err := toWebtransportMultiaddr(sess.RemoteAddr()) - if err != nil { - return nil, fmt.Errorf("error determining remote addr: %w", err) - } - - str, err := sess.OpenStreamSync(ctx) - if err != nil { - return nil, err - } - defer str.Close() - +func (t *transport) verifyChallengeOnOutboundConnection(ctx context.Context, conn net.Conn, p peer.ID, certHashes []multihash.DecodedMultihash) (sec.SecureConn, error) { // Now run a Noise handshake (using early data) and get all the certificate hashes from the server. // We will verify that the certhashes we used to dial is a subset of the certhashes we received from the server. var verified bool @@ -261,7 +142,7 @@ func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p p if err != nil { return nil, fmt.Errorf("failed to create Noise transport: %w", err) } - c, err := n.SecureOutbound(ctx, &webtransportStream{Stream: str, wsess: sess}, p) + c, err := n.SecureOutbound(ctx, conn, p) if err != nil { return nil, err } @@ -273,10 +154,8 @@ func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p p if !verified { return nil, errors.New("didn't verify") } - return &connSecurityMultiaddrs{ - ConnSecurity: c, - ConnMultiaddrs: &connMultiaddrs{local: local, remote: remote}, - }, nil + + return c, nil } func decodeCertHashesFromProtobuf(b [][]byte) ([]multihash.DecodedMultihash, error) { @@ -296,37 +175,6 @@ func (t *transport) CanDial(addr ma.Multiaddr) bool { return ok } -func (t *transport) Listen(laddr ma.Multiaddr) (tpt.Listener, error) { - isWebTransport, _ := IsWebtransportMultiaddr(laddr) - if !isWebTransport { - return nil, fmt.Errorf("cannot listen on non-WebTransport addr: %s", laddr) - } - if t.staticTLSConf == nil { - t.listenOnce.Do(func() { - t.certManager, t.listenOnceErr = newCertManager(t.privKey, t.clock) - t.hasCertManager.Store(true) - }) - if t.listenOnceErr != nil { - return nil, t.listenOnceErr - } - } else { - return nil, errors.New("static TLS config not supported on WebTransport") - } - tlsConf := t.staticTLSConf.Clone() - if tlsConf == nil { - tlsConf = &tls.Config{GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { - return t.certManager.GetConfig(), nil - }} - } - tlsConf.NextProtos = append(tlsConf.NextProtos, http3.NextProtoH3) - - ln, err := t.connManager.ListenQUIC(laddr, tlsConf, t.allowWindowIncrease) - if err != nil { - return nil, err - } - return newListener(ln, t, t.staticTLSConf != nil) -} - func (t *transport) Protocols() []int { return []int{ma.P_WEBTRANSPORT} } @@ -335,37 +183,6 @@ func (t *transport) Proxy() bool { return false } -func (t *transport) Close() error { - t.listenOnce.Do(func() {}) - if t.certManager != nil { - return t.certManager.Close() - } - return nil -} - -func (t *transport) allowWindowIncrease(conn quic.Connection, size uint64) bool { - t.connMx.Lock() - defer t.connMx.Unlock() - - c, ok := t.conns[conn.Context().Value(quic.ConnectionTracingKey).(uint64)] - if !ok { - return false - } - return c.allowWindowIncrease(size) -} - -func (t *transport) addConn(sess *webtransport.Session, c *conn) { - t.connMx.Lock() - t.conns[sess.Context().Value(quic.ConnectionTracingKey).(uint64)] = c - t.connMx.Unlock() -} - -func (t *transport) removeConn(sess *webtransport.Session) { - t.connMx.Lock() - delete(t.conns, sess.Context().Value(quic.ConnectionTracingKey).(uint64)) - t.connMx.Unlock() -} - // extractSNI returns what the SNI should be for the given maddr. If there is an // SNI component in the multiaddr, then it will be returned and // foundSniComponent will be true. If there's no SNI component, but there is a @@ -407,12 +224,3 @@ func (t *transport) Resolve(_ context.Context, maddr ma.Multiaddr) ([]ma.Multiad } return []ma.Multiaddr{beforeQuicMA.Encapsulate(quicComponent).Encapsulate(sniComponent).Encapsulate(afterQuicMA)}, nil } - -// AddCertHashes adds the current certificate hashes to a multiaddress. -// If called before Listen, it's a no-op. -func (t *transport) AddCertHashes(m ma.Multiaddr) (ma.Multiaddr, bool) { - if !t.hasCertManager.Load() { - return m, false - } - return m.Encapsulate(t.certManager.AddrComponent()), true -} diff --git a/p2p/transport/webtransport/transport_browser.go b/p2p/transport/webtransport/transport_browser.go new file mode 100644 index 0000000000..1a72ab2545 --- /dev/null +++ b/p2p/transport/webtransport/transport_browser.go @@ -0,0 +1,156 @@ +//go:build js + +package libp2pwebtransport + +import ( + "context" + "errors" + "fmt" + "syscall/js" + + "github.com/benbjohnson/clock" + "github.com/libp2p/go-libp2p/core/connmgr" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/pnet" + tpt "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/security/noise" + + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/multiformats/go-multihash" + "go.uber.org/multierr" +) + +type transport struct { + privKey ic.PrivKey + pid peer.ID + clock clock.Clock + + rcmgr network.ResourceManager + gater connmgr.ConnectionGater + + noise *noise.Transport +} + +func New(key ic.PrivKey, psk pnet.PSK, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, opts ...Option) (*transport, error) { + return new(key, psk, gater, rcmgr, nil, opts...) +} + +func (t *transport) dial(ctx context.Context, tgt ma.Multiaddr, p peer.ID, sni string, certHashes []multihash.DecodedMultihash, scope network.ConnManagementScope) (tpt.CapableConn, error) { + webtransport := js.Global().Get("WebTransport") + if webtransport.IsUndefined() { + return nil, fmt.Errorf("WebTransport is not supported in your browser") + } + + var raddr string + if sni != "" { + raddr = sni + } else { + var err error + _, raddr, err = manet.DialArgs(tgt) + if err != nil { + return nil, err + } + } + + url := fmt.Sprintf("https://%s%s?type=noise", raddr, webtransportHTTPEndpoint) + + ch := make([]any, len(certHashes)) + for i, h := range certHashes { + if h.Code != multihash.SHA2_256 { + // https://developer.mozilla.org/en-US/docs/Web/API/WebTransport/WebTransport#parameters + // At time of writing, SHA-256 is the only hash algorithm listed in the specification. + continue + } + + ch[i] = map[string]any{ + "algorithm": "sha-256", + "value": byteSliceToJS(h.Digest), + } + } + + wt := webtransport.New(url, map[string]any{"serverCertificateHashes": ch}) + _, err := await(ctx, wt.Get("ready")) + if err != nil { + return nil, fmt.Errorf("initial connection: %w", err) + } + + c := newConn(scope, t, wt, tgt, p, addr{url}) + + s, err := c.openStream(ctx) + if err != nil { + c.Close() + return nil, err + } + defer s.Close() + + verified, err := t.verifyChallengeOnOutboundConnection(ctx, s, p, certHashes) + if err != nil { + c.Close() + return nil, fmt.Errorf("verifying challenge: %w", err) + } + c.rpk = verified.RemotePublicKey() + + return c, nil +} + +func (t *transport) Listen(a ma.Multiaddr) (tpt.Listener, error) { + return nil, errors.New("Listen not implemented when GOOS=js.") +} + +// await tries to await a piece of code, it will leave the promise in an undefined +// state if the context is canceled or expires. +func await(ctx context.Context, v js.Value) (success []js.Value, err error) { + // This does not look very efficient but I don't care about performance right + // now and this makes the code WAY more readable than callback hell. + c := make(chan struct{}, 1) + var s, f js.Func + s = js.FuncOf(func(_ js.Value, args []js.Value) any { + success = args + c <- struct{}{} + s.Release() + f.Release() + return nil + }) + f = js.FuncOf(func(_ js.Value, args []js.Value) any { + errs := make([]error, len(args)) + for i, v := range args { + errs[i] = errors.New(v.String()) + } + err = fmt.Errorf("JS catch: %w", multierr.Combine(errs...)) + c <- struct{}{} + s.Release() + f.Release() + return nil + }) + + // Here we create an adhoc promise that we will race against the real one. + // This allows us to callback into s which will Release s and f. Removing + // references to v and hopefully allowing the JS GC to cancel the promise. + var resolve js.Value + capture := js.FuncOf(func(_ js.Value, args []js.Value) any { + resolve = args[0] + return nil + }) + promises := js.Global().Get("Promise") + stopper := promises.New(capture) + capture.Release() + promises.Call("race", []any{v, stopper}).Call("then", s, f) + select { + case <-ctx.Done(): + resolve.Invoke() // This will trigger s and cleanup in a thread safe manner. + return nil, ctx.Err() + case <-c: + return + } +} + +func byteSliceToJS(buf []byte) js.Value { + uint8Array := js.Global().Get("Uint8Array").New(len(buf)) + if js.CopyBytesToJS(uint8Array, buf) != len(buf) { + panic("expected to copy all bytes") + } + return uint8Array +} diff --git a/p2p/transport/webtransport/transport_native.go b/p2p/transport/webtransport/transport_native.go new file mode 100644 index 0000000000..ce63cb3253 --- /dev/null +++ b/p2p/transport/webtransport/transport_native.go @@ -0,0 +1,231 @@ +//go:build !js + +package libp2pwebtransport + +import ( + "context" + "crypto/tls" + "crypto/x509" + "errors" + "fmt" + "io" + "sync" + "sync/atomic" + + "github.com/libp2p/go-libp2p/core/connmgr" + ic "github.com/libp2p/go-libp2p/core/crypto" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/libp2p/go-libp2p/core/pnet" + tpt "github.com/libp2p/go-libp2p/core/transport" + "github.com/libp2p/go-libp2p/p2p/security/noise" + "github.com/libp2p/go-libp2p/p2p/transport/quicreuse" + + "github.com/benbjohnson/clock" + ma "github.com/multiformats/go-multiaddr" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/multiformats/go-multihash" + "github.com/quic-go/quic-go" + "github.com/quic-go/quic-go/http3" + "github.com/quic-go/webtransport-go" +) + +// WithTLSClientConfig sets a custom tls.Config used for dialing. +// This option is most useful for setting a custom tls.Config.RootCAs certificate pool. +// When dialing a multiaddr that contains a /certhash component, this library will set InsecureSkipVerify and +// overwrite the VerifyPeerCertificate callback. +func WithTLSClientConfig(c *tls.Config) Option { + return func(t *transport) error { + t.tlsClientConf = c + return nil + } +} + +var _ tpt.Resolver = &transport{} +var _ io.Closer = &transport{} + +type transport struct { + privKey ic.PrivKey + pid peer.ID + clock clock.Clock + + connManager *quicreuse.ConnManager + rcmgr network.ResourceManager + gater connmgr.ConnectionGater + + listenOnce sync.Once + listenOnceErr error + certManager *certManager + hasCertManager atomic.Bool // set to true once the certManager is initialized + staticTLSConf *tls.Config + tlsClientConf *tls.Config + + noise *noise.Transport + + connMx sync.Mutex + conns map[uint64]*conn // using quic-go's ConnectionTracingKey as map key +} + +func New(key ic.PrivKey, psk pnet.PSK, connManager *quicreuse.ConnManager, gater connmgr.ConnectionGater, rcmgr network.ResourceManager, opts ...Option) (tpt.Transport, error) { + return new(key, psk, gater, rcmgr, func(t *transport) { + t.connManager = connManager + t.conns = map[uint64]*conn{} + }, opts...) +} + +func (t *transport) Close() error { + t.listenOnce.Do(func() {}) + if t.certManager != nil { + return t.certManager.Close() + } + return nil +} + +func (t *transport) dial(ctx context.Context, addr ma.Multiaddr, p peer.ID, sni string, certHashes []multihash.DecodedMultihash, scope network.ConnManagementScope) (tpt.CapableConn, error) { + _, raddr, err := manet.DialArgs(addr) + if err != nil { + return nil, err + } + url := fmt.Sprintf("https://%s%s?type=noise", raddr, webtransportHTTPEndpoint) + + var tlsConf *tls.Config + if t.tlsClientConf != nil { + tlsConf = t.tlsClientConf.Clone() + } else { + tlsConf = &tls.Config{} + } + tlsConf.NextProtos = append(tlsConf.NextProtos, http3.NextProtoH3) + + if sni != "" { + tlsConf.ServerName = sni + } + + if len(certHashes) > 0 { + // This is not insecure. We verify the certificate ourselves. + // See https://www.w3.org/TR/webtransport/#certificate-hashes. + tlsConf.InsecureSkipVerify = true + tlsConf.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error { + return verifyRawCerts(rawCerts, certHashes) + } + } + qconn, err := t.connManager.DialQUIC(ctx, addr, tlsConf, t.allowWindowIncrease) + if err != nil { + return nil, err + } + dialer := webtransport.Dialer{ + RoundTripper: &http3.RoundTripper{ + Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { + return qconn.(quic.EarlyConnection), nil + }, + }, + } + rsp, sess, err := dialer.Dial(ctx, url, nil) + if err != nil { + return nil, err + } + if rsp.StatusCode < 200 || rsp.StatusCode > 299 { + return nil, fmt.Errorf("invalid response status code: %d", rsp.StatusCode) + } + + sconn, err := t.upgrade(ctx, sess, p, certHashes) + if err != nil { + sess.CloseWithError(1, "") + return nil, err + } + if t.gater != nil && !t.gater.InterceptSecured(network.DirOutbound, p, sconn) { + sess.CloseWithError(errorCodeConnectionGating, "") + return nil, fmt.Errorf("secured connection gated") + } + conn := newConn(t, sess, sconn, scope) + t.addConn(sess, conn) + return conn, nil +} + +func (t *transport) upgrade(ctx context.Context, sess *webtransport.Session, p peer.ID, certHashes []multihash.DecodedMultihash) (*connSecurityMultiaddrs, error) { + local, err := toWebtransportMultiaddr(sess.LocalAddr()) + if err != nil { + return nil, fmt.Errorf("error determining local addr: %w", err) + } + remote, err := toWebtransportMultiaddr(sess.RemoteAddr()) + if err != nil { + return nil, fmt.Errorf("error determining remote addr: %w", err) + } + + str, err := sess.OpenStreamSync(ctx) + if err != nil { + return nil, err + } + defer str.Close() + + c, err := t.verifyChallengeOnOutboundConnection(ctx, &webtransportStream{Stream: str, wsess: sess}, p, certHashes) + if err != nil { + return nil, err + } + return &connSecurityMultiaddrs{ + ConnSecurity: c, + ConnMultiaddrs: &connMultiaddrs{local: local, remote: remote}, + }, nil +} + +func (t *transport) Listen(laddr ma.Multiaddr) (tpt.Listener, error) { + isWebTransport, _ := IsWebtransportMultiaddr(laddr) + if !isWebTransport { + return nil, fmt.Errorf("cannot listen on non-WebTransport addr: %s", laddr) + } + if t.staticTLSConf == nil { + t.listenOnce.Do(func() { + t.certManager, t.listenOnceErr = newCertManager(t.privKey, t.clock) + t.hasCertManager.Store(true) + }) + if t.listenOnceErr != nil { + return nil, t.listenOnceErr + } + } else { + return nil, errors.New("static TLS config not supported on WebTransport") + } + tlsConf := t.staticTLSConf.Clone() + if tlsConf == nil { + tlsConf = &tls.Config{GetConfigForClient: func(*tls.ClientHelloInfo) (*tls.Config, error) { + return t.certManager.GetConfig(), nil + }} + } + tlsConf.NextProtos = append(tlsConf.NextProtos, http3.NextProtoH3) + + ln, err := t.connManager.ListenQUIC(laddr, tlsConf, t.allowWindowIncrease) + if err != nil { + return nil, err + } + return newListener(ln, t, t.staticTLSConf != nil) +} + +func (t *transport) allowWindowIncrease(conn quic.Connection, size uint64) bool { + t.connMx.Lock() + defer t.connMx.Unlock() + + c, ok := t.conns[conn.Context().Value(quic.ConnectionTracingKey).(uint64)] + if !ok { + return false + } + return c.allowWindowIncrease(size) +} + +func (t *transport) addConn(sess *webtransport.Session, c *conn) { + t.connMx.Lock() + t.conns[sess.Context().Value(quic.ConnectionTracingKey).(uint64)] = c + t.connMx.Unlock() +} + +func (t *transport) removeConn(sess *webtransport.Session) { + t.connMx.Lock() + delete(t.conns, sess.Context().Value(quic.ConnectionTracingKey).(uint64)) + t.connMx.Unlock() +} + +// AddCertHashes adds the current certificate hashes to a multiaddress. +// If called before Listen, it's a no-op. +func (t *transport) AddCertHashes(m ma.Multiaddr) (ma.Multiaddr, bool) { + if !t.hasCertManager.Load() { + return m, false + } + return m.Encapsulate(t.certManager.AddrComponent()), true +}