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.
- 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 | 
            +
             |