From cf31465904a58259c8395e71fb449d7002d3354b Mon Sep 17 00:00:00 2001 From: Matt Joiner Date: Thu, 26 Jun 2014 17:29:12 +1000 Subject: [PATCH] torrentfs: Write client status to HTTP port --- client.go | 9 +++++++++ cmd/torrentfs/main.go | 9 ++++++--- connection.go | 43 +++++++++++++++++++++++++++++++++++++++++++ torrent.go | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index fc4d48ff..f99d6af1 100644 --- 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) { diff --git a/cmd/torrentfs/main.go b/cmd/torrentfs/main.go index 3fe50bcd..97f916b3 100644 --- a/cmd/torrentfs/main.go +++ b/cmd/torrentfs/main.go @@ -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() diff --git a/connection.go b/connection.go index df774451..a0e45ec3 100644 --- a/connection.go +++ b/connection.go @@ -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 { diff --git a/torrent.go b/torrent.go index cbb4cd32..1e3cf91b 100644 --- a/torrent.go +++ b/torrent.go @@ -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 } -- 2.48.1