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