Merge branch 'master' of https://git.maemo.org/projects/feedingit
[feedingit] / src / httpprogresshandler.py
1 #!/usr/bin/env python2.5
2
3 # Copyright (c) 2011 Neal H. Walfield <neal@walfield.org>
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 import urllib2 
19 import httplib
20 import time
21
22 class ProgressSocket(object):
23     """
24     Monitor what is being sent and received.
25     """
26     def __init__(self, socket, connection):
27         self.socket = socket
28         self.connection = connection
29
30     def __getattribute__(self, attr):
31         # print "%s.__getattribute__(%s)" % (self.__class__.__name__, attr)
32
33         def send(data):
34             # 100k at a time.
35             bs = 100 * 1024
36             sent = 0
37             while sent < len (data):
38                 remaining = len (data) - sent
39                 if remaining < bs:
40                     amount = remaining
41                 else:
42                     amount = bs
43         
44                 self.socket.sendall(data[sent:sent+amount])
45                 sent += amount
46                 self.connection.stats['sent'] += amount
47                 self.connection.opener.stats['sent'] += amount
48         
49                 if self.connection.callback is not None:
50                     self.connection.callback ()
51         
52         def read(*args, **kwargs):
53             data = self.socket.read (*args, **kwargs)
54             # print "GOT: %s" % (data[0:240],)
55             self.connection.stats['received'] += len (data)
56             self.connection.opener.stats['received'] += len (data)
57             if self.connection.callback is not None:
58                 self.connection.callback ()
59             return data
60
61         if attr == 'send' or attr == 'sendall':
62             return send
63         if attr == 'read':
64             return read
65
66         try:
67             return super (ProgressSocket, self).__getattribute__(attr)
68         except AttributeError:
69             socket = super (ProgressSocket, self).__getattribute__('socket')
70             return socket.__getattribute__(attr)
71
72     def makefile(self, mode, bufsize):
73         return ProgressSocket (socket=self.socket.makefile(mode, bufsize),
74                                connection=self.connection)
75
76     def close(self):
77         return self.socket.close ()
78
79 def HTTPProgressConnectionBuilder(callback, opener):
80     class HTTPProgressConnection(httplib.HTTPConnection):
81         def __init__(self, *args, **kwargs):
82             self.method = None
83             self.url = None
84             return httplib.HTTPConnection.__init__ (self, *args, **kwargs)
85
86         def putrequest(self, method, url, *args, **kwargs):
87             self.method = method
88             self.url = url
89             return httplib.HTTPConnection.putrequest (
90                 self, method, url, *args, **kwargs)
91
92         def connect(self):
93             httplib.HTTPConnection.connect(self)
94             # Wrap the socket.
95             self.sock = ProgressSocket(socket=self.sock,
96                                        connection=self)
97
98     HTTPProgressConnection.callback = callback
99     HTTPProgressConnection.opener = opener
100     HTTPProgressConnection.stats \
101         = {'sent': 0, 'received': 0, 'started':time.time()}
102     return HTTPProgressConnection
103
104 class HTTPProgressHandler(urllib2.HTTPHandler):
105     def __init__(self, callback):
106         self.callback = callback
107         self.stats = {'sent': 0, 'received': 0, 'started':time.time()}
108         return urllib2.HTTPHandler.__init__(self)
109
110     def http_open(self, request):
111         return self.do_open(
112             HTTPProgressConnectionBuilder(self.callback, self),
113             request)
114
115 if __name__ == '__main__':
116     def callback(connection):
117         req = ""
118         if connection.method:
119             req += connection.method + " "
120         req += connection.host + ':' + str (connection.port)
121         if connection.url:
122             req += connection.url
123
124         cstats = connection.stats
125         ostats = connection.opener.stats
126
127         print (("%s: connection: %d sent, %d received: %d kb/s; "
128                 + "opener: %d sent, %d received, %d kb/s")
129                % (req,
130                   cstats['sent'], cstats['received'],
131                   ((cstats['sent'] + cstats['received'])
132                    / (time.time() - cstats['started']) / 1024),
133                   ostats['sent'], ostats['received'],
134                   ((ostats['sent'] + ostats['received'])
135                    / (time.time() - ostats['started']) / 1024)))
136
137     opener = urllib2.build_opener(HTTPProgressHandler(callback))
138
139     data = opener.open ('http://google.com')
140     downloaded = 0
141     for d in data:
142         downloaded += len (d)
143     print "Document is %d bytes in size" % (downloaded,)