From fe2fb69ed8ec378fec450a24c03f5268c2a729e9 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Sun, 5 Apr 2020 13:55:14 +1000 Subject: [PATCH] Import code https://github.com/backkem/gowttest --- go.mod | 6 +- go.sum | 58 ++++++++ webtorrent/LICENSE | 21 +++ webtorrent/buffer/buffer.go | 68 ++++++++++ webtorrent/client.go | 255 ++++++++++++++++++++++++++++++++++++ webtorrent/main.go | 20 +++ webtorrent/transport.go | 127 ++++++++++++++++++ 7 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 webtorrent/LICENSE create mode 100644 webtorrent/buffer/buffer.go create mode 100644 webtorrent/client.go create mode 100644 webtorrent/main.go create mode 100644 webtorrent/transport.go diff --git a/go.mod b/go.mod index 969cddf0..5cd62b5e 100644 --- a/go.mod +++ b/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 ) diff --git a/go.sum b/go.sum index 5d75a9dc..11e9f605 100644 --- a/go.sum +++ b/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= diff --git a/webtorrent/LICENSE b/webtorrent/LICENSE new file mode 100644 index 00000000..99d4f266 --- /dev/null +++ b/webtorrent/LICENSE @@ -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. diff --git a/webtorrent/buffer/buffer.go b/webtorrent/buffer/buffer.go new file mode 100644 index 00000000..bac28abd --- /dev/null +++ b/webtorrent/buffer/buffer.go @@ -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 +} diff --git a/webtorrent/client.go b/webtorrent/client.go new file mode 100644 index 00000000..377399cb --- /dev/null +++ b/webtorrent/client.go @@ -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"` +} diff --git a/webtorrent/main.go b/webtorrent/main.go new file mode 100644 index 00000000..f434f05d --- /dev/null +++ b/webtorrent/main.go @@ -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) + } +} diff --git a/webtorrent/transport.go b/webtorrent/transport.go new file mode 100644 index 00000000..374df116 --- /dev/null +++ b/webtorrent/transport.go @@ -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) + }) +} -- 2.48.1