From e5fd9ede549a96e936b1692542b8753dfc1b3f90 Mon Sep 17 00:00:00 2001
From: Matt Joiner <anacrolix@gmail.com>
Date: Mon, 13 Apr 2020 14:31:39 +1000
Subject: [PATCH] Tidy up the webtorrent package, remove buffer

---
 torrent.go                                  |  2 +-
 webtorrent/buffer/buffer.go                 | 68 ---------------------
 webtorrent/{client.go => tracker_client.go} | 63 ++++---------------
 webtorrent/tracker_protocol.go              | 45 ++++++++++++++
 webtorrent/transport.go                     | 21 +++----
 wstracker.go                                |  2 +-
 6 files changed, 70 insertions(+), 131 deletions(-)
 delete mode 100644 webtorrent/buffer/buffer.go
 rename webtorrent/{client.go => tracker_client.go} (70%)
 create mode 100644 webtorrent/tracker_protocol.go

diff --git a/torrent.go b/torrent.go
index a5c51bf8..731d4956 100644
--- a/torrent.go
+++ b/torrent.go
@@ -1322,7 +1322,7 @@ func (t *Torrent) startScrapingTracker(_url string) {
 					return fmt.Sprintf("%q: %v", u.String(), m.Text())
 				}))}
 			go func() {
-				err := wst.Client.Run(t.announceRequest(tracker.Started), u.String())
+				err := wst.TrackerClient.Run(t.announceRequest(tracker.Started), u.String())
 				if err != nil {
 					t.logger.WithValues(log.Error).Printf("error running websocket tracker announcer: %v", err)
 				}
diff --git a/webtorrent/buffer/buffer.go b/webtorrent/buffer/buffer.go
deleted file mode 100644
index bac28abd..00000000
--- a/webtorrent/buffer/buffer.go
+++ /dev/null
@@ -1,68 +0,0 @@
-// 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/tracker_client.go
similarity index 70%
rename from webtorrent/client.go
rename to webtorrent/tracker_client.go
index 9d232d02..311b8cfe 100644
--- a/webtorrent/client.go
+++ b/webtorrent/tracker_client.go
@@ -1,6 +1,7 @@
 package webtorrent
 
 import (
+	"crypto/rand"
 	"encoding/json"
 	"fmt"
 	"sync"
@@ -8,14 +9,13 @@ import (
 	"github.com/anacrolix/log"
 
 	"github.com/anacrolix/torrent/tracker"
-	"github.com/anacrolix/torrent/webtorrent/buffer"
 	"github.com/gorilla/websocket"
 	"github.com/pion/datachannel"
 	"github.com/pion/webrtc/v2"
 )
 
 // Client represents the webtorrent client
-type Client struct {
+type TrackerClient struct {
 	lock           sync.Mutex
 	peerIDBinary   string
 	infoHashBinary string
@@ -28,15 +28,7 @@ type Client struct {
 // outboundOffer represents an outstanding offer.
 type outboundOffer struct {
 	originalOffer webrtc.SessionDescription
-	transport     *Transport
-}
-
-func binaryToJsonString(b []byte) string {
-	var seq []rune
-	for _, v := range b {
-		seq = append(seq, rune(v))
-	}
-	return string(seq)
+	transport     *transport
 }
 
 type DataChannelContext struct {
@@ -47,8 +39,8 @@ type DataChannelContext struct {
 
 type onDataChannelOpen func(_ datachannel.ReadWriteCloser, dcc DataChannelContext)
 
-func NewClient(peerId, infoHash [20]byte, onConn onDataChannelOpen, logger log.Logger) *Client {
-	return &Client{
+func NewClient(peerId, infoHash [20]byte, onConn onDataChannelOpen, logger log.Logger) *TrackerClient {
+	return &TrackerClient{
 		outboundOffers: make(map[string]outboundOffer),
 		peerIDBinary:   binaryToJsonString(peerId[:]),
 		infoHashBinary: binaryToJsonString(infoHash[:]),
@@ -57,7 +49,7 @@ func NewClient(peerId, infoHash [20]byte, onConn onDataChannelOpen, logger log.L
 	}
 }
 
-func (c *Client) Run(ar tracker.AnnounceRequest, url string) error {
+func (c *TrackerClient) Run(ar tracker.AnnounceRequest, url string) error {
 	t, _, err := websocket.DefaultDialer.Dial(url, nil)
 	if err != nil {
 		return fmt.Errorf("failed to dial tracker: %w", err)
@@ -75,17 +67,18 @@ func (c *Client) Run(ar tracker.AnnounceRequest, url string) error {
 	return c.trackerReadLoop()
 }
 
-func (c *Client) announce(request tracker.AnnounceRequest) error {
-	transport, offer, err := NewTransport()
+func (c *TrackerClient) announce(request tracker.AnnounceRequest) error {
+	transport, offer, err := newTransport()
 	if err != nil {
 		return fmt.Errorf("failed to create transport: %w", err)
 	}
 
-	randOfferID, err := buffer.RandomBytes(20)
+	var randOfferId [20]byte
+	_, err = rand.Read(randOfferId[:])
 	if err != nil {
 		return fmt.Errorf("failed to generate bytes: %w", err)
 	}
-	offerIDBinary := randOfferID.ToStringLatin1()
+	offerIDBinary := binaryToJsonString(randOfferId[:])
 
 	c.lock.Lock()
 	c.outboundOffers[offerIDBinary] = outboundOffer{
@@ -124,7 +117,7 @@ func (c *Client) announce(request tracker.AnnounceRequest) error {
 	return nil
 }
 
-func (c *Client) trackerReadLoop() error {
+func (c *TrackerClient) trackerReadLoop() error {
 
 	c.lock.Lock()
 	tracker := c.tracker
@@ -147,7 +140,7 @@ func (c *Client) trackerReadLoop() error {
 		}
 		switch {
 		case ar.Offer != nil:
-			_, answer, err := NewTransportFromOffer(*ar.Offer, c.onConn, ar.OfferID)
+			_, answer, err := newTransportFromOffer(*ar.Offer, c.onConn, ar.OfferID)
 			if err != nil {
 				return fmt.Errorf("write AnnounceResponse: %w", err)
 			}
@@ -195,33 +188,3 @@ func (c *Client) trackerReadLoop() error {
 		}
 	}
 }
-
-type AnnounceRequest struct {
-	Numwant    int     `json:"numwant"`
-	Uploaded   int     `json:"uploaded"`
-	Downloaded int     `json:"downloaded"`
-	Left       int64   `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/tracker_protocol.go b/webtorrent/tracker_protocol.go
new file mode 100644
index 00000000..48c460b0
--- /dev/null
+++ b/webtorrent/tracker_protocol.go
@@ -0,0 +1,45 @@
+package webtorrent
+
+import (
+	"github.com/pion/webrtc/v2"
+)
+
+type AnnounceRequest struct {
+	Numwant    int     `json:"numwant"`
+	Uploaded   int     `json:"uploaded"`
+	Downloaded int     `json:"downloaded"`
+	Left       int64   `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"`
+}
+
+// I wonder if this is a defacto standard way to decode bytes to JSON for webtorrent. I don't really
+// care.
+func binaryToJsonString(b []byte) string {
+	var seq []rune
+	for _, v := range b {
+		seq = append(seq, rune(v))
+	}
+	return string(seq)
+}
diff --git a/webtorrent/transport.go b/webtorrent/transport.go
index db2a94c9..5bdf99c0 100644
--- a/webtorrent/transport.go
+++ b/webtorrent/transport.go
@@ -12,8 +12,7 @@ import (
 
 var (
 	api = func() *webrtc.API {
-		// Enable the detach API (since it's non-standard but more idiomatic)
-		// (This should be done once globally)
+		// Enable the detach API (since it's non-standard but more idiomatic).
 		s := webrtc.SettingEngine{}
 		s.DetachDataChannels()
 		return webrtc.NewAPI(webrtc.WithSettingEngine(s))
@@ -28,15 +27,15 @@ func newPeerConnection() (*webrtc.PeerConnection, error) {
 	return api.NewPeerConnection(config)
 }
 
-type Transport struct {
+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) {
+// newTransport creates a transport and returns a WebRTC offer to be announced
+func newTransport() (*transport, webrtc.SessionDescription, error) {
 	peerConnection, err := newPeerConnection()
 	if err != nil {
 		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v\n", err)
@@ -61,13 +60,13 @@ func NewTransport() (*Transport, webrtc.SessionDescription, error) {
 		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to set local description: %v\n", err)
 	}
 
-	t := &Transport{pc: peerConnection, dc: dataChannel}
+	t := &transport{pc: peerConnection, dc: dataChannel}
 	return t, offer, nil
 }
 
-// NewTransportFromOffer creates a transport from a WebRTC offer and and returns a WebRTC answer to
+// newTransportFromOffer creates a transport from a WebRTC offer and and returns a WebRTC answer to
 // be announced.
-func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannelOpen, offerId string) (*Transport, webrtc.SessionDescription, error) {
+func newTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannelOpen, offerId string) (*transport, webrtc.SessionDescription, error) {
 	peerConnection, err := newPeerConnection()
 	if err != nil {
 		return nil, webrtc.SessionDescription{}, fmt.Errorf("failed to peer connection: %v", err)
@@ -76,7 +75,7 @@ func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannel
 		fmt.Printf("ICE Connection State has changed: %s\n", connectionState.String())
 	})
 
-	t := &Transport{pc: peerConnection}
+	t := &transport{pc: peerConnection}
 
 	err = peerConnection.SetRemoteDescription(offer)
 	if err != nil {
@@ -104,7 +103,7 @@ func NewTransportFromOffer(offer webrtc.SessionDescription, onOpen onDataChannel
 }
 
 // SetAnswer sets the WebRTC answer
-func (t *Transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
+func (t *transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(datachannel.ReadWriteCloser)) error {
 	t.handleOpen(onOpen)
 
 	err := t.pc.SetRemoteDescription(answer)
@@ -114,7 +113,7 @@ func (t *Transport) SetAnswer(answer webrtc.SessionDescription, onOpen func(data
 	return nil
 }
 
-func (t *Transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
+func (t *transport) handleOpen(onOpen func(datachannel.ReadWriteCloser)) {
 	t.lock.Lock()
 	dc := t.dc
 	t.lock.Unlock()
diff --git a/wstracker.go b/wstracker.go
index 4001b73e..8cf66e86 100644
--- a/wstracker.go
+++ b/wstracker.go
@@ -9,7 +9,7 @@ import (
 
 type websocketTracker struct {
 	url url.URL
-	*webtorrent.Client
+	*webtorrent.TrackerClient
 }
 
 func (me websocketTracker) statusLine() string {
-- 
2.51.0