~ubuntu-branches/ubuntu/utopic/gozerbot/utopic

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Jeremy Malcolm
  • Date: 2012-04-03 21:58:28 UTC
  • mfrom: (3.1.11 sid)
  • Revision ID: package-import@ubuntu.com-20120403215828-6mik0tzug5na93la
Tags: 0.99.1-2
* Removes logfiles on purge (Closes: #668767)
* Reverted location of installed files back to /usr/lib/gozerbot.

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