]> Sergey Matveev's repositories - btrtrc.git/commitdiff
torrentfs: Write client status to HTTP port
authorMatt Joiner <anacrolix@gmail.com>
Thu, 26 Jun 2014 07:29:12 +0000 (17:29 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Thu, 26 Jun 2014 07:29:12 +0000 (17:29 +1000)
client.go
cmd/torrentfs/main.go
connection.go
torrent.go

index fc4d48ff84bab1cbec02448f94b9a286516209a7..f99d6af164f3dfe2fb9a137f9e4b3a84fb482cb5 100644 (file)
--- a/client.go
+++ b/client.go
@@ -109,6 +109,15 @@ type Client struct {
        dataWaiter chan struct{}
 }
 
+func (cl *Client) WriteStatus(w io.Writer) {
+       cl.mu.Lock()
+       defer cl.mu.Unlock()
+       for _, t := range cl.torrents {
+               fmt.Fprintf(w, "%s: %f%%\n", t.MetaInfo.Name, 100*(1-float32(t.BytesLeft())/float32(t.Length())))
+               t.WriteStatus(w)
+       }
+}
+
 // Read torrent data at the given offset. Returns ErrDataNotReady if the data
 // isn't available.
 func (cl *Client) TorrentReadAt(ih InfoHash, off int64, p []byte) (n int, err error) {
index 3fe50bcd02048578e17253c7818e92459cb6ac46..97f916b315d464a7b0de829d2493e1486d932239 100644 (file)
@@ -26,7 +26,7 @@ var (
        mountDir        string
        disableTrackers = flag.Bool("disableTrackers", false, "disables trackers")
        testPeer        = flag.String("testPeer", "", "the address for a test peer")
-       pprofAddr       = flag.String("pprofAddr", "", "pprof HTTP server bind address")
+       httpAddr        = flag.String("httpAddr", "", "HTTP server bind address")
        readaheadBytes  = flag.Int("readaheadBytes", 10*1024*1024, "bytes to readahead in each torrent from the last read piece")
        testPeerAddr    *net.TCPAddr
 )
@@ -120,8 +120,8 @@ func main() {
                os.Exit(2)
        }
        log.SetFlags(log.LstdFlags | log.Lshortfile)
-       if *pprofAddr != "" {
-               go http.ListenAndServe(*pprofAddr, nil)
+       if *httpAddr != "" {
+               go http.ListenAndServe(*httpAddr, nil)
        }
        conn, err := fuse.Mount(mountDir)
        if err != nil {
@@ -136,6 +136,9 @@ func main() {
                DisableTrackers:  *disableTrackers,
                DownloadStrategy: &torrent.ResponsiveDownloadStrategy{*readaheadBytes},
        }
+       http.DefaultServeMux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
+               client.WriteStatus(w)
+       })
        client.Start()
        addTorrentDir(client, torrentPath)
        resolveTestPeerAddr()
index df774451888f8981a5d56a51fdf97eee41d3b250..a0e45ec36fe22dcef0ede2a2017a56f7a0d66140 100644 (file)
@@ -3,6 +3,8 @@ package torrent
 import (
        "container/list"
        "encoding"
+       "fmt"
+       "io"
        "log"
        "net"
        "sync"
@@ -34,6 +36,47 @@ type connection struct {
        PeerMaxRequests int // Maximum pending requests the peer allows.
 }
 
+func (cn *connection) completedString() string {
+       if cn.PeerPieces == nil {
+               return "?"
+       }
+       f := float32(cn.piecesPeerHasCount()) / float32(cn.totalPiecesCount())
+       return fmt.Sprintf("%d%%", int(f*100))
+}
+
+func (cn *connection) totalPiecesCount() int {
+       return len(cn.PeerPieces)
+}
+
+func (cn *connection) piecesPeerHasCount() (count int) {
+       for _, has := range cn.PeerPieces {
+               if has {
+                       count++
+               }
+       }
+       return
+}
+
+func (cn *connection) WriteStatus(w io.Writer) {
+       fmt.Fprintf(w, "%q: %s-%s: %s completed: ", cn.PeerId, cn.Socket.LocalAddr(), cn.Socket.RemoteAddr(), cn.completedString())
+       c := func(b byte) {
+               fmt.Fprintf(w, "%c", b)
+       }
+       // https://trac.transmissionbt.com/wiki/PeerStatusText
+       if len(cn.Requests) != 0 {
+               c('D')
+       } else if cn.Interested {
+               c('d')
+       }
+       if !cn.PeerChoked && !cn.Interested {
+               c('K')
+       }
+       if !cn.Choked && !cn.PeerInterested {
+               c('?')
+       }
+       fmt.Fprintln(w)
+}
+
 func (c *connection) Close() {
        c.mu.Lock()
        if c.closed {
index cbb4cd3221cf9494eb158f57dfbb9fa70e6d067c..1e3cf91bbb79e419dfa02e3d2e017fc603bf234f 100644 (file)
@@ -3,6 +3,7 @@ package torrent
 import (
        "container/list"
        "fmt"
+       "io"
        "net"
        "sort"
 
@@ -38,6 +39,37 @@ type torrent struct {
        lastReadPiece int
 }
 
+func (t *torrent) pieceStatusChar(index int) byte {
+       p := t.Pieces[index]
+       switch {
+       case p.Complete():
+               return 'C'
+       case p.QueuedForHash:
+               return 'Q'
+       case p.Hashing:
+               return 'H'
+       case t.PiecePartiallyDownloaded(index):
+               return 'P'
+       default:
+               return '.'
+       }
+}
+
+func (t *torrent) WriteStatus(w io.Writer) {
+       fmt.Fprint(w, "Pieces: ")
+       for index := range t.Pieces {
+               fmt.Fprintf(w, "%c", t.pieceStatusChar(index))
+       }
+       fmt.Fprintln(w)
+       fmt.Fprintln(w, "Priorities: ")
+       for e := t.Priorities.Front(); e != nil; e = e.Next() {
+               fmt.Fprintf(w, "\t%v\n", e.Value)
+       }
+       for _, c := range t.Conns {
+               c.WriteStatus(w)
+       }
+}
+
 func (t *torrent) String() string {
        return t.MetaInfo.Name
 }
@@ -49,6 +81,10 @@ func (t *torrent) BytesLeft() (left int64) {
        return
 }
 
+func (t *torrent) PiecePartiallyDownloaded(index int) bool {
+       return t.PieceNumPendingBytes(pp.Integer(index)) != t.PieceLength(pp.Integer(index))
+}
+
 func NumChunksForPiece(chunkSize int, pieceSize int) int {
        return (pieceSize + chunkSize - 1) / chunkSize
 }