This commit is contained in:
Matt Joiner 2020-04-05 13:55:14 +10:00
parent 6920298770
commit fe2fb69ed8
7 changed files with 553 additions and 2 deletions

6
go.mod
View File

@ -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
View File

@ -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=

21
webtorrent/LICENSE Normal file
View File

@ -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.

View File

@ -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
}

255
webtorrent/client.go Normal file
View File

@ -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"`
}

20
webtorrent/main.go Normal file
View File

@ -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)
}
}

127
webtorrent/transport.go Normal file
View File

@ -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)
})
}