murder 0.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +17 -0
  3. data/README +224 -0
  4. data/Rakefile +52 -0
  5. data/VERSION +1 -0
  6. data/dist/BitTornado/BT1/Choker.py +128 -0
  7. data/dist/BitTornado/BT1/Connecter.py +288 -0
  8. data/dist/BitTornado/BT1/Downloader.py +594 -0
  9. data/dist/BitTornado/BT1/DownloaderFeedback.py +155 -0
  10. data/dist/BitTornado/BT1/Encrypter.py +333 -0
  11. data/dist/BitTornado/BT1/FileSelector.py +245 -0
  12. data/dist/BitTornado/BT1/Filter.py +12 -0
  13. data/dist/BitTornado/BT1/HTTPDownloader.py +251 -0
  14. data/dist/BitTornado/BT1/NatCheck.py +95 -0
  15. data/dist/BitTornado/BT1/PiecePicker.py +320 -0
  16. data/dist/BitTornado/BT1/Rerequester.py +426 -0
  17. data/dist/BitTornado/BT1/Statistics.py +177 -0
  18. data/dist/BitTornado/BT1/Storage.py +584 -0
  19. data/dist/BitTornado/BT1/StorageWrapper.py +1045 -0
  20. data/dist/BitTornado/BT1/StreamCheck.py +135 -0
  21. data/dist/BitTornado/BT1/T2T.py +193 -0
  22. data/dist/BitTornado/BT1/Uploader.py +145 -0
  23. data/dist/BitTornado/BT1/__init__.py +1 -0
  24. data/dist/BitTornado/BT1/btformats.py +100 -0
  25. data/dist/BitTornado/BT1/fakeopen.py +89 -0
  26. data/dist/BitTornado/BT1/makemetafile.py +263 -0
  27. data/dist/BitTornado/BT1/track.py +1067 -0
  28. data/dist/BitTornado/ConfigDir.py +401 -0
  29. data/dist/BitTornado/ConfigReader.py +1068 -0
  30. data/dist/BitTornado/ConnChoice.py +31 -0
  31. data/dist/BitTornado/CreateIcons.py +105 -0
  32. data/dist/BitTornado/CurrentRateMeasure.py +37 -0
  33. data/dist/BitTornado/HTTPHandler.py +167 -0
  34. data/dist/BitTornado/PSYCO.py +5 -0
  35. data/dist/BitTornado/RateLimiter.py +153 -0
  36. data/dist/BitTornado/RateMeasure.py +75 -0
  37. data/dist/BitTornado/RawServer.py +195 -0
  38. data/dist/BitTornado/ServerPortHandler.py +188 -0
  39. data/dist/BitTornado/SocketHandler.py +375 -0
  40. data/dist/BitTornado/__init__.py +63 -0
  41. data/dist/BitTornado/bencode.py +319 -0
  42. data/dist/BitTornado/bitfield.py +162 -0
  43. data/dist/BitTornado/clock.py +27 -0
  44. data/dist/BitTornado/download_bt1.py +882 -0
  45. data/dist/BitTornado/inifile.py +169 -0
  46. data/dist/BitTornado/iprangeparse.py +194 -0
  47. data/dist/BitTornado/launchmanycore.py +381 -0
  48. data/dist/BitTornado/natpunch.py +254 -0
  49. data/dist/BitTornado/parseargs.py +137 -0
  50. data/dist/BitTornado/parsedir.py +150 -0
  51. data/dist/BitTornado/piecebuffer.py +86 -0
  52. data/dist/BitTornado/selectpoll.py +109 -0
  53. data/dist/BitTornado/subnetparse.py +218 -0
  54. data/dist/BitTornado/torrentlistparse.py +38 -0
  55. data/dist/BitTornado/zurllib.py +100 -0
  56. data/dist/murder_client.py +291 -0
  57. data/dist/murder_make_torrent.py +46 -0
  58. data/dist/murder_tracker.py +28 -0
  59. data/doc/examples/Capfile +28 -0
  60. data/lib/capistrano/recipes/deploy/strategy/murder.rb +52 -0
  61. data/lib/murder.rb +43 -0
  62. data/lib/murder/admin.rb +47 -0
  63. data/lib/murder/murder.rb +121 -0
  64. data/murder.gemspec +101 -0
  65. metadata +129 -0
@@ -0,0 +1,31 @@
1
+ connChoices=(
2
+ {'name':'automatic',
3
+ 'rate':{'min':0, 'max':5000, 'def': 0},
4
+ 'conn':{'min':0, 'max':100, 'def': 0},
5
+ 'automatic':1},
6
+ {'name':'unlimited',
7
+ 'rate':{'min':0, 'max':5000, 'def': 0, 'div': 50},
8
+ 'conn':{'min':4, 'max':100, 'def': 4}},
9
+ {'name':'dialup/isdn',
10
+ 'rate':{'min':3, 'max': 8, 'def': 5},
11
+ 'conn':{'min':2, 'max': 3, 'def': 2},
12
+ 'initiate': 12},
13
+ {'name':'dsl/cable slow',
14
+ 'rate':{'min':10, 'max': 48, 'def': 13},
15
+ 'conn':{'min':4, 'max': 20, 'def': 4}},
16
+ {'name':'dsl/cable fast',
17
+ 'rate':{'min':20, 'max': 100, 'def': 40},
18
+ 'conn':{'min':4, 'max': 30, 'def': 6}},
19
+ {'name':'T1',
20
+ 'rate':{'min':100, 'max': 300, 'def':150},
21
+ 'conn':{'min':4, 'max': 40, 'def':10}},
22
+ {'name':'T3+',
23
+ 'rate':{'min':400, 'max':2000, 'def':500},
24
+ 'conn':{'min':4, 'max':100, 'def':20}},
25
+ {'name':'seeder',
26
+ 'rate':{'min':0, 'max':5000, 'def':0, 'div': 50},
27
+ 'conn':{'min':1, 'max':100, 'def':1}},
28
+ {'name':'SUPER-SEED', 'super-seed':1}
29
+ )
30
+
31
+ connChoiceList = map(lambda x:x['name'], connChoices)
@@ -0,0 +1,105 @@
1
+ # Generated from bt_MakeCreateIcons - 05/10/04 22:15:33
2
+ # T-0.3.0 (BitTornado)
3
+
4
+ from binascii import a2b_base64
5
+ from zlib import decompress
6
+ from os.path import join
7
+
8
+ icons = {
9
+ "icon_bt.ico":
10
+ "eJyt1K+OFEEQx/FaQTh5GDRZhSQpiUHwCrxCBYXFrjyJLXeXEARPsZqUPMm+" +
11
+ "AlmP+PGtngoLDji69zMz2zt/qqtr1mxHv7621d4+MnvK/jl66Bl2drV+e7Wz" +
12
+ "S/v12A7rY4fDtuvOwfF4tOPXo52/fLLz+WwpWd6nqRXHKXux39sTrtnjNd7g" +
13
+ "PW7wGSd860f880kffjvJ2QYS1Zcw4AjcoaA5yRFIFDQXOgKJguZmjkCioB4T" +
14
+ "Y2CqxpTXA7sHEgVNEC8RSBQ0gfk7xtknCupgk3EEEgXlNgFHIFHQTMoRSBQ0" +
15
+ "E+1ouicKmsk7AomCJiGOQKKgSZIjkChoEucIJAqaZDoCiYImwb4iydULmqQ7" +
16
+ "AomC1kLcEQ/jSBQ0i+MIJAqaBXMEElVdi9siOgKJgmZhfWWlVjTddXW/FtsR" +
17
+ "SBQ0BeAIJAqaonAEEgVNoTgCiYKmeByBREHaqiVWRtSRrAJzBBIFTdE5AomC" +
18
+ "phBPpxPP57dVkDfrTl063nUVnWe383fZx9tb3uN+o7U+BLDtuvcQm8d/27Y/" +
19
+ "jO3o5/ay+YPv/+f6y30e1OyB7QcsGWFj",
20
+ "icon_done.ico":
21
+ "eJyt1K2OVEEQhuEaQbJyMWgyCklSEoPgFvYWKigsduRKbLndhCC4itGk5Erm" +
22
+ "Fsh4xMdbfSoMOGDpnuf89Jyf6uqaMdvRr69ttbdPzJ6xf4Eeeo6dXa3vXu/s" +
23
+ "0n49tsP62OGw7bpzcDwe7fj1aOcvn+x8PltKlg9pasVxyl7u9/aUe/Z4gxu8" +
24
+ "xy0+44Rv/Yp/vujDbxc520Ci+hYGHIF7FDQXOQKJguZGRyBR0DzMEUgU1GNi" +
25
+ "DEzVmPJ6YfdAoqAJ4hUCiYImMH/HOPtEQR1sMo5AoqDcJuAIJAqaSTkCiYJm" +
26
+ "oh1N90RBM3lHIFHQJMQRSBQ0SXIEEgVN4hyBREGTTEcgUdAk2FckuXpBk3RH" +
27
+ "IFHQWoh74mEciYJmcRyBREGzYI5AoqprcVtERyBR0Cysr6zUiqa7rh7WYjsC" +
28
+ "iYKmAByBREFTFI5AoqApFEcgUdAUjyOQKEhbtcTKiDqSVWCOQKKgKTpHIFHQ" +
29
+ "FOLpdOL9fLcK8nY9qUvHu66i8+x2/i77eHfH77h/0VofAth23Xuoz/+2bX8Y" +
30
+ "29HP7WXzB+f/5/7Lcx7V7JHtB9dPG3I=",
31
+ "black.ico":
32
+ "eJzt1zsOgkAYReFLLCztjJ2UlpLY485kOS7DpbgESwqTcQZDghjxZwAfyfl0" +
33
+ "LIieGzUWSom/pan840rHnbSUtPHHX9Je9+tAh2ybNe8TZZ/vk8ajJ4zl6JVJ" +
34
+ "+xFx+0R03Djx1/2B8bcT9L/bt0+4Wq+4se8e/VTfMvGqb4n3nYiIGz+lvt9s" +
35
+ "9EpE2T4xJN4xNFYWU6t+JWXuXDFzTom7SodSyi/S+iwtwjlJ80KaNY/C34rW" +
36
+ "aT8nvK5uhF7ohn7Yqfb87kffLAAAAAAAAAAAAAAAAAAAGMUNy7dADg==",
37
+ "blue.ico":
38
+ "eJzt10EOwUAYhuGv6cLSTux06QD2dTM9jmM4iiNYdiEZ81cIFTWddtDkfbQW" +
39
+ "De8XogtS5h9FIf+81H4jLSSt/ekvaavrdaCDez4SZV+PpPHoicBy9ErSfkQ8" +
40
+ "fCI6Hjgx6f7A+McJ+r/t95i46xMP7bf8Uz9o4k0/XMT338voP5shK0MkjXcM" +
41
+ "YSqam6Qunatyf7Nk7iztaqk8SaujNLfzIM0qKX88ZX8rWmf7Nfa+W8N61rW+" +
42
+ "7TR7fverHxYAAAAAAAAAAAAAAAAAAIziApVZ444=",
43
+ "green.ico":
44
+ "eJzt1zEOgjAAheFHGBzdjJuMHsAdbybxNB7Do3gERwaT2mJIBCOWlqok/yc4" +
45
+ "EP1fNDIoZfZRFLLPa5120krS1p72kvZ6XAeGHLtHouzrkTQePOFZDl5J2g+I" +
46
+ "+08Exz0nZt2PjH+coP/bvveEaY2L+/VN13/1PSbe9v0FfP+jTP6ziVmJkTQ+" +
47
+ "MISZaO6SujSmyu3dkpmbdKil8iptLtLSnWdpUUn58yn3t6J39l/j3tc2XM91" +
48
+ "Xd/tNHt296sfFgAAAAAAAAAAAAAAAAAATOIOVLEoDg==",
49
+ "red.ico":
50
+ "eJzt10EOwUAYhuGv6cLSTux06QD2dTOO4xiO4giWXUjG/BVCRTuddtDkfbQW" +
51
+ "De8XogtS5h9FIf+81GEjLSSt/ekvaavbdaCVez0SZd+PpPHoicBy9ErSfkQ8" +
52
+ "fCI6Hjgx6f7AeOcE/d/2QyceesaD+g1/1u+e+NwPF/H99zL6z2bIyhBJ4y1D" +
53
+ "mIb6LqlK5/a5v1syd5F2lVSepdVJmtt5lGZ7KX8+ZX8rGmfzNfa+e8N61rW+" +
54
+ "7dR7fverHxYAAAAAAAAAAAAAAAAAAIziCpgs444=",
55
+ "white.ico":
56
+ "eJzt1zsOgkAYReFLKCztjJ2ULsAed6bLcRnuwYTaJVhSmIwzGBLEiD8D+EjO" +
57
+ "p2NB9NyosVBK/C3L5B+XOmykhaS1P/6StrpfBzoUp6J5nyj7fJ80Hj1hLEev" +
58
+ "TNqPiNsnouPGib/uD4y/naD/3b59wtV6xY199+in+paJV31LvO9ERNz4KfX9" +
59
+ "ZqNXIsr2iSHxjqGxspha9Sspc+f2qXNK3FXalVJ+kVZnaR7OUZrtpbR5FP5W" +
60
+ "tE77OeF1dSP0Qjf0w06153c/+mYBAAAAAAAAAAAAAAAAAMAobj//I7s=",
61
+ "yellow.ico":
62
+ "eJzt1zsOgkAYReFLKCztjJ2ULsAedybLcRkuxSVYUpiM82M0ihGHgVFJzidY" +
63
+ "ED03vgqlzN+KQv5+qf1GWkha+9Nf0lbX60AX556ORNnXI2k8eiKwHL2StB8R" +
64
+ "D5+IjgdOTLo/MP5xgv5v+8ETd/3iYf2W/+oHTLzth4t4/3sZ/WszZGWIpPGO" +
65
+ "IUxE8yupS+eq3H9smTtLu1oqT9LqKM3tPEizSsofT9nfitbZfow979awnnWt" +
66
+ "bzvNnt/96osFAAAAAAAAAAAAAAAAAACjuABhjmIs",
67
+ "black1.ico":
68
+ "eJzt0zEOgkAUANEhFpZSGTstTWzkVt5Cj8ZROAIHMNGPWBCFDYgxMZkHn2Iz" +
69
+ "G5YCyOLKc+K54XSANbCPiSV2tOt/qjgW3XtSnN41FH/Qv29Jx/P7qefp7W8P" +
70
+ "4z85HQ+9JRG/7BpTft31DPUKyiVcFjEZzQ/TTtdzrWnKmCr6evv780qSJEmS" +
71
+ "JEmSJEmSJEmSpPnunVFDcA==",
72
+ "green1.ico":
73
+ "eJzt0zEKwkAQRuEXLCyTSuy0DHgxb6F4shzFI+QAgpkkFoombowIwvt2Z4vh" +
74
+ "X5gtFrJYRUGca/Y7WAFlVLTY0vf/1elxTwqP3xoKf5B/vjIenp+fOs+r/LWT" +
75
+ "/uQ34aGpUqQnv+1ygDqHagnHRVRG+2H6unfrtZkq6hz5evP7eSVJkiRJkiRJ" +
76
+ "kiRJkiRJ0nwNoWQ+AA==",
77
+ "yellow1.ico":
78
+ "eJzt0zEKwkAQRuEXLCxNJXZaCl7MW8Sj5SgeIQcQ4oS1UDTJxkhAeN/ubDH8" +
79
+ "C7PFQhGrLIlzx/kEW+AYFS0OpP6/atuXPSk8fKsv/EX+/cpweH5+6jyf8kn+" +
80
+ "k0fCfVPlyE/+2q2CZgP1Gi6rqILuw6R69uh1mTrqGvlmv/y8kiRJkiRJkiRJ" +
81
+ "kiRJkiRpvjsp9L8k",
82
+ "alloc.gif":
83
+ "eJxz93SzsEw0YRBh+M4ABi0MS3ue///P8H8UjIIRBhR/sjAyMDAx6IAyAihP" +
84
+ "MHAcYWDlkPHYsOBgM4ewVsyJDQsPNzEoebF8CHjo0smjH3dmRsDjI33C7Dw3" +
85
+ "MiYuOtjNyDShRSNwyemJguJJKhaGS32nGka61Vg2NJyYKRd+bY+nwtMzjbqV" +
86
+ "Qh84gxMCJgnlL4vJuqJyaa5NfFLNLsNVV2a7syacfVWkHd4bv7RN1ltM7ejm" +
87
+ "tMtNZ19Oyb02p8C3aqr3dr2GbXl/7fZyOej5rW653WZ7MzzHZV+v7O2/EZM+" +
88
+ "Pt45kbX6ScWHNWfOilo3n5thucXv8org1XF3DRQYrAEWiVY3"
89
+ }
90
+
91
+ def GetIcons():
92
+ return icons.keys()
93
+
94
+ def CreateIcon(icon, savedir):
95
+ try:
96
+ f = open(join(savedir,icon),"wb")
97
+ f.write(decompress(a2b_base64(icons[icon])))
98
+ success = 1
99
+ except:
100
+ success = 0
101
+ try:
102
+ f.close()
103
+ except:
104
+ pass
105
+ return success
@@ -0,0 +1,37 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from clock import clock
5
+
6
+ class Measure:
7
+ def __init__(self, max_rate_period, fudge = 1):
8
+ self.max_rate_period = max_rate_period
9
+ self.ratesince = clock() - fudge
10
+ self.last = self.ratesince
11
+ self.rate = 0.0
12
+ self.total = 0l
13
+
14
+ def update_rate(self, amount):
15
+ self.total += amount
16
+ t = clock()
17
+ self.rate = (self.rate * (self.last - self.ratesince) +
18
+ amount) / (t - self.ratesince + 0.0001)
19
+ self.last = t
20
+ if self.ratesince < t - self.max_rate_period:
21
+ self.ratesince = t - self.max_rate_period
22
+
23
+ def get_rate(self):
24
+ self.update_rate(0)
25
+ return self.rate
26
+
27
+ def get_rate_noupdate(self):
28
+ return self.rate
29
+
30
+ def time_until_rate(self, newrate):
31
+ if self.rate <= newrate:
32
+ return 0
33
+ t = clock() - self.ratesince
34
+ return ((self.rate * t) / newrate) - t
35
+
36
+ def get_total(self):
37
+ return self.total
@@ -0,0 +1,167 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from cStringIO import StringIO
5
+ from sys import stdout
6
+ import time
7
+ from clock import clock
8
+ from gzip import GzipFile
9
+ try:
10
+ True
11
+ except:
12
+ True = 1
13
+ False = 0
14
+
15
+ DEBUG = False
16
+
17
+ weekdays = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
18
+
19
+ months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
20
+ 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
21
+
22
+ class HTTPConnection:
23
+ def __init__(self, handler, connection):
24
+ self.handler = handler
25
+ self.connection = connection
26
+ self.buf = ''
27
+ self.closed = False
28
+ self.done = False
29
+ self.donereading = False
30
+ self.next_func = self.read_type
31
+
32
+ def get_ip(self):
33
+ return self.connection.get_ip()
34
+
35
+ def data_came_in(self, data):
36
+ if self.donereading or self.next_func is None:
37
+ return True
38
+ self.buf += data
39
+ while True:
40
+ try:
41
+ i = self.buf.index('\n')
42
+ except ValueError:
43
+ return True
44
+ val = self.buf[:i]
45
+ self.buf = self.buf[i+1:]
46
+ self.next_func = self.next_func(val)
47
+ if self.donereading:
48
+ return True
49
+ if self.next_func is None or self.closed:
50
+ return False
51
+
52
+ def read_type(self, data):
53
+ self.header = data.strip()
54
+ words = data.split()
55
+ if len(words) == 3:
56
+ self.command, self.path, garbage = words
57
+ self.pre1 = False
58
+ elif len(words) == 2:
59
+ self.command, self.path = words
60
+ self.pre1 = True
61
+ if self.command != 'GET':
62
+ return None
63
+ else:
64
+ return None
65
+ if self.command not in ('HEAD', 'GET'):
66
+ return None
67
+ self.headers = {}
68
+ return self.read_header
69
+
70
+ def read_header(self, data):
71
+ data = data.strip()
72
+ if data == '':
73
+ self.donereading = True
74
+ if self.headers.get('accept-encoding','').find('gzip') > -1:
75
+ self.encoding = 'gzip'
76
+ else:
77
+ self.encoding = 'identity'
78
+ r = self.handler.getfunc(self, self.path, self.headers)
79
+ if r is not None:
80
+ self.answer(r)
81
+ return None
82
+ try:
83
+ i = data.index(':')
84
+ except ValueError:
85
+ return None
86
+ self.headers[data[:i].strip().lower()] = data[i+1:].strip()
87
+ if DEBUG:
88
+ print data[:i].strip() + ": " + data[i+1:].strip()
89
+ return self.read_header
90
+
91
+ def answer(self, (responsecode, responsestring, headers, data)):
92
+ if self.closed:
93
+ return
94
+ if self.encoding == 'gzip':
95
+ compressed = StringIO()
96
+ gz = GzipFile(fileobj = compressed, mode = 'wb', compresslevel = 9)
97
+ gz.write(data)
98
+ gz.close()
99
+ cdata = compressed.getvalue()
100
+ if len(cdata) >= len(data):
101
+ self.encoding = 'identity'
102
+ else:
103
+ if DEBUG:
104
+ print "Compressed: %i Uncompressed: %i\n" % (len(cdata),len(data))
105
+ data = cdata
106
+ headers['Content-Encoding'] = 'gzip'
107
+
108
+ # i'm abusing the identd field here, but this should be ok
109
+ if self.encoding == 'identity':
110
+ ident = '-'
111
+ else:
112
+ ident = self.encoding
113
+ self.handler.log( self.connection.get_ip(), ident, '-',
114
+ self.header, responsecode, len(data),
115
+ self.headers.get('referer','-'),
116
+ self.headers.get('user-agent','-') )
117
+ self.done = True
118
+ r = StringIO()
119
+ r.write('HTTP/1.0 ' + str(responsecode) + ' ' +
120
+ responsestring + '\r\n')
121
+ if not self.pre1:
122
+ headers['Content-Length'] = len(data)
123
+ for key, value in headers.items():
124
+ r.write(key + ': ' + str(value) + '\r\n')
125
+ r.write('\r\n')
126
+ if self.command != 'HEAD':
127
+ r.write(data)
128
+ self.connection.write(r.getvalue())
129
+ if self.connection.is_flushed():
130
+ self.connection.shutdown(1)
131
+
132
+ class HTTPHandler:
133
+ def __init__(self, getfunc, minflush):
134
+ self.connections = {}
135
+ self.getfunc = getfunc
136
+ self.minflush = minflush
137
+ self.lastflush = clock()
138
+
139
+ def external_connection_made(self, connection):
140
+ self.connections[connection] = HTTPConnection(self, connection)
141
+
142
+ def connection_flushed(self, connection):
143
+ if self.connections[connection].done:
144
+ connection.shutdown(1)
145
+
146
+ def connection_lost(self, connection):
147
+ ec = self.connections[connection]
148
+ ec.closed = True
149
+ del ec.connection
150
+ del ec.next_func
151
+ del self.connections[connection]
152
+
153
+ def data_came_in(self, connection, data):
154
+ c = self.connections[connection]
155
+ if not c.data_came_in(data) and not c.closed:
156
+ c.connection.shutdown(1)
157
+
158
+ def log(self, ip, ident, username, header,
159
+ responsecode, length, referrer, useragent):
160
+ year, month, day, hour, minute, second, a, b, c = time.localtime(time.time())
161
+ print '%s %s %s [%02d/%3s/%04d:%02d:%02d:%02d] "%s" %i %i "%s" "%s"' % (
162
+ ip, ident, username, day, months[month], year, hour,
163
+ minute, second, header, responsecode, length, referrer, useragent)
164
+ t = clock()
165
+ if t - self.lastflush > self.minflush:
166
+ self.lastflush = t
167
+ stdout.flush()
@@ -0,0 +1,5 @@
1
+ # edit this file to enable/disable Psyco
2
+ # psyco = 1 -- enabled
3
+ # psyco = 0 -- disabled
4
+
5
+ psyco = 0
@@ -0,0 +1,153 @@
1
+ # Written by Bram Cohen
2
+ # see LICENSE.txt for license information
3
+
4
+ from traceback import print_exc
5
+ from binascii import b2a_hex
6
+ from clock import clock
7
+ from CurrentRateMeasure import Measure
8
+ from cStringIO import StringIO
9
+ from math import sqrt
10
+
11
+ try:
12
+ True
13
+ except:
14
+ True = 1
15
+ False = 0
16
+ try:
17
+ sum([1])
18
+ except:
19
+ sum = lambda a: reduce(lambda x,y: x+y, a, 0)
20
+
21
+ DEBUG = False
22
+
23
+ MAX_RATE_PERIOD = 20.0
24
+ MAX_RATE = 10e10
25
+ PING_BOUNDARY = 1.2
26
+ PING_SAMPLES = 7
27
+ PING_DISCARDS = 1
28
+ PING_THRESHHOLD = 5
29
+ PING_DELAY = 5 # cycles 'til first upward adjustment
30
+ PING_DELAY_NEXT = 2 # 'til next
31
+ ADJUST_UP = 1.05
32
+ ADJUST_DOWN = 0.95
33
+ UP_DELAY_FIRST = 5
34
+ UP_DELAY_NEXT = 2
35
+ SLOTS_STARTING = 6
36
+ SLOTS_FACTOR = 1.66/1000
37
+
38
+ class RateLimiter:
39
+ def __init__(self, sched, unitsize, slotsfunc = lambda x: None):
40
+ self.sched = sched
41
+ self.last = None
42
+ self.unitsize = unitsize
43
+ self.slotsfunc = slotsfunc
44
+ self.measure = Measure(MAX_RATE_PERIOD)
45
+ self.autoadjust = False
46
+ self.upload_rate = MAX_RATE * 1000
47
+ self.slots = SLOTS_STARTING # garbage if not automatic
48
+
49
+ def set_upload_rate(self, rate):
50
+ # rate = -1 # test automatic
51
+ if rate < 0:
52
+ if self.autoadjust:
53
+ return
54
+ self.autoadjust = True
55
+ self.autoadjustup = 0
56
+ self.pings = []
57
+ rate = MAX_RATE
58
+ self.slots = SLOTS_STARTING
59
+ self.slotsfunc(self.slots)
60
+ else:
61
+ self.autoadjust = False
62
+ if not rate:
63
+ rate = MAX_RATE
64
+ self.upload_rate = rate * 1000
65
+ self.lasttime = clock()
66
+ self.bytes_sent = 0
67
+
68
+ def queue(self, conn):
69
+ assert conn.next_upload is None
70
+ if self.last is None:
71
+ self.last = conn
72
+ conn.next_upload = conn
73
+ self.try_send(True)
74
+ else:
75
+ conn.next_upload = self.last.next_upload
76
+ self.last.next_upload = conn
77
+ self.last = conn
78
+
79
+ def try_send(self, check_time = False):
80
+ t = clock()
81
+ self.bytes_sent -= (t - self.lasttime) * self.upload_rate
82
+ self.lasttime = t
83
+ if check_time:
84
+ self.bytes_sent = max(self.bytes_sent, 0)
85
+ cur = self.last.next_upload
86
+ while self.bytes_sent <= 0:
87
+ bytes = cur.send_partial(self.unitsize)
88
+ self.bytes_sent += bytes
89
+ self.measure.update_rate(bytes)
90
+ if bytes == 0 or cur.backlogged():
91
+ if self.last is cur:
92
+ self.last = None
93
+ cur.next_upload = None
94
+ break
95
+ else:
96
+ self.last.next_upload = cur.next_upload
97
+ cur.next_upload = None
98
+ cur = self.last.next_upload
99
+ else:
100
+ self.last = cur
101
+ cur = cur.next_upload
102
+ else:
103
+ self.sched(self.try_send, self.bytes_sent / self.upload_rate)
104
+
105
+ def adjust_sent(self, bytes):
106
+ self.bytes_sent = min(self.bytes_sent+bytes, self.upload_rate*3)
107
+ self.measure.update_rate(bytes)
108
+
109
+
110
+ def ping(self, delay):
111
+ if DEBUG:
112
+ print delay
113
+ if not self.autoadjust:
114
+ return
115
+ self.pings.append(delay > PING_BOUNDARY)
116
+ if len(self.pings) < PING_SAMPLES+PING_DISCARDS:
117
+ return
118
+ if DEBUG:
119
+ print 'cycle'
120
+ pings = sum(self.pings[PING_DISCARDS:])
121
+ del self.pings[:]
122
+ if pings >= PING_THRESHHOLD: # assume flooded
123
+ if self.upload_rate == MAX_RATE:
124
+ self.upload_rate = self.measure.get_rate()*ADJUST_DOWN
125
+ else:
126
+ self.upload_rate = min(self.upload_rate,
127
+ self.measure.get_rate()*1.1)
128
+ self.upload_rate = max(int(self.upload_rate*ADJUST_DOWN),2)
129
+ self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
130
+ self.slotsfunc(self.slots)
131
+ if DEBUG:
132
+ print 'adjust down to '+str(self.upload_rate)
133
+ self.lasttime = clock()
134
+ self.bytes_sent = 0
135
+ self.autoadjustup = UP_DELAY_FIRST
136
+ else: # not flooded
137
+ if self.upload_rate == MAX_RATE:
138
+ return
139
+ self.autoadjustup -= 1
140
+ if self.autoadjustup:
141
+ return
142
+ self.upload_rate = int(self.upload_rate*ADJUST_UP)
143
+ self.slots = int(sqrt(self.upload_rate*SLOTS_FACTOR))
144
+ self.slotsfunc(self.slots)
145
+ if DEBUG:
146
+ print 'adjust up to '+str(self.upload_rate)
147
+ self.lasttime = clock()
148
+ self.bytes_sent = 0
149
+ self.autoadjustup = UP_DELAY_NEXT
150
+
151
+
152
+
153
+