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)
}
+++ /dev/null
-// 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
-}
package webtorrent
import (
+ "crypto/rand"
"encoding/json"
"fmt"
"sync"
"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
// 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 {
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[:]),
}
}
-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)
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{
return nil
}
-func (c *Client) trackerReadLoop() error {
+func (c *TrackerClient) trackerReadLoop() error {
c.lock.Lock()
tracker := c.tracker
}
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)
}
}
}
}
-
-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"`
-}
--- /dev/null
+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)
+}
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))
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)
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)
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 {
}
// 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)
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()
type websocketTracker struct {
url url.URL
- *webtorrent.Client
+ *webtorrent.TrackerClient
}
func (me websocketTracker) statusLine() string {