diff --git a/config/config.go b/config/config.go index 59cdbee576..cca27d0214 100644 --- a/config/config.go +++ b/config/config.go @@ -4,6 +4,7 @@ import ( "context" "crypto/rand" "fmt" + "github.com/libp2p/go-libp2p-core/introspection" "time" "github.com/libp2p/go-libp2p-core/connmgr" @@ -44,6 +45,13 @@ type NATManagerC func(network.Network) bhost.NATManager type RoutingC func(host.Host) (routing.PeerRouting, error) +// IntrospectorC represents an introspector constructor. +type IntrospectorC func(host.Host, metrics.Reporter) (introspection.Introspector, error) + +// IntrospectionEndpointC is a type that represents an introspect.Endpoint +// constructor. +type IntrospectionEndpointC func(introspection.Introspector) (introspection.Endpoint, error) + // AutoNATConfig defines the AutoNAT behavior for the libp2p host. type AutoNATConfig struct { ForceReachability *network.Reachability @@ -92,6 +100,9 @@ type Config struct { EnableAutoRelay bool AutoNATConfig StaticRelays []peer.AddrInfo + + Introspector IntrospectorC + IntrospectionEndpoint IntrospectionEndpointC } func (cfg *Config) makeSwarm(ctx context.Context) (*swarm.Swarm, error) { @@ -185,13 +196,15 @@ func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) { return nil, err } - h, err := bhost.NewHost(ctx, swrm, &bhost.HostOpts{ + opts := &bhost.HostOpts{ ConnManager: cfg.ConnManager, AddrsFactory: cfg.AddrsFactory, NATManager: cfg.NATManager, EnablePing: !cfg.DisablePing, UserAgent: cfg.UserAgent, - }) + } + + h, err := bhost.NewHost(ctx, swrm, opts) if err != nil { swrm.Close() @@ -340,6 +353,30 @@ func (cfg *Config) NewNode(ctx context.Context) (host.Host, error) { return nil, fmt.Errorf("cannot enable autorelay; autonat failed to start: %v", err) } + if cfg.Introspector != nil { + var ( + introspector introspection.Introspector + endpoint introspection.Endpoint + err error + ) + + if introspector, err = cfg.Introspector(h, cfg.Reporter); err != nil { + h.Close() + return nil, fmt.Errorf("failed to create introspector: %w", err) + } + + if cfg.IntrospectionEndpoint != nil { + if endpoint, err = cfg.IntrospectionEndpoint(introspector); err != nil { + h.Close() + return nil, fmt.Errorf("failed to create introspection endpoint: %w", err) + } + } + if err := h.SetIntrospection(introspector, endpoint); err != nil { + h.Close() + return nil, fmt.Errorf("failed to set introspection objects on host: %w", err) + } + } + // start the host background tasks h.Start() diff --git a/go.mod b/go.mod index bf09aee29c..fd07def206 100644 --- a/go.mod +++ b/go.mod @@ -3,46 +3,46 @@ module github.com/libp2p/go-libp2p go 1.12 require ( - github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f // indirect + github.com/benbjohnson/clock v1.0.2 github.com/gogo/protobuf v1.3.1 - github.com/ipfs/go-cid v0.0.5 + github.com/gorilla/websocket v1.4.2 + github.com/ipfs/go-cid v0.0.6 github.com/ipfs/go-detect-race v0.0.1 github.com/ipfs/go-ipfs-util v0.0.1 github.com/ipfs/go-log v1.0.4 + github.com/ipfs/go-log/v2 v2.1.1 github.com/jbenet/go-cienv v0.1.0 github.com/jbenet/goprocess v0.1.4 github.com/libp2p/go-conn-security-multistream v0.2.0 - github.com/libp2p/go-eventbus v0.1.0 + github.com/libp2p/go-eventbus v0.2.0 github.com/libp2p/go-libp2p-autonat v0.2.3 - github.com/libp2p/go-libp2p-blankhost v0.1.6 - github.com/libp2p/go-libp2p-circuit v0.2.2 - github.com/libp2p/go-libp2p-core v0.5.6 + github.com/libp2p/go-libp2p-blankhost v0.1.4 + github.com/libp2p/go-libp2p-circuit v0.2.3 + github.com/libp2p/go-libp2p-core v0.6.0 github.com/libp2p/go-libp2p-discovery v0.4.0 + github.com/libp2p/go-libp2p-introspector v0.0.4 github.com/libp2p/go-libp2p-loggables v0.1.0 github.com/libp2p/go-libp2p-mplex v0.2.3 github.com/libp2p/go-libp2p-nat v0.0.6 github.com/libp2p/go-libp2p-netutil v0.1.0 github.com/libp2p/go-libp2p-peerstore v0.2.4 github.com/libp2p/go-libp2p-secio v0.2.2 - github.com/libp2p/go-libp2p-swarm v0.2.4 + github.com/libp2p/go-libp2p-swarm v0.2.7 github.com/libp2p/go-libp2p-testing v0.1.1 github.com/libp2p/go-libp2p-tls v0.1.3 github.com/libp2p/go-libp2p-transport-upgrader v0.3.0 - github.com/libp2p/go-libp2p-yamux v0.2.7 - github.com/libp2p/go-sockaddr v0.1.0 // indirect + github.com/libp2p/go-libp2p-yamux v0.2.8 github.com/libp2p/go-stream-muxer-multistream v0.3.0 github.com/libp2p/go-tcp-transport v0.2.0 github.com/libp2p/go-ws-transport v0.3.1 - github.com/libp2p/go-yamux v1.3.6 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-multiaddr v0.2.2 github.com/multiformats/go-multiaddr-dns v0.2.0 github.com/multiformats/go-multiaddr-net v0.1.5 - github.com/multiformats/go-multibase v0.0.2 // indirect github.com/multiformats/go-multistream v0.1.1 - github.com/stretchr/testify v1.5.1 + github.com/stretchr/testify v1.6.0 github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 - go.uber.org/zap v1.15.0 // indirect - golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 // indirect - golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476 // indirect - golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 // indirect + go.uber.org/zap v1.15.0 + golang.org/x/crypto v0.0.0-20200602180216-279210d13fed // indirect + golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 // indirect ) diff --git a/go.sum b/go.sum index 32a709ce9e..440baed14c 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,12 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +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/AndreasBriese/bbloom v0.0.0-20180913140656-343706a395b7/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= @@ -6,7 +14,12 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +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.0.2 h1:Z0CN0Yb4ig9sGPXkvAQcGJfnrrMQ5QYLCMPRi9iD7YE= +github.com/benbjohnson/clock v1.0.2/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190213025234-306aecffea32/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8= github.com/btcsuite/btcd v0.0.0-20190523000118-16327141da8c/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= @@ -20,13 +33,17 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= +github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -34,8 +51,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018 h1:6xT9KW8zLC5IlbaIF5Q7JNieBoACT7iW0YTxQHR0in0= github.com/davidlazar/go-crypto v0.0.0-20170701192655-dcfb0a7ac018/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= -github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f h1:BOaYiTvg8p9vBUXpklC22XSK/mifLF7lG9jtmYYi3Tc= -github.com/davidlazar/go-crypto v0.0.0-20190912175916-7055855a373f/go.mod h1:rQYf4tfk5sSwFsnDg3qYaBxSjsD9S8+59vW0dKUgme4= github.com/dgraph-io/badger v1.5.5-0.20190226225317-8115aed38f8f/go.mod h1:VZxzAIRPHRVNRKRo6AXrX9BJegn6il06VMTZVJYCIjQ= github.com/dgraph-io/badger v1.6.0-rc1/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= @@ -44,9 +59,16 @@ github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70d github.com/dgryski/go-farm v0.0.0-20190104051053-3adb47b1fb0f/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 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/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= @@ -54,23 +76,44 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +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= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +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.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/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/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -78,18 +121,20 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo= github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.2/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM= github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= -github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= +github.com/ipfs/go-cid v0.0.6 h1:go0y+GcDOGeJIV01FeBsta4FHngoA4Wz7KMeLkXAhMs= +github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.4.0/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA= @@ -107,14 +152,17 @@ github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1Y github.com/ipfs/go-ipfs-util v0.0.1 h1:Wz9bL2wB2YBJqggkA4dD7oSmqB4cAnpNbGrlHJulv50= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= +github.com/ipfs/go-log v1.0.1/go.mod h1:HuWlQttfN6FWNHRhlY5yMk/lW7evQC0HHGOxEwMRR8I= github.com/ipfs/go-log v1.0.2/go.mod h1:1MNjMxe0u6xvJZgeqbJ8vdo2TKaGwZ1a0Bpza+sr2Sk= github.com/ipfs/go-log v1.0.3/go.mod h1:OsLySYkwIbiSUR/yBTdv1qPtcE4FW3WPWk/ewz9Ru+A= github.com/ipfs/go-log v1.0.4 h1:6nLQdX4W8P9yZZFH7mO+X/PzjN8Laozm/lMJ6esdgzY= github.com/ipfs/go-log v1.0.4/go.mod h1:oDCg2FkjogeFOhqqb+N39l2RpTNPL6F/StPkB3kPgcs= +github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= -github.com/ipfs/go-log/v2 v2.0.5 h1:fL4YI+1g5V/b1Yxr1qAiXTMg1H8z9vx/VmJxBuQMHvU= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= +github.com/ipfs/go-log/v2 v2.1.1 h1:G4TtqN+V9y9HY9TA6BwbCVyyBZ2B9MbCjR2MtGx8FR0= +github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA= github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= @@ -129,9 +177,12 @@ github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsj github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= 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/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +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/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= @@ -144,9 +195,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 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/libp2p/go-addr-util v0.0.1 h1:TpTQm9cXVRVSKsYbgQ7GKc3KbbHVTnbostgGaDEP+88= github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ= github.com/libp2p/go-addr-util v0.0.2 h1:7cWK5cdA5x72jX0g8iLrQWm5TRJZ6CzGdPEhWj7plWU= github.com/libp2p/go-addr-util v0.0.2/go.mod h1:Ecd6Fb3yIuLzq4bD7VcywcVSBtefcAwnUISBM3WG15E= @@ -156,8 +207,9 @@ github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoR github.com/libp2p/go-conn-security-multistream v0.1.0/go.mod h1:aw6eD7LOsHEX7+2hJkDxw1MteijaVcI+/eP2/x3J1xc= github.com/libp2p/go-conn-security-multistream v0.2.0 h1:uNiDjS58vrvJTg9jO6bySd1rMKejieG7v45ekqHbZ1M= github.com/libp2p/go-conn-security-multistream v0.2.0/go.mod h1:hZN4MjlNetKD3Rq5Jb/P5ohUnFLNzEAR4DLSzpn2QLU= -github.com/libp2p/go-eventbus v0.1.0 h1:mlawomSAjjkk97QnYiEmHsLu7E136+2oCWSHRUvMfzQ= github.com/libp2p/go-eventbus v0.1.0/go.mod h1:vROgu5cs5T7cv7POWlWxBaVLxfSegC5UGQf8A2eEmx4= +github.com/libp2p/go-eventbus v0.2.0 h1:6tCKJBkJKVgAvy/0MLPvU55M3D1Y8uo2m8e1dPwUmOo= +github.com/libp2p/go-eventbus v0.2.0/go.mod h1:Ptlq8GCVEDe1ooZepb+k1RRy+KRAV55LuIvWg7zwoco= github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZxBdp967ls1g+k8= github.com/libp2p/go-flow-metrics v0.0.3 h1:8tAs/hSdNvUiLgtlSy3mxwxWP4I9y/jlkPFT7epKdeM= github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= @@ -168,40 +220,42 @@ github.com/libp2p/go-libp2p v0.8.3/go.mod h1:EsH1A+8yoWK+L4iKcbPYu6MPluZ+CHWI9El github.com/libp2p/go-libp2p-autonat v0.1.1/go.mod h1:OXqkeGOY2xJVWKAGV2inNF5aKN/djNA3fdpCWloIudE= github.com/libp2p/go-libp2p-autonat v0.2.0/go.mod h1:DX+9teU4pEEoZUqR1PiMlqliONQdNbfzE1C718tcViI= github.com/libp2p/go-libp2p-autonat v0.2.1/go.mod h1:MWtAhV5Ko1l6QBsHQNSuM6b1sRkXrpk0/LqCr+vCVxI= -github.com/libp2p/go-libp2p-autonat v0.2.2 h1:4dlgcEEugTFWSvdG2UIFxhnOMpX76QaZSRAtXmYB8n4= github.com/libp2p/go-libp2p-autonat v0.2.2/go.mod h1:HsM62HkqZmHR2k1xgX34WuWDzk/nBwNHoeyyT4IWV6A= github.com/libp2p/go-libp2p-autonat v0.2.3 h1:w46bKK3KTOUWDe5mDYMRjJu1uryqBp8HCNDp/TWMqKw= github.com/libp2p/go-libp2p-autonat v0.2.3/go.mod h1:2U6bNWCNsAG9LEbwccBDQbjzQ8Krdjge1jLTE9rdoMM= github.com/libp2p/go-libp2p-blankhost v0.1.1/go.mod h1:pf2fvdLJPsC1FsVrNP3DUUvMzUts2dsLLBEpo1vW1ro= +github.com/libp2p/go-libp2p-blankhost v0.1.4 h1:I96SWjR4rK9irDHcHq3XHN6hawCRTPUADzkJacgZLvk= github.com/libp2p/go-libp2p-blankhost v0.1.4/go.mod h1:oJF0saYsAXQCSfDq254GMNmLNz6ZTHTOvtF4ZydUvwU= -github.com/libp2p/go-libp2p-blankhost v0.1.6 h1:CkPp1/zaCrCnBo0AdsQA0O1VkUYoUOtyHOnoa8gKIcE= -github.com/libp2p/go-libp2p-blankhost v0.1.6/go.mod h1:jONCAJqEP+Z8T6EQviGL4JsQcLx1LgTGtVqFNY8EMfQ= github.com/libp2p/go-libp2p-circuit v0.1.4/go.mod h1:CY67BrEjKNDhdTk8UgBX1Y/H5c3xkAcs3gnksxY7osU= github.com/libp2p/go-libp2p-circuit v0.2.1/go.mod h1:BXPwYDN5A8z4OEY9sOfr2DUQMLQvKt/6oku45YUmjIo= -github.com/libp2p/go-libp2p-circuit v0.2.2 h1:87RLabJ9lrhoiSDDZyCJ80ZlI5TLJMwfyoGAaWXzWqA= github.com/libp2p/go-libp2p-circuit v0.2.2/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4= +github.com/libp2p/go-libp2p-circuit v0.2.3 h1:3Uw1fPHWrp1tgIhBz0vSOxRUmnKL8L/NGUyEd5WfSGM= +github.com/libp2p/go-libp2p-circuit v0.2.3/go.mod h1:nkG3iE01tR3FoQ2nMm06IUrCpCyJp1Eo4A1xYdpjfs4= github.com/libp2p/go-libp2p-core v0.0.1/go.mod h1:g/VxnTZ/1ygHxH3dKok7Vno1VfpvGcGip57wjTU4fco= github.com/libp2p/go-libp2p-core v0.0.4/go.mod h1:jyuCQP356gzfCFtRKyvAbNkyeuxb7OlyhWZ3nls5d2I= github.com/libp2p/go-libp2p-core v0.2.0/go.mod h1:X0eyB0Gy93v0DZtSYbEM7RnMChm9Uv3j7yRXjO77xSI= github.com/libp2p/go-libp2p-core v0.2.2/go.mod h1:8fcwTbsG2B+lTgRJ1ICZtiM5GWCWZVoVrLaDRvIRng0= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= +github.com/libp2p/go-libp2p-core v0.3.1-0.20200210163958-6d6f8284b841/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= github.com/libp2p/go-libp2p-core v0.3.1/go.mod h1:thvWy0hvaSBhnVBaW37BvzgVV68OUhgJJLAa6almrII= github.com/libp2p/go-libp2p-core v0.4.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.0/go.mod h1:49XGI+kc38oGVwqSBhDEwytaAxgZasHhFfQKibzTls0= github.com/libp2p/go-libp2p-core v0.5.1/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.2/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.3/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= github.com/libp2p/go-libp2p-core v0.5.4/go.mod h1:uN7L2D4EvPCvzSH5SrhR72UWbnSGpt5/a35Sm4upn4Y= -github.com/libp2p/go-libp2p-core v0.5.5 h1:/yiFUZDoBWqvpWeHHJ1iA8SOs5obT1/+UdNfckwD57M= github.com/libp2p/go-libp2p-core v0.5.5/go.mod h1:vj3awlOr9+GMZJFH9s4mpt9RHHgGqeHCopzbYKZdRjM= -github.com/libp2p/go-libp2p-core v0.5.6 h1:IxFH4PmtLlLdPf4fF/i129SnK/C+/v8WEX644MxhC48= github.com/libp2p/go-libp2p-core v0.5.6/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.5.7-0.20200520143746-39497bae12c5/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= +github.com/libp2p/go-libp2p-core v0.6.0 h1:u03qofNYTBN+yVg08PuAKylZogVf0xcTEeM8skGf+ak= +github.com/libp2p/go-libp2p-core v0.6.0/go.mod h1:txwbVEhHEXikXn9gfC7/UDDw7rkxuX0bJvM49Ykaswo= github.com/libp2p/go-libp2p-crypto v0.1.0/go.mod h1:sPUokVISZiy+nNuTTH/TY+leRSxnFj/2GLjtOTW90hI= github.com/libp2p/go-libp2p-discovery v0.2.0/go.mod h1:s4VGaxYMbw4+4+tsoQTqh7wfxg97AEdo4GYBt6BadWg= github.com/libp2p/go-libp2p-discovery v0.3.0/go.mod h1:o03drFnz9BVAZdzC/QUQ+NeQOu38Fu7LJGEOK2gQltw= github.com/libp2p/go-libp2p-discovery v0.4.0 h1:dK78UhopBk48mlHtRCzbdLm3q/81g77FahEBTjcqQT8= github.com/libp2p/go-libp2p-discovery v0.4.0/go.mod h1:bZ0aJSrFc/eX2llP0ryhb1kpgkPyTo23SJ5b7UQCMh4= +github.com/libp2p/go-libp2p-introspector v0.0.4 h1:eA5JtLI8FwFUzDRC8waCeNzDZYDbMHyvMrBcHDikXvY= +github.com/libp2p/go-libp2p-introspector v0.0.4/go.mod h1:SkaOljzZheY14Y1DA+b+DcpBPgxmlHkQqHLz/zO7g+w= github.com/libp2p/go-libp2p-loggables v0.1.0 h1:h3w8QFfCt2UJl/0/NW4K829HX/0S4KD31PQ7m8UXXO8= github.com/libp2p/go-libp2p-loggables v0.1.0/go.mod h1:EyumB2Y6PrYjr55Q3/tiJ/o3xoDasoRYM7nOzEpoa90= github.com/libp2p/go-libp2p-mplex v0.2.0/go.mod h1:Ejl9IyjvXJ0T9iqUTE1jpYATQ9NM3g+OtR+EMMODbKo= @@ -220,12 +274,13 @@ github.com/libp2p/go-libp2p-peerstore v0.1.3/go.mod h1:BJ9sHlm59/80oSkpWgr1MyY1c github.com/libp2p/go-libp2p-peerstore v0.2.0/go.mod h1:N2l3eVIeAitSg3Pi2ipSrJYnqhVnMNQZo9nkSCuAbnQ= github.com/libp2p/go-libp2p-peerstore v0.2.1/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= github.com/libp2p/go-libp2p-peerstore v0.2.2/go.mod h1:NQxhNjWxf1d4w6PihR8btWIRjwRLBr4TYKfNgrUkOPA= -github.com/libp2p/go-libp2p-peerstore v0.2.3 h1:MofRq2l3c15vQpEygTetV+zRRrncz+ktiXW7H2EKoEQ= github.com/libp2p/go-libp2p-peerstore v0.2.3/go.mod h1:K8ljLdFn590GMttg/luh4caB/3g0vKuY01psze0upRw= github.com/libp2p/go-libp2p-peerstore v0.2.4 h1:jU9S4jYN30kdzTpDAR7SlHUD+meDUjTODh4waLWF1ws= github.com/libp2p/go-libp2p-peerstore v0.2.4/go.mod h1:ss/TWTgHZTMpsU/oKVVPQCGuDHItOpf2W8RxAi50P2s= github.com/libp2p/go-libp2p-pnet v0.2.0 h1:J6htxttBipJujEjz1y0a5+eYoiPcFHhSYHH6na5f0/k= github.com/libp2p/go-libp2p-pnet v0.2.0/go.mod h1:Qqvq6JH/oMZGwqs3N1Fqhv8NVhrdYcO0BW4wssv21LA= +github.com/libp2p/go-libp2p-quic-transport v0.5.0 h1:BUN1lgYNUrtv4WLLQ5rQmC9MCJ6uEXusezGvYRNoJXE= +github.com/libp2p/go-libp2p-quic-transport v0.5.0/go.mod h1:IEcuC5MLxvZ5KuHKjRu+dr3LjCT1Be3rcD/4d8JrX8M= github.com/libp2p/go-libp2p-secio v0.1.0/go.mod h1:tMJo2w7h3+wN4pgU2LSYeiKPrfqBgkOsdiKK77hE7c8= github.com/libp2p/go-libp2p-secio v0.2.0/go.mod h1:2JdZepB8J5V9mBp79BmwsaPQhRPNN2NrnB2lKQcdy6g= github.com/libp2p/go-libp2p-secio v0.2.1/go.mod h1:cWtZpILJqkqrSkiYcDBh5lA3wbT2Q+hz3rJQq3iftD8= @@ -234,8 +289,8 @@ github.com/libp2p/go-libp2p-secio v0.2.2/go.mod h1:wP3bS+m5AUnFA+OFO7Er03uO1mncH github.com/libp2p/go-libp2p-swarm v0.1.0/go.mod h1:wQVsCdjsuZoc730CgOvh5ox6K8evllckjebkdiY5ta4= github.com/libp2p/go-libp2p-swarm v0.2.2/go.mod h1:fvmtQ0T1nErXym1/aa1uJEyN7JzaTNyBcHImCxRpPKU= github.com/libp2p/go-libp2p-swarm v0.2.3/go.mod h1:P2VO/EpxRyDxtChXz/VPVXyTnszHvokHKRhfkEgFKNM= -github.com/libp2p/go-libp2p-swarm v0.2.4 h1:94XL76/tFeTdJNcIGugi+1uZo5O/a7y4i21PirwbgZI= -github.com/libp2p/go-libp2p-swarm v0.2.4/go.mod h1:/xIpHFPPh3wmSthtxdGbkHZ0OET1h/GGZes8Wku/M5Y= +github.com/libp2p/go-libp2p-swarm v0.2.7 h1:4lV/sf7f0NuVqunOpt1I11+Z54+xp+m0eeAvxj/LyRc= +github.com/libp2p/go-libp2p-swarm v0.2.7/go.mod h1:ZSJ0Q+oq/B1JgfPHJAT2HTall+xYRNYp1xs4S2FBWKA= github.com/libp2p/go-libp2p-testing v0.0.2/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.3/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= github.com/libp2p/go-libp2p-testing v0.0.4/go.mod h1:gvchhf3FQOtBdr+eFUABet5a4MBLK8jM3V4Zghvmi+E= @@ -251,10 +306,10 @@ github.com/libp2p/go-libp2p-transport-upgrader v0.3.0/go.mod h1:i+SKzbRnvXdVbU3D github.com/libp2p/go-libp2p-yamux v0.2.0/go.mod h1:Db2gU+XfLpm6E4rG5uGCFX6uXA8MEXOxFcRoXUODaK8= github.com/libp2p/go-libp2p-yamux v0.2.2/go.mod h1:lIohaR0pT6mOt0AZ0L2dFze9hds9Req3OfS+B+dv4qw= github.com/libp2p/go-libp2p-yamux v0.2.5/go.mod h1:Zpgj6arbyQrmZ3wxSZxfBmbdnWtbZ48OpsfmQVTErwA= -github.com/libp2p/go-libp2p-yamux v0.2.7 h1:vzKu0NVtxvEIDGCv6mjKRcK0gipSgaXmJZ6jFv0d/dk= github.com/libp2p/go-libp2p-yamux v0.2.7/go.mod h1:X28ENrBMU/nm4I3Nx4sZ4dgjZ6VhLEn0XhIoZ5viCwU= +github.com/libp2p/go-libp2p-yamux v0.2.8 h1:0s3ELSLu2O7hWKfX1YjzudBKCP0kZ+m9e2+0veXzkn4= +github.com/libp2p/go-libp2p-yamux v0.2.8/go.mod h1:/t6tDqeuZf0INZMTgd0WxIRbtK2EzI2h7HbFm9eAKI4= github.com/libp2p/go-maddr-filter v0.0.4/go.mod h1:6eT12kSQMA9x2pvFQa+xesMKUBlj9VImZbj3B9FBH/Q= -github.com/libp2p/go-maddr-filter v0.0.5 h1:CW3AgbMO6vUvT4kf87y4N+0P8KUl2aqLYhrGyDUbLSg= github.com/libp2p/go-maddr-filter v0.0.5/go.mod h1:Jk+36PMfIqCJhAnaASRH83bdAvfDRp/w6ENFaC9bG+M= github.com/libp2p/go-mplex v0.0.3/go.mod h1:pK5yMLmOoBR1pNCqDlA2GQrdAVTMkqFalaTWe7l4Yd0= github.com/libp2p/go-mplex v0.1.0/go.mod h1:SXgmdki2kwCUlCCbfGLEgHjC4pFqhTp0ZoV6aiKgxDU= @@ -271,7 +326,6 @@ github.com/libp2p/go-netroute v0.1.2 h1:UHhB35chwgvcRI392znJA3RCBtZ3MpE3ahNCN5MR github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= -github.com/libp2p/go-openssl v0.0.4 h1:d27YZvLoTyMhIN4njrkr8zMDOM4lfpHIp6A+TK9fovg= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.5 h1:pQkejVhF0xp08D4CQUcw8t+BFJeXowja6RVcb5p++EA= github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= @@ -282,8 +336,6 @@ github.com/libp2p/go-reuseport-transport v0.0.3 h1:zzOeXnTooCkRvoH+bSXEfXhn76+LA github.com/libp2p/go-reuseport-transport v0.0.3/go.mod h1:Spv+MPft1exxARzP2Sruj2Wb5JSyHNncjf1Oi2dEbzM= github.com/libp2p/go-sockaddr v0.0.2 h1:tCuXfpA9rq7llM/v834RKc/Xvovy/AqM9kHvTV/jY/Q= github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= -github.com/libp2p/go-sockaddr v0.1.0 h1:Y4s3/jNoryVRKEBrkJ576F17CPOaMIzUeCsg7dlTDj0= -github.com/libp2p/go-sockaddr v0.1.0/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= github.com/libp2p/go-stream-muxer v0.0.1/go.mod h1:bAo8x7YkSpadMTbtTaxGVHWUQsR/l5MEaHbKaliuT14= github.com/libp2p/go-stream-muxer-multistream v0.2.0/go.mod h1:j9eyPol/LLRqT+GPLSxvimPhNph4sfYfMoDPd7HkzIc= github.com/libp2p/go-stream-muxer-multistream v0.3.0 h1:TqnSHPJEIqDEO7h1wZZ0p3DXdvDSiLHQidKKUGZtiOY= @@ -299,17 +351,25 @@ github.com/libp2p/go-ws-transport v0.3.1/go.mod h1:bpgTJmRZAvVHrgHybCVyqoBmyLQ1f github.com/libp2p/go-yamux v1.2.2/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.0/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.5 h1:ibuz4naPAully0pN6J/kmUARiqLpnDQIzI/8GCOrljg= github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= -github.com/libp2p/go-yamux v1.3.6 h1:O5qcBXRcfqecvQ/My9NqDNHB3/5t58yuJYqthcKhhgE= -github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/libp2p/go-yamux v1.3.7 h1:v40A1eSPJDIZwz2AvrV3cxpTZEGDP11QJbukmEhYyQI= +github.com/libp2p/go-yamux v1.3.7/go.mod h1:fr7aVgmdNGJK+N1g+b6DW6VxzbRCjCOejR/hkmpooHE= +github.com/lucas-clemente/quic-go v0.16.0 h1:jJw36wfzGJhmOhAOaOC2lS36WgeqXQszH47A7spo1LI= +github.com/lucas-clemente/quic-go v0.16.0/go.mod h1:I0+fcNTdb9eS1ZcjQZbDVPGchJ86chcIxPALn9lEJqE= +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-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= +github.com/marten-seemann/qtls v0.9.1 h1:O0YKQxNVPaiFgMng0suWEOY2Sb4LT2sRn9Qimq3Z1IQ= +github.com/marten-seemann/qtls v0.9.1/go.mod h1:T1MmAdDPyISzxlK6kjRr0pcZFBVd1OZbBb/j3cvzHhk= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.12/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.28 h1:gQhy5bsJa8zTlVI8lywCTZp1lguor+xevFoYlzeCTQY= github.com/miekg/dns v1.1.28/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= @@ -323,20 +383,25 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.1/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 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.0/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= -github.com/multiformats/go-multiaddr v0.2.1 h1:SgG/cw5vqyB5QQe5FPe2TqggU9WtrA9X4nZw7LlVqOI= github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= github.com/multiformats/go-multiaddr v0.2.2 h1:XZLDTszBIJe6m0zF6ITBrEcZR73OPUhCBBS9rYAuUzI= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= @@ -355,10 +420,9 @@ github.com/multiformats/go-multiaddr-net v0.1.3/go.mod h1:ilNnaM9HbmVFqsb/qcNysj github.com/multiformats/go-multiaddr-net v0.1.4/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= github.com/multiformats/go-multiaddr-net v0.1.5 h1:QoRKvu0xHN1FCFJcMQLbG/yQE2z441L5urvG3+qyz7g= github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysjCu4PVONlrBZpHIrw/qQuA= -github.com/multiformats/go-multibase v0.0.1 h1:PN9/v21eLywrFWdFNsFKaU04kLJzuYzmrJR+ubhT9qA= github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= -github.com/multiformats/go-multibase v0.0.2 h1:2pAgScmS1g9XjH7EtAfNhTuyrWYEWcxy0G5Wo85hWDA= -github.com/multiformats/go-multibase v0.0.2/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= @@ -372,29 +436,66 @@ github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.8.1/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= @@ -408,19 +509,24 @@ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb6 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/src-d/envconfig v1.0.0/go.mod h1:Q9YQZ7BKITldTBnoxsE5gOeB5y66RyPXeue/R4aaNBc= +github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= +github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1/go.mod h1:8UvriyWtv5Q5EOgjHaSseUEdkQfvwFv1I/In/O2M9gc= github.com/whyrusleeping/go-logging v0.0.0-20170515211332-0457bb6b88fc/go.mod h1:bopw91TMyo8J3tvftk8xmU2kPmlrt4nScJQZU2hE5EM= github.com/whyrusleeping/go-logging v0.0.1/go.mod h1:lDPYj54zutzG1XYfHAhcc7oNXEburHQBn+Iqd4yS4vE= -github.com/whyrusleeping/mafmt v1.2.8 h1:TCghSl5kkwEE0j+sU/gudyhVMRlpBin8fMBBHg59EbA= github.com/whyrusleeping/mafmt v1.2.8/go.mod h1:faQJFPbLSxzD9xpA02ttW/tS9vZykNvXwGvqIpk20FA= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9 h1:Y1/FEOpaCpD21WxrmfeIYCFPuVPRCY2XZTWzTNHGw30= github.com/whyrusleeping/mdns v0.0.0-20190826153040-b9b60ed33aa9/go.mod h1:j4l84WPFclQPj320J9gp0XwNKBb3U0zt5CBqjPp22G4= @@ -428,6 +534,7 @@ github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 h1: github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go.mod h1:X2c0RVCI1eSUFI8eLcY3c0423ykwiUdxLJtkDvruhjI= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -444,16 +551,19 @@ go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKY go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.14.1 h1:nYDKopTbvAPq/NrUVZwT15y2lpROBiLLyoRTbXOYWOo= go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -461,9 +571,12 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5 h1:Q7tZBpemrlsc2I7IyODzhtallWRSm4Q0d09pL6XbQtU= +golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed h1:g4KENRiCMEx58Q7/ecwfT0N2o8z35Fnbsjig/Alf2T4= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -475,51 +588,69 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478 h1:l5EDrHhldLYb3ZRHDUhXF7Om7MvYXnkV9/iQNo1lX6g= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476 h1:E7ct1C6/33eOdrGZKMoyntcEvs2dwZnDe30crG5vpYU= -golang.org/x/net v0.0.0-20200519113804-d87ec0cfa476/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980 h1:OjiUf46hAmXblsZdnoSXsEUSKU8r1UEzcL5RVZ4gO9Y= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -527,23 +658,42 @@ golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -552,6 +702,15 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +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/introspect/default_inspector.go b/introspect/default_inspector.go new file mode 100644 index 0000000000..b5f7b7e666 --- /dev/null +++ b/introspect/default_inspector.go @@ -0,0 +1,247 @@ +package introspect + +import ( + "fmt" + "math" + "runtime" + "sync" + "time" + + "github.com/libp2p/go-libp2p-core/event" + "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/introspection" + pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p-core/metrics" + "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" + + "github.com/libp2p/go-eventbus" + "github.com/multiformats/go-multiaddr" +) + +var _ introspection.Introspector = (*DefaultIntrospector)(nil) + +// DefaultIntrospector is an object that introspects the system. +type DefaultIntrospector struct { + *eventManager + + host host.Host + bus event.Bus + wsub event.Subscription + reporter metrics.Reporter + started time.Time + + closeCh chan struct{} + closeWg sync.WaitGroup +} + +func NewDefaultIntrospector(host host.Host, reporter metrics.Reporter) (introspection.Introspector, error) { + bus := host.EventBus() + if bus == nil { + return nil, fmt.Errorf("introspector requires a host with eventbus capability") + } + + sub, err := bus.Subscribe(event.WildcardSubscription, eventbus.BufSize(256)) + if err != nil { + return nil, fmt.Errorf("failed to susbcribe for events with WildcardSubscription") + } + + d := &DefaultIntrospector{ + eventManager: newEventManager(sub.Out()), + host: host, + bus: bus, + wsub: sub, + reporter: reporter, + started: time.Now(), + closeCh: make(chan struct{}), + } + + d.closeWg.Add(1) + go d.processEvents() + return d, nil +} + +func (d *DefaultIntrospector) Close() error { + if err := d.wsub.Close(); err != nil { + return fmt.Errorf("failed while trying to close wildcard eventbus subscription: %w", err) + } + + close(d.closeCh) + d.closeWg.Wait() + + return nil +} + +func (d *DefaultIntrospector) FetchRuntime() (*pb.Runtime, error) { + return &pb.Runtime{ + Implementation: "go-libp2p", + Platform: runtime.GOOS, + PeerId: d.host.ID().Pretty(), + Version: "", + }, nil +} + +func (d *DefaultIntrospector) FetchFullState() (state *pb.State, err error) { + var ( + now = time.Now() + netconns = d.host.Network().Conns() + conns = make([]*pb.Connection, 0, len(netconns)) + traffic *pb.Traffic + ) + + for _, conn := range netconns { + c, err := d.IntrospectConnection(conn) + if err != nil { + return nil, err + } + conns = append(conns, c) + } + + // subsystems and traffic. + traffic, err = d.IntrospectGlobalTraffic() + if err != nil { + return nil, err + } + + state = &pb.State{ + // timestamps in millis since epoch. + StartTs: uint64(d.started.UnixNano() / int64(time.Millisecond)), + InstantTs: uint64(now.UnixNano() / int64(time.Millisecond)), + Subsystems: &pb.Subsystems{ + Connections: conns, + }, + Traffic: traffic, + } + + return state, nil +} + +// IntrospectGlobalTraffic introspects and returns total traffic stats for this swarm. +func (d *DefaultIntrospector) IntrospectGlobalTraffic() (*pb.Traffic, error) { + if d.reporter == nil { + return nil, nil + } + + metrics := d.reporter.GetBandwidthTotals() + t := &pb.Traffic{ + TrafficIn: &pb.DataGauge{ + CumBytes: uint64(metrics.TotalIn), + InstBw: uint64(metrics.RateIn), + }, + TrafficOut: &pb.DataGauge{ + CumBytes: uint64(metrics.TotalOut), + InstBw: uint64(metrics.RateOut), + }, + } + + return t, nil +} + +func (d *DefaultIntrospector) IntrospectConnection(conn network.Conn) (*pb.Connection, error) { + stat := conn.Stat() + openTs := uint64(stat.Opened.UnixNano() / 1000000) + + res := &pb.Connection{ + Id: []byte(conn.ID()), + Status: pb.Status_ACTIVE, + PeerId: conn.RemotePeer().Pretty(), + Endpoints: &pb.EndpointPair{ + SrcMultiaddr: conn.LocalMultiaddr().String(), + DstMultiaddr: conn.RemoteMultiaddr().String(), + }, + Role: translateRole(stat), + + Timeline: &pb.Connection_Timeline{ + OpenTs: openTs, + UpgradedTs: openTs, + // TODO ClosedTs, UpgradedTs. + }, + } + + // TODO this is a per-peer, not a per-conn measurement. In the future, when + // we have multiple connections per peer, this will produce inaccurate + // numbers. Also, we do not record stream-level stats. + // We don't have packet I/O stats. + if r := d.reporter; r != nil { + bw := r.GetBandwidthForPeer(conn.RemotePeer()) + res.Traffic = &pb.Traffic{ + TrafficIn: &pb.DataGauge{ + CumBytes: uint64(bw.TotalIn), + InstBw: uint64(math.Round(bw.RateIn)), + }, + TrafficOut: &pb.DataGauge{ + CumBytes: uint64(bw.TotalOut), + InstBw: uint64(math.Round(bw.RateOut)), + }, + } + } + + // TODO I don't think we pin the multiplexer and the secure channel we've + // negotiated anywhere. + res.Attribs = &pb.Connection_Attributes{} + + // TransportId with format "ip4+tcp" or "ip6+udp+quic". + res.TransportId = func() []byte { + tptAddr, _ := peer.SplitAddr(conn.RemoteMultiaddr()) + var str string + multiaddr.ForEach(tptAddr, func(c multiaddr.Component) bool { + str += c.Protocol().Name + "+" + return true + }) + return []byte(str[0 : len(str)-1]) + }() + + // TODO there's the ping protocol, but that's higher than this layer. + // How do we source this? We may need some kind of latency manager. + res.LatencyNs = 0 + + streams := conn.GetStreams() + res.Streams = &pb.StreamList{ + Streams: make([]*pb.Stream, 0, len(streams)), + } + + for _, stream := range streams { + s, err := d.IntrospectStream(stream) + if err != nil { + return nil, err + } + res.Streams.Streams = append(res.Streams.Streams, s) + } + + return res, nil +} + +func (d *DefaultIntrospector) IntrospectStream(stream network.Stream) (*pb.Stream, error) { + stat := stream.Stat() + openTs := uint64(stat.Opened.UnixNano() / 1000000) + + res := &pb.Stream{ + Id: []byte(stream.ID()), + Status: pb.Status_ACTIVE, + Conn: &pb.Stream_ConnectionRef{ + Connection: &pb.Stream_ConnectionRef_ConnId{ + ConnId: []byte(stream.Conn().ID()), + }, + }, + Protocol: string(stream.Protocol()), + Role: translateRole(stat), + Timeline: &pb.Stream_Timeline{ + OpenTs: openTs, + // TODO CloseTs. + }, + // TODO Traffic: we are not tracking per-stream traffic stats at the moment. + Traffic: &pb.Traffic{TrafficIn: &pb.DataGauge{}, TrafficOut: &pb.DataGauge{}}, + } + return res, nil +} + +func translateRole(stat network.Stat) pb.Role { + switch stat.Direction { + case network.DirInbound: + return pb.Role_RESPONDER + case network.DirOutbound: + return pb.Role_INITIATOR + default: + return 99 // TODO placeholder value + } +} diff --git a/introspect/default_inspector_test.go b/introspect/default_inspector_test.go new file mode 100644 index 0000000000..15d7b14802 --- /dev/null +++ b/introspect/default_inspector_test.go @@ -0,0 +1,112 @@ +package introspect_test + +import ( + "context" + "testing" + "time" + + "github.com/libp2p/go-libp2p" + introspection_pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p-core/metrics" + "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peerstore" + "github.com/libp2p/go-libp2p-core/protocol" + "github.com/libp2p/go-libp2p/introspect" + + "github.com/stretchr/testify/require" +) + +func TestConnsAndStreamIntrospect(t *testing.T) { + ctx := context.Background() + + bwc1 := metrics.NewBandwidthCounter() + h1, err := libp2p.New(ctx, libp2p.BandwidthReporter(bwc1)) + require.NoError(t, err) + + bwc2 := metrics.NewBandwidthCounter() + h2, err := libp2p.New(ctx, libp2p.BandwidthReporter(bwc2)) + require.NoError(t, err) + + introspector1, err := introspect.NewDefaultIntrospector(h1, bwc1) + require.NoError(t, err) + _, _ = introspect.NewDefaultIntrospector(h2, bwc2) + + h1.Peerstore().AddAddrs(h2.ID(), h2.Network().ListenAddresses(), peerstore.PermanentAddrTTL) + err = h1.Connect(ctx, h2.Peerstore().PeerInfo(h2.ID())) + require.NoError(t, err) + + // ----- H1 opens two streams to H2 + pid1, pid2 := protocol.ID("1"), protocol.ID("2") + h2.SetStreamHandler(pid1, func(stream network.Stream) {}) + h2.SetStreamHandler(pid2, func(stream network.Stream) {}) + + s1, err := h1.NewStream(ctx, h2.ID(), pid1) + require.NoError(t, err) + s2, err := h1.NewStream(ctx, h2.ID(), pid2) + require.NoError(t, err) + + // send 4 bytes on stream 1 & 5 bytes on stream 2 + msg1 := "abcd" + msg2 := "12345" + _, err = s1.Write([]byte(msg1)) + require.NoError(t, err) + _, err = s2.Write([]byte(msg2)) + require.NoError(t, err) + + // wait for the metrics to kick in + require.Eventually(t, func() bool { + state, _ := introspector1.FetchFullState() + return state.Traffic.TrafficOut.CumBytes != 0 + }, 3*time.Second, 100*time.Millisecond) + + // ----- Introspect host 1. + state, err := introspector1.FetchFullState() + require.NoError(t, err) + conns := state.Subsystems.Connections + + // connection asserts + require.Len(t, conns, 1) + require.NotEmpty(t, conns[0].Id) + require.Equal(t, h2.ID().String(), conns[0].PeerId) + require.Equal(t, introspection_pb.Status_ACTIVE, conns[0].Status) + require.Equal(t, introspection_pb.Role_INITIATOR, conns[0].Role) + require.Equal(t, h1.Network().Conns()[0].LocalMultiaddr().String(), conns[0].Endpoints.SrcMultiaddr) + require.Equal(t, h1.Network().Conns()[0].RemoteMultiaddr().String(), conns[0].Endpoints.DstMultiaddr) + + // stream asserts. + streams := conns[0].Streams.Streams + require.Len(t, streams, 2) + require.NoError(t, err) + + // map stream to protocols + protos := make(map[string]*introspection_pb.Stream) + for _, s := range streams { + protos[s.Protocol] = s + } + + // introspect stream 1 + stream1 := protos["1"] + require.NotEmpty(t, stream1) + require.Equal(t, "1", stream1.Protocol) + require.Equal(t, introspection_pb.Role_INITIATOR, stream1.Role) + require.Equal(t, introspection_pb.Status_ACTIVE, stream1.Status) + require.NotEmpty(t, stream1.Id) + require.NotNil(t, stream1.Traffic) + require.NotNil(t, stream1.Traffic.TrafficIn) + require.NotNil(t, stream1.Traffic.TrafficOut) + + // introspect stream 2 + stream2 := protos["2"] + require.NotEmpty(t, stream2) + require.Equal(t, "2", stream2.Protocol) + require.Equal(t, introspection_pb.Role_INITIATOR, stream2.Role) + require.Equal(t, introspection_pb.Status_ACTIVE, stream2.Status) + require.NotEmpty(t, stream2.Id) + require.NotEqual(t, stream2.Id, stream1.Id) + + // introspect traffic + tr := state.Traffic + require.NoError(t, err) + require.NotZero(t, tr.TrafficOut.CumBytes) + require.Zero(t, tr.TrafficIn.CumBytes == 0) +} diff --git a/introspect/event_manager.go b/introspect/event_manager.go new file mode 100644 index 0000000000..3318c38691 --- /dev/null +++ b/introspect/event_manager.go @@ -0,0 +1,194 @@ +package introspect + +import ( + "encoding/json" + "errors" + "fmt" + "reflect" + "sync" + "time" + + "github.com/ipfs/go-log/v2" + + "github.com/libp2p/go-libp2p-core/event" + pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p-core/peer" + + "github.com/multiformats/go-multiaddr" +) + +var ( + jsType = reflect.TypeOf(new(event.RawJSON)).Elem() + peerIdType = reflect.TypeOf(new(peer.ID)).Elem() + timeType = reflect.TypeOf(new(time.Time)).Elem() + maddrType = reflect.TypeOf(new(multiaddr.Multiaddr)).Elem() +) + +type eventManager struct { + sync.RWMutex + logger *log.ZapEventLogger + + inCh <-chan interface{} + outCh chan *pb.Event + metadata map[reflect.Type]*pb.EventType + + closed bool + closeCh chan struct{} + closeWg sync.WaitGroup +} + +func newEventManager(inCh <-chan interface{}) *eventManager { + em := &eventManager{ + inCh: inCh, + logger: log.Logger("introspection/event-manager"), + outCh: make(chan *pb.Event, cap(inCh)), + closeCh: make(chan struct{}), + metadata: make(map[reflect.Type]*pb.EventType), + } + + em.closeWg.Add(1) + go em.processEvents() + return em +} + +func (em *eventManager) Close() error { + em.Lock() + defer em.Unlock() + + if em.closed { + em.closeWg.Wait() + return nil + } + + close(em.closeCh) + em.closeWg.Wait() + return nil +} + +func (em *eventManager) EventChan() <-chan *pb.Event { + return em.outCh +} + +func (em *eventManager) EventMetadata() []*pb.EventType { + em.RLock() + defer em.RUnlock() + + res := make([]*pb.EventType, 0, len(em.metadata)) + for k := range em.metadata { + v := em.metadata[k] + res = append(res, v) + } + return res +} + +func (em *eventManager) processEvents() { + defer em.closeWg.Done() + defer close(em.outCh) + + for { + select { + case <-em.closeCh: + return + + case evt, more := <-em.inCh: + if !more { + return + } + + e, err := em.createEvent(evt) + if err != nil { + em.logger.Warnf("failed to process event; err: %s", err) + continue + } + + select { + case em.outCh <- e: + case <-em.closeCh: + return + default: + em.logger.Warnf("failed to queue event") + } + } + } +} + +func (em *eventManager) createEvent(evt interface{}) (*pb.Event, error) { + js, err := json.Marshal(evt) + if err != nil { + return nil, fmt.Errorf("failed to marshal event to json; err: %w", err) + } + + ret := &pb.Event{ + Type: &pb.EventType{}, + Ts: uint64(time.Now().UnixNano() / int64(time.Millisecond)), + Content: string(js), + } + + key := reflect.TypeOf(evt) + + em.RLock() + et, ok := em.metadata[key] + em.RUnlock() + + if ok { + // just send the name if we've already seen the event before + ret.Type.Name = et.Name + return ret, nil + } + + if key.Kind() != reflect.Struct { + return nil, errors.New("event type must be a struct") + } + + ret.Type.Name = key.Name() + ret.Type.PropertyTypes = make([]*pb.EventType_EventProperty, 0, key.NumField()) + + for i := 0; i < key.NumField(); i++ { + fld := key.Field(i) + fldType := fld.Type + + prop := &pb.EventType_EventProperty{} + prop.Name = fld.Name + + if fldType.Kind() == reflect.Array || fldType.Kind() == reflect.Slice { + prop.HasMultiple = true + fldType = fld.Type.Elem() + } + + switch fldType { + case jsType: + prop.Type = pb.EventType_EventProperty_JSON + case peerIdType: + prop.Type = pb.EventType_EventProperty_PEERID + case maddrType: + prop.Type = pb.EventType_EventProperty_MULTIADDR + case timeType: + prop.Type = pb.EventType_EventProperty_TIME + default: + switch fldType.Kind() { + case reflect.String: + prop.Type = pb.EventType_EventProperty_STRING + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, reflect.Float32, reflect.Float64: + prop.Type = pb.EventType_EventProperty_NUMBER + default: + prop.Type = pb.EventType_EventProperty_JSON + } + } + + ret.Type.PropertyTypes = append(ret.Type.PropertyTypes, prop) + } + + em.Lock() + et, ok = em.metadata[key] + if ok { + // another write added the entry in the interim; discard ours. + em.Unlock() + ret.Type = et + return ret, nil + } + em.metadata[key] = ret.Type + em.Unlock() + return ret, nil +} diff --git a/introspect/event_manager_test.go b/introspect/event_manager_test.go new file mode 100644 index 0000000000..3878cb35bc --- /dev/null +++ b/introspect/event_manager_test.go @@ -0,0 +1,149 @@ +package introspect + +import ( + "testing" + "time" + + "github.com/multiformats/go-multiaddr" + "github.com/stretchr/testify/require" + + "github.com/libp2p/go-libp2p-core/event" + pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p-core/peer" +) + +type omnievent struct { + String string + Strings []string + Int int + Ints []int + RawJSON event.RawJSON + RawJSONs []event.RawJSON + PeerID peer.ID + PeerIDs []peer.ID + Time time.Time + Times []time.Time + Multiaddr multiaddr.Multiaddr + Multiaddrs []multiaddr.Multiaddr + Nested struct { + String string + Strings []string + Int int + Ints []int + RawJSON event.RawJSON + RawJSONs []event.RawJSON + PeerID peer.ID + PeerIDs []peer.ID + Time time.Time + Times []time.Time + Multiaddr multiaddr.Multiaddr + Multiaddrs []multiaddr.Multiaddr + } +} + +type EventA omnievent +type EventB omnievent + +func TestEventManager(t *testing.T) { + inCh := make(chan interface{}, 10) + em := newEventManager(inCh) + defer em.Close() + + require.Empty(t, em.EventMetadata()) + inCh <- EventA{} + + evt := <-em.EventChan() + require.NotNil(t, evt) + + compare := []struct { + Name string + Type pb.EventType_EventProperty_PropertyType + Multiple bool + }{ + {"String", pb.EventType_EventProperty_STRING, false}, + {"Strings", pb.EventType_EventProperty_STRING, true}, + {"Int", pb.EventType_EventProperty_NUMBER, false}, + {"Ints", pb.EventType_EventProperty_NUMBER, true}, + {"RawJSON", pb.EventType_EventProperty_JSON, false}, + {"RawJSONs", pb.EventType_EventProperty_JSON, true}, + {"PeerID", pb.EventType_EventProperty_PEERID, false}, + {"PeerIDs", pb.EventType_EventProperty_PEERID, true}, + {"Time", pb.EventType_EventProperty_TIME, false}, + {"Times", pb.EventType_EventProperty_TIME, true}, + {"Multiaddr", pb.EventType_EventProperty_MULTIADDR, false}, + {"Multiaddrs", pb.EventType_EventProperty_MULTIADDR, true}, + {"Nested", pb.EventType_EventProperty_JSON, false}, + } + + require.Equal(t, "EventA", evt.Type.Name) + + for i, pt := range evt.Type.PropertyTypes { + require.Equal(t, compare[i].Name, pt.Name) + require.Equal(t, compare[i].Type, pt.Type) + require.Equal(t, compare[i].Multiple, pt.HasMultiple) + } + + require.Len(t, em.EventMetadata(), 1) + require.Equal(t, evt.Type, em.EventMetadata()[0]) + + // send another event of type EventA; it should not inline the type definition. + inCh <- EventA{} + + evt = <-em.EventChan() + require.NotNil(t, evt) + require.Equal(t, "EventA", evt.Type.Name) + require.Nil(t, evt.Type.PropertyTypes) + + // send a new event; the type definition must be inlined. + inCh <- EventB{} + + evt = <-em.EventChan() + require.NotNil(t, evt) + require.Equal(t, "EventB", evt.Type.Name) + + for i, pt := range evt.Type.PropertyTypes { + require.Equal(t, compare[i].Name, pt.Name) + require.Equal(t, compare[i].Type, pt.Type) + require.Equal(t, compare[i].Multiple, pt.HasMultiple) + } + + require.Len(t, em.EventMetadata(), 2) +} + +func TestSubscriptionClosedClosesOut(t *testing.T) { + inCh := make(chan interface{}, 10) + em := newEventManager(inCh) + defer em.Close() + + require.Empty(t, em.EventMetadata()) + inCh <- EventA{} + + evt := <-em.EventChan() + require.NotNil(t, evt) + close(inCh) + + evt, more := <-em.EventChan() + require.Nil(t, evt) + require.False(t, more) +} + +func TestCloseStopsProcessing(t *testing.T) { + inCh := make(chan interface{}, 10) + em := newEventManager(inCh) + + require.Empty(t, em.EventMetadata()) + inCh <- EventA{} + evt := <-em.EventChan() + require.NotNil(t, evt) + + err := em.Close() + require.NoError(t, err) + + inCh <- EventA{} + inCh <- EventA{} + require.Len(t, inCh, 2) + + evt, more := <-em.EventChan() + require.Nil(t, evt) + require.False(t, more) +} diff --git a/introspect/mock_inspector.go b/introspect/mock_inspector.go new file mode 100644 index 0000000000..ab84bbfbdf --- /dev/null +++ b/introspect/mock_inspector.go @@ -0,0 +1,39 @@ +package introspect + +import ( + "github.com/stretchr/testify/mock" + + "github.com/libp2p/go-libp2p-core/introspection" + pb "github.com/libp2p/go-libp2p-core/introspection/pb" +) + +type MockIntrospector struct { + *eventManager + mock.Mock + + EventCh chan interface{} +} + +var _ introspection.Introspector = (*MockIntrospector)(nil) + +func NewMockIntrospector() *MockIntrospector { + mi := &MockIntrospector{ + EventCh: make(chan interface{}, 128), + } + mi.eventManager = newEventManager(mi.EventCh) + return mi +} + +func (m *MockIntrospector) Close() error { + return m.eventManager.Close() +} + +func (m *MockIntrospector) FetchRuntime() (*pb.Runtime, error) { + args := m.MethodCalled("FetchRuntime") + return args.Get(0).(*pb.Runtime), args.Error(1) +} + +func (m *MockIntrospector) FetchFullState() (*pb.State, error) { + args := m.MethodCalled("FetchFullState") + return args.Get(0).(*pb.State), args.Error(1) +} diff --git a/introspect/ws/server.go b/introspect/ws/server.go new file mode 100644 index 0000000000..0d3dbfd098 --- /dev/null +++ b/introspect/ws/server.go @@ -0,0 +1,352 @@ +package ws + +import ( + "encoding/binary" + "errors" + "fmt" + "hash/fnv" + "net" + "net/http" + "sync" + + "github.com/libp2p/go-libp2p-core/introspection" + "github.com/libp2p/go-libp2p-core/introspection/pb" + + "github.com/benbjohnson/clock" + "github.com/gorilla/websocket" + logging "github.com/ipfs/go-log" +) + +// ProtoVersion is the current version of the introspection protocol. +const ProtoVersion uint32 = 1 + +// ProtoVersionPb is the proto representation of the current introspection protocol. +var ProtoVersionPb = &pb.Version{Version: ProtoVersion} + +var ( + logger = logging.Logger("introspection/ws-server") + upgrader = websocket.Upgrader{} +) + +type sessionEvent struct { + session *session + doneCh chan struct{} +} + +type Endpoint struct { + // state initialized by constructor + introspector introspection.Introspector + config *EndpointConfig + server *http.Server + clock clock.Clock + + sessions map[*session]struct{} + + // state managed in the event loop + sessionOpenedCh chan *sessionEvent + sessionClosedCh chan *sessionEvent + getSessionsCh chan chan []*introspection.Session + killSessionsCh chan struct{} + + evalForTest chan func() + + // state managed by locking + lk sync.RWMutex + listeners []net.Listener + + connsWg sync.WaitGroup + controlWg sync.WaitGroup + + closeCh chan struct{} + isClosed bool +} + +var _ introspection.Endpoint = (*Endpoint)(nil) + +type EndpointConfig struct { + ListenAddrs []string + Clock clock.Clock +} + +// EndpointWithConfig returns a function compatible with the +// libp2p.Introspection constructor option, which when called, creates an +// Endpoint with the supplied configuration. +func EndpointWithConfig(config *EndpointConfig) func(i introspection.Introspector) (introspection.Endpoint, error) { + return func(i introspection.Introspector) (introspection.Endpoint, error) { + return NewEndpoint(i, config) + } +} + +// NewEndpoint creates a WebSockets server to serve introspect data. +func NewEndpoint(introspector introspection.Introspector, config *EndpointConfig) (*Endpoint, error) { + if introspector == nil || config == nil { + return nil, errors.New("introspector and configuration can't be nil") + } + + mux := http.NewServeMux() + + srv := &Endpoint{ + introspector: introspector, + server: &http.Server{Handler: mux}, + config: config, + clock: config.Clock, + + sessions: make(map[*session]struct{}, 16), + evalForTest: make(chan func()), + + sessionOpenedCh: make(chan *sessionEvent), + sessionClosedCh: make(chan *sessionEvent), + killSessionsCh: make(chan struct{}), + getSessionsCh: make(chan chan []*introspection.Session), + + closeCh: make(chan struct{}), + } + + if srv.clock == nil { + // use the real clock. + srv.clock = clock.New() + } + + // register introspect session + mux.HandleFunc("/introspect", srv.wsUpgrader()) + return srv, nil +} + +// Start starts this WS server. +func (e *Endpoint) Start() error { + e.lk.Lock() + defer e.lk.Unlock() + + if len(e.listeners) > 0 { + return errors.New("failed to start WS server: already started") + } + if len(e.config.ListenAddrs) == 0 { + return errors.New("failed to start WS server: no listen addresses supplied") + } + + logger.Infof("WS introspection server starting, listening on %e", e.config.ListenAddrs) + + for _, addr := range e.config.ListenAddrs { + l, err := net.Listen("tcp", addr) + if err != nil { + return fmt.Errorf("failed to start WS server: %wsvc", err) + } + + go func() { + if err := e.server.Serve(l); err != http.ErrServerClosed { + logger.Errorf("failed to start WS server, err: %e", err) + } + }() + + e.listeners = append(e.listeners, l) + } + + // start the worker + e.controlWg.Add(1) + go e.worker() + + return nil +} + +// Close closes a WS introspect server. +func (e *Endpoint) Close() error { + e.lk.Lock() + defer e.lk.Unlock() + + if e.isClosed { + return nil + } + + close(e.killSessionsCh) + + // wait for all connections to be dead. + e.connsWg.Wait() + + close(e.closeCh) + + // Close the server, which in turn closes all listeners. + if err := e.server.Close(); err != nil { + return err + } + + // cancel the context and wait for all goroutines to shut down + e.controlWg.Wait() + + e.listeners = nil + e.sessions = nil + e.isClosed = true + return nil +} + +// ListenAddrs returns the actual listen addresses of this server. +func (e *Endpoint) ListenAddrs() []string { + e.lk.RLock() + defer e.lk.RUnlock() + + res := make([]string, 0, len(e.listeners)) + for _, l := range e.listeners { + res = append(res, l.Addr().String()) + } + return res +} + +func (e *Endpoint) Sessions() []*introspection.Session { + ch := make(chan []*introspection.Session) + e.getSessionsCh <- ch + return <-ch +} + +func (e *Endpoint) wsUpgrader() http.HandlerFunc { + return func(w http.ResponseWriter, rq *http.Request) { + upgrader.CheckOrigin = func(rq *http.Request) bool { return true } + wsconn, err := upgrader.Upgrade(w, rq, nil) + if err != nil { + logger.Errorf("upgrade to websocket failed, err: %e", err) + return + } + + done := make(chan struct{}, 1) + select { + case e.sessionOpenedCh <- &sessionEvent{newSession(e, wsconn), done}: + case <-e.closeCh: + _ = wsconn.Close() + return + } + + select { + case <-done: + case <-e.closeCh: + _ = wsconn.Close() + return + } + } +} + +func (e *Endpoint) worker() { + defer e.controlWg.Done() + + eventCh := e.introspector.EventChan() + for { + select { + case rq := <-e.sessionOpenedCh: + session := rq.session + e.sessions[session] = struct{}{} + + e.connsWg.Add(1) + go func() { + session.run() + + select { + case e.sessionClosedCh <- &sessionEvent{session, rq.doneCh}: + case <-e.closeCh: + return + } + }() + + case rq := <-e.sessionClosedCh: + delete(e.sessions, rq.session) + e.connsWg.Done() + + case evt, more := <-eventCh: + if !more { + eventCh = nil + continue + } + + if len(e.sessions) == 0 { + continue + } + + // generate the event and broadcast it to all sessions. + if err := e.broadcastEvent(evt); err != nil { + logger.Warnf("error while broadcasting event; err: %e", err) + } + + case fnc := <-e.evalForTest: + fnc() + + case ch := <-e.getSessionsCh: + sessions := make([]*introspection.Session, 0, len(e.sessions)) + for sess := range e.sessions { + sessions = append(sessions, &introspection.Session{RemoteAddr: sess.wsconn.RemoteAddr().String()}) + } + ch <- sessions + + case <-e.killSessionsCh: + for sess := range e.sessions { + sess.kill() + } + + case <-e.closeCh: + return + } + } +} + +func (e *Endpoint) broadcastEvent(evt *pb.Event) error { + pkt := &pb.ServerMessage{ + Version: ProtoVersionPb, + Payload: &pb.ServerMessage_Event{Event: evt}, + } + + msg, err := envelopePacket(pkt) + if err != nil { + return fmt.Errorf("failed to generate enveloped event message; err: %w", err) + } + + for sess := range e.sessions { + sess.trySendEvent(msg) + } + + return nil +} + +func (e *Endpoint) createStateMsg() ([]byte, error) { + st, err := e.introspector.FetchFullState() + if err != nil { + return nil, fmt.Errorf("failed to fetch state, err=%e", err) + } + + pkt := &pb.ServerMessage{ + Version: ProtoVersionPb, + Payload: &pb.ServerMessage_State{State: st}, + } + + return envelopePacket(pkt) +} + +func (e *Endpoint) createRuntimeMsg() ([]byte, error) { + rt, err := e.introspector.FetchRuntime() + if err != nil { + return nil, fmt.Errorf("failed to fetch runtime mesage, err=%e", err) + } + + rt.EventTypes = e.introspector.EventMetadata() + pkt := &pb.ServerMessage{ + Version: ProtoVersionPb, + Payload: &pb.ServerMessage_Runtime{Runtime: rt}, + } + + return envelopePacket(pkt) +} + +func envelopePacket(pkt *pb.ServerMessage) ([]byte, error) { + // TODO buffer pool. + size := pkt.Size() + buf := make([]byte, 12+size) + if _, err := pkt.MarshalToSizedBuffer(buf[12:]); err != nil { + return nil, err + } + + f := fnv.New32a() + _, err := f.Write(buf[12:]) + if err != nil { + return nil, fmt.Errorf("failed creating fnc hash digest, err: %w", err) + } + + binary.LittleEndian.PutUint32(buf[0:4], ProtoVersion) + binary.LittleEndian.PutUint32(buf[4:8], f.Sum32()) + binary.LittleEndian.PutUint32(buf[8:12], uint32(size)) + + return buf, nil +} diff --git a/introspect/ws/server_common_test.go b/introspect/ws/server_common_test.go new file mode 100644 index 0000000000..160b7b4bed --- /dev/null +++ b/introspect/ws/server_common_test.go @@ -0,0 +1,95 @@ +package ws + +import ( + "encoding/binary" + "fmt" + "hash/fnv" + "testing" + + "github.com/benbjohnson/clock" + "github.com/gorilla/websocket" + "github.com/stretchr/testify/require" + + "github.com/libp2p/go-libp2p-core/introspection" + pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p/introspect" +) + +func createTestServer(t *testing.T) (*Endpoint, *introspect.MockIntrospector) { + t.Helper() + + mocki := introspect.NewMockIntrospector() + cfg := &EndpointConfig{ListenAddrs: []string{"localhost:0"}, Clock: clock.NewMock()} + server, err := NewEndpoint(mocki, cfg) + require.NoError(t, err) + return server, mocki +} + +type connWrapper struct { + *websocket.Conn + t *testing.T +} + +func createConn(t *testing.T, server *Endpoint) *connWrapper { + addr := fmt.Sprintf("ws://%s/introspect", server.ListenAddrs()[0]) + conn, _, err := websocket.DefaultDialer.Dial(addr, nil) + require.NoError(t, err) + return &connWrapper{conn, t} +} + +func (cw *connWrapper) sendCommand(cmd *pb.ClientCommand) { + cw.t.Helper() + + msg, err := cmd.Marshal() + require.NoError(cw.t, err) + + err = cw.WriteMessage(websocket.BinaryMessage, msg) + require.NoError(cw.t, err) +} + +func (cw *connWrapper) greet() { + cw.t.Helper() + + cw.sendCommand(&pb.ClientCommand{Id: 200, Command: pb.ClientCommand_HELLO}) + + msg := cw.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(cw.t, 200, resp.Id) + require.EqualValues(cw.t, pb.CommandResponse_OK, resp.Result) + require.Empty(cw.t, resp.Error) +} + +func (cw *connWrapper) readNext() *pb.ServerMessage { + cw.t.Helper() + + _, msg, err := cw.ReadMessage() + require.NoError(cw.t, err) + + var ( + // parse the message + version = msg[0:4] + checksum = msg[4:8] + length = msg[8:12] + payload = msg[12:] + ) + + require.EqualValues(cw.t, introspection.ProtoVersion, binary.LittleEndian.Uint32(version)) + require.EqualValues(cw.t, len(payload), binary.LittleEndian.Uint32(length)) + + // validate hash. + h := fnv.New32a() + _, err = h.Write(payload) + require.NoError(cw.t, err) + require.EqualValues(cw.t, h.Sum32(), binary.LittleEndian.Uint32(checksum)) + + smsg := &pb.ServerMessage{} + + // read the protocol message directly + require.NoError(cw.t, smsg.Unmarshal(payload)) + + require.NotNil(cw.t, smsg.Payload, "nil message received from server") + require.Equal(cw.t, introspection.ProtoVersion, smsg.Version.Version, "incorrect proto version receieved from client") + + return smsg +} diff --git a/introspect/ws/server_config_test.go b/introspect/ws/server_config_test.go new file mode 100644 index 0000000000..50bb50b45b --- /dev/null +++ b/introspect/ws/server_config_test.go @@ -0,0 +1,90 @@ +package ws + +import ( + "testing" + + "github.com/stretchr/testify/require" + + pb "github.com/libp2p/go-libp2p-core/introspection/pb" +) + +func TestValidConfiguration(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + config := &pb.Configuration{ + RetentionPeriodMs: uint64(MaxRetentionPeriod.Milliseconds() - 1), + StateSnapshotIntervalMs: uint64(MinStateSnapshotInterval.Milliseconds() + 1), + } + + // on HELLO + conn.sendCommand(&pb.ClientCommand{Id: 200, Command: pb.ClientCommand_HELLO, Config: config}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 200, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + require.EqualValues(t, config, resp.EffectiveConfig) + require.Empty(t, resp.Error) + + // on UPDATE_VALUES, adjust the values to verify new values have been set. + config.RetentionPeriodMs -= 1 + config.StateSnapshotIntervalMs += 1 + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_UPDATE_CONFIG, Config: config}) + + msg = conn.readNext() + resp = msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + require.EqualValues(t, config, resp.EffectiveConfig) + require.Empty(t, resp.Error) +} + +func TestHelloWithInvalidConfig(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + expected := &pb.Configuration{ + RetentionPeriodMs: uint64(MaxRetentionPeriod.Milliseconds()), + StateSnapshotIntervalMs: uint64(MinStateSnapshotInterval.Milliseconds()), + } + + config := &pb.Configuration{ + RetentionPeriodMs: uint64(MaxRetentionPeriod.Milliseconds() + 1), + StateSnapshotIntervalMs: uint64(MinStateSnapshotInterval.Milliseconds() - 1), + } + + // on HELLO + conn.sendCommand(&pb.ClientCommand{Id: 200, Command: pb.ClientCommand_HELLO, Config: config}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 200, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + require.EqualValues(t, expected, resp.EffectiveConfig) + require.Empty(t, resp.Error) + + // on UPDATE_VALUES, adjust the values to verify new values have been set. + config.RetentionPeriodMs += 1 + config.StateSnapshotIntervalMs -= 1 + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_UPDATE_CONFIG, Config: config}) + + msg = conn.readNext() + resp = msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + require.EqualValues(t, expected, resp.EffectiveConfig) + require.Empty(t, resp.Error) +} diff --git a/introspect/ws/server_hello_test.go b/introspect/ws/server_hello_test.go new file mode 100644 index 0000000000..69aecd9692 --- /dev/null +++ b/introspect/ws/server_hello_test.go @@ -0,0 +1,56 @@ +package ws + +import ( + "testing" + + "github.com/stretchr/testify/require" + + pb "github.com/libp2p/go-libp2p-core/introspection/pb" +) + +func TestStartSession(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() +} + +func TestDoubleHelloFails(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_HELLO}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_ERR, resp.Result) +} + +func TestNoHelloFails(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.sendCommand(&pb.ClientCommand{Id: 200, Command: pb.ClientCommand_REQUEST, Source: pb.ClientCommand_RUNTIME}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 200, resp.Id) + require.EqualValues(t, pb.CommandResponse_ERR, resp.Result) +} diff --git a/introspect/ws/server_lifecycle_test.go b/introspect/ws/server_lifecycle_test.go new file mode 100644 index 0000000000..88ef827dbc --- /dev/null +++ b/introspect/ws/server_lifecycle_test.go @@ -0,0 +1,53 @@ +package ws + +import ( + "io" + "strings" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func TestServerClose(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + + // create 100 connections. + var wg sync.WaitGroup + conns := make([]*connWrapper, 100) + wg.Add(100) + for i := range conns { + conn := createConn(t, server) + go func() { + _, _, err := conn.ReadMessage() + if strings.Contains(err.Error(), io.ErrUnexpectedEOF.Error()) { + wg.Done() + } + }() + conns[i] = conn + } + + err := server.Close() + require.NoError(t, err) + wg.Wait() +} + +func TestServerHandlesClosedConns(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + + conns := make([]*connWrapper, 100) + for i := range conns { + conn := createConn(t, server) + conns[i] = conn + } + + require.Len(t, server.Sessions(), 100) + + err := conns[0].Close() + require.NoError(t, err) + + require.Eventually(t, func() bool { return len(server.Sessions()) == 99 }, 2*time.Second, 100*time.Millisecond) +} diff --git a/introspect/ws/server_push_test.go b/introspect/ws/server_push_test.go new file mode 100644 index 0000000000..61431cc4c1 --- /dev/null +++ b/introspect/ws/server_push_test.go @@ -0,0 +1,137 @@ +package ws + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + pb "github.com/libp2p/go-libp2p-core/introspection/pb" +) + +type simpleEvent struct { + String string + Number int +} + +type EventA simpleEvent +type EventB simpleEvent +type EventC simpleEvent + +func TestPushEvents(t *testing.T) { + server, mocki := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_PUSH_ENABLE, Source: pb.ClientCommand_EVENTS}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + + assertEvent := func(name string) { + msg := conn.readNext() + evt := msg.Payload.(*pb.ServerMessage_Event).Event + + require.EqualValues(t, name, evt.Type.Name) + require.NotEmpty(t, evt.Content) + require.NotZero(t, evt.Ts) + } + + mocki.EventCh <- EventA{String: "hello", Number: 100} + mocki.EventCh <- EventA{String: "hello", Number: 100} + mocki.EventCh <- EventB{String: "hello", Number: 100} + + assertEvent("EventA") + assertEvent("EventA") + assertEvent("EventB") +} + +func TestPushStopPushEvents(t *testing.T) { + server, mocki := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_PUSH_ENABLE, Source: pb.ClientCommand_EVENTS}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + + assertEvent := func(name string) { + msg := conn.readNext() + evt := msg.Payload.(*pb.ServerMessage_Event).Event + + require.EqualValues(t, name, evt.Type.Name) + require.NotEmpty(t, evt.Content) + require.NotZero(t, evt.Ts) + } + + mocki.EventCh <- EventA{String: "hello", Number: 100} + assertEvent("EventA") + + // now disable the pusher and verify that we actually missed those events. + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_PUSH_DISABLE, Source: pb.ClientCommand_EVENTS}) + msg = conn.readNext() + resp = msg.Payload.(*pb.ServerMessage_Response).Response + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + + time.Sleep(1 * time.Second) + + // these events will be missed. + mocki.EventCh <- EventA{String: "hello", Number: 100} + mocki.EventCh <- EventB{String: "hello", Number: 100} + + // enable the pusher again + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_PUSH_ENABLE, Source: pb.ClientCommand_EVENTS}) + msg = conn.readNext() + resp = msg.Payload.(*pb.ServerMessage_Response).Response + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + + // these events will be received. + mocki.EventCh <- EventC{String: "hello", Number: 100} + mocki.EventCh <- EventC{String: "hello", Number: 100} + + assertEvent("EventC") + assertEvent("EventC") +} + +func TestPushEventsIdempotent(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_PUSH_ENABLE, Source: pb.ClientCommand_EVENTS}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) + + conn.sendCommand(&pb.ClientCommand{Id: 202, Command: pb.ClientCommand_PUSH_ENABLE, Source: pb.ClientCommand_EVENTS}) + + msg = conn.readNext() + resp = msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 202, resp.Id) + require.EqualValues(t, pb.CommandResponse_OK, resp.Result) +} diff --git a/introspect/ws/server_request_test.go b/introspect/ws/server_request_test.go new file mode 100644 index 0000000000..27a7e63d25 --- /dev/null +++ b/introspect/ws/server_request_test.go @@ -0,0 +1,64 @@ +package ws + +import ( + "testing" + + "github.com/stretchr/testify/require" + + pb "github.com/libp2p/go-libp2p-core/introspection/pb" +) + +func TestRequestState(t *testing.T) { + server, mocki := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + + mocki.On("FetchFullState").Return(&pb.State{}, nil) + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_REQUEST, Source: pb.ClientCommand_STATE}) + + msg := conn.readNext() + require.NotNil(t, msg.Payload.(*pb.ServerMessage_State).State) + mocki.AssertNumberOfCalls(t, "FetchFullState", 1) +} + +func TestRequestRuntime(t *testing.T) { + server, mocki := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + + mocki.On("FetchRuntime").Return(&pb.Runtime{}, nil) + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_REQUEST, Source: pb.ClientCommand_RUNTIME}) + + msg := conn.readNext() + require.NotNil(t, msg.Payload.(*pb.ServerMessage_Runtime).Runtime) + mocki.AssertNumberOfCalls(t, "FetchRuntime", 1) +} + +func TestRequestEventsFails(t *testing.T) { + server, _ := createTestServer(t) + require.NoError(t, server.Start()) + defer server.Close() + + conn := createConn(t, server) + defer conn.Close() + + conn.greet() + + conn.sendCommand(&pb.ClientCommand{Id: 201, Command: pb.ClientCommand_REQUEST, Source: pb.ClientCommand_EVENTS}) + + msg := conn.readNext() + resp := msg.Payload.(*pb.ServerMessage_Response).Response + + require.EqualValues(t, 201, resp.Id) + require.EqualValues(t, pb.CommandResponse_ERR, resp.Result) +} diff --git a/introspect/ws/session.go b/introspect/ws/session.go new file mode 100644 index 0000000000..ef86168e28 --- /dev/null +++ b/introspect/ws/session.go @@ -0,0 +1,424 @@ +package ws + +import ( + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/libp2p/go-libp2p-core/introspection/pb" + + "github.com/benbjohnson/clock" + "github.com/gorilla/websocket" + "go.uber.org/zap" +) + +var handlers = map[pb.ClientCommand_Command]func(*session, *pb.ClientCommand) *pb.ServerMessage{ + pb.ClientCommand_HELLO: (*session).handleHelloCmd, + pb.ClientCommand_REQUEST: (*session).handleRequestCmd, + pb.ClientCommand_PUSH_ENABLE: (*session).handlePushEnableCmd, + pb.ClientCommand_PUSH_DISABLE: (*session).handlePushDisableCmd, + pb.ClientCommand_PUSH_PAUSE: (*session).handlePushPauseCmd, + pb.ClientCommand_PUSH_RESUME: (*session).handlePushResumeCmd, + pb.ClientCommand_UPDATE_CONFIG: (*session).handleUpdateConfigCmd, +} + +var ( + MaxRetentionPeriod = 120 * time.Second + MinStateSnapshotInterval = 500 * time.Millisecond + + WriteTimeout = 5 * time.Second + ConnBufferSize = 1 << 8 + + DefaultSessionConfig = &pb.Configuration{ + RetentionPeriodMs: uint64(MaxRetentionPeriod / time.Millisecond), + StateSnapshotIntervalMs: uint64(time.Second / time.Millisecond), + } +) + +type qitem struct { + ts time.Time + payload []byte +} + +type session struct { + server *Endpoint + wsconn *websocket.Conn + logger *zap.SugaredLogger + writeCh chan []byte + + pushingEvents bool + pushingState bool + paused bool + + eventCh chan []byte + commandCh chan *pb.ClientCommand + + stateTicker *clock.Ticker + + greeted bool + config *pb.Configuration + q []*qitem + + wg sync.WaitGroup + closed int32 + closeCh chan struct{} +} + +func newSession(sv *Endpoint, wsconn *websocket.Conn) *session { + ch := &session{ + server: sv, + wsconn: wsconn, + config: DefaultSessionConfig, + stateTicker: new(clock.Ticker), + + writeCh: make(chan []byte, ConnBufferSize), + eventCh: make(chan []byte, ConnBufferSize), + commandCh: make(chan *pb.ClientCommand), + closeCh: make(chan struct{}), + } + + ch.logger = logger.Named(wsconn.RemoteAddr().String()) + return ch +} + +func (s *session) run() { + s.wg.Add(3) + + go s.writeLoop() + go s.readLoop() + go s.control() + + s.wg.Wait() +} + +func (s *session) trySendEvent(msg []byte) { + select { + case s.eventCh <- msg: + case <-s.closeCh: + default: + s.logger.Warnf("unable to queue event; dropping") + } +} + +func (s *session) queueWrite(msg []byte) { + select { + case s.writeCh <- msg: + case <-s.closeCh: + s.logger.Warnf("dropping queued message upon close") + } +} + +func (s *session) control() { + defer s.wg.Done() + + // dummy ticker that won't tick unless enabled. + pruneQTicker := time.NewTicker(2 * time.Second) + defer pruneQTicker.Stop() + defer func() { + if s.pushingState { + s.stateTicker.Stop() + } + }() + + for { + select { + case <-s.stateTicker.C: + msg, err := s.server.createStateMsg() + if err != nil { + s.logger.Warnf("failed to generate state message on tick; err: %s", err) + continue + } + if s.paused { + s.q = append(s.q, &qitem{time.Now(), msg}) + continue + } + s.queueWrite(msg) + + case now := <-pruneQTicker.C: + if !s.paused || len(s.q) == 0 { + continue + } + + i := 0 + thres := now.Add(-time.Duration(s.config.RetentionPeriodMs) * time.Millisecond) + for ; i < len(s.q) && s.q[i].ts.Before(thres); i++ { + } + s.q = s.q[i:] + + case evt := <-s.eventCh: + if !s.pushingEvents { + continue + } + if s.paused { + s.q = append(s.q, &qitem{time.Now(), evt}) + continue + } + s.queueWrite(evt) + + case cmd := <-s.commandCh: + var resp *pb.ServerMessage + handler, ok := handlers[cmd.Command] + if !ok { + err := fmt.Errorf("unknown command type: %v", cmd.Command) + resp = createCmdErrorResp(cmd, err) + s.logger.Warnf("%s", err) + } else { + resp = handler(s, cmd) + } + + if resp != nil { + msg, err := envelopePacket(resp) + if err != nil { + s.logger.Warnf("failed to marshal client message; err: %s", err) + s.kill() + return + } + s.queueWrite(msg) + } + + case <-s.closeCh: + s.q = nil + return + } + } +} + +func (s *session) writeLoop() { + defer s.wg.Done() + + for { + select { + case msg := <-s.writeCh: + _ = s.wsconn.SetWriteDeadline(time.Now().Add(WriteTimeout)) + if err := s.wsconn.WriteMessage(websocket.BinaryMessage, msg); err != nil { + s.logger.Warnf("failed to send binary message to client with addr %s, err=%s", err) + s.kill() + return + } + + case <-s.closeCh: + return + } + } +} + +func (s *session) kill() { + if atomic.SwapInt32(&s.closed, 1) == 0 { + close(s.closeCh) + _ = s.wsconn.Close() + } +} + +func (s *session) readLoop() { + defer s.wg.Done() + + for { + mt, message, err := s.wsconn.ReadMessage() + switch err.(type) { + case nil: + case *websocket.CloseError: + s.logger.Warnf("connection closed; err: %s", err) + s.kill() + return + default: + s.logger.Warnf("failed to read message from ws connection; err: %s", err) + s.kill() + return + } + + s.logger.Debugf("received message from ws connection; type: %d; recv: %x", mt, message) + + cmd := new(pb.ClientCommand) + if err := cmd.Unmarshal(message); err != nil { + s.logger.Warnf("failed to read client message; err: %s", err) + s.kill() + return + } + + select { + case s.commandCh <- cmd: + case <-s.closeCh: + return + } + } +} + +func (s *session) handleHelloCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client had already greeted server")) + } + s.greeted = true + if cmd.Config != nil { + s.config = s.validateConfig(*cmd.Config) + } + resp := createCmdOKResp(cmd) + resp.Payload.(*pb.ServerMessage_Response).Response.EffectiveConfig = s.config + return resp +} + +func (s *session) handleRequestCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + var ( + bytes []byte + err error + ) + switch cmd.Source { + case pb.ClientCommand_EVENTS: + err = fmt.Errorf("illegal request for events messages") + case pb.ClientCommand_RUNTIME: + bytes, err = s.server.createRuntimeMsg() + case pb.ClientCommand_STATE: + bytes, err = s.server.createStateMsg() + } + if err != nil { + return createCmdErrorResp(cmd, err) + } + s.writeCh <- bytes + return nil // response is the actual requested payload +} + +func (s *session) handlePushEnableCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + switch cmd.Source { + case pb.ClientCommand_STATE: + if s.pushingState { + break // do nothing + } + s.pushingState = true + s.stateTicker = s.server.clock.Ticker(time.Duration(s.config.StateSnapshotIntervalMs) * time.Millisecond) + case pb.ClientCommand_EVENTS: + s.pushingEvents = true + default: + return createCmdErrorResp(cmd, fmt.Errorf("specified source does not support pushing")) + } + return createCmdOKResp(cmd) +} + +func (s *session) handlePushDisableCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + switch cmd.Source { + case pb.ClientCommand_STATE: + if !s.pushingState { + break // do nothing + } + s.pushingState = false + s.stateTicker.Stop() + case pb.ClientCommand_EVENTS: + s.pushingEvents = false + default: + return createCmdErrorResp(cmd, fmt.Errorf("specified source does not support pushing")) + } + + // if all pushers are disabled, clear the queue. + if !s.pushingState && !s.pushingEvents { + s.q = nil + } + + return createCmdOKResp(cmd) +} + +func (s *session) handlePushPauseCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + s.paused = true + return createCmdOKResp(cmd) +} + +func (s *session) handlePushResumeCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + if !s.paused { + // if we are not paused, there's nothing to do. + return createCmdOKResp(cmd) + } + + msg := createCmdOKResp(cmd) + bytes, err := envelopePacket(msg) + if err != nil { + s.logger.Warnf("failed to marshal client message; err: %s", err) + s.kill() + return nil + } + + s.queueWrite(bytes) + for _, msg := range s.q { + s.queueWrite(msg.payload) + } + + s.q = nil + s.paused = false + return nil +} + +func (s *session) handleUpdateConfigCmd(cmd *pb.ClientCommand) *pb.ServerMessage { + if !s.greeted { + return createCmdErrorResp(cmd, fmt.Errorf("client has not greeted server yet")) + } + + if cmd.Config == nil { + return createCmdErrorResp(cmd, fmt.Errorf("client passed nil configuration")) + } + + old, neu := s.config, cmd.Config + neu = s.validateConfig(*neu) + + if s.pushingState && old.StateSnapshotIntervalMs != neu.StateSnapshotIntervalMs { + // reset the state ticker to the new interval, if we're pushing state. + s.stateTicker.Stop() + s.stateTicker = s.server.clock.Ticker(time.Duration(neu.StateSnapshotIntervalMs) * time.Millisecond) + } + + s.config = neu + resp := createCmdOKResp(cmd) + resp.Payload.(*pb.ServerMessage_Response).Response.EffectiveConfig = s.config + return resp +} + +func (s *session) validateConfig(config pb.Configuration) *pb.Configuration { + if min := uint64(MinStateSnapshotInterval.Milliseconds()); config.StateSnapshotIntervalMs < min { + config.StateSnapshotIntervalMs = min + } + if max := uint64(MaxRetentionPeriod.Milliseconds()); config.RetentionPeriodMs > max { + config.RetentionPeriodMs = max + } + return &config +} + +func createCmdErrorResp(cmd *pb.ClientCommand, err error) *pb.ServerMessage { + return &pb.ServerMessage{ + Version: ProtoVersionPb, + Payload: &pb.ServerMessage_Response{ + Response: &pb.CommandResponse{ + Id: cmd.Id, + Result: pb.CommandResponse_ERR, + Error: err.Error(), + }, + }, + } +} + +func createCmdOKResp(cmd *pb.ClientCommand) *pb.ServerMessage { + return &pb.ServerMessage{ + Version: ProtoVersionPb, + Payload: &pb.ServerMessage_Response{ + Response: &pb.CommandResponse{ + Id: cmd.Id, + Result: pb.CommandResponse_OK, + }, + }, + } +} diff --git a/introspect_test.go b/introspect_test.go new file mode 100644 index 0000000000..3d1c399170 --- /dev/null +++ b/introspect_test.go @@ -0,0 +1,260 @@ +package libp2p + +import ( + "context" + "fmt" + "io" + "runtime" + "testing" + "time" + + "github.com/libp2p/go-libp2p-core/host" + introspection "github.com/libp2p/go-libp2p-core/introspection" + introspection_pb "github.com/libp2p/go-libp2p-core/introspection/pb" + "github.com/libp2p/go-libp2p-core/metrics" + "github.com/libp2p/go-libp2p-core/network" + "github.com/libp2p/go-libp2p-core/peer" + "github.com/libp2p/go-libp2p-core/protocol" + introspector "github.com/libp2p/go-libp2p-introspector" + + "github.com/gogo/protobuf/proto" + "github.com/gorilla/websocket" + "github.com/stretchr/testify/require" +) + +var ( + msg1 = []byte("1") + msg2 = []byte("12") + msg3 = []byte("111") + msg4 = []byte("0000") + + p1 = protocol.ID("h1h3") + p2 = protocol.ID("h2h1") +) + +// TODO Send Pause & Send Data +func TestIntrospector(t *testing.T) { + require := require.New(t) + + iaddr := "127.0.0.1:0" + ctx := context.Background() + + // create host 1 with introspect + h1, err := New(ctx, + Introspection( + introspector.NewDefaultIntrospector(), + introspector.WsServerWithConfig(&introspector.WsServerConfig{ + ListenAddrs: []string{iaddr}, + }), + ), + BandwidthReporter(metrics.NewBandwidthCounter()), + ) + require.NoError(err) + defer h1.Close() + + // create host 2 + h2, err := New(ctx) + defer h2.Close() + + // create host 3 + h3, err := New(ctx) + defer h3.Close() + + // host1 -> CONNECTS -> host2 + require.NoError(h1.Connect(ctx, peer.AddrInfo{ID: h2.ID(), Addrs: h2.Addrs()})) + + // host3 -> CONNECTS -> host1 + require.NoError(h3.Connect(ctx, peer.AddrInfo{ID: h1.ID(), Addrs: h1.Addrs()})) + + // host1 -> OPENS STREAM 1 -> host3, Writes a message & then reads the response + h3.SetStreamHandler(p1, func(s network.Stream) { + buf := make([]byte, len(msg1)) + + _, err := io.ReadFull(s, buf) + require.NoError(err) + + _, err = s.Write(msg2) + require.NoError(err) + }) + + s1, err := h1.NewStream(ctx, h3.ID(), p1) + require.NoError(err) + + _, err = s1.Write(msg1) + require.NoError(err) + + buf := make([]byte, len(msg2)) + _, err = io.ReadFull(s1, buf) + require.NoError(err) + + // host2 -> OPENS Stream 2 -> host1 , writes a message & reads the response + h1.SetStreamHandler(p2, func(s network.Stream) { + buf := make([]byte, len(msg3)) + + _, err := io.ReadFull(s, buf) + require.NoError(err) + + _, err = s.Write(msg4) + require.NoError(err) + }) + + s2, err := h2.NewStream(ctx, h1.ID(), p2) + require.NoError(err) + + _, err = s2.Write(msg3) + require.NoError(err) + + buf = make([]byte, len(msg4)) + _, err = io.ReadFull(s2, buf) + require.NoError(err) + + // create a connection with the introspect server + addrs := h1.(host.IntrospectableHost).IntrospectionEndpoint().ListenAddrs() + url := fmt.Sprintf("ws://%s/introspect", addrs[0]) + + fmt.Println(addrs) + + // wait till connection is established + var connection *websocket.Conn + require.Eventually(func() bool { + connection, _, err = websocket.DefaultDialer.Dial(url, nil) + return err == nil + }, 5*time.Second, 500*time.Millisecond) + defer connection.Close() + + // first, we get the runtime and assert it + pd := fetchProtocolWrapper(require, connection) + rt := pd.GetRuntime() + require.NotNil(t, rt) + require.Equal(h1.ID().String(), rt.PeerId) + require.Equal(runtime.GOOS, rt.Platform) + require.Equal("go-libp2p", rt.Implementation) + + // followed by the state message + pd = fetchProtocolWrapper(require, connection) + st := pd.GetState() + require.NotNil(t, st) + assertState(require, st, h1, h2, h3, false) + + // we should then periodically get a state message..lets wait for one with traffic + var st2 *introspection_pb.State + require.Eventually(func() bool { + pd = fetchProtocolWrapper(require, connection) + st2 = pd.GetState() + require.NotNil(t, st2) + + if st2.Traffic.TrafficOut.CumBytes != 0 && + st2.Subsystems.Connections[0].Traffic.TrafficOut.CumBytes != 0 && st2.Subsystems.Connections[1].Traffic.TrafficOut.CumBytes != 0 { + return true + } + + return false + }, 10*time.Second, 1500*time.Millisecond) + assertState(require, st2, h1, h2, h3, true) + + // Pause + cl := &introspection_pb.ClientSignal{Signal: introspection_pb.ClientSignal_PAUSE_PUSH_EMITTER} + bz, err := proto.Marshal(cl) + require.NoError(err) + require.NotEmpty(bz) + require.NoError(connection.WriteMessage(websocket.BinaryMessage, bz)) + time.Sleep(1 * time.Second) + + // We get a runtime and state message when we unpause + cl = &introspection_pb.ClientSignal{Signal: introspection_pb.ClientSignal_UNPAUSE_PUSH_EMITTER} + bz, err = proto.Marshal(cl) + require.NoError(err) + require.NotEmpty(bz) + require.NoError(connection.WriteMessage(websocket.BinaryMessage, bz)) + + require.Eventually(func() bool { + pd = fetchProtocolWrapper(require, connection) + + return pd.GetRuntime() != nil && pd.GetState() == nil + }, 10*time.Second, 1*time.Second) + + require.Eventually(func() bool { + pd = fetchProtocolWrapper(require, connection) + + return pd.GetState() != nil && pd.GetRuntime() == nil + }, 10*time.Second, 1*time.Second) + + assertState(require, pd.GetState(), h1, h2, h3, true) +} + +func assertState(require *require.Assertions, state *introspection_pb.State, h1, h2, h3 host.Host, + assertTraffic bool) { + + if assertTraffic { + require.Greater(state.Traffic.TrafficIn.CumBytes, uint64(100)) + require.Greater(state.Traffic.TrafficOut.CumBytes, uint64(100)) + } + + // Connections + conns := state.Subsystems.Connections + peerIdToConns := make(map[string]*introspection_pb.Connection) + for _, c := range conns { + peerIdToConns[c.PeerId] = c + } + require.Len(peerIdToConns, 2) + + pconn := make(map[string]network.Conn) + for _, c := range h1.Network().Conns() { + pconn[c.RemotePeer().String()] = c + } + require.Len(pconn, 2) + + // host1 -> host2 connection + h2Conn := peerIdToConns[h2.ID().String()] + require.NotEmpty(h2Conn.Id) + require.Equal(introspection_pb.Status_ACTIVE, h2Conn.Status) + require.Equal(pconn[h2.ID().String()].LocalMultiaddr().String(), h2Conn.Endpoints.SrcMultiaddr) + require.Equal(pconn[h2.ID().String()].RemoteMultiaddr().String(), h2Conn.Endpoints.DstMultiaddr) + require.Equal(introspection_pb.Role_INITIATOR, h2Conn.Role) + + if assertTraffic { + require.Greater(h2Conn.Traffic.TrafficIn.CumBytes, uint64(len(msg3))) + } + + // host3 -> host1 connection + h3Conn := peerIdToConns[h3.ID().String()] + require.NotEmpty(h3Conn.Id) + require.Equal(introspection_pb.Status_ACTIVE, h3Conn.Status) + require.Equal(pconn[h3.ID().String()].LocalMultiaddr().String(), h3Conn.Endpoints.SrcMultiaddr) + require.Equal(pconn[h3.ID().String()].RemoteMultiaddr().String(), h3Conn.Endpoints.DstMultiaddr) + require.Equal(introspection_pb.Role_RESPONDER, h3Conn.Role) + + if assertTraffic { + require.Greater(h3Conn.Traffic.TrafficIn.CumBytes, uint64(len(msg2))) + require.Greater(h3Conn.Traffic.TrafficOut.CumBytes, uint64(len(msg1))) + } + // stream1 + require.Len(h3Conn.Streams.Streams, 1) + h3Stream := h3Conn.Streams.Streams[0] + require.NotEmpty(h3Stream.Id) + require.Equal(string(p1), h3Stream.Protocol) + require.Equal(introspection_pb.Role_INITIATOR, h3Stream.Role) + require.Equal(introspection_pb.Status_ACTIVE, h3Stream.Status) + // require.True(len(msg1) == int(h3Stream.Traffic.TrafficOut.CumBytes)) + // require.True(len(msg2) == int(h3Stream.Traffic.TrafficIn.CumBytes)) + + // stream 2 + require.Len(h2Conn.Streams.Streams, 1) + h1Stream := h2Conn.Streams.Streams[0] + require.NotEmpty(h1Stream.Id) + require.Equal(string(p2), h1Stream.Protocol) + require.Equal(introspection_pb.Role_RESPONDER, h1Stream.Role) + require.Equal(introspection_pb.Status_ACTIVE, h1Stream.Status) + // require.True(len(msg3) == int(h1Stream.Traffic.TrafficIn.CumBytes)) +} + +func fetchProtocolWrapper(require *require.Assertions, conn *websocket.Conn) *introspection_pb.ProtocolDataPacket { + _, msg, err := conn.ReadMessage() + require.NoError(err) + require.NotEmpty(msg) + pd := &introspection_pb.ProtocolDataPacket{} + require.NoError(proto.Unmarshal(msg[12:], pd)) + require.NotNil(pd.Message) + require.Equal(introspection.ProtoVersion, pd.Version.Version) + return pd +} diff --git a/options.go b/options.go index 93002ccf5f..d5128871a1 100644 --- a/options.go +++ b/options.go @@ -10,6 +10,7 @@ import ( "time" circuit "github.com/libp2p/go-libp2p-circuit" + "github.com/libp2p/go-libp2p-core/connmgr" "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/metrics" @@ -195,6 +196,34 @@ func ConnectionManager(connman connmgr.ConnManager) Option { } } +// Introspection configures the host to use the given Introspector, and the +// supplied Introspection Endpoint. +// +// Example: +// +// import ( +// "github.com/libp2p/go-libp2p/introspect" +// "github.com/libp2p/go-libp2p/introspect/ws" +// ) +// +// host, err := libp2p.New( +// libp2p.Introspection( +// introspect.NewDefaultIntrospector, +// ws.EndpointWithConfig(&ws.EndpointConfig{ListenAddrs: []string{"localhost:6061"}}), +// ), +// ) +// +func Introspection(introspectorCtor config.IntrospectorC, endpointCtor config.IntrospectionEndpointC) Option { + return func(cfg *Config) error { + if cfg.Introspector != nil { + return fmt.Errorf("cannot specify multiple introspectors") + } + cfg.Introspector = introspectorCtor + cfg.IntrospectionEndpoint = endpointCtor + return nil + } +} + // AddrsFactory configures libp2p to use the given address factory. func AddrsFactory(factory config.AddrsFactory) Option { return func(cfg *Config) error { diff --git a/p2p/host/basic/basic_host.go b/p2p/host/basic/basic_host.go index 530b998ac0..4ea1bec940 100644 --- a/p2p/host/basic/basic_host.go +++ b/p2p/host/basic/basic_host.go @@ -13,17 +13,20 @@ import ( "github.com/libp2p/go-libp2p-core/crypto" "github.com/libp2p/go-libp2p-core/event" "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/introspection" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peerstore" "github.com/libp2p/go-libp2p-core/protocol" "github.com/libp2p/go-libp2p-core/record" - "github.com/libp2p/go-eventbus" inat "github.com/libp2p/go-libp2p-nat" + "github.com/libp2p/go-libp2p/p2p/protocol/identify" "github.com/libp2p/go-libp2p/p2p/protocol/ping" + "github.com/libp2p/go-eventbus" + logging "github.com/ipfs/go-log" "github.com/multiformats/go-multiaddr" @@ -37,6 +40,8 @@ import ( // peer (for all addresses). const maxAddressResolution = 32 +var _ host.IntrospectableHost = (*BasicHost)(nil) + // addrChangeTickrInterval is the interval between two address change ticks. var addrChangeTickrInterval = 5 * time.Second @@ -101,6 +106,9 @@ type BasicHost struct { addrChangeChan chan struct{} + introspector introspection.Introspector + introspectionEndpoint introspection.Endpoint + disableSignedPeerRecord bool signKey crypto.PrivKey caBook peerstore.CertifiedAddrBook @@ -140,6 +148,14 @@ type HostOpts struct { // UserAgent sets the user-agent for the host. Defaults to ClientVersion. UserAgent string + // Introspector is used by host subsystems to register themselves as metrics + // providers and fetch the current state. + Introspector introspection.Introspector + + // IntrospectionEndpoint is the introspect endpoint through which + // introspect data is served to clients. + IntrospectionEndpoint introspection.Endpoint + // DisableSignedPeerRecord disables the generation of Signed Peer Records on this host. DisableSignedPeerRecord bool } @@ -155,6 +171,7 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo AddrsFactory: DefaultAddrsFactory, maResolver: madns.DefaultResolver, eventbus: eventbus.NewBus(), + introspector: opts.Introspector, addrChangeChan: make(chan struct{}, 1), ctx: hostCtx, ctxCancel: cancel, @@ -196,6 +213,7 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo h.mux = opts.MultistreamMuxer } + // Start ID Service // we can't set this as a default above because it depends on the *BasicHost. if h.disableSignedPeerRecord { h.ids = identify.NewIDService(h, identify.UserAgent(opts.UserAgent), identify.DisableSignedPeerRecord()) @@ -245,6 +263,33 @@ func NewHost(ctx context.Context, net network.Network, opts *HostOpts) (*BasicHo return h, nil } +func (h *BasicHost) SetIntrospection(introspector introspection.Introspector, endpoint introspection.Endpoint) error { + if h.introspectionEndpoint != nil { + if err := h.introspectionEndpoint.Close(); err != nil { + return fmt.Errorf("failed to close existing introspection endpoint: %w", err) + } + } + if h.introspector != nil { + if err := h.introspector.Close(); err != nil { + return fmt.Errorf("failed to close existing introspector: %w", err) + } + } + h.introspector = introspector + h.introspectionEndpoint = endpoint + if h.introspectionEndpoint == nil { + return nil + } + return h.introspectionEndpoint.Start() +} + +func (h *BasicHost) Introspector() introspection.Introspector { + return h.introspector +} + +func (h *BasicHost) IntrospectionEndpoint() introspection.Endpoint { + return h.introspectionEndpoint +} + // New constructs and sets up a new *BasicHost with given Network and options. // The following options can be passed: // * NATPortMap @@ -873,6 +918,18 @@ func (h *BasicHost) Close() error { _ = h.emitters.evtLocalAddrsUpdated.Close() h.Network().Close() + if h.introspectionEndpoint != nil { + if err := h.introspectionEndpoint.Close(); err != nil { + log.Errorf("failed while shutting down introspection endpoint; err: %s", err) + } + } + + if h.introspector != nil { + if err := h.introspector.Close(); err != nil { + log.Errorf("failed while shutting down introspectir; err: %s", err) + } + } + h.refCount.Wait() }) diff --git a/p2p/host/routed/routed.go b/p2p/host/routed/routed.go index aecab09568..dc2c0fd5b9 100644 --- a/p2p/host/routed/routed.go +++ b/p2p/host/routed/routed.go @@ -8,6 +8,7 @@ import ( "github.com/libp2p/go-libp2p-core/connmgr" "github.com/libp2p/go-libp2p-core/event" "github.com/libp2p/go-libp2p-core/host" + "github.com/libp2p/go-libp2p-core/introspection" "github.com/libp2p/go-libp2p-core/network" "github.com/libp2p/go-libp2p-core/peer" "github.com/libp2p/go-libp2p-core/peerstore" @@ -22,6 +23,8 @@ import ( var log = logging.Logger("routedhost") +var _ host.IntrospectableHost = (*RoutedHost)(nil) + // AddressTTL is the expiry time for our addresses. // We expire them quickly. const AddressTTL = time.Second * 10 @@ -134,6 +137,22 @@ func logRoutingErrDifferentPeers(ctx context.Context, wanted, got peer.ID, err e log.Event(ctx, "routingError", lm) } +func (h *RoutedHost) Introspector() introspection.Introspector { + i, ok := h.host.(host.IntrospectableHost) + if !ok { + return nil + } + return i.Introspector() +} + +func (h *RoutedHost) IntrospectionEndpoint() introspection.Endpoint { + i, ok := h.host.(host.IntrospectableHost) + if !ok { + return nil + } + return i.IntrospectionEndpoint() +} + func (rh *RoutedHost) ID() peer.ID { return rh.host.ID() } diff --git a/p2p/net/mock/mock_conn.go b/p2p/net/mock/mock_conn.go index 6bde1ea2dc..d2780e410c 100644 --- a/p2p/net/mock/mock_conn.go +++ b/p2p/net/mock/mock_conn.go @@ -2,7 +2,9 @@ package mocknet import ( "container/list" + "strconv" "sync" + "sync/atomic" process "github.com/jbenet/goprocess" ic "github.com/libp2p/go-libp2p-core/crypto" @@ -12,12 +14,16 @@ import ( manet "github.com/multiformats/go-multiaddr-net" ) +var connCounter int64 + // conn represents one side's perspective of a // live connection between two peers. // it goes over a particular link. type conn struct { notifLk sync.Mutex + id int64 + local peer.ID remote peer.ID @@ -43,6 +49,7 @@ func newConn(p process.Process, ln, rn *peernet, l *link, dir network.Direction) c.local = ln.peer c.remote = rn.peer c.stat = network.Stat{Direction: dir} + c.id = atomic.AddInt64(&connCounter, 1) c.localAddr = ln.ps.Addrs(ln.peer)[0] for _, a := range rn.ps.Addrs(rn.peer) { @@ -61,6 +68,10 @@ func newConn(p process.Process, ln, rn *peernet, l *link, dir network.Direction) return c } +func (c *conn) ID() string { + return strconv.FormatInt(c.id, 10) +} + func (c *conn) Close() error { return c.pairProc.Close() } diff --git a/p2p/net/mock/mock_stream.go b/p2p/net/mock/mock_stream.go index ecb32ddbbf..9ddf5eedf2 100644 --- a/p2p/net/mock/mock_stream.go +++ b/p2p/net/mock/mock_stream.go @@ -5,6 +5,7 @@ import ( "errors" "io" "net" + "strconv" "sync" "sync/atomic" "time" @@ -14,12 +15,15 @@ import ( protocol "github.com/libp2p/go-libp2p-core/protocol" ) +var streamCounter int64 + // stream implements network.Stream type stream struct { notifLk sync.Mutex rstream *stream conn *conn + id int64 write *io.PipeWriter read *io.PipeReader @@ -57,6 +61,7 @@ func newStream(w *io.PipeWriter, r *io.PipeReader, dir network.Direction) *strea s := &stream{ read: r, write: w, + id: atomic.AddInt64(&streamCounter, 1), reset: make(chan struct{}, 1), close: make(chan struct{}, 1), closed: make(chan struct{}), @@ -86,6 +91,10 @@ func (s *stream) Write(p []byte) (n int, err error) { return len(p), nil } +func (s *stream) ID() string { + return strconv.FormatInt(s.id, 10) +} + func (s *stream) Protocol() protocol.ID { // Ignore type error. It means that the protocol is unset. p, _ := s.protocol.Load().(protocol.ID)