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
23
from errno import EAGAIN, EBADF
25
restlock = thread.allocate_lock()
26
locked = lockdec(restlock)
28
class RestResult(LazyDict):
30
def __init__(self, url="", name=""):
31
LazyDict.__init__(self)
39
class RestClient(object):
41
def __init__(self, url, keyfile=None, certfile=None, port=None):
42
if not url.endswith('/'):
46
splitted = u[1].split(':')
47
if len(splitted) == 2:
57
self.ip = socket.gethostbyname(self.host)
63
self.keyfile = keyfile
64
self.certfile = certfile
67
def addcb(self, callback):
70
self.callbacks.append(callback)
71
rlog(0, self.name, 'added callback %s' % str(callback))
74
def delcb(self, callback):
76
del self.callbacks[callback]
77
rlog(0, self.name, 'deleted callback %s' % str(callback))
81
def do(self, func, url, *args, **kwargs):
82
result = RestResult(url)
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
92
if result.status == 200:
94
result.data = loads(r)
97
rlog(5, url, "result: %s" % str(result))
99
result.error = str(ex)
101
for cb in self.callbacks:
104
rlog(5, self.name, 'called callback %s' % str(cb))
105
except Exception, ex:
109
def post(self, *args, **kwargs):
110
return self.do(posturl, self.url, *args, **kwargs)
112
def add(self, *args, **kwargs):
113
return self.do(posturl, self.url, *args, **kwargs)
115
def delete(self, nr=None):
117
return self.do(deleteurl, self.url + '/' + str(nr))
119
return self.do(deleteurl, self.url)
121
def get(self, nr=None):
123
return self.do(geturl4, self.url)
125
return self.do(geturl4, self.url + '/' + str(nr))
127
class RestClientAsync(RestClient, asynchat.async_chat):
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
136
self.name = name or self.url
140
def handle_error(self):
141
exctype, excvalue, tb = sys.exc_info()
142
if exctype == socket.error:
144
errno, errtxt = excvalue
145
if errno in [EAGAIN, EBADF]:
146
rlog(10, self.name, "%s %s" % (errno, errtxt))
150
self.error = str(excvalue)
152
rlog(10, self.name, exceptionmsg())
153
self.error = exceptionmsg()
155
result = RestResult(self.url, self.name)
156
result.error = self.error
158
for cb in self.callbacks:
161
rlog(0, self.name, 'called callback %s' % str(cb))
162
except Exception, ex:
166
def handle_expt(self):
169
def handle_connect(self):
170
rlog(10, self.name, 'connected %s' % str(self))
174
assert(int(self.port))
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:
182
self.connect((self.ip, int(self.port)))
183
except socket.error, ex:
185
except Exception, ex:
188
rlog(10, self.name, "can't start %s" % self.error)
193
def found_terminator(self):
194
rlog(10, self.name, 'found terminator')
195
if self.reading_headers:
196
self.reading_headers = False
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)
203
self.set_terminator(None)
205
rlog(5, self.name, 'headers: %s' % self.headers)
207
def collect_incoming_data(self, data):
208
self.buffer = self.buffer + data
210
def handle_close(self):
211
self.reading_headers = False
212
self.handle_incoming()
213
rlog(10, self.name, 'closed')
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
225
result.error = self.error
227
elif self.buffer == "":
232
res = loads(self.buffer)
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()
245
for cb in self.callbacks:
248
rlog(0, self.name, 'called callback %s' % str(cb))
249
except Exception, ex:
254
def dorequest(self, method, path, postdata={}, headers={}):
256
postdata = urllib.urlencode(postdata)
258
if not headers.has_key('Content-Length'):
259
headers['Content-Length'] = len(postdata)
261
for i,j in headers.iteritems():
262
headerstxt += "%s: %s\r\n" % (i.lower(), j)
266
s = toenc("%s %s HTTP/1.0\r\n%s\r\n%s\r\n\r\n" % (method, path, headerstxt, postdata), 'ascii')
268
s = toenc("%s %s HTTP/1.0\r\n\r\n" % (method, path), 'ascii')
270
rlog(10, self.url, 'sending %s' % s)
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)
279
self.dorequest('GET', self.path)
281
def post(self, *args, **kwargs):
282
self.sendpost(kwargs)