cmd/torrent/main.go | 24 ++++++++++++++++++++++++ go.mod | 18 ++++++++++++++++-- go.sum | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++ otel.go | 3 +++ webrtc.go | 16 ++++++++++++++++ webtorrent/otel.go | 6 ++++++ webtorrent/tracker-client.go | 141 +++++++++++++++++++++++++++-------------------------- webtorrent/transport.go | 187 +++++++++++++++++++++++++++++++++++++++-------------- webtorrent/transport_test.go | 34 ++++++++++++++++++++++++++++++++++ diff --git a/cmd/torrent/main.go b/cmd/torrent/main.go index 8c942e391af6cdec1c3ce6059d98aa91356f14ea..58a266636741de9c9654d8ddb447fee71e879b4d 100644 --- a/cmd/torrent/main.go +++ b/cmd/torrent/main.go @@ -2,19 +2,25 @@ // Downloads torrents from the command-line. package main import ( + "context" "encoding/json" "fmt" "io" stdLog "log" "net/http" "os" + "time" "github.com/davecgh/go-spew/spew" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/sdk/trace" "github.com/anacrolix/bargle" "github.com/anacrolix/envpprof" + "github.com/anacrolix/log" xprometheus "github.com/anacrolix/missinggo/v2/prometheus" "github.com/anacrolix/torrent/bencode" @@ -26,10 +32,28 @@ prometheus.MustRegister(xprometheus.NewExpvarCollector()) http.Handle("/metrics", promhttp.Handler()) } +func shutdownTracerProvider(ctx context.Context, tp *trace.TracerProvider) { + started := time.Now() + err := tp.Shutdown(ctx) + elapsed := time.Since(started) + log.Levelf(log.Error, "shutting down tracer provider (took %v): %v", elapsed, err) +} + func main() { defer stdLog.SetFlags(stdLog.Flags() | stdLog.Lshortfile) + + ctx := context.Background() + tracingExporter, err := otlptracegrpc.New(ctx) + if err != nil { + log.Fatalf("creating tracing exporter: %v", err) + } + tracerProvider := trace.NewTracerProvider(trace.WithBatcher(tracingExporter)) + defer shutdownTracerProvider(ctx, tracerProvider) + otel.SetTracerProvider(tracerProvider) + main := bargle.Main{} main.Defer(envpprof.Stop) + main.Defer(func() { shutdownTracerProvider(ctx, tracerProvider) }) debug := false debugFlag := bargle.NewFlag(&debug) debugFlag.AddLong("debug") diff --git a/go.mod b/go.mod index 8e351f99c4ae461e6286125d6e9816bb5a9b6e87..b557b3f341a05806359f6ddbc62d4e2e479a7a8f 100644 --- a/go.mod +++ b/go.mod @@ -8,13 +8,14 @@ github.com/RoaringBitmap/roaring v1.2.1 github.com/ajwerner/btree v0.0.0-20211221152037-f427b3e689c0 github.com/alexflint/go-arg v1.4.3 github.com/anacrolix/args v0.5.1-0.20220509024600-c3b77d0b61ac + github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a github.com/anacrolix/chansync v0.3.0 github.com/anacrolix/dht/v2 v2.18.0 github.com/anacrolix/envpprof v1.2.1 github.com/anacrolix/fuse v0.2.0 github.com/anacrolix/generics v0.0.0-20220618083756-f99e35403a60 github.com/anacrolix/go-libutp v1.2.0 - github.com/anacrolix/log v0.13.2-0.20220427063716-a4894bb521c6 + github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 github.com/anacrolix/missinggo v1.3.0 github.com/anacrolix/missinggo/perf v1.0.0 github.com/anacrolix/missinggo/v2 v2.7.0 @@ -45,21 +46,28 @@ github.com/prometheus/client_golang v1.12.2 github.com/stretchr/testify v1.8.0 github.com/tidwall/btree v1.3.1 go.etcd.io/bbolt v1.3.6 + go.opentelemetry.io/otel v1.8.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0 + go.opentelemetry.io/otel/sdk v1.8.0 + go.opentelemetry.io/otel/trace v1.8.0 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 ) require ( github.com/alecthomas/atomic v0.1.0-alpha2 // indirect github.com/alexflint/go-scalar v1.1.0 // indirect - github.com/anacrolix/bargle v0.0.0-20220630015206-d7a4d433886a // indirect github.com/anacrolix/mmsg v1.0.0 // indirect github.com/anacrolix/stm v0.4.0 // indirect github.com/benbjohnson/immutable v0.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.2.2 // indirect + github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/kr/pretty v0.3.0 // indirect github.com/kr/text v0.2.0 // indirect @@ -86,12 +94,18 @@ github.com/prometheus/procfs v0.7.3 // indirect github.com/rogpeppe/go-internal v1.8.1 // indirect github.com/rs/dnscache v0.0.0-20211102005908-e0241e321417 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect + go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.8.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.8.0 // indirect + go.opentelemetry.io/proto/otlp v0.18.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/exp v0.0.0-20220613132600-b0d781184e0d // indirect golang.org/x/net v0.0.0-20220630215102-69896b714898 // indirect golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect golang.org/x/sys v0.0.0-20220702020025-31831981b65f // indirect + golang.org/x/text v0.3.7 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect + google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect + google.golang.org/grpc v1.46.2 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b936cbe34d1cc4de00469e23c1cb9140d18a32b9..2cb9b1c323e79d4b308aa466b5467ba23a9c3c79 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,7 @@ filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w= github.com/RoaringBitmap/roaring v0.4.17/go.mod h1:D3qVegWTmfCaX4Bl5CrBE9hfrSrrXIr8KVNvRsDi1NI= github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo= @@ -93,6 +94,8 @@ github.com/anacrolix/log v0.10.0/go.mod h1:s5yBP/j046fm9odtUTbHOfDUq/zh1W8OkPpJtnX0oQI= github.com/anacrolix/log v0.10.1-0.20220123034749-3920702c17f8/go.mod h1:GmnE2c0nvz8pOIPUSC9Rawgefy1sDXqposC2wgtBZE4= github.com/anacrolix/log v0.13.2-0.20220427063716-a4894bb521c6 h1:WH/Xcok0GpNID/NUV80CfTwUYXdbhR3pX/DXboxGhNI= github.com/anacrolix/log v0.13.2-0.20220427063716-a4894bb521c6/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= +github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30 h1:bAgFzUxN1K3U8KwOzqCOhiygOr5NqYO3kNlV9tvp2Rc= +github.com/anacrolix/log v0.13.2-0.20220711050817-613cb738ef30/go.mod h1:D4+CvN8SnruK6zIFS/xPoRJmtvtnxs+CSfDQ+BFxZ68= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62 h1:P04VG6Td13FHMgS5ZBcJX23NPC/fiC4cp9bXwYujdYM= github.com/anacrolix/lsan v0.0.0-20211126052245-807000409a62/go.mod h1:66cFKPCO7Sl4vbFnAaSq7e4OXtdMhRSBagJGWgmpJbM= github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s= @@ -131,6 +134,7 @@ github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96 h1:QAVZ3pN/J4/UziniAhJR2OZ9Ox5kOY2053tBbbqUPYA= github.com/anacrolix/upnp v0.1.3-0.20220123035249-922794e51c96/go.mod h1:Wa6n8cYIdaG35x15aH3Zy6d03f7P728QfdcDeD/IEOs= github.com/anacrolix/utp v0.1.0 h1:FOpQOmIwYsnENnz7tAGohA+r6iXpRjrq8ssKSre2Cp4= github.com/anacrolix/utp v0.1.0/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= @@ -148,7 +152,10 @@ github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8= github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og= +github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= +github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -157,6 +164,13 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -177,6 +191,10 @@ github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.9.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= @@ -185,6 +203,7 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/go-unsnap-stream v0.0.0-20190901134440-81cf024a9e0a/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -202,11 +221,17 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -250,6 +275,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -264,6 +290,7 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -276,6 +303,9 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 h1:BZHcxBETFHIdVyhyEfOvn/RdU/QGdLI4y34qQGjGWO0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -421,6 +451,7 @@ github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= @@ -438,6 +469,7 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -474,6 +506,21 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg= +go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.8.0 h1:ao8CJIShCaIbaMsGxy+jp2YHSudketpDgDRcbirov78= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.8.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.8.0 h1:LrHL1A3KqIgAgi6mK7Q0aczmzU414AONAGT5xtnp+uo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.8.0/go.mod h1:w8aZL87GMOvOBa2lU/JlVXE1q4chk/0FX+8ai4513bw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0 h1:00hCSGLIxdYK/Z7r8GkaX0QIlfvgU3tmnLlQvcnix6U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.8.0/go.mod h1:twhIvtDQW2sWP1O2cT1N8nkSBgKCRZv2z6COTTBrf8Q= +go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk= +go.opentelemetry.io/otel/sdk v1.8.0/go.mod h1:uPSfc+yfDH2StDM/Rm35WE8gXSNdvCg023J6HeGNO0c= +go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY= +go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.18.0 h1:W5hyXNComRa23tGpKwG+FRAc4rfF6ZUg1JReK+QHS80= +go.opentelemetry.io/proto/otlp v0.18.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -552,6 +599,7 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -569,6 +617,7 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= 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= @@ -624,9 +673,12 @@ golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -643,7 +695,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -745,12 +799,15 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 h1:b9mVrqYfq3P4bCdaLg1qtBnPzUYgglsIdjZkL/fQVOE= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= 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= @@ -764,6 +821,12 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= 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= @@ -776,6 +839,7 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -788,6 +852,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 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.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/otel.go b/otel.go new file mode 100644 index 0000000000000000000000000000000000000000..5dddd6af5ae30b87e428d68e4ad42e29829584da --- /dev/null +++ b/otel.go @@ -0,0 +1,3 @@ +package torrent + +const tracerName = "anacrolix.torrent" diff --git a/webrtc.go b/webrtc.go index 4a86e89232a8028df5f612d7325ed16fd3bcd4ae..aff1e0a3c5675017b7ff819123aea8ce705d44e2 100644 --- a/webrtc.go +++ b/webrtc.go @@ -1,6 +1,9 @@ package torrent import ( + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/trace" "net" "strconv" "time" @@ -56,13 +59,26 @@ // Do we need these for WebRTC connections exposed as net.Conns? Can we set them somewhere inside // PeerConnection or on the channel or some transport? func (w webrtcNetConn) SetDeadline(t time.Time) error { + w.Span.AddEvent("SetDeadline", trace.WithAttributes(attribute.String("time", t.String()))) return nil } func (w webrtcNetConn) SetReadDeadline(t time.Time) error { + w.Span.AddEvent("SetReadDeadline", trace.WithAttributes(attribute.String("time", t.String()))) return nil } func (w webrtcNetConn) SetWriteDeadline(t time.Time) error { + w.Span.AddEvent("SetWriteDeadline", trace.WithAttributes(attribute.String("time", t.String()))) return nil } + +func (w webrtcNetConn) Read(b []byte) (n int, err error) { + _, span := otel.Tracer(tracerName).Start(w.Context, "Read") + defer span.End() + span.SetAttributes(attribute.Int("buf_len", len(b))) + n, err = w.ReadWriteCloser.Read(b) + span.RecordError(err) + span.SetAttributes(attribute.Int("bytes_read", n)) + return +} diff --git a/webtorrent/otel.go b/webtorrent/otel.go new file mode 100644 index 0000000000000000000000000000000000000000..2c09964219914df4c2d0493c93b2089799b67854 --- /dev/null +++ b/webtorrent/otel.go @@ -0,0 +1,6 @@ +package webtorrent + +const ( + tracerName = "anacrolix.torrent.webtorrent" + webrtcConnTypeKey = "webtorrent.webrtc.conn.type" +) diff --git a/webtorrent/tracker-client.go b/webtorrent/tracker-client.go index 3b8c6a6bbcdfb2a4cbe08a9a78fce75eace7cc7d..195c06b033dee4c1bf743b64df37a6ad38ea859f 100644 --- a/webtorrent/tracker-client.go +++ b/webtorrent/tracker-client.go @@ -1,9 +1,12 @@ package webtorrent import ( + "context" "crypto/rand" "encoding/json" "fmt" + "github.com/anacrolix/generics" + "go.opentelemetry.io/otel/trace" "sync" "time" @@ -32,7 +35,7 @@ Dialer *websocket.Dialer mu sync.Mutex cond sync.Cond - outboundOffers map[string]outboundOffer // OfferID to outboundOffer + outboundOffers map[string]outboundOfferValue // OfferID to outboundOfferValue wsConn *websocket.Conn closed bool stats TrackerClientStats @@ -49,22 +52,27 @@ func (me *TrackerClient) peerIdBinary() string { return binaryToJsonString(me.PeerId[:]) } -// outboundOffer represents an outstanding offer. type outboundOffer struct { + offerId string + outboundOfferValue +} + +// outboundOfferValue represents an outstanding offer. +type outboundOfferValue struct { originalOffer webrtc.SessionDescription peerConnection *wrappedPeerConnection - dataChannel *webrtc.DataChannel infoHash [20]byte + dataChannel *webrtc.DataChannel } type DataChannelContext struct { - // Can these be obtained by just calling the relevant methods on peerConnection? - Local, Remote webrtc.SessionDescription - OfferId string - LocalOffered bool - InfoHash [20]byte + OfferId string + LocalOffered bool + InfoHash [20]byte // This is private as some methods might not be appropriate with data channel context. peerConnection *wrappedPeerConnection + Span trace.Span + Context context.Context } func (me *DataChannelContext) GetSelectedIceCandidatePair() (*webrtc.ICECandidatePair, error) { @@ -183,6 +191,7 @@ func (tc *TrackerClient) closeUnusedOffers() { for _, offer := range tc.outboundOffers { offer.peerConnection.Close() + offer.dataChannel.Close() } tc.outboundOffers = nil } @@ -200,6 +209,9 @@ } func (tc *TrackerClient) Announce(event tracker.AnnounceEvent, infoHash [20]byte) error { metrics.Add("outbound announces", 1) + if event == tracker.Stopped { + return tc.announce(event, infoHash, nil) + } var randOfferId [20]byte _, err := rand.Read(randOfferId[:]) if err != nil { @@ -207,19 +219,35 @@ return fmt.Errorf("generating offer_id bytes: %w", err) } offerIDBinary := binaryToJsonString(randOfferId[:]) - pc, dc, offer, err := newOffer() + pc, dc, offer, err := tc.newOffer(tc.Logger, offerIDBinary, infoHash) if err != nil { return fmt.Errorf("creating offer: %w", err) } - request, err := tc.GetAnnounceRequest(event, infoHash) + err = tc.announce(event, infoHash, []outboundOffer{{ + offerId: offerIDBinary, + outboundOfferValue: outboundOfferValue{ + originalOffer: offer, + peerConnection: pc, + infoHash: infoHash, + dataChannel: dc, + }}, + }) if err != nil { + dc.Close() pc.Close() + } + return err +} + +func (tc *TrackerClient) announce(event tracker.AnnounceEvent, infoHash [20]byte, offers []outboundOffer) error { + request, err := tc.GetAnnounceRequest(event, infoHash) + if err != nil { return fmt.Errorf("getting announce parameters: %w", err) } req := AnnounceRequest{ - Numwant: 1, // If higher we need to create equal amount of offers. + Numwant: len(offers), Uploaded: request.Uploaded, Downloaded: request.Downloaded, Left: request.Left, @@ -227,15 +255,16 @@ Event: request.Event.String(), Action: "announce", InfoHash: binaryToJsonString(infoHash[:]), PeerID: tc.peerIdBinary(), - Offers: []Offer{{ - OfferID: offerIDBinary, - Offer: offer, - }}, + } + for _, offer := range offers { + req.Offers = append(req.Offers, Offer{ + OfferID: offer.offerId, + Offer: offer.originalOffer, + }) } data, err := json.Marshal(req) if err != nil { - pc.Close() return fmt.Errorf("marshalling request: %w", err) } @@ -243,17 +272,10 @@ tc.mu.Lock() defer tc.mu.Unlock() err = tc.writeMessage(data) if err != nil { - pc.Close() return fmt.Errorf("write AnnounceRequest: %w", err) } - if tc.outboundOffers == nil { - tc.outboundOffers = make(map[string]outboundOffer) - } - tc.outboundOffers[offerIDBinary] = outboundOffer{ - peerConnection: pc, - dataChannel: dc, - originalOffer: offer, - infoHash: infoHash, + for _, offer := range offers { + generics.MakeMapIfNilAndSet(&tc.outboundOffers, offer.offerId, offer.outboundOfferValue) } return nil } @@ -288,30 +310,43 @@ if err != nil { tc.Logger.WithDefaultLevel(log.Warning).Printf("error decoding info_hash in offer: %v", err) break } - tc.handleOffer(*ar.Offer, ar.OfferID, ih, ar.PeerID) + err = tc.handleOffer(offerContext{ + SessDesc: *ar.Offer, + Id: ar.OfferID, + InfoHash: ih, + }, ar.PeerID) + if err != nil { + tc.Logger.Levelf(log.Error, "handling offer for infohash %x: %v", ih, err) + } case ar.Answer != nil: tc.handleAnswer(ar.OfferID, *ar.Answer) + default: + tc.Logger.Levelf(log.Warning, "unhandled announce response %q", message) } } +} + +type offerContext struct { + SessDesc webrtc.SessionDescription + Id string + InfoHash [20]byte } func (tc *TrackerClient) handleOffer( - offer webrtc.SessionDescription, - offerId string, - infoHash [20]byte, + offerContext offerContext, peerId string, ) error { - peerConnection, answer, err := newAnsweringPeerConnection(offer) + peerConnection, answer, err := tc.newAnsweringPeerConnection(offerContext) if err != nil { - return fmt.Errorf("write AnnounceResponse: %w", err) + return fmt.Errorf("creating answering peer connection: %w", err) } response := AnnounceResponse{ Action: "announce", - InfoHash: binaryToJsonString(infoHash[:]), + InfoHash: binaryToJsonString(offerContext.InfoHash[:]), PeerID: tc.peerIdBinary(), ToPeerID: peerId, Answer: &answer, - OfferID: offerId, + OfferID: offerContext.Id, } data, err := json.Marshal(response) if err != nil { @@ -324,27 +359,6 @@ if err := tc.writeMessage(data); err != nil { peerConnection.Close() return fmt.Errorf("writing response: %w", err) } - timer := time.AfterFunc(30*time.Second, func() { - metrics.Add("answering peer connections timed out", 1) - peerConnection.Close() - }) - peerConnection.OnDataChannel(func(d *webrtc.DataChannel) { - setDataChannelOnOpen(d, peerConnection, func(dc datachannel.ReadWriteCloser) { - timer.Stop() - metrics.Add("answering peer connection conversions", 1) - tc.mu.Lock() - tc.stats.ConvertedInboundConns++ - tc.mu.Unlock() - tc.OnConn(dc, DataChannelContext{ - Local: answer, - Remote: offer, - OfferId: offerId, - LocalOffered: false, - InfoHash: infoHash, - peerConnection: peerConnection, - }) - }) - }) return nil } @@ -358,22 +372,11 @@ return } // tc.Logger.WithDefaultLevel(log.Debug).Printf("offer %q got answer %v", offerId, answer) metrics.Add("outbound offers answered", 1) - err := offer.setAnswer(answer, func(dc datachannel.ReadWriteCloser) { - metrics.Add("outbound offers answered with datachannel", 1) - tc.mu.Lock() - tc.stats.ConvertedOutboundConns++ - tc.mu.Unlock() - tc.OnConn(dc, DataChannelContext{ - Local: offer.originalOffer, - Remote: answer, - OfferId: offerId, - LocalOffered: true, - InfoHash: offer.infoHash, - peerConnection: offer.peerConnection, - }) - }) + err := offer.peerConnection.SetRemoteDescription(answer) if err != nil { - tc.Logger.WithDefaultLevel(log.Warning).Printf("error using outbound offer answer: %v", err) + err = fmt.Errorf("using outbound offer answer: %w", err) + offer.peerConnection.span.RecordError(err) + tc.Logger.LevelPrint(log.Error, err) return } delete(tc.outboundOffers, offerId) diff --git a/webtorrent/transport.go b/webtorrent/transport.go index e4c3b73d88dd9d55094523b8992c44d7613a93d9..d60fb7a7028e5bbd28821b567b1c828d83ebaefb 100644 --- a/webtorrent/transport.go +++ b/webtorrent/transport.go @@ -1,14 +1,24 @@ package webtorrent import ( + "context" "expvar" "fmt" - "io" - "sync" - + "github.com/anacrolix/log" "github.com/anacrolix/missinggo/v2/pproffd" "github.com/pion/datachannel" "github.com/pion/webrtc/v3" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + "io" + "sync" + "time" +) + +const ( + dataChannelLabel = "webrtc-datachannel" ) var ( @@ -26,108 +36,173 @@ type wrappedPeerConnection struct { *webrtc.PeerConnection closeMu sync.Mutex pproffd.CloseWrapper + span trace.Span + ctx context.Context } func (me *wrappedPeerConnection) Close() error { me.closeMu.Lock() defer me.closeMu.Unlock() - return me.CloseWrapper.Close() + err := me.CloseWrapper.Close() + me.span.End() + return err } -func newPeerConnection() (*wrappedPeerConnection, error) { +func newPeerConnection(logger log.Logger) (*wrappedPeerConnection, error) { newPeerConnectionMu.Lock() defer newPeerConnectionMu.Unlock() + ctx, span := otel.Tracer(tracerName).Start(context.Background(), "PeerConnection") pc, err := api.NewPeerConnection(config) if err != nil { + span.SetStatus(codes.Error, err.Error()) + span.RecordError(err) + span.End() return nil, err } - return &wrappedPeerConnection{ + wpc := &wrappedPeerConnection{ PeerConnection: pc, CloseWrapper: pproffd.NewCloseWrapper(pc), - }, nil + ctx: ctx, + span: span, + } + // If the state change handler intends to call Close, it should call it on the wrapper. + wpc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + logger.Levelf(log.Warning, "webrtc PeerConnection state changed to %v", state) + span.AddEvent("connection state changed", trace.WithAttributes(attribute.String("state", state.String()))) + }) + return wpc, nil } -// newOffer creates a transport and returns a WebRTC offer to be announced -func newOffer() ( +func setAndGatherLocalDescription(peerConnection *wrappedPeerConnection, sdp webrtc.SessionDescription) (_ webrtc.SessionDescription, err error) { + gatherComplete := webrtc.GatheringCompletePromise(peerConnection.PeerConnection) + peerConnection.span.AddEvent("setting local description") + err = peerConnection.SetLocalDescription(sdp) + if err != nil { + err = fmt.Errorf("setting local description: %w", err) + return + } + <-gatherComplete + peerConnection.span.AddEvent("gathering complete") + return *peerConnection.LocalDescription(), nil +} + +// newOffer creates a transport and returns a WebRTC offer to be announced. See +// https://github.com/pion/webrtc/blob/master/examples/data-channels/jsfiddle/main.go for what this is modelled on. +func (tc *TrackerClient) newOffer( + logger log.Logger, + offerId string, + infoHash [20]byte, +) ( peerConnection *wrappedPeerConnection, dataChannel *webrtc.DataChannel, offer webrtc.SessionDescription, err error, ) { - peerConnection, err = newPeerConnection() + peerConnection, err = newPeerConnection(logger) if err != nil { return } - dataChannel, err = peerConnection.CreateDataChannel("webrtc-datachannel", nil) + + peerConnection.span.SetAttributes(attribute.String(webrtcConnTypeKey, "offer")) + + dataChannel, err = peerConnection.CreateDataChannel(dataChannelLabel, nil) if err != nil { + err = fmt.Errorf("creating data channel: %w", err) peerConnection.Close() - return } + initDataChannel(dataChannel, peerConnection, func(dc datachannel.ReadWriteCloser, dcCtx context.Context, dcSpan trace.Span) { + metrics.Add("outbound offers answered with datachannel", 1) + tc.mu.Lock() + tc.stats.ConvertedOutboundConns++ + tc.mu.Unlock() + tc.OnConn(dc, DataChannelContext{ + OfferId: offerId, + LocalOffered: true, + InfoHash: infoHash, + peerConnection: peerConnection, + Context: dcCtx, + Span: dcSpan, + }) + }) + offer, err = peerConnection.CreateOffer(nil) if err != nil { + dataChannel.Close() peerConnection.Close() return } - gatherComplete := webrtc.GatheringCompletePromise(peerConnection.PeerConnection) - err = peerConnection.SetLocalDescription(offer) + offer, err = setAndGatherLocalDescription(peerConnection, offer) if err != nil { + dataChannel.Close() peerConnection.Close() - return } - <-gatherComplete - - offer = *peerConnection.LocalDescription() return } -func initAnsweringPeerConnection( - peerConnection *wrappedPeerConnection, - offer webrtc.SessionDescription, +type onDetachedDataChannelFunc func(detached datachannel.ReadWriteCloser, ctx context.Context, span trace.Span) + +func (tc *TrackerClient) initAnsweringPeerConnection( + peerConn *wrappedPeerConnection, + offerContext offerContext, ) (answer webrtc.SessionDescription, err error) { - err = peerConnection.SetRemoteDescription(offer) + peerConn.span.SetAttributes(attribute.String(webrtcConnTypeKey, "answer")) + + timer := time.AfterFunc(30*time.Second, func() { + peerConn.span.SetStatus(codes.Error, "answer timeout") + metrics.Add("answering peer connections timed out", 1) + peerConn.Close() + }) + peerConn.OnDataChannel(func(d *webrtc.DataChannel) { + initDataChannel(d, peerConn, func(detached datachannel.ReadWriteCloser, ctx context.Context, span trace.Span) { + timer.Stop() + metrics.Add("answering peer connection conversions", 1) + tc.mu.Lock() + tc.stats.ConvertedInboundConns++ + tc.mu.Unlock() + tc.OnConn(detached, DataChannelContext{ + OfferId: offerContext.Id, + LocalOffered: false, + InfoHash: offerContext.InfoHash, + peerConnection: peerConn, + Context: ctx, + Span: span, + }) + }) + }) + + err = peerConn.SetRemoteDescription(offerContext.SessDesc) if err != nil { return } - answer, err = peerConnection.CreateAnswer(nil) + answer, err = peerConn.CreateAnswer(nil) if err != nil { return } - gatherComplete := webrtc.GatheringCompletePromise(peerConnection.PeerConnection) - err = peerConnection.SetLocalDescription(answer) - if err != nil { - return - } - <-gatherComplete - - answer = *peerConnection.LocalDescription() + answer, err = setAndGatherLocalDescription(peerConn, answer) return } -// newAnsweringPeerConnection creates a transport from a WebRTC offer and and returns a WebRTC answer to be -// announced. -func newAnsweringPeerConnection(offer webrtc.SessionDescription) ( +// newAnsweringPeerConnection creates a transport from a WebRTC offer and returns a WebRTC answer to be announced. +func (tc *TrackerClient) newAnsweringPeerConnection( + offerContext offerContext, +) ( peerConn *wrappedPeerConnection, answer webrtc.SessionDescription, err error, ) { - peerConn, err = newPeerConnection() + peerConn, err = newPeerConnection(tc.Logger) if err != nil { err = fmt.Errorf("failed to create new connection: %w", err) return } - answer, err = initAnsweringPeerConnection(peerConn, offer) + answer, err = tc.initAnsweringPeerConnection(peerConn, offerContext) if err != nil { + peerConn.span.RecordError(err) peerConn.Close() } return } -func (t *outboundOffer) setAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error { - setDataChannelOnOpen(t.dataChannel, t.peerConnection, onOpen) - err := t.peerConnection.SetRemoteDescription(answer) - return err -} - type datachannelReadWriter interface { datachannel.Reader datachannel.Writer @@ -141,29 +216,47 @@ func (me ioCloserFunc) Close() error { return me() } -func setDataChannelOnOpen( +func initDataChannel( dc *webrtc.DataChannel, pc *wrappedPeerConnection, - onOpen func(closer datachannel.ReadWriteCloser), + onOpen onDetachedDataChannelFunc, ) { + var span trace.Span + dc.OnClose(func() { + span.End() + }) dc.OnOpen(func() { + pc.span.AddEvent("data channel opened") + var ctx context.Context + ctx, span = otel.Tracer(tracerName).Start(pc.ctx, "DataChannel") raw, err := dc.Detach() if err != nil { // This shouldn't happen if the API is configured correctly, and we call from OnOpen. panic(err) } - onOpen(hookDataChannelCloser(raw, pc)) + onOpen(hookDataChannelCloser(raw, pc, span, dc), ctx, span) }) } // Hooks the datachannel's Close to Close the owning PeerConnection. The datachannel takes ownership // and responsibility for the PeerConnection. -func hookDataChannelCloser(dcrwc datachannel.ReadWriteCloser, pc *wrappedPeerConnection) datachannel.ReadWriteCloser { +func hookDataChannelCloser( + dcrwc datachannel.ReadWriteCloser, + pc *wrappedPeerConnection, + dataChannelSpan trace.Span, + originalDataChannel *webrtc.DataChannel, +) datachannel.ReadWriteCloser { return struct { datachannelReadWriter io.Closer }{ dcrwc, - ioCloserFunc(pc.Close), + ioCloserFunc(func() error { + dcrwc.Close() + pc.Close() + originalDataChannel.Close() + dataChannelSpan.End() + return nil + }), } } diff --git a/webtorrent/transport_test.go b/webtorrent/transport_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b993487dd5d6373775b1ef2617bb49b4d03b47aa --- /dev/null +++ b/webtorrent/transport_test.go @@ -0,0 +1,34 @@ +package webtorrent + +import ( + "github.com/anacrolix/log" + qt "github.com/frankban/quicktest" + "github.com/pion/webrtc/v3" + "testing" +) + +func TestClosingPeerConnectionDoesNotCloseUnopenedDataChannel(t *testing.T) { + c := qt.New(t) + var tc TrackerClient + pc, dc, _, err := tc.newOffer(log.Default, "", [20]byte{}) + c.Assert(err, qt.IsNil) + defer pc.Close() + defer dc.Close() + peerConnClosed := make(chan struct{}) + pc.OnConnectionStateChange(func(state webrtc.PeerConnectionState) { + if state == webrtc.PeerConnectionStateClosed { + close(peerConnClosed) + } + }) + dc.OnClose(func() { + // This should not be called because the DataChannel is never opened. + t.Fatal("DataChannel.OnClose handler called") + }) + t.Logf("data channel ready state before close: %v", dc.ReadyState()) + dc.OnError(func(err error) { + t.Logf("data channel error: %v", err) + }) + pc.Close() + c.Check(dc.ReadyState(), qt.Equals, webrtc.DataChannelStateClosed) + <-peerConnClosed +}