Import code https://github.com/backkem/gowttest
This commit is contained in:
parent
6920298770
commit
fe2fb69ed8
6
go.mod
6
go.mod
|
@ -22,13 +22,15 @@ require (
|
|||
github.com/elliotchance/orderedmap v1.2.0
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/google/btree v1.0.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jessevdk/go-flags v1.4.0
|
||||
github.com/mattn/go-sqlite3 v2.0.2+incompatible
|
||||
github.com/pion/datachannel v1.4.16
|
||||
github.com/pion/webrtc/v2 v2.2.4
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/tinylib/msgp v1.1.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.4
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa // indirect
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
)
|
||||
|
|
58
go.sum
58
go.sum
|
@ -117,6 +117,8 @@ github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoN
|
|||
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/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/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -160,6 +162,7 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
|
|||
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/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/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
|
@ -180,6 +183,8 @@ github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKp
|
|||
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
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.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd h1:1e+0Z+T4t1mKL5xxvxXh5FkjuiToQGKreCobLu7lR3Y=
|
||||
github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8=
|
||||
github.com/gosuri/uilive v0.0.3 h1:kvo6aB3pez9Wbudij8srWo4iY6SFTTxTKOkb+uRCE8I=
|
||||
|
@ -214,8 +219,12 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
|
|||
github.com/kr/pty v1.1.1/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/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9 h1:tbuodUh2vuhOVZAdW3NEUvosFHUMJwUNl7jk/VSEiwc=
|
||||
github.com/lucas-clemente/quic-go v0.7.1-0.20190401152353-907071221cf9/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
|
||||
github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236 h1:kYGljja/OYeRs672gWqkHNMDVAtLN8/0UZpJNDCQ3s4=
|
||||
github.com/lukechampine/stm v0.0.0-20191022212748-05486c32d236/go.mod h1:wTLsd5FC9rts7GkMpsPGk64CIuea+03yaLAp19Jmlg8=
|
||||
github.com/marten-seemann/qtls v0.2.3 h1:0yWJ43C62LsZt08vuQJDK1uC1czUc3FJeCLPoNAI4vA=
|
||||
github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
|
||||
github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.10 h1:qxFzApOv4WsAL965uUPIsXzAKCZxN2p9UqdhFS4ZW10=
|
||||
|
@ -240,6 +249,40 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ
|
|||
github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ=
|
||||
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pion/datachannel v1.4.16 h1:dvuDC0IBMUDQvwO+gRu0Dv+W5j7rrgNpCmtheb6iYnc=
|
||||
github.com/pion/datachannel v1.4.16/go.mod h1:gRGhxZv7X2/30Qxes4WEXtimKBXcwj/3WsDtBlHnvJY=
|
||||
github.com/pion/dtls/v2 v2.0.0-rc.7/go.mod h1:U199DvHpRBN0muE9+tVN4TMy1jvEhZIZ63lk4xkvVSk=
|
||||
github.com/pion/dtls/v2 v2.0.0-rc.9 h1:wPb0JKmYoleAM2o8vQSPaUM+geJq7l0AdeUlPsg19ec=
|
||||
github.com/pion/dtls/v2 v2.0.0-rc.9/go.mod h1:6eFkFvpo0T+odQ+39HFEtOO7LX5cUlFqXdSo4ucZtGg=
|
||||
github.com/pion/ice v0.7.10 h1:ydViRi+ZqvCzbJYNLuQoOqOJflSPVIGVC3NL6PDecm8=
|
||||
github.com/pion/ice v0.7.10/go.mod h1:YufCtyMmPeZXGTuARCracYfe0mb3EXbcRUS+vHJB5Cs=
|
||||
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
|
||||
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
|
||||
github.com/pion/mdns v0.0.4 h1:O4vvVqr4DGX63vzmO6Fw9vpy3lfztVWHGCQfyw0ZLSY=
|
||||
github.com/pion/mdns v0.0.4/go.mod h1:R1sL0p50l42S5lJs91oNdUL58nm0QHrhxnSegr++qC0=
|
||||
github.com/pion/quic v0.1.1 h1:D951FV+TOqI9A0rTF7tHx0Loooqz+nyzjEyj8o3PuMA=
|
||||
github.com/pion/quic v0.1.1/go.mod h1:zEU51v7ru8Mp4AUBJvj6psrSth5eEFNnVQK5K48oV3k=
|
||||
github.com/pion/rtcp v1.2.1 h1:S3yG4KpYAiSmBVqKAfgRa5JdwBNj4zK3RLUa8JYdhak=
|
||||
github.com/pion/rtcp v1.2.1/go.mod h1:a5dj2d6BKIKHl43EnAOIrCczcjESrtPuMgfmL6/K6QM=
|
||||
github.com/pion/rtp v1.3.2 h1:Yfzf1mU4Zmg7XWHitzYe2i+l+c68iO+wshzIUW44p1c=
|
||||
github.com/pion/rtp v1.3.2/go.mod h1:q9wPnA96pu2urCcW/sK/RiDn597bhGoAQQ+y2fDwHuY=
|
||||
github.com/pion/sctp v1.7.6 h1:8qZTdJtbKfAns/Hv5L0PAj8FyXcsKhMH1pKUCGisQg4=
|
||||
github.com/pion/sctp v1.7.6/go.mod h1:ichkYQ5tlgCQwEwvgfdcAolqx1nHbYCxo4D7zK/K0X8=
|
||||
github.com/pion/sdp/v2 v2.3.4 h1:+f3F5Xl7ynVhc9Il8Dc7BFroYJWG3PMbfWtwFlVI+kg=
|
||||
github.com/pion/sdp/v2 v2.3.4/go.mod h1:jccXVYW0fuK6ds2pwKr89SVBDYlCjhgMI6nucl5R5rA=
|
||||
github.com/pion/srtp v1.3.1 h1:WNDLN41ST0P6cXRpzx97JJW//vChAEo1+Etdqo+UMnM=
|
||||
github.com/pion/srtp v1.3.1/go.mod h1:nxEytDDGTN+eNKJ1l5gzOCWQFuksgijorsSlgEjc40Y=
|
||||
github.com/pion/stun v0.3.3 h1:brYuPl9bN9w/VM7OdNzRSLoqsnwlyNvD9MVeJrHjDQw=
|
||||
github.com/pion/stun v0.3.3/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M=
|
||||
github.com/pion/transport v0.6.0/go.mod h1:iWZ07doqOosSLMhZ+FXUTq+TamDoXSllxpbGcfkCmbE=
|
||||
github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRhSzR+CZ8=
|
||||
github.com/pion/transport v0.10.0 h1:9M12BSneJm6ggGhJyWpDveFOstJsTiQjkLf4M44rm80=
|
||||
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
|
||||
github.com/pion/turn/v2 v2.0.3 h1:SJUUIbcPoehlyZgMyIUbBBDhI03sBx32x3JuSIBKBWA=
|
||||
github.com/pion/turn/v2 v2.0.3/go.mod h1:kl1hmT3NxcLynpXVnwJgObL8C9NaCyPTeqI2DcCpSZs=
|
||||
github.com/pion/webrtc v1.2.0 h1:3LGGPQEMacwG2hcDfhdvwQPz315gvjZXOfY4vaF4+I4=
|
||||
github.com/pion/webrtc/v2 v2.2.4 h1:elWyBI/6S2kNoJ5rcTj2EMAvp/fmeiEqiuYbUd0ShRA=
|
||||
github.com/pion/webrtc/v2 v2.2.4/go.mod h1:9vTqkEISnB5AXlggrFSBLGSz/kopQuYUYzHubuFpnCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -261,6 +304,7 @@ github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R
|
|||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8=
|
||||
github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
|
||||
|
@ -283,6 +327,8 @@ github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0
|
|||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
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/syncthing/syncthing v0.14.48-rc.4/go.mod h1:nw3siZwHPA6M8iSfjDCWQ402eqvEIasMQOE8nFOxy7M=
|
||||
github.com/tinylib/msgp v1.0.2 h1:DfdQrzQa7Yh2es9SuLkixqxuXS2SxsdYn0KbdrOGWD8=
|
||||
github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
|
||||
|
@ -308,8 +354,12 @@ go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
|||
go.opencensus.io v0.20.2 h1:NAfh7zF0/3/HqtMvJNZ/RFrSlCE6ZTlHmKfhL/Dm1Jk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/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-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
|
@ -325,12 +375,17 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
|
|||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b h1:ZWpVMTsK0ey5WJCu+vVdfMldWq7/ezaOcjnKWIHWVkE=
|
||||
golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191125084936-ffdde1057850 h1:Vq85/r8R9IdcUHmZ0/nQlUg1v15rzvQ2sHdnZAj/x7s=
|
||||
golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -344,6 +399,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190910064555-bbd175535a8b h1:3S2h5FadpNr0zUUCVZjlKIEYF+KaX/OBplTGo89CYHI=
|
||||
|
@ -384,6 +441,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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=
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2019 Michiel De Backker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,68 @@
|
|||
// Package buffer mirrors the Node.JS buffer type.
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Buffer mirrors the Node.JS Buffer type.
|
||||
type Buffer struct {
|
||||
b []byte
|
||||
}
|
||||
|
||||
// New creates a new buffer from b
|
||||
func New(b []byte) *Buffer {
|
||||
return &Buffer{b: b}
|
||||
}
|
||||
|
||||
// From creates a new buffer from a string
|
||||
func From(s string) *Buffer {
|
||||
return &Buffer{b: []byte(s)}
|
||||
}
|
||||
|
||||
// FromHex creates a new buffer from a hex string.
|
||||
func FromHex(in string) (*Buffer, error) {
|
||||
decoded, err := hex.DecodeString(in)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode hex: %v", err)
|
||||
}
|
||||
return &Buffer{b: decoded}, nil
|
||||
}
|
||||
|
||||
// ToStringBase64 turns the buffer into a base64 string.
|
||||
func (b *Buffer) ToStringBase64() string {
|
||||
return base64.StdEncoding.EncodeToString(b.b)
|
||||
}
|
||||
|
||||
// ToStringLatin1 turns the buffer into a string using
|
||||
// Latin-1 supplement block and C0/C1 control codes.
|
||||
func (b *Buffer) ToStringLatin1() string {
|
||||
seq := []rune{}
|
||||
for _, v := range b.b {
|
||||
seq = append(seq, rune(v))
|
||||
}
|
||||
return string(seq)
|
||||
}
|
||||
|
||||
// ToStringHex converts the buffer to a hex string
|
||||
func (b *Buffer) ToStringHex() string {
|
||||
return hex.EncodeToString(b.b)
|
||||
}
|
||||
|
||||
// RandomBytes returns securely generated random bytes.
|
||||
// It will return an error if the system's secure random
|
||||
// number generator fails to function correctly, in which
|
||||
// case the caller should not continue.
|
||||
func RandomBytes(n int) (*Buffer, error) {
|
||||
b := make([]byte, n)
|
||||
_, err := rand.Read(b)
|
||||
// Note that err == nil only if we read len(b) bytes.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return New(b), nil
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/anacrolix/torrent/metainfo"
|
||||
"github.com/anacrolix/torrent/webtorrent/buffer"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pion/datachannel"
|
||||
"github.com/pion/webrtc/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
trackerURL = `wss://tracker.openwebtorrent.com/` // For simplicity
|
||||
)
|
||||
|
||||
// Client represents the webtorrent client
|
||||
type Client struct {
|
||||
peerID string
|
||||
peerIDBinary string
|
||||
|
||||
infoHash string
|
||||
infoHashBinary string
|
||||
totalLength int
|
||||
|
||||
offeredPeers map[string]Peer // OfferID to Peer
|
||||
|
||||
tracker *websocket.Conn
|
||||
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
// Peer represents a remote peer
|
||||
type Peer struct {
|
||||
peerID string
|
||||
transport *Transport
|
||||
}
|
||||
|
||||
func NewClient() (*Client, error) {
|
||||
c := &Client{
|
||||
offeredPeers: make(map[string]Peer),
|
||||
lock: &sync.Mutex{},
|
||||
}
|
||||
|
||||
randPeerID, err := buffer.RandomBytes(9)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate bytes: %v", err)
|
||||
}
|
||||
peerIDBuffer := buffer.From("-WW0007-" + randPeerID.ToStringBase64())
|
||||
c.peerID = peerIDBuffer.ToStringHex()
|
||||
c.peerIDBinary = peerIDBuffer.ToStringLatin1()
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Client) LoadFile(p string) error {
|
||||
meta, err := metainfo.LoadFromFile(p)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load meta info: %v\n", err)
|
||||
}
|
||||
|
||||
info, err := meta.UnmarshalInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal info: %v\n", err)
|
||||
}
|
||||
c.totalLength = int(info.TotalLength())
|
||||
|
||||
c.infoHash = meta.HashInfoBytes().String()
|
||||
b, err := buffer.FromHex(c.infoHash)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create buffer: %v\n", err)
|
||||
}
|
||||
c.infoHashBinary = b.ToStringLatin1()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Run() error {
|
||||
t, _, err := websocket.DefaultDialer.Dial(trackerURL, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to dial tracker: %v", err)
|
||||
}
|
||||
defer t.Close()
|
||||
c.tracker = t
|
||||
|
||||
go c.announce()
|
||||
c.trackerReadLoop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) announce() {
|
||||
transpot, offer, err := NewTransport()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create transport: %v\n", err)
|
||||
}
|
||||
|
||||
randOfferID, err := buffer.RandomBytes(20)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to generate bytes: %v\n", err)
|
||||
}
|
||||
// OfferID := randOfferID.ToStringHex()
|
||||
offerIDBinary := randOfferID.ToStringLatin1()
|
||||
|
||||
c.lock.Lock()
|
||||
c.offeredPeers[offerIDBinary] = Peer{transport: transpot}
|
||||
c.lock.Unlock()
|
||||
|
||||
req := AnnounceRequest{
|
||||
Numwant: 1, // If higher we need to create equal amount of offers
|
||||
Uploaded: 0,
|
||||
Downloaded: 0,
|
||||
Left: int(c.totalLength),
|
||||
Event: "started",
|
||||
Action: "announce",
|
||||
InfoHash: c.infoHashBinary,
|
||||
PeerID: c.peerIDBinary,
|
||||
Offers: []Offer{
|
||||
{
|
||||
OfferID: offerIDBinary,
|
||||
Offer: offer,
|
||||
}},
|
||||
}
|
||||
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
log.Fatal("failed to marshal request:", err)
|
||||
}
|
||||
c.lock.Lock()
|
||||
tracker := c.tracker
|
||||
err = tracker.WriteMessage(websocket.TextMessage, data)
|
||||
if err != nil {
|
||||
log.Fatal("write AnnounceRequest:", err)
|
||||
c.lock.Unlock()
|
||||
}
|
||||
c.lock.Unlock()
|
||||
}
|
||||
|
||||
func (c *Client) trackerReadLoop() {
|
||||
|
||||
c.lock.Lock()
|
||||
tracker := c.tracker
|
||||
c.lock.Unlock()
|
||||
for {
|
||||
_, message, err := tracker.ReadMessage()
|
||||
if err != nil {
|
||||
log.Fatal("read error: %v", err)
|
||||
}
|
||||
log.Printf("recv: %s", message)
|
||||
|
||||
var ar AnnounceResponse
|
||||
if err := json.Unmarshal(message, &ar); err != nil {
|
||||
log.Printf("error unmarshaling announce response: %v", err)
|
||||
continue
|
||||
}
|
||||
if ar.InfoHash != c.infoHashBinary {
|
||||
log.Printf("announce response for different hash: %s", ar.InfoHash)
|
||||
continue
|
||||
}
|
||||
switch {
|
||||
case ar.Offer != nil:
|
||||
t, answer, err := NewTransportFromOffer(*ar.Offer, c.handleDataChannel)
|
||||
if err != nil {
|
||||
log.Fatal("write AnnounceResponse:", err)
|
||||
}
|
||||
|
||||
req := AnnounceResponse{
|
||||
Action: "announce",
|
||||
InfoHash: c.infoHashBinary,
|
||||
PeerID: c.peerIDBinary,
|
||||
ToPeerID: ar.PeerID,
|
||||
Answer: &answer,
|
||||
OfferID: ar.OfferID,
|
||||
}
|
||||
data, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
log.Fatal("failed to marshal request:", err)
|
||||
}
|
||||
|
||||
c.lock.Lock()
|
||||
err = tracker.WriteMessage(websocket.TextMessage, data)
|
||||
if err != nil {
|
||||
log.Fatal("write AnnounceResponse:", err)
|
||||
c.lock.Unlock()
|
||||
}
|
||||
c.lock.Unlock()
|
||||
|
||||
// Do something with the peer
|
||||
_ = Peer{peerID: ar.PeerID, transport: t}
|
||||
case ar.Answer != nil:
|
||||
c.lock.Lock()
|
||||
peer, ok := c.offeredPeers[ar.OfferID]
|
||||
c.lock.Unlock()
|
||||
if !ok {
|
||||
fmt.Printf("could not find peer for offer %s", ar.OfferID)
|
||||
continue
|
||||
}
|
||||
err = peer.transport.SetAnswer(*ar.Answer, c.handleDataChannel)
|
||||
if err != nil {
|
||||
log.Fatal("failed to sent answer: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleDataChannel(dc datachannel.ReadWriteCloser) {
|
||||
go c.dcReadLoop(dc)
|
||||
//go c.dcWriteLoop(dc)
|
||||
}
|
||||
|
||||
func (c *Client) dcReadLoop(d io.Reader) {
|
||||
for {
|
||||
buffer := make([]byte, 1024)
|
||||
n, err := d.Read(buffer)
|
||||
if err != nil {
|
||||
log.Fatal("Datachannel closed; Exit the readloop:", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Message from DataChannel: %s\n", string(buffer[:n]))
|
||||
}
|
||||
}
|
||||
|
||||
type AnnounceRequest struct {
|
||||
Numwant int `json:"numwant"`
|
||||
Uploaded int `json:"uploaded"`
|
||||
Downloaded int `json:"downloaded"`
|
||||
Left int `json:"left"`
|
||||
Event string `json:"event"`
|
||||
Action string `json:"action"`
|
||||
InfoHash string `json:"info_hash"`
|
||||
PeerID string `json:"peer_id"`
|
||||
Offers []Offer `json:"offers"`
|
||||
}
|
||||
|
||||
type Offer struct {
|
||||
OfferID string `json:"offer_id"`
|
||||
Offer webrtc.SessionDescription `json:"offer"`
|
||||
}
|
||||
|
||||
type AnnounceResponse struct {
|
||||
InfoHash string `json:"info_hash"`
|
||||
Action string `json:"action"`
|
||||
Interval *int `json:"interval,omitempty"`
|
||||
Complete *int `json:"complete,omitempty"`
|
||||
Incomplete *int `json:"incomplete,omitempty"`
|
||||
PeerID string `json:"peer_id,omitempty"`
|
||||
ToPeerID string `json:"to_peer_id,omitempty"`
|
||||
Answer *webrtc.SessionDescription `json:"answer,omitempty"`
|
||||
Offer *webrtc.SessionDescription `json:"offer,omitempty"`
|
||||
OfferID string `json:"offer_id,omitempty"`
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wt, err := NewClient()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create client: %v", err)
|
||||
}
|
||||
err = wt.LoadFile("./sintel.torrent")
|
||||
if err != nil {
|
||||
log.Fatalf("failed to load file: %v", err)
|
||||
}
|
||||
err = wt.Run()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to run: %v", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/pion/datachannel"
|
||||
|
||||
"github.com/pion/webrtc/v2"
|
||||
)
|
||||
|
||||
type Transport struct {
|
||||
pc *webrtc.PeerConnection
|
||||
dc *webrtc.DataChannel
|
||||
|
||||
lock *sync.Mutex
|
||||
}
|
||||
|
||||
// NewTransport creates a transport and returns a WebRTC offer
|
||||
// to be announced
|
||||
func NewTransport() (*Transport, webrtc.SessionDescription, error) {
|
||||
// Enable the detach API (since it's non-standard but more idiomatic)
|
||||
// (This should be done once globally)
|
||||
s := webrtc.SettingEngine{}
|
||||
s.DetachDataChannels()
|
||||
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
||||
|
||||
config := webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
|
||||
peerConnection, err := api.NewPeerConnection(config)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v\n", err)
|
||||
}
|
||||
dataChannel, err := peerConnection.CreateDataChannel("webrtc-datachannel", nil)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to data channel: %v\n", err)
|
||||
}
|
||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||
fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
|
||||
})
|
||||
|
||||
dataChannel.OnMessage(func(msg webrtc.DataChannelMessage) {
|
||||
fmt.Printf("Message from DataChannel '%s': '%s'\n", dataChannel.Label(), string(msg.Data))
|
||||
})
|
||||
offer, err := peerConnection.CreateOffer(nil)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to create offer: %v\n", err)
|
||||
}
|
||||
err = peerConnection.SetLocalDescription(offer)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %v\n", err)
|
||||
}
|
||||
|
||||
t := &Transport{pc: peerConnection, dc: dataChannel, lock: &sync.Mutex{}}
|
||||
return t, offer, nil
|
||||
}
|
||||
|
||||
// NewTransportFromOffer creates a transport from a WebRTC offer and
|
||||
// and returns a WebRTC answer to be announced
|
||||
func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) (*Transport, webrtc.SessionDescription, error) {
|
||||
// Enable the detach API (since it's non-standard but more idiomatic)
|
||||
// (This should be done once globally)
|
||||
s := webrtc.SettingEngine{}
|
||||
s.DetachDataChannels()
|
||||
api := webrtc.NewAPI(webrtc.WithSettingEngine(s))
|
||||
|
||||
config := webrtc.Configuration{ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}}
|
||||
peerConnection, err := api.NewPeerConnection(config)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v", err)
|
||||
}
|
||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||
fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
|
||||
})
|
||||
|
||||
t := &Transport{pc: peerConnection, lock: &sync.Mutex{}}
|
||||
peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
|
||||
fmt.Printf("New DataChannel %s %d\n", d.Label(), d.ID())
|
||||
t.lock.Lock()
|
||||
t.dc = d
|
||||
t.lock.Unlock()
|
||||
t.handleOpen(onOpen)
|
||||
})
|
||||
|
||||
err = peerConnection.SetRemoteDescription(offer)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
|
||||
}
|
||||
answer, err := peerConnection.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
|
||||
}
|
||||
err = peerConnection.SetLocalDescription(answer)
|
||||
if err != nil {
|
||||
return nil, webrtc.SessionDescription{}, fmt.Errorf("%v", err)
|
||||
}
|
||||
|
||||
return t, answer, nil
|
||||
}
|
||||
|
||||
// SetAnswer sets the WebRTC answer
|
||||
func (t *Transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
|
||||
t.handleOpen(onOpen)
|
||||
|
||||
err := t.pc.SetRemoteDescription(answer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
|
||||
t.lock.Lock()
|
||||
dc := t.dc
|
||||
t.lock.Unlock()
|
||||
dc.OnOpen(func() {
|
||||
fmt.Printf("Data channel '%s'-'%d' open.\n", dc.Label(), dc.ID())
|
||||
|
||||
// Detach the data channel
|
||||
raw, err := dc.Detach()
|
||||
if err != nil {
|
||||
log.Fatalf("failed to detach: %v", err) // TODO: Error handling
|
||||
}
|
||||
|
||||
onOpen(raw)
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue