1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
#!/usr/bin/env python
# throttled_ftpd.py
"""ftpd supporting bandwidth throttling capabilities for data transfer.
"""
import os
import time
import asyncore
from pyftpdlib import ftpserver
class ThrottledDTPHandler(ftpserver.DTPHandler):
"""A DTPHandler which wraps sending and receiving in a data counter
and temporarily sleeps the channel so that you burst to no more than
x Kb/sec average.
"""
# maximum number of bytes to transmit in a second (0 == no limit)
read_limit = 0
write_limit = 0
def __init__(self, sock_obj, cmd_channel):
ftpserver.DTPHandler.__init__(self, sock_obj, cmd_channel)
self._timenext = 0
self._datacount = 0
self._sleeping = False
self._throttler = None
def readable(self):
return not self._sleeping and ftpserver.DTPHandler.readable(self)
def writable(self):
return not self._sleeping and ftpserver.DTPHandler.writable(self)
def recv(self, buffer_size):
chunk = asyncore.dispatcher.recv(self, buffer_size)
if self.read_limit:
self.throttle_bandwidth(len(chunk), self.read_limit)
return chunk
def send(self, data):
num_sent = asyncore.dispatcher.send(self, data)
if self.write_limit:
self.throttle_bandwidth(num_sent, self.write_limit)
return num_sent
def throttle_bandwidth(self, len_chunk, max_speed):
"""A method which counts data transmitted so that you burst to
no more than x Kb/sec average.
"""
self._datacount += len_chunk
if self._datacount >= max_speed:
self._datacount = 0
now = time.time()
sleepfor = self._timenext - now
if sleepfor > 0:
# we've passed bandwidth limits
def unsleep():
self._sleeping = False
self._sleeping = True
self._throttler = ftpserver.CallLater(sleepfor * 2, unsleep)
self._timenext = now + 1
def close(self):
if self._throttler is not None and not self._throttler.cancelled:
self._throttler.cancel()
ftpserver.DTPHandler.close(self)
if __name__ == '__main__':
authorizer = ftpserver.DummyAuthorizer()
authorizer.add_user('user', '12345', os.getcwd(), perm='elradfmw')
authorizer.add_anonymous(os.getcwd())
# use the modified DTPHandler class and set a speed limit for both
# sending and receiving
dtp_handler = ThrottledDTPHandler
dtp_handler.read_limit = 30072 # 30 Kb/sec (30 * 1024)
dtp_handler.write_limit = 30072 # 30 Kb/sec (30 * 1024)
ftp_handler = ftpserver.FTPHandler
ftp_handler.authorizer = authorizer
# have the ftp handler use the different dtp handler
ftp_handler.dtp_handler = dtp_handler
ftpd = ftpserver.FTPServer(('', 21), ftp_handler)
ftpd.serve_forever()
|