murder 0.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/LICENSE +17 -0
- data/README +224 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/dist/BitTornado/BT1/Choker.py +128 -0
- data/dist/BitTornado/BT1/Connecter.py +288 -0
- data/dist/BitTornado/BT1/Downloader.py +594 -0
- data/dist/BitTornado/BT1/DownloaderFeedback.py +155 -0
- data/dist/BitTornado/BT1/Encrypter.py +333 -0
- data/dist/BitTornado/BT1/FileSelector.py +245 -0
- data/dist/BitTornado/BT1/Filter.py +12 -0
- data/dist/BitTornado/BT1/HTTPDownloader.py +251 -0
- data/dist/BitTornado/BT1/NatCheck.py +95 -0
- data/dist/BitTornado/BT1/PiecePicker.py +320 -0
- data/dist/BitTornado/BT1/Rerequester.py +426 -0
- data/dist/BitTornado/BT1/Statistics.py +177 -0
- data/dist/BitTornado/BT1/Storage.py +584 -0
- data/dist/BitTornado/BT1/StorageWrapper.py +1045 -0
- data/dist/BitTornado/BT1/StreamCheck.py +135 -0
- data/dist/BitTornado/BT1/T2T.py +193 -0
- data/dist/BitTornado/BT1/Uploader.py +145 -0
- data/dist/BitTornado/BT1/__init__.py +1 -0
- data/dist/BitTornado/BT1/btformats.py +100 -0
- data/dist/BitTornado/BT1/fakeopen.py +89 -0
- data/dist/BitTornado/BT1/makemetafile.py +263 -0
- data/dist/BitTornado/BT1/track.py +1067 -0
- data/dist/BitTornado/ConfigDir.py +401 -0
- data/dist/BitTornado/ConfigReader.py +1068 -0
- data/dist/BitTornado/ConnChoice.py +31 -0
- data/dist/BitTornado/CreateIcons.py +105 -0
- data/dist/BitTornado/CurrentRateMeasure.py +37 -0
- data/dist/BitTornado/HTTPHandler.py +167 -0
- data/dist/BitTornado/PSYCO.py +5 -0
- data/dist/BitTornado/RateLimiter.py +153 -0
- data/dist/BitTornado/RateMeasure.py +75 -0
- data/dist/BitTornado/RawServer.py +195 -0
- data/dist/BitTornado/ServerPortHandler.py +188 -0
- data/dist/BitTornado/SocketHandler.py +375 -0
- data/dist/BitTornado/__init__.py +63 -0
- data/dist/BitTornado/bencode.py +319 -0
- data/dist/BitTornado/bitfield.py +162 -0
- data/dist/BitTornado/clock.py +27 -0
- data/dist/BitTornado/download_bt1.py +882 -0
- data/dist/BitTornado/inifile.py +169 -0
- data/dist/BitTornado/iprangeparse.py +194 -0
- data/dist/BitTornado/launchmanycore.py +381 -0
- data/dist/BitTornado/natpunch.py +254 -0
- data/dist/BitTornado/parseargs.py +137 -0
- data/dist/BitTornado/parsedir.py +150 -0
- data/dist/BitTornado/piecebuffer.py +86 -0
- data/dist/BitTornado/selectpoll.py +109 -0
- data/dist/BitTornado/subnetparse.py +218 -0
- data/dist/BitTornado/torrentlistparse.py +38 -0
- data/dist/BitTornado/zurllib.py +100 -0
- data/dist/murder_client.py +291 -0
- data/dist/murder_make_torrent.py +46 -0
- data/dist/murder_tracker.py +28 -0
- data/doc/examples/Capfile +28 -0
- data/lib/capistrano/recipes/deploy/strategy/murder.rb +52 -0
- data/lib/murder.rb +43 -0
- data/lib/murder/admin.rb +47 -0
- data/lib/murder/murder.rb +121 -0
- data/murder.gemspec +101 -0
- 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,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
|
+
|