[Debtorrent-commits] r79 - in /debtorrent/branches/http-listen/DebTorrent: BT1/track.py zurllib.py
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Sat Jun 2 05:01:04 UTC 2007
Author: camrdale-guest
Date: Sat Jun 2 05:01:03 2007
New Revision: 79
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=79
Log:
More documentation.
Modified:
debtorrent/branches/http-listen/DebTorrent/BT1/track.py
debtorrent/branches/http-listen/DebTorrent/zurllib.py
Modified: debtorrent/branches/http-listen/DebTorrent/BT1/track.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/BT1/track.py?rev=79&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/BT1/track.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/BT1/track.py Sat Jun 2 05:01:03 2007
@@ -1,8 +1,22 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Tools to track a download swarm.
+
+ at type defaults: C{list} of (C[string}, C{unknown}, C{string})
+ at var defaults: the parameter names, default values, and descriptions
+ at type alas: C{string}
+ at var alas: the message to send when the data is not found
+ at type local_IPs: L{DebTorrent.subnetparse.IP_List}
+ at var local_IPs: the list of IP subnets that are considered local
+ at type http_via_filter: C{Regular Expression}
+ at var http_via_filter: the regular expression object to search 'via'
+ header information for the NAT IP address
+
+"""
from DebTorrent.parseargs import parseargs, formatDefinitions
from DebTorrent.RawServer import RawServer, autodetect_ipv6, autodetect_socket_style
@@ -100,6 +114,14 @@
]
def statefiletemplate(x):
+ """Check the saved state file for corruption.
+
+ @type x: C{dictionary}
+ @param x: the dictionary of information retrieved from the state file
+ @raise ValueError: if the state file info is corrupt
+
+ """
+
if type(x) != DictType:
raise ValueError
for cname, cinfo in x.items():
@@ -159,6 +181,16 @@
def isotime(secs = None):
+ """Create an ISO formatted string of the time.
+
+ @type secs: C{float}
+ @param secs: number of seconds since the epoch
+ (optional, default is to use the current time)
+ @rtype: C{string}
+ @return: the ISO formatted string representation of the time
+
+ """
+
if secs == None:
secs = time()
return strftime('%Y-%m-%d %H:%M UTC', gmtime(secs))
@@ -166,6 +198,15 @@
http_via_filter = re.compile(' for ([0-9.]+)\Z')
def _get_forwarded_ip(headers):
+ """Extract the unNATed IP address from the headers.
+
+ @type headers: C{dictionary}
+ @param headers: the headers received from the client
+ @rtype: C{string}
+ @return: the extracted IP address
+
+ """
+
header = headers.get('x-forwarded-for')
if header:
try:
@@ -192,12 +233,33 @@
return header
def get_forwarded_ip(headers):
+ """Extract the unNATed IP address from the headers.
+
+ @type headers: C{dictionary}
+ @param headers: the headers received from the client
+ @rtype: C{string}
+ @return: the extracted IP address (or None if one could not be extracted)
+
+ """
+
x = _get_forwarded_ip(headers)
if not is_valid_ip(x) or local_IPs.includes(x):
return None
return x
def compact_peer_info(ip, port):
+ """Create a compact representation of peer contact info.
+
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type port: C{int}
+ @param port: the port number to contact the peer on
+ @rtype: C{string}
+ @return: the compact representation (or the empty string if there is no
+ compact representation)
+
+ """
+
try:
s = ( ''.join([chr(int(i)) for i in ip.split('.')])
+ chr((port & 0xFF00) >> 8) + chr(port & 0xFF) )
@@ -208,7 +270,122 @@
return s
class Tracker:
+ """Track a download swarm.
+
+ @type config: C{dictionary}
+ @ivar config: the configuration parameters
+ @type response_size: unknown
+ @ivar response_size: unknown
+ @type dfile: C{string}
+ @ivar dfile: the state file to use when saving the current state
+ @type natcheck: C{int}
+ @ivar natcheck: how many times to check if a downloader is behind a NAT
+ @type parse_dir_interval: C{int}
+ @ivar parse_dir_interval: seconds between reloading of the allowed
+ directory or file, and the lists of allowed and banned IPs
+ @type favicon: C{string}
+ @ivar favicon: file containing x-icon data
+ @type rawserver: L{DebTorrent.RawServer}
+ @ivar rawserver: the server to use for scheduling
+ @type cached: unknown
+ @ivar cached: unknown
+ @type cached_t: unknown
+ @ivar cached_t: unknown
+ @type times: unknown
+ @ivar times: unknown
+ @type state: C{dictionary}
+ @ivar state: the current state information for the tracking
+ @type seedcount: unknown
+ @ivar seedcount: unknown
+ @type allowed_IPs: unknown
+ @ivar allowed_IPs: unknown
+ @type banned_IPs: unknown
+ @ivar banned_IPs: unknown
+ @type allowed_ip_mtime: unknown
+ @ivar allowed_ip_mtime: unknown
+ @type banned_ip_mtime: unknown
+ @ivar banned_ip_mtime: unknown
+ @type only_local_override_ip: C{boolean}
+ @ivar only_local_override_ip: whether to ignore the "ip" parameter from
+ machines which aren't on local network IPs
+ @type downloads: unknown
+ @ivar downloads: unknown
+ @type completed: unknown
+ @ivar completed: unknown
+ @type becache: C{dictionary}
+ @ivar becache: keys are the infohashes, values are the sets of peers.
+ peer set format::
+ [[l0, s0], [l1, s1], ...]
+ l,s = lists of leechers and seeders
+ l0,s0 = compact representation, don't require crypto
+ l1,s1 = compact representation, support crypto
+ l2,s2 = [compact representation, crypto_flag], for all peers
+ additionally, if --compact_reqd = 0:
+ l3,s3 = [ip,port,peerid] for all peers
+ l4,l4 = [ip,port] for all peers
+ @type cache_default: unknown
+ @ivar cache_default: unknown
+ @type trackerid: unknown
+ @ivar trackerid: unknown
+ @type reannounce_interval: C{int}
+ @ivar reannounce_interval: seconds downloaders should wait between reannouncements
+ @type save_dfile_interval: C{int}
+ @ivar save_dfile_interval: seconds between saving the state file
+ @type show_names: C{boolean}
+ @ivar show_names: whether to display names from allowed dir
+ @type prevtime: unknown
+ @ivar prevtime: unknown
+ @type timeout_downloaders_interval: C{int}
+ @ivar timeout_downloaders_interval: seconds between expiring downloaders
+ @type logfile: unknown
+ @ivar logfile: unknown
+ @type log: unknown
+ @ivar log: unknown
+ @type allow_get: unknown
+ @ivar allow_get: unknown
+ @type t2tlist: L{T2T.T2TList}
+ @ivar t2tlist: unknown
+ @type allowed: unknown
+ @ivar allowed: unknown
+ @type allowed_list_mtime: unknown
+ @ivar allowed_list_mtime: unknown
+ @type allowed_dir_files: unknown
+ @ivar allowed_dir_files: unknown
+ @type allowed_dir_blocked: unknown
+ @ivar allowed_dir_blocked: unknown
+ @type uq_broken: unknown
+ @ivar uq_broken: unknown
+ @type keep_dead: unknown
+ @ivar keep_dead: unknown
+ @type Filter: unknown
+ @ivar Filter: unknown
+ @type is_aggregator: unknown
+ @ivar is_aggregator: unknown
+ @type aggregator_key: unknown
+ @ivar aggregator_key: unknown
+ @type aggregate_forward: unknown
+ @ivar aggregate_forward: unknown
+ @type aggregate_password: unknown
+ @ivar aggregate_password: unknown
+ @type dedicated_seed_id: unknown
+ @ivar dedicated_seed_id: unknown
+ @type is_seeded: unknown
+ @ivar is_seeded: unknown
+ @type cachetime: unknown
+ @ivar cachetime: unknown
+
+ """
+
def __init__(self, config, rawserver):
+ """Initialize the instance.
+
+ @type config: C{dictionary}
+ @param config: the configuration parameters
+ @type rawserver: L{DebTorrent.RawServer}
+ @param rawserver: the server to use for scheduling
+
+ """
+
self.config = config
self.response_size = config['response_size']
self.dfile = config['dfile']
@@ -261,14 +438,7 @@
self.completed = self.state.setdefault('completed', {})
self.becache = {}
- ''' format: infohash: [[l0, s0], [l1, s1], ...]
- l0,s0 = compact, not requirecrypto=1
- l1,s1 = compact, only supportcrypto=1
- l2,s2 = [compact, crypto_flag], all peers
- if --compact_reqd 0:
- l3,s3 = [ip,port,id]
- l4,l4 = [ip,port] nopeerid
- '''
+
if config['compact_reqd']:
x = 3
else:
@@ -320,6 +490,19 @@
if config['hupmonitor']:
def huphandler(signum, frame, self = self):
+ """Function to handle SIGHUP signals.
+
+ Reopens the log file when a SIGHUP is received.
+
+ @type signum: unknown
+ @param signum: ignored
+ @type frame: unknown
+ @param frame: ignored
+ @type self: L{Track}
+ @param self: the Track instance to reopen the log of
+
+ """
+
try:
self.log.close ()
self.log = open(self.logfile,'a')
@@ -397,10 +580,18 @@
self.cachetimeupdate()
def cachetimeupdate(self):
+ """Update the L{cachetime} every second."""
self.cachetime += 1 # raw clock, but more efficient for cache
self.rawserver.add_task(self.cachetimeupdate,1)
def aggregate_senddata(self, query):
+ """Fork sending data to a tracker aggregator.
+
+ @type query: C{string}
+ @param query: the query to send
+
+ """
+
url = self.aggregate_forward+'?'+query
if self.aggregate_password is not None:
url += '&password='+self.aggregate_password
@@ -408,8 +599,17 @@
rq.setDaemon(False)
rq.start()
- def _aggregate_senddata(self, url): # just send, don't attempt to error check,
- try: # discard any returned data
+ def _aggregate_senddata(self, url):
+ """Send a URL request to a tracker data aggregator.
+
+ Just send, don't attempt to error check, discard any returned data.
+
+ @type url: C{string}
+ @param url: the URL to request
+
+ """
+
+ try:
h = urlopen(url)
h.read()
h.close()
@@ -418,6 +618,16 @@
def get_infopage(self):
+ """Format the info page to display for normal browsers.
+
+ Formats the currently tracked torrents into a table in human-readable
+ format to display in a browser window.
+
+ @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', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
@@ -510,6 +720,18 @@
def scrapedata(self, hash, return_name = True):
+ """Retrieve the scrape data for a single torrent.
+
+ @type hash: C{string}
+ @param hash: the infohash of the torrent to get scrape data for
+ @type return_name: C{boolean}
+ @param return_name: whether to return the name of the torrent
+ (optional, defaults to True)
+ @rtype: C{dictionary}
+ @return: the scrape data for the torrent
+
+ """
+
l = self.downloads[hash]
n = self.completed.get(hash, 0)
c = self.seedcount[hash]
@@ -520,6 +742,16 @@
return (f)
def get_scrape(self, paramslist):
+ """Get the scrape data for all the active torrents.
+
+ @type paramslist: C{dictionary}
+ @param paramslist: the query parameters from the GET request
+ @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+ @return: the HTTP status code, status message, headers, and bencoded
+ message body
+
+ """
+
fs = {}
if paramslist.has_key('info_hash'):
if self.config['scrape_allowed'] not in ['specific', 'full']:
@@ -549,19 +781,41 @@
def get_file(self, hash):
- if not self.allow_get:
- return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
- 'get function is not available with this tracker.')
- if not self.allowed.has_key(hash):
- return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
- fname = self.allowed[hash]['file']
- fpath = self.allowed[hash]['path']
- return (200, 'OK', {'Content-Type': 'application/x-debtorrent',
- 'Content-Disposition': 'attachment; filename=' + fname},
- open(fpath, 'rb').read())
+ """Get the metainfo file for a torrent.
+
+ @type hash: C{string}
+ @param hash: the infohash of the torrent to get the metainfo of
+ @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+ @return: the HTTP status code, status message, headers, and bencoded
+ metainfo file
+
+ """
+
+ if not self.allow_get:
+ return (400, 'Not Authorized', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'},
+ 'get function is not available with this tracker.')
+ if not self.allowed.has_key(hash):
+ return (404, 'Not Found', {'Content-Type': 'text/plain', 'Pragma': 'no-cache'}, alas)
+ fname = self.allowed[hash]['file']
+ fpath = self.allowed[hash]['path']
+ return (200, 'OK', {'Content-Type': 'application/x-debtorrent',
+ 'Content-Disposition': 'attachment; filename=' + fname},
+ open(fpath, 'rb').read())
def check_allowed(self, infohash, paramslist):
+ """Determine whether the tracker is tracking this torrent.
+
+ @type infohash: C{string}
+ @param infohash: the infohash of the torrent to check
+ @type paramslist: C{dictionary}
+ @param paramslist: the query parameters from the GET request
+ @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+ @return: the HTTP status code, status message, headers, and bencoded
+ message body if the request is not allowed, or None if it is
+
+ """
+
if ( self.aggregator_key is not None
and not ( paramslist.has_key('password')
and paramslist['password'][0] == self.aggregator_key ) ):
@@ -595,12 +849,42 @@
def add_data(self, infohash, event, ip, paramslist):
+ """Add the received data from the peer to the cache.
+
+ @type infohash: C{string}
+ @param infohash: the infohash of the torrent to add to
+ @type event: C{string}
+ @param event: the type of event being reported by the peer
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type paramslist: C{dictionary}
+ @param paramslist: the query parameters from the GET request
+ @rtype: C{int}
+ @return: the number of peers to return in a peer list
+ @raise ValueError: if the request from the peer is corrupt
+
+ """
+
peers = self.downloads.setdefault(infohash, {})
ts = self.times.setdefault(infohash, {})
self.completed.setdefault(infohash, 0)
self.seedcount.setdefault(infohash, 0)
def params(key, default = None, l = paramslist):
+ """Get the user parameter, or the default.
+
+ @type key: C{string}
+ @param key: the parameter to get
+ @type default: C{string}
+ @param default: the default value to use if no parameter is set
+ (optional, defaults to None)
+ @type l: C{dictionary}
+ @param l: the user parameters (optional, defaults to L{paramslist})
+ @rtype: C{string}
+ @return: the parameter's value
+
+ """
+
if l.has_key(key):
return l[key][0]
return default
@@ -740,6 +1024,28 @@
def peerlist(self, infohash, stopped, tracker, is_seed,
return_type, rsize, supportcrypto):
+ """Create a list of peers to return to the client.
+
+ @type infohash: C{string}
+ @param infohash: the infohash of the torrent to get peers from
+ @type stopped: C{boolean}
+ @param stopped: whether the peer has stopped
+ @type tracker: C{boolean}
+ @param tracker: whether the peer is a tracker
+ @type is_seed: C{boolean}
+ @param is_seed: whether the peer is currently seeding
+ @type return_type: C{int}
+ @param return_type: the format of the list to return (compact, ...)
+ @type rsize: C{int}
+ @param rsize: the number of peers to return
+ @type supportcrypto: C{boolean}
+ @param supportcrypto: whether the peer supports encrypted communication
+ (not used)
+ @rtype: C{dictionary}
+ @return: the info to return to the client
+
+ """
+
data = {} # return data
seeds = self.seedcount[infohash]
data['complete'] = seeds
@@ -840,6 +1146,23 @@
def get(self, connection, path, headers):
+ """Respond to a GET request to the tracker.
+
+ Process a GET request from a peer/tracker/browser. Process the request,
+ calling the helper functions above if needed. Return the response to
+ be returned to the requester.
+
+ @type connection: unknown
+ @param connection: the conection the request came in on
+ @type path: C{string}
+ @param path: the URL being requested
+ @type headers: C{dictionary}
+ @param headers: the headers from the request
+ @rtype: (C{int}, C{string}, C{dictionary}, C{string})
+ @return: the HTTP status code, status message, headers, and message body
+
+ """
+
real_ip = connection.get_ip()
ip = real_ip
if is_ipv4(ip):
@@ -868,6 +1191,20 @@
paramslist = {}
def params(key, default = None, l = paramslist):
+ """Get the user parameter, or the default.
+
+ @type key: C{string}
+ @param key: the parameter to get
+ @type default: C{string}
+ @param default: the default value to use if no parameter is set
+ (optional, defaults to None)
+ @type l: C{dictionary}
+ @param l: the user parameters (optional, defaults to L{paramslist})
+ @rtype: C{string}
+ @return: the parameter's value
+
+ """
+
if l.has_key(key):
return l[key][0]
return default
@@ -962,6 +1299,21 @@
def natcheckOK(self, infohash, peerid, ip, port, peer):
+ """Add the unNATed peer to the cache.
+
+ @type infohash: C{string}
+ @param infohash: the infohash of the torrent the peer is in
+ @type peerid: C{string}
+ @param peerid: the peer ID of the peer
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type port: C{int}
+ @param port: the port to contact the peer on
+ @type peer: C{dictionary}
+ @param peer: various information about the peer
+
+ """
+
seed = not peer['left']
bc = self.becache.setdefault(infohash,self.cache_default)
cp = compact_peer_info(ip, port)
@@ -978,12 +1330,40 @@
def natchecklog(self, peerid, ip, port, result):
+ """Log the results of any NAT checks performed.
+
+ @type peerid: C{string}
+ @param peerid: the peer ID of the peer
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type port: C{int}
+ @param port: the port to contact the peer on
+ @type result: C{int}
+ @param result: the HTTP status code result of the NAT check
+
+ """
+
year, month, day, hour, minute, second, a, b, c = localtime(time())
print '%s - %s [%02d/%3s/%04d:%02d:%02d:%02d] "!natcheck-%s:%i" %i 0 - -' % (
ip, quote(peerid), day, months[month], year, hour, minute, second,
ip, port, result)
def connectback_result(self, result, downloadid, peerid, ip, port):
+ """Process a NAT check attempt and result.
+
+ @type result: C{boolean}
+ @param result: whether the NAT check was successful
+ @type downloadid: C{string}
+ @param downloadid: the infohash of the torrent the peer is in
+ @type peerid: C{string}
+ @param peerid: the peer ID of the peer
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type port: C{int}
+ @param port: the port to contact the peer on
+
+ """
+
record = self.downloads.get(downloadid,{}).get(peerid)
if ( record is None
or (record['ip'] != ip and record.get('given ip') != ip)
@@ -1009,6 +1389,7 @@
def remove_from_state(self, *l):
+ """Remove all the input parameter names from the current state."""
for s in l:
try:
del self.state[s]
@@ -1016,6 +1397,7 @@
pass
def save_state(self):
+ """Save the state file to disk."""
self.rawserver.add_task(self.save_state, self.save_dfile_interval)
h = open(self.dfile, 'wb')
h.write(bencode(self.state))
@@ -1023,6 +1405,7 @@
def parse_allowed(self):
+ """Periodically parse the directory and list for allowed torrents."""
self.rawserver.add_task(self.parse_allowed, self.parse_dir_interval)
if self.config['allowed_dir']:
@@ -1057,6 +1440,7 @@
def read_ip_lists(self):
+ """Periodically parse the allowed and banned IPs lists."""
self.rawserver.add_task(self.read_ip_lists,self.parse_dir_interval)
f = self.config['allowed_ips']
@@ -1079,6 +1463,15 @@
def delete_peer(self, infohash, peerid):
+ """Delete all cached data for the peer.
+
+ @type infohash: C{string}
+ @param infohash: the infohash of the torrent to delete the peer from
+ @type peerid: C{string}
+ @param peerid: the peer ID of the peer to delete
+
+ """
+
dls = self.downloads[infohash]
peer = dls[peerid]
if not peer['left']:
@@ -1093,6 +1486,7 @@
del dls[peerid]
def expire_downloaders(self):
+ """Periodically remove all old downloaders from the cached data."""
for x in self.times.keys():
for myid, t in self.times[x].items():
if t < self.prevtime:
@@ -1109,6 +1503,13 @@
def track(args):
+ """Start the server and tracker.
+
+ @type args: C{list}
+ @param args: the command line arguments to the tracker
+
+ """
+
if len(args) == 0:
print formatDefinitions(defaults, 80)
return
@@ -1128,6 +1529,15 @@
print '# Shutting down: ' + isotime()
def size_format(s):
+ """Format a byte size for reading by the user.
+
+ @type s: C{long}
+ @param s: the number of bytes
+ @rtype: C{string}
+ @return: the formatted size with appropriate units
+
+ """
+
if (s < 1024):
r = str(s) + 'B'
elif (s < 1048576):
Modified: debtorrent/branches/http-listen/DebTorrent/zurllib.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/branches/http-listen/DebTorrent/zurllib.py?rev=79&op=diff
==============================================================================
--- debtorrent/branches/http-listen/DebTorrent/zurllib.py (original)
+++ debtorrent/branches/http-listen/DebTorrent/zurllib.py Sat Jun 2 05:01:03 2007
@@ -1,8 +1,17 @@
# Written by John Hoffman
# Modified by Cameron Dale
# see LICENSE.txt for license information
+#
+# $Id$
-# $Id$
+"""A high-level fetcher for WWW data, similar to the urllib module.
+
+ at type VERSION: C{string}
+ at var VERSION: the User-Agent header to send on all connections
+ at type MAX_REDIRECTS: C{int}
+ at var MAX_REDIRECTS: the maximum number of redirects to follow
+
+"""
from httplib import HTTPConnection, HTTPSConnection, HTTPException
from urlparse import urlparse
@@ -17,16 +26,22 @@
MAX_REDIRECTS = 10
-class btHTTPcon(HTTPConnection): # attempt to add automatic connection timeout
+class btHTTPcon(HTTPConnection):
+ """Attempt to add automatic connection timeout to HTTPConnection."""
+
def connect(self):
+ """Redefine the connect to include a socket timeout."""
HTTPConnection.connect(self)
try:
self.sock.settimeout(30)
except:
pass
-class btHTTPScon(HTTPSConnection): # attempt to add automatic connection timeout
+class btHTTPScon(HTTPSConnection):
+ """Attempt to add automatic connection timeout to HTTPSConnection."""
+
def connect(self):
+ """Redefine the connect to include a socket timeout."""
HTTPSConnection.connect(self)
try:
self.sock.settimeout(30)
@@ -34,12 +49,41 @@
pass
class urlopen:
+ """Opens a URL for reading.
+
+ @type tries: C{int}
+ @ivar tries: the number of attempts to open it so far
+ @type error_return: C{dictionary}
+ @ivar error_return: the bencoded returned data if an error occurred
+ @type connection: L{btHTTPcon} or L{btHTTPScon}
+ @ivar connection: the connection to the server
+ @type response: C{httplib.HTTPResponse}
+ @ivar response: the response from the server
+
+ """
+
def __init__(self, url):
+ """Initialize the instance and call the open method.
+
+ @type url: C{string}
+ @param url: the URL to open
+
+ """
+
self.tries = 0
self._open(url.strip())
self.error_return = None
def _open(self, url):
+ """Open a connection and request the URL, saving the response.
+
+ @type url: C{string}
+ @param url: the URL to open
+ @raise IOError: if there was a problem with the URL, or if the
+ server returned an error
+
+ """
+
self.tries += 1
if self.tries > MAX_REDIRECTS:
raise IOError, ('http error', 500,
@@ -84,11 +128,24 @@
raise IOError, ('http error', status, self.response.reason)
def read(self):
+ """Read the response data from the previous request.
+
+ @rtype: C{string}
+ @return: the response, or the error if an error occurred
+
+ """
if self.error_return:
return self.error_return
return self._read()
def _read(self):
+ """Read the response data and maybe decompress it.
+
+ @rtype: C{string}
+ @return: the processed response data
+
+ """
+
data = self.response.read()
if self.response.getheader('Content-Encoding','').find('gzip') >= 0:
try:
@@ -100,4 +157,5 @@
return data
def close(self):
+ """Closes the connection to the server."""
self.connection.close()
More information about the Debtorrent-commits
mailing list