Begin exposing all the magic that Client.WriteStatus has so people can parse Client state their own way
It wasn't possible to move Client.WriteStatus to an external package to verify it doesn't depend on same-package access to data because then it can't be used in the tests, and it's extremely useful there. So I've settled for not locking the Client, and trying to use all the public methods. It's a work in progress.
This commit is contained in:
parent
49315045a0
commit
4143b7470d
66
client.go
66
client.go
|
@ -9,11 +9,9 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/big"
|
|
||||||
mathRand "math/rand"
|
mathRand "math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -85,6 +83,12 @@ type Client struct {
|
||||||
torrents map[metainfo.Hash]*Torrent
|
torrents map[metainfo.Hash]*Torrent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cl *Client) BadPeerIPs() []string {
|
||||||
|
cl.mu.RLock()
|
||||||
|
defer cl.mu.RUnlock()
|
||||||
|
return slices.FromMapKeys(cl.badPeerIPs).([]string)
|
||||||
|
}
|
||||||
|
|
||||||
func (cl *Client) IPBlockList() iplist.Ranger {
|
func (cl *Client) IPBlockList() iplist.Ranger {
|
||||||
cl.mu.Lock()
|
cl.mu.Lock()
|
||||||
defer cl.mu.Unlock()
|
defer cl.mu.Unlock()
|
||||||
|
@ -117,67 +121,45 @@ func (cl *Client) ListenAddr() net.Addr {
|
||||||
return torrentAddr(cl.listenAddr)
|
return torrentAddr(cl.listenAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type hashSorter struct {
|
|
||||||
Hashes []metainfo.Hash
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs hashSorter) Len() int {
|
|
||||||
return len(hs.Hashes)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs hashSorter) Less(a, b int) bool {
|
|
||||||
return (&big.Int{}).SetBytes(hs.Hashes[a][:]).Cmp((&big.Int{}).SetBytes(hs.Hashes[b][:])) < 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (hs hashSorter) Swap(a, b int) {
|
|
||||||
hs.Hashes[a], hs.Hashes[b] = hs.Hashes[b], hs.Hashes[a]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cl *Client) sortedTorrents() (ret []*Torrent) {
|
func (cl *Client) sortedTorrents() (ret []*Torrent) {
|
||||||
var hs hashSorter
|
return slices.Sort(slices.FromMapElems(cl.torrents), func(l, r metainfo.Hash) bool {
|
||||||
for ih := range cl.torrents {
|
return l.AsString() < r.AsString()
|
||||||
hs.Hashes = append(hs.Hashes, ih)
|
}).([]*Torrent)
|
||||||
}
|
|
||||||
sort.Sort(hs)
|
|
||||||
for _, ih := range hs.Hashes {
|
|
||||||
ret = append(ret, cl.torrent(ih))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Writes out a human readable status of the client, such as for writing to a
|
// Writes out a human readable status of the client, such as for writing to a
|
||||||
// HTTP status page.
|
// HTTP status page.
|
||||||
func (cl *Client) WriteStatus(_w io.Writer) {
|
func (cl *Client) WriteStatus(_w io.Writer) {
|
||||||
cl.mu.RLock()
|
|
||||||
defer cl.mu.RUnlock()
|
|
||||||
w := bufio.NewWriter(_w)
|
w := bufio.NewWriter(_w)
|
||||||
defer w.Flush()
|
defer w.Flush()
|
||||||
if addr := cl.ListenAddr(); addr != nil {
|
if addr := cl.ListenAddr(); addr != nil {
|
||||||
fmt.Fprintf(w, "Listening on %s\n", cl.ListenAddr())
|
fmt.Fprintf(w, "Listening on %s\n", addr)
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprintln(w, "Not listening!")
|
fmt.Fprintln(w, "Not listening!")
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "Peer ID: %+q\n", cl.peerID)
|
fmt.Fprintf(w, "Peer ID: %+q\n", cl.PeerID())
|
||||||
fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.badPeerIPs))
|
fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.BadPeerIPs()))
|
||||||
if cl.dHT != nil {
|
if dht := cl.DHT(); dht != nil {
|
||||||
dhtStats := cl.dHT.Stats()
|
dhtStats := dht.Stats()
|
||||||
fmt.Fprintf(w, "DHT nodes: %d (%d good, %d banned)\n", dhtStats.Nodes, dhtStats.GoodNodes, dhtStats.BadNodes)
|
fmt.Fprintf(w, "DHT nodes: %d (%d good, %d banned)\n", dhtStats.Nodes, dhtStats.GoodNodes, dhtStats.BadNodes)
|
||||||
fmt.Fprintf(w, "DHT Server ID: %x\n", cl.dHT.ID())
|
fmt.Fprintf(w, "DHT Server ID: %x\n", dht.ID())
|
||||||
fmt.Fprintf(w, "DHT port: %d\n", missinggo.AddrPort(cl.dHT.Addr()))
|
fmt.Fprintf(w, "DHT port: %d\n", missinggo.AddrPort(dht.Addr()))
|
||||||
fmt.Fprintf(w, "DHT announces: %d\n", dhtStats.ConfirmedAnnounces)
|
fmt.Fprintf(w, "DHT announces: %d\n", dhtStats.ConfirmedAnnounces)
|
||||||
fmt.Fprintf(w, "Outstanding transactions: %d\n", dhtStats.OutstandingTransactions)
|
fmt.Fprintf(w, "Outstanding transactions: %d\n", dhtStats.OutstandingTransactions)
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "# Torrents: %d\n", len(cl.torrents))
|
fmt.Fprintf(w, "# Torrents: %d\n", len(cl.Torrents()))
|
||||||
fmt.Fprintln(w)
|
fmt.Fprintln(w)
|
||||||
for _, t := range cl.sortedTorrents() {
|
for _, t := range slices.Sorted(cl.Torrents(), func(l, r *Torrent) bool {
|
||||||
if t.name() == "" {
|
return l.InfoHash().AsString() < r.InfoHash().AsString()
|
||||||
|
}).([]*Torrent) {
|
||||||
|
if t.Name() == "" {
|
||||||
fmt.Fprint(w, "<unknown name>")
|
fmt.Fprint(w, "<unknown name>")
|
||||||
} else {
|
} else {
|
||||||
fmt.Fprint(w, t.name())
|
fmt.Fprint(w, t.Name())
|
||||||
}
|
}
|
||||||
fmt.Fprint(w, "\n")
|
fmt.Fprint(w, "\n")
|
||||||
if t.haveInfo() {
|
if t.Info() != nil {
|
||||||
fmt.Fprintf(w, "%f%% of %d bytes (%s)", 100*(1-float64(t.bytesLeft())/float64(t.length)), t.length, humanize.Bytes(uint64(t.length)))
|
fmt.Fprintf(w, "%f%% of %d bytes (%s)", 100*(1-float64(t.BytesMissing())/float64(t.Info().TotalLength())), t.length, humanize.Bytes(uint64(t.Info().TotalLength())))
|
||||||
} else {
|
} else {
|
||||||
w.WriteString("<missing metainfo>")
|
w.WriteString("<missing metainfo>")
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ func (h Hash) Bytes() []byte {
|
||||||
return h[:]
|
return h[:]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Hash) AsString() string {
|
func (h Hash) AsString() string {
|
||||||
return string(h[:])
|
return string(h[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
10
torrent.go
10
torrent.go
|
@ -488,6 +488,12 @@ func (t *Torrent) newMetaInfo() (mi *metainfo.MetaInfo) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) BytesMissing() int64 {
|
||||||
|
t.mu().RLock()
|
||||||
|
defer t.mu().RUnlock()
|
||||||
|
return t.bytesLeft()
|
||||||
|
}
|
||||||
|
|
||||||
func (t *Torrent) bytesLeft() (left int64) {
|
func (t *Torrent) bytesLeft() (left int64) {
|
||||||
for i := 0; i < t.numPieces(); i++ {
|
for i := 0; i < t.numPieces(); i++ {
|
||||||
left += int64(t.pieces[i].bytesLeft())
|
left += int64(t.pieces[i].bytesLeft())
|
||||||
|
@ -1366,3 +1372,7 @@ func (t *Torrent) SetMaxEstablishedConns(max int) (oldMax int) {
|
||||||
t.openNewConns()
|
t.openNewConns()
|
||||||
return oldMax
|
return oldMax
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *Torrent) mu() missinggo.RWLocker {
|
||||||
|
return &t.cl.mu
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue