r415 - in /debtorrent/trunk: DebTorrent/BT1/AptListener.py DebTorrent/launchmanycore.py debian/changelog
camrdale at users.alioth.debian.org
camrdale at users.alioth.debian.org
Thu Sep 17 03:37:08 UTC 2009
Author: camrdale
Date: Thu Sep 17 03:37:07 2009
New Revision: 415
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=415
Log:
Add a graphical display of the pieces all peers have, thanks
Steve Cotton (Closes: #515771)
Modified:
debtorrent/trunk/DebTorrent/BT1/AptListener.py
debtorrent/trunk/DebTorrent/launchmanycore.py
debtorrent/trunk/debian/changelog
Modified: debtorrent/trunk/DebTorrent/BT1/AptListener.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/AptListener.py?rev=415&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/AptListener.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/AptListener.py Thu Sep 17 03:37:07 2009
@@ -25,7 +25,7 @@
from time import time, gmtime, strftime
from DebTorrent.clock import clock
from sha import sha
-from binascii import b2a_hex
+from binascii import b2a_hex, a2b_hex
from makemetafile import TorrentCreator
from DebTorrent.HTTPCache import HTTPCache
import os, logging
@@ -230,11 +230,14 @@
self.connection_update(open_conns)
- def get_infopage(self):
+ def get_infopage(self, show_piecemaps):
"""Format the info page to display for normal browsers.
Formats the currently downloading torrents into a table in human-readable
format to display in a browser window.
+
+ @type show_piecemaps: C{string}
+ @param show_piecemaps: "svg" to include SVG piecemaps. "png" may be supported in future
@rtype: (C{int}, C{string}, C{dictionary}, C{string})
@return: the HTTP status code, status message, headers, and message body
@@ -324,12 +327,188 @@
'<li><em>distributed copies:</em> the number of copies of the complete torrent seen in non-seeding peers</li>\n' \
'</ul>\n')
+ # Draw the piece maps
+ s.write('<h3>Piece maps</h3>\n')
+ if show_piecemaps == 'svg':
+ for x in data:
+ ( name, hash, status, progress, peers, seeds, seedsmsg, dist,
+ uprate, dnrate, upamt, dnamt, httpdnamt, size, t, msg ) = x
+ s.write('<h4>%s (%s)</h4>\n' % (name, b2a_hex(hash)))
+ s.write('<a href="piecemap.svg?info_hash=%s"><object data="piecemap.svg?info_hash=%s" width="100%%" height="%d"></object></a>\n'
+ % (b2a_hex(hash), b2a_hex(hash), self.svg_height_piecemap(peers)))
+ #TODO use an accurate size for the image
+
+ s.write('<ul>\n' \
+ '<li><em>blue:</em> You have this piece</li>\n' \
+ '<li><em>purple:</em> A peer has this piece</li>\n' \
+ '<li><em>white:</em> You/Peer does not have this piece, but does have a later piece</li>\n' \
+ '<li><em>grey:</em> You/Peer does not have this piece, and does not have any later piece</li>\n' \
+ '</ul>\n')
+ else:
+ s.write('<p><a href="index.html?piecemaps=svg">Click here to show</a></p>')
+
s.write('</body>\n' \
'</html>\n')
return (200, 'OK', {'Server': VERSION, 'Content-Type': 'text/html; charset=iso-8859-1'}, s.getvalue())
except:
logger.exception('Error returning info_page')
return (500, 'Internal Server Error', {'Server': VERSION, 'Content-Type': 'text/html; charset=iso-8859-1'}, 'Server Error')
+
+
+ def svg_height_piecemap(self, number_of_peers):
+ """Returns the size (currently only height) of the piecemap image.
+
+ This has an inbuild race condition - the actual request to draw the piecemap may come in a separate request.
+
+ """
+ #TODO - move the SVG stuff to a separate class, make these class constants
+ markwidth=1
+ markheight=6
+ linepadding=1
+ lineheight=markheight + linepadding
+
+ # This works round the behaviour of Iceweasel, which stretches these images to be a minimum of 50 pixels hight
+ totalheight = lineheight * (number_of_peers+1) + linepadding
+ workaroundheight = totalheight
+ if (workaroundheight < 50):
+ workaroundheight = 50
+
+ return workaroundheight
+
+
+ def get_piecemap_for_torrent_svg(self, info_hash):
+ """Shows the piecemap (which peers have what) for a given torrent.
+
+ The HTTP request should include the appropriate torrent hash ( http://.../piecemap.svg?info_hash=... )
+
+ @type info_hash: C{string}
+ @param info_hash: the info_hash passed to the HTTP query
+
+ @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+ @return: the HTTP status code, status message, headers, and message body
+
+ """
+
+ try:
+ if not self.config['show_infopage']:
+ return (404, 'Not Found', {'Server': VERSION, 'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
+
+ if info_hash == None:
+ return (400, 'Bad request', {'Server': VERSION, 'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, 'No hash specified in request')
+ hash = a2b_hex(info_hash)
+ piecelist_list = self.handler.get_piecemap(hash)
+ if piecelist_list == None:
+ return (404, 'Not Found', {'Server': VERSION, 'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, 'Unknown hash')
+
+ s = StringIO()
+ self.svg_draw_piecemap(s, piecelist_list, ["blue", "purple"])
+
+ return (200, 'OK', {'Server': VERSION, 'Content-Type': 'image/svg+xml; charset=utf-8'}, s.getvalue())
+ except:
+ logger.exception('Error returning info_page')
+ return (500, 'Internal Server Error', {'Server': VERSION, 'Content-Type': 'text/html; charset=iso-8859-1'}, 'Server Error')
+
+ def svg_draw_piecemap(self, s, piecelist_list, color_list):
+ """Draw (in SVG) a map of which pieces are present in the piecelist
+
+ @type s: C{StringIO}
+ @param s: Output stream for data that will be sent to the webbrowser.
+ @type piecelist_list: C{List} of C{Bitfield}
+ @param piecelist_list: The bitfields representing which pieces are present.
+ @type color_list: C{List} of C{String}
+ @param color_list: List of colors for drawing the pieces. color_list[i] is used for piecelist_list[i]; if there are more piecelists than colors then the last color is reused.
+
+ """
+
+ # Constants for the size of the drawing
+ # Vertically, each line is of total height lineheight, with padding at the bottom.
+ markwidth=1
+ markheight=6
+ linepadding=1
+ lineheight=markheight + linepadding
+ fittowindow = True
+
+ # Since some piecelists have more pieces than others, find the longest.
+ mostpieces = 0
+ for piecelist in piecelist_list:
+ if (len(piecelist) > mostpieces):
+ mostpieces = len(piecelist)
+
+ # The bitfields have a lot of whitespace. Compress the drawing by showing only the pieces held by at least one peer.
+ # piecelist[piecenumber] will be drawn at pixellist[piecenumber]
+ pixellist = [0] * mostpieces
+ y = 0
+ for x in xrange(0, mostpieces):
+ someonehas = False
+ pixellist[x] = y
+ for i in xrange(0, len(piecelist_list)):
+ if (piecelist_list[i][x]):
+ someonehas = True
+ if someonehas:
+ y += 1
+
+ # And this is the width of the compressed drawing
+ imagewidth = y
+ # This works round the behaviour of Iceweasel, which stretches these images to be a minimum of 50 pixels hight
+ totalheight = lineheight * len(piecelist_list) + linepadding
+ workaroundheight = totalheight
+ if (workaroundheight < 50):
+ workaroundheight = 50
+
+ if (fittowindow):
+ s.write('<svg:svg xmlns:svg="http://www.w3.org/2000/svg" width="100%%" height="%spx" viewBox="0 0 %s %s" preserveAspectRatio="none" shape-rendering="crispEdges">\n'
+ % (workaroundheight, markwidth * imagewidth, workaroundheight))
+ else:
+ s.write('<svg:svg xmlns:svg="http://www.w3.org/2000/svg" width="%spx" height="%spx" shape-rendering="crispEdges">\n'
+ % (markwidth * imagewidth, workaroundheight))
+
+ # Draw the background in one go.
+ s.write('<svg:rect x="0" y="0" width="%spx" height="%spx" fill="black" />\n'
+ % (markwidth * imagewidth, totalheight))
+
+ color = "blue"; # Default which should be overridden by color_list[0]
+
+ for i in xrange(0, len(piecelist_list)):
+ piecelist = piecelist_list[i]
+ y = i * lineheight + linepadding # the y-coordinate that we're drawing at
+
+ # This makes each row white, on a black grid (the previous background showing through)
+ s.write('<svg:rect x="0" y="%spx" width="%spx" height="%spx" fill="white" />\n'
+ % (y, imagewidth * markwidth, markheight))
+
+ # Set default drawing color
+ if (i < len(color_list)):
+ color = color_list[i]
+ s.write('<svg:g fill="%s">\n' % (color))
+
+ # Clump the data together into runs of "we have this piece" and "we lack this piece"
+ runstart = 0
+ runtype = piecelist[0]
+ for piece in xrange (1, len(piecelist)):
+ if (piecelist[piece] != runtype):
+ # Just past the end of the current run. Draw it if it was a "we have" run.
+ if (runtype):
+ s.write('<svg:rect x="%spx" y="%spx" width="%spx" height="%spx" title="%s-%s" />\n'
+ % (pixellist[runstart] * markwidth, y, (pixellist[piece]-pixellist[runstart]) * markwidth, markheight,
+ runstart, piece-1))
+
+ # Now start counting the new run.
+ runstart = piece
+ runtype = piecelist[piece]
+
+ # Reached the end of the data, draw the final run
+ if (runtype):
+ s.write('<svg:rect x="%spx" y="%spx" width="%spx" height="%spx" title="%s-%s" />\n'
+ % (pixellist[runstart] * markwidth, y, (pixellist[piece]-pixellist[runstart]) * markwidth, markheight,
+ runstart, piece-1))
+ else:
+ # Show how far the "no pieces" bit is
+ s.write('<svg:rect x="%spx" y="%spx" width="%spx" height="%spx" fill="grey" />\n'
+ % (pixellist[runstart] * markwidth, y, (pixellist[piece]-pixellist[runstart]) * markwidth, markheight))
+
+ s.write("</svg:g>\n\n")
+
+ s.write('</svg:svg>')
def get_meow(self):
@@ -787,9 +966,12 @@
kw = unquote(s[:i])
paramslist.setdefault(kw, [])
paramslist[kw] += [unquote(s[i+1:])]
+ logger.debug('paramslist['+str(kw)+'] =='+str(paramslist[kw]))
if path == '' or path == 'index.html':
- return self.get_infopage()
+ return self.get_infopage(params('piecemaps'))
+ if path == 'piecemap.svg':
+ return self.get_piecemap_for_torrent_svg(params('info_hash'))
if path == 'meow':
return self.get_meow()
if path == 'favicon.ico':
Modified: debtorrent/trunk/DebTorrent/launchmanycore.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/launchmanycore.py?rev=415&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/launchmanycore.py (original)
+++ debtorrent/trunk/DebTorrent/launchmanycore.py Thu Sep 17 03:37:07 2009
@@ -35,6 +35,7 @@
import logging
from DebTorrent.BT1.AptListener import AptListener
from DebTorrent.HTTPHandler import HTTPHandler
+from binascii import b2a_hex, a2b_hex
logger = logging.getLogger('DebTorrent.launchmanycore')
@@ -497,6 +498,38 @@
return data
+ def get_piecemap(self, id):
+ """Return the piecemap (which peers have what) for a given torrent.
+
+ @type id: C{string}
+ @param id: Info hash for the torrent, as provided by gather_stats.
+
+ Internal: The "id" argument should match the identifier that
+ gather_stats provides in data[][1]. As of revision 373 it provides the
+ long-lived torrent ID, not the hash.
+
+ @rtype: C{list}
+ @return: List of piecemaps. The first will be for the local peer, then the other peers in undefined order.
+
+ """
+
+ hash = None
+ for search_hash in self.torrent_list:
+ if self.torrent_cache[search_hash]['metainfo'].get('identifier', search_hash) == id:
+ hash = search_hash
+
+ if hash == None:
+ logger.warning('Could not find hash for id %s\n' % b2a_hex(id))
+ return None
+
+ piecelist_list = []
+ piecelist_list.append(self.downloads[hash].d.storagewrapper.have)
+ if (self.downloads[hash].d.downloader != None):
+ for peer in self.downloads[hash].d.downloader.downloads:
+ piecelist_list.append(peer.have)
+
+ return piecelist_list
+
def remove(self, hash):
"""Stop and remove a running torrent.
Modified: debtorrent/trunk/debian/changelog
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/debian/changelog?rev=415&op=diff
==============================================================================
--- debtorrent/trunk/debian/changelog (original)
+++ debtorrent/trunk/debian/changelog Thu Sep 17 03:37:07 2009
@@ -1,11 +1,13 @@
-debtorrent (0.1.9.1) unstable; urgency=low
+debtorrent (0.2.0) unstable; urgency=low
* Prevent symlink attack when upgrading from pre 0.1.6 (Closes: #500180)
* Add a status verb to init.d scripts (Closes: #498606)
* Fix exception when server doesn't return modified time (Closes: #515753)
* Fix piuparts uninstallation failure
+ * Add a graphical display of the pieces all peers have, thanks
+ Steve Cotton (Closes: #515771)
- -- Cameron Dale <camrdale at gmail.com> Tue, 08 Sep 2009 23:16:21 -0700
+ -- Cameron Dale <camrdale at gmail.com> Wed, 16 Sep 2009 20:02:25 -0700
debtorrent (0.1.9) unstable; urgency=low
More information about the Debtorrent-commits
mailing list