1
# gozerbot/rest/client.py
5
""" Rest Client class """
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
16
from simplejson import loads
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
24
restlock = thread.allocate_lock()
25
locked = lockdec(restlock)
27
class RestResult(LazyDict):
29
def __init__(self, url="", name=""):
30
LazyDict.__init__(self)
38
class RestClient(object):
40
def __init__(self, url, keyfile=None, certfile=None, port=None):
41
if not url.endswith('/'):
45
splitted = u[1].split(':')
46
if len(splitted) == 2:
56
self.ip = socket.gethostbyname(self.host)
62
self.keyfile = keyfile
63
self.certfile = certfile
66
def addcb(self, callback):
69
self.callbacks.append(callback)
70
rlog(0, self.name, 'added callback %s' % str(callback))
73
def delcb(self, callback):
75
del self.callbacks[callback]
76
rlog(0, self.name, 'deleted callback %s' % str(callback))
80
def do(self, func, url, *args, **kwargs):
81
result = RestResult(url)
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
91
if result.status == 200:
93
result.data = loads(r)
96
rlog(5, url, "result: %s" % str(result))
98
result.error = str(ex)
100
for cb in self.callbacks:
103
rlog(5, self.name, 'called callback %s' % str(cb))
104
except Exception, ex:
108
def post(self, *args, **kwargs):
109
return self.do(posturl, self.url, *args, **kwargs)
111
def add(self, *args, **kwargs):
112
return self.do(posturl, self.url, *args, **kwargs)
114
def delete(self, nr=None):
116
return self.do(deleteurl, self.url + '/' + str(nr))
118
return self.do(deleteurl, self.url)
120
def get(self, nr=None):
122
return self.do(geturl4, self.url)
124
return self.do(geturl4, self.url + '/' + str(nr))
126
class RestClientAsync(RestClient, asynchat.async_chat):
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
135
self.name = name or self.url
139
def handle_error(self):
140
exctype, excvalue, tb = sys.exc_info()
141
if exctype == socket.error:
143
errno, errtxt = excvalue
144
if errno in [11, 35, 9]:
145
rlog(10, self.name, "%s %s" % (errno, errtxt))
149
self.error = str(excvalue)
151
rlog(10, self.name, exceptionmsg())
152
self.error = exceptionmsg()
154
result = RestResult(self.url, self.name)
155
result.error = self.error
157
for cb in self.callbacks:
160
rlog(0, self.name, 'called callback %s' % str(cb))
161
except Exception, ex:
165
def handle_expt(self):
168
def handle_connect(self):
169
rlog(10, self.name, 'connected %s' % str(self))
173
assert(int(self.port))
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:
181
self.connect((self.ip, int(self.port)))
182
except socket.error, ex:
184
except Exception, ex:
187
rlog(10, self.name, "can't start %s" % self.error)
192
def found_terminator(self):
193
rlog(10, self.name, 'found terminator')
194
if self.reading_headers:
195
self.reading_headers = False
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)
202
self.set_terminator(None)
204
rlog(5, self.name, 'headers: %s' % self.headers)
206
def collect_incoming_data(self, data):
207
self.buffer = self.buffer + data
209
def handle_close(self):
210
self.reading_headers = False
211
self.handle_incoming()
212
rlog(10, self.name, 'closed')
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
224
result.error = self.error
226
elif self.buffer == "":
231
res = loads(self.buffer)
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()
244
for cb in self.callbacks:
247
rlog(0, self.name, 'called callback %s' % str(cb))
248
except Exception, ex:
253
def dorequest(self, method, path, postdata={}, headers={}):
255
postdata = urllib.urlencode(postdata)
257
if not headers.has_key('Content-Length'):
258
headers['Content-Length'] = len(postdata)
260
for i,j in headers.iteritems():
261
headerstxt += "%s: %s\r\n" % (i.lower(), j)
265
s = toenc("%s %s HTTP/1.0\r\n%s\r\n%s\r\n\r\n" % (method, path, headerstxt, postdata), 'ascii')
267
s = toenc("%s %s HTTP/1.0\r\n\r\n" % (method, path), 'ascii')
269
rlog(10, self.url, 'sending %s' % s)
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)
278
self.dorequest('GET', self.path)
280
def post(self, *args, **kwargs):
281
self.sendpost(kwargs)