From 4143b7470d99d22ca15a8f37add81e7178e17c96 Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Sat, 30 Jul 2016 00:37:52 +1000 Subject: [PATCH] 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. --- client.go | 66 ++++++++++++++++++------------------------------ metainfo/hash.go | 2 +- torrent.go | 10 ++++++++ 3 files changed, 35 insertions(+), 43 deletions(-) diff --git a/client.go b/client.go index d1654f8f..0e0d18eb 100644 --- a/client.go +++ b/client.go @@ -9,11 +9,9 @@ import ( "fmt" "io" "log" - "math/big" mathRand "math/rand" "net" "net/url" - "sort" "strconv" "strings" "time" @@ -85,6 +83,12 @@ type Client struct { 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 { cl.mu.Lock() defer cl.mu.Unlock() @@ -117,67 +121,45 @@ func (cl *Client) ListenAddr() net.Addr { 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) { - var hs hashSorter - for ih := range cl.torrents { - hs.Hashes = append(hs.Hashes, ih) - } - sort.Sort(hs) - for _, ih := range hs.Hashes { - ret = append(ret, cl.torrent(ih)) - } - return + return slices.Sort(slices.FromMapElems(cl.torrents), func(l, r metainfo.Hash) bool { + return l.AsString() < r.AsString() + }).([]*Torrent) } // Writes out a human readable status of the client, such as for writing to a // HTTP status page. func (cl *Client) WriteStatus(_w io.Writer) { - cl.mu.RLock() - defer cl.mu.RUnlock() w := bufio.NewWriter(_w) defer w.Flush() if addr := cl.ListenAddr(); addr != nil { - fmt.Fprintf(w, "Listening on %s\n", cl.ListenAddr()) + fmt.Fprintf(w, "Listening on %s\n", addr) } else { fmt.Fprintln(w, "Not listening!") } - fmt.Fprintf(w, "Peer ID: %+q\n", cl.peerID) - fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.badPeerIPs)) - if cl.dHT != nil { - dhtStats := cl.dHT.Stats() + fmt.Fprintf(w, "Peer ID: %+q\n", cl.PeerID()) + fmt.Fprintf(w, "Banned IPs: %d\n", len(cl.BadPeerIPs())) + if dht := cl.DHT(); dht != nil { + dhtStats := dht.Stats() 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 port: %d\n", missinggo.AddrPort(cl.dHT.Addr())) + fmt.Fprintf(w, "DHT Server ID: %x\n", dht.ID()) + fmt.Fprintf(w, "DHT port: %d\n", missinggo.AddrPort(dht.Addr())) fmt.Fprintf(w, "DHT announces: %d\n", dhtStats.ConfirmedAnnounces) 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) - for _, t := range cl.sortedTorrents() { - if t.name() == "" { + for _, t := range slices.Sorted(cl.Torrents(), func(l, r *Torrent) bool { + return l.InfoHash().AsString() < r.InfoHash().AsString() + }).([]*Torrent) { + if t.Name() == "" { fmt.Fprint(w, "") } else { - fmt.Fprint(w, t.name()) + fmt.Fprint(w, t.Name()) } fmt.Fprint(w, "\n") - if t.haveInfo() { - fmt.Fprintf(w, "%f%% of %d bytes (%s)", 100*(1-float64(t.bytesLeft())/float64(t.length)), t.length, humanize.Bytes(uint64(t.length))) + if t.Info() != nil { + 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 { w.WriteString("") } diff --git a/metainfo/hash.go b/metainfo/hash.go index c755468c..000179ee 100644 --- a/metainfo/hash.go +++ b/metainfo/hash.go @@ -13,7 +13,7 @@ func (h Hash) Bytes() []byte { return h[:] } -func (h *Hash) AsString() string { +func (h Hash) AsString() string { return string(h[:]) } diff --git a/torrent.go b/torrent.go index eefee14c..da0c92bb 100644 --- a/torrent.go +++ b/torrent.go @@ -488,6 +488,12 @@ func (t *Torrent) newMetaInfo() (mi *metainfo.MetaInfo) { return } +func (t *Torrent) BytesMissing() int64 { + t.mu().RLock() + defer t.mu().RUnlock() + return t.bytesLeft() +} + func (t *Torrent) bytesLeft() (left int64) { for i := 0; i < t.numPieces(); i++ { left += int64(t.pieces[i].bytesLeft()) @@ -1366,3 +1372,7 @@ func (t *Torrent) SetMaxEstablishedConns(max int) (oldMax int) { t.openNewConns() return oldMax } + +func (t *Torrent) mu() missinggo.RWLocker { + return &t.cl.mu +}