~ubuntu-branches/ubuntu/quantal/gozerbot/quantal

« back to all changes in this revision

Viewing changes to build/lib/gozerbot/rest/client.py

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Malcolm
  • Date: 2010-09-29 18:20:02 UTC
  • mfrom: (3.1.7 sid)
  • Revision ID: james.westby@ubuntu.com-20100929182002-gi532gnem1vlu6jy
Tags: 0.9.1.3-5
Added python2.5 build dependency. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# gozerbot/rest/client.py
 
2
#
 
3
#
 
4
 
 
5
""" Rest Client class """
 
6
 
 
7
# gozerbot imports
 
8
from gozerbot.utils.url import geturl3, geturl4, posturl, deleteurl, useragent
 
9
from gozerbot.utils.generic import toenc
 
10
from gozerbot.utils.log import rlog
 
11
from gozerbot.utils.exception import handle_exception, exceptionmsg
 
12
from gozerbot.utils.locking import lockdec
 
13
from gozerbot.utils.lazydict import LazyDict
 
14
 
 
15
# simple json import
 
16
from simplejson import loads
 
17
 
 
18
# basic imports
 
19
from urllib2 import HTTPError, URLError
 
20
from httplib import InvalidURL
 
21
from urlparse import urlparse
 
22
import socket, asynchat, urllib, sys, thread, re, asyncore, time
 
23
 
 
24
restlock = thread.allocate_lock()
 
25
locked = lockdec(restlock)
 
26
 
 
27
class RestResult(LazyDict):
 
28
 
 
29
    def __init__(self, url="", name=""):
 
30
        LazyDict.__init__(self)
 
31
        self.url = url
 
32
        self.name = name
 
33
        self.data = None
 
34
        self.error = None
 
35
        self.status = None
 
36
        self.reason = ""
 
37
 
 
38
class RestClient(object):
 
39
 
 
40
    def __init__(self, url, keyfile=None, certfile=None, port=None):
 
41
        if not url.endswith('/'):
 
42
            url += '/'
 
43
        try:
 
44
            u = urlparse(url)
 
45
            splitted = u[1].split(':')
 
46
            if len(splitted) == 2:
 
47
                host, port = splitted
 
48
            else:
 
49
                host = splitted[0]
 
50
                port = port or 9999
 
51
            path = u[2]
 
52
        except Exception, ex:
 
53
            raise
 
54
        self.host = host 
 
55
        try:
 
56
            self.ip = socket.gethostbyname(self.host)
 
57
        except Exception, ex:
 
58
            handle_exception()
 
59
        self.path = path
 
60
        self.port = port
 
61
        self.url = url
 
62
        self.keyfile = keyfile
 
63
        self.certfile = certfile
 
64
        self.callbacks = []
 
65
 
 
66
    def addcb(self, callback): 
 
67
        if not callback: 
 
68
            return
 
69
        self.callbacks.append(callback)
 
70
        rlog(0, self.name, 'added callback %s' % str(callback))
 
71
        return self
 
72
 
 
73
    def delcb(self, callback):
 
74
        try:
 
75
            del self.callbacks[callback]
 
76
            rlog(0, self.name, 'deleted callback %s' % str(callback))
 
77
        except ValueError:
 
78
            pass
 
79
        
 
80
    def do(self, func, url, *args, **kwargs):
 
81
        result = RestResult(url)
 
82
        try:
 
83
            rlog(5, url, "calling %s" % str(func))
 
84
            res = func(url, {}, kwargs, self.keyfile, self.certfile, self.port)
 
85
            result.status = res.status
 
86
            result.reason = res.reason
 
87
            if result.status >= 400:
 
88
                result.error = result.status
 
89
            else:
 
90
                result.error = None
 
91
            if result.status == 200:
 
92
                r = res.read()
 
93
                result.data = loads(r)
 
94
            else:
 
95
                result.data = None
 
96
            rlog(5, url, "result: %s" % str(result)) 
 
97
        except Exception, ex:
 
98
            result.error = str(ex)
 
99
            result.data = None
 
100
        for cb in self.callbacks:
 
101
            try:
 
102
                cb(self, result)
 
103
                rlog(5, self.name, 'called callback %s' % str(cb))
 
104
            except Exception, ex:
 
105
                handle_exception()
 
106
        return result
 
107
 
 
108
    def post(self, *args, **kwargs):
 
109
        return self.do(posturl, self.url, *args, **kwargs)
 
110
 
 
111
    def add(self, *args, **kwargs):
 
112
        return self.do(posturl, self.url, *args, **kwargs)
 
113
 
 
114
    def delete(self, nr=None):
 
115
         if nr:
 
116
             return self.do(deleteurl, self.url + '/' + str(nr))
 
117
         else:
 
118
             return self.do(deleteurl, self.url)
 
119
 
 
120
    def get(self, nr=None):
 
121
        if not nr:
 
122
            return self.do(geturl4, self.url)
 
123
        else:
 
124
            return self.do(geturl4, self.url + '/' + str(nr))
 
125
 
 
126
class RestClientAsync(RestClient, asynchat.async_chat):
 
127
 
 
128
    def __init__(self, url, name=""):
 
129
        RestClient.__init__(self, url)
 
130
        asynchat.async_chat.__init__(self)
 
131
        self.set_terminator("\r\n\r\n")
 
132
        self.reading_headers = True
 
133
        self.error = None
 
134
        self.buffer = ''
 
135
        self.name = name or self.url
 
136
        self.headers = {}
 
137
        self.status = None
 
138
 
 
139
    def handle_error(self):
 
140
        exctype, excvalue, tb = sys.exc_info()
 
141
        if exctype == socket.error:
 
142
            try:
 
143
                errno, errtxt = excvalue
 
144
                if errno in [11, 35, 9]:
 
145
                    rlog(10, self.name, "%s %s" % (errno, errtxt))
 
146
                    return
 
147
            except ValueError:
 
148
                pass
 
149
            self.error = str(excvalue)
 
150
        else:
 
151
            rlog(10, self.name, exceptionmsg())
 
152
            self.error = exceptionmsg()
 
153
        self.buffer = ''
 
154
        result = RestResult(self.url, self.name)
 
155
        result.error = self.error
 
156
        result.data = None
 
157
        for cb in self.callbacks:
 
158
            try:
 
159
                cb(self, result)
 
160
                rlog(0, self.name, 'called callback %s' % str(cb))
 
161
            except Exception, ex:
 
162
                handle_exception()
 
163
        self.close()
 
164
 
 
165
    def handle_expt(self):
 
166
        handle_exception()
 
167
 
 
168
    def handle_connect(self):
 
169
        rlog(10, self.name, 'connected %s' % str(self))
 
170
         
 
171
    def start(self):
 
172
        assert(self.host)
 
173
        assert(int(self.port))
 
174
        try:
 
175
            rlog(5, self.name, 'starting client')
 
176
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
 
177
            self.connect((self.ip, int(self.port)))
 
178
        except socket.error, ex:
 
179
            self.error = str(ex)
 
180
            try:
 
181
                self.connect((self.ip, int(self.port)))
 
182
            except socket.error, ex:
 
183
                self.error = str(ex)
 
184
        except Exception, ex:
 
185
            self.error = str(ex)
 
186
        if self.error:
 
187
            rlog(10, self.name, "can't start %s" % self.error)
 
188
        else:
 
189
            return True
 
190
 
 
191
    @locked
 
192
    def found_terminator(self):
 
193
        rlog(10, self.name, 'found terminator')
 
194
        if self.reading_headers:
 
195
            self.reading_headers = False
 
196
            try:
 
197
                self.headers = self.buffer.split('\r\n')
 
198
                self.status = int(self.headers[0].split()[1])
 
199
            except (ValueError, IndexError):
 
200
                rlog(10, self.name, "can't parse headers %s" % self.headers)
 
201
                return
 
202
            self.set_terminator(None)
 
203
            self.buffer = ''
 
204
            rlog(5, self.name, 'headers: %s' % self.headers)
 
205
 
 
206
    def collect_incoming_data(self, data):
 
207
        self.buffer = self.buffer + data
 
208
 
 
209
    def handle_close(self):
 
210
        self.reading_headers = False
 
211
        self.handle_incoming()
 
212
        rlog(10, self.name, 'closed')
 
213
        self.close()
 
214
      
 
215
    def handle_incoming(self):
 
216
        rlog(5, self.name, "incoming: " + self.buffer)
 
217
        if not self.reading_headers:
 
218
            result = RestResult(self.url, self.name)
 
219
            if self.status >= 400:
 
220
                rlog(10, self.name, 'error status: %s' % self.status)  
 
221
                result.error = self.status
 
222
                result.data = None
 
223
            elif self.error:
 
224
                result.error = self.error
 
225
                result.data = None
 
226
            elif self.buffer == "":
 
227
                result.data = ""
 
228
                result.error = None
 
229
            else:
 
230
                try:
 
231
                    res = loads(self.buffer)
 
232
                    if not res:
 
233
                        self.buffer = ''
 
234
                        return
 
235
                    result.data = res
 
236
                    result.error = None
 
237
                except ValueError, ex:
 
238
                    rlog(10, self.name, "can't decode %s" % self.buffer)
 
239
                    result.error = str(ex)
 
240
                except Exception, ex:
 
241
                    rlog(10, self.name, exceptionmsg())
 
242
                    result.error = exceptionmsg()                
 
243
                    result.data = None
 
244
            for cb in self.callbacks:
 
245
                try:
 
246
                    cb(self, result)
 
247
                    rlog(0, self.name, 'called callback %s' % str(cb))
 
248
                except Exception, ex:
 
249
                    handle_exception()
 
250
            self.buffer = ''
 
251
 
 
252
    @locked
 
253
    def dorequest(self, method, path, postdata={}, headers={}):
 
254
        if postdata:
 
255
            postdata = urllib.urlencode(postdata)
 
256
        if headers:
 
257
            if not headers.has_key('Content-Length'):
 
258
                headers['Content-Length'] = len(postdata)
 
259
            headerstxt = ""
 
260
            for i,j in headers.iteritems():
 
261
                headerstxt += "%s: %s\r\n" % (i.lower(), j)
 
262
        else:
 
263
            headerstxt = ""
 
264
        if method == 'POST':
 
265
            s = toenc("%s %s HTTP/1.0\r\n%s\r\n%s\r\n\r\n" % (method, path, headerstxt, postdata), 'ascii')
 
266
        else:
 
267
            s = toenc("%s %s HTTP/1.0\r\n\r\n" % (method, path), 'ascii')
 
268
        if self.start():
 
269
            rlog(10, self.url, 'sending %s' % s)
 
270
            self.push(s)
 
271
 
 
272
    def sendpost(self, postdata):
 
273
        headers = {'Content-Type': 'application/x-www-form-urlencoded', \
 
274
'Accept': 'text/plain; text/html', 'User-Agent': useragent()}
 
275
        self.dorequest('POST', self.path, postdata, headers)
 
276
 
 
277
    def sendget(self):
 
278
        self.dorequest('GET', self.path)
 
279
 
 
280
    def post(self, *args, **kwargs):
 
281
        self.sendpost(kwargs)
 
282
 
 
283
    def get(self):
 
284
        self.sendget()