summaryrefslogtreecommitdiffstats
path: root/third_party/pyftpdlib/demo/throttled_ftpd.py
blob: dd3395c7ebf02ed0aed52c27c5a95a8b996187fb (plain)
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()