PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/addons/script.module.turtle/lib/jsonrpclib/SimpleJSONRPCServer.py

http://apple-tv2-xbmc.googlecode.com/
Python | 282 lines | 213 code | 12 blank | 57 comment | 44 complexity | 3c3c1814c02cb4eb9a46a862f1b000db MD5 | raw file
Possible License(s): GPL-3.0
  1. import jsonrpclib
  2. from jsonrpclib import Fault, jsonclass
  3. from jsonrpclib.jsonrpc import USE_UNIX_SOCKETS
  4. import SimpleXMLRPCServer
  5. import SocketServer
  6. import socket
  7. import logging
  8. import os
  9. import types
  10. import traceback
  11. import sys
  12. import urllib
  13. import re
  14. try:
  15. import fcntl
  16. except ImportError:
  17. # For Windows
  18. fcntl = None
  19. def get_version(request):
  20. # must be a dict
  21. if 'jsonrpc' in request.keys():
  22. return 2.0
  23. if 'id' in request.keys():
  24. return 1.0
  25. return None
  26. def validate_request(request):
  27. if type(request) is not types.DictType:
  28. fault = Fault(
  29. -32600, 'Request must be {}, not %s.' % type(request)
  30. )
  31. return fault
  32. rpcid = request.get('id', None)
  33. version = get_version(request)
  34. if not version:
  35. fault = Fault(-32600, 'Request %s invalid.' % request, rpcid=rpcid)
  36. return fault
  37. request.setdefault('params', [])
  38. method = request.get('method', None)
  39. params = request.get('params')
  40. param_types = (types.ListType, types.DictType, types.TupleType)
  41. if not method or type(method) not in types.StringTypes or \
  42. type(params) not in param_types:
  43. fault = Fault(
  44. -32600, 'Invalid request parameters or method.', rpcid=rpcid
  45. )
  46. return fault
  47. return True
  48. class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
  49. def __init__(self, encoding=None):
  50. SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
  51. allow_none=True,
  52. encoding=encoding)
  53. def _marshaled_dispatch(self, data, dispatch_method=None):
  54. response = None
  55. try:
  56. request = jsonrpclib.loads(data)
  57. except Exception, e:
  58. fault = Fault(-32700, 'Request %s invalid. (%s)' % (data, e))
  59. response = fault.response()
  60. return response
  61. if not request:
  62. fault = Fault(-32600, 'Request invalid -- no request data.')
  63. return fault.response()
  64. if type(request) is types.ListType:
  65. # This SHOULD be a batch, by spec
  66. responses = []
  67. for req_entry in request:
  68. result = validate_request(req_entry)
  69. if type(result) is Fault:
  70. responses.append(result.response())
  71. continue
  72. resp_entry = self._marshaled_single_dispatch(req_entry)
  73. if resp_entry is not None:
  74. responses.append(resp_entry)
  75. if len(responses) > 0:
  76. response = '[%s]' % ','.join(responses)
  77. else:
  78. response = ''
  79. else:
  80. result = validate_request(request)
  81. if type(result) is Fault:
  82. return result.response()
  83. response = self._marshaled_single_dispatch(request)
  84. return response
  85. def _marshaled_single_dispatch(self, request):
  86. # TODO - Use the multiprocessing and skip the response if
  87. # it is a notification
  88. # Put in support for custom dispatcher here
  89. # (See SimpleXMLRPCServer._marshaled_dispatch)
  90. method = request.get('method')
  91. params = request.get('params')
  92. try:
  93. response = self._dispatch(method, params)
  94. except:
  95. exc_type, exc_value, exc_tb = sys.exc_info()
  96. fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
  97. return fault.response()
  98. if 'id' not in request.keys() or request['id'] == None:
  99. # It's a notification
  100. return None
  101. try:
  102. response = jsonrpclib.dumps(response,
  103. methodresponse=True,
  104. rpcid=request['id']
  105. )
  106. return response
  107. except:
  108. exc_type, exc_value, exc_tb = sys.exc_info()
  109. fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
  110. return fault.response()
  111. def _dispatch(self, method, params):
  112. func = None
  113. try:
  114. func = self.funcs[method]
  115. except KeyError:
  116. if self.instance is not None:
  117. if hasattr(self.instance, '_dispatch'):
  118. return self.instance._dispatch(method, params)
  119. else:
  120. try:
  121. func = SimpleXMLRPCServer.resolve_dotted_attribute(
  122. self.instance,
  123. method,
  124. True
  125. )
  126. except AttributeError:
  127. pass
  128. if func is not None:
  129. try:
  130. if type(params) is types.ListType:
  131. #MODIFIED to pass service method name as well
  132. response = func(method, *params)
  133. else:
  134. #MODIFIED to pass service method name as well
  135. response = func(method, **params)
  136. return response
  137. except TypeError:
  138. return Fault(-32602, 'Invalid parameters.')
  139. except:
  140. err_lines = traceback.format_exc().splitlines()
  141. trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
  142. fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
  143. trace_string)
  144. return fault
  145. else:
  146. return Fault(-32601, 'Method %s not supported.' % method)
  147. class SimpleJSONRPCRequestHandler(
  148. SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
  149. #Added by ajju, might be useful in future for JSONP requests
  150. # def do_GET(self):
  151. # try:
  152. # urlParts = re.compile(r'/(.+?)/(.+?)\?(.*)').findall(self.path)
  153. # self.path = '/' + urlParts[0][0]
  154. # if not self.is_rpc_path_valid():
  155. # self.report_404()
  156. # return
  157. # dataObj = {}
  158. # dataObj['method'] = urlParts[0][1]
  159. #
  160. # params = {}
  161. # paramstring = urlParts[0][2]
  162. # if len(paramstring) >= 2:
  163. # paramstring = paramstring.replace('?', '')
  164. # if (paramstring[len(paramstring) - 1] == '/'):
  165. # paramstring = paramstring[0:len(paramstring) - 2]
  166. # pairsofparams = paramstring.split('&')
  167. # for i in range(len(pairsofparams)):
  168. # splitparams = {}
  169. # splitparams = pairsofparams[i].split('=')
  170. # if (len(splitparams)) == 2:
  171. # params[splitparams[0]] = urllib.unquote_plus(splitparams[1])
  172. # dataObj['params'] = params
  173. # if params['JSON-response-callback'] == 'rpc.callbacks.r1':
  174. # dataObj['jsonrpc'] = '2.0'
  175. # dataObj['id'] = '1'
  176. # params.pop('JSON-response-callback')
  177. # try:
  178. # import json
  179. # except ImportError:
  180. # import simplejson as json
  181. # data = json.dumps(dataObj)
  182. # response = self.server._marshaled_dispatch(data)
  183. # self.send_response(200)
  184. # except Exception, e:
  185. # self.send_response(500)
  186. # err_lines = traceback.format_exc().splitlines()
  187. # trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
  188. # fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
  189. # response = fault.response()
  190. # if response == None:
  191. # response = ''
  192. # self.send_header("Content-type", "application/json-rpc")
  193. # self.send_header("Content-length", str(len(response)))
  194. # self.end_headers()
  195. # self.wfile.write(response)
  196. # self.wfile.flush()
  197. # self.connection.shutdown(1)
  198. def do_POST(self):
  199. if not self.is_rpc_path_valid():
  200. self.report_404()
  201. return
  202. try:
  203. max_chunk_size = 10 * 1024 * 1024
  204. size_remaining = int(self.headers["content-length"])
  205. L = []
  206. while size_remaining:
  207. chunk_size = min(size_remaining, max_chunk_size)
  208. L.append(self.rfile.read(chunk_size))
  209. size_remaining -= len(L[-1])
  210. data = ''.join(L)
  211. response = self.server._marshaled_dispatch(data)
  212. self.send_response(200)
  213. except Exception, e:
  214. self.send_response(500)
  215. err_lines = traceback.format_exc().splitlines()
  216. trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
  217. fault = jsonrpclib.Fault(-32603, 'Server error: %s' % trace_string)
  218. response = fault.response()
  219. if response == None:
  220. response = ''
  221. self.send_header("Content-type", "application/json-rpc")
  222. self.send_header("Content-length", str(len(response)))
  223. self.end_headers()
  224. self.wfile.write(response)
  225. self.wfile.flush()
  226. self.connection.shutdown(1)
  227. class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher):
  228. allow_reuse_address = True
  229. def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
  230. logRequests=True, encoding=None, bind_and_activate=True,
  231. address_family=socket.AF_INET):
  232. self.logRequests = logRequests
  233. SimpleJSONRPCDispatcher.__init__(self, encoding)
  234. # TCPServer.__init__ has an extra parameter on 2.6+, so
  235. # check Python version and decide on how to call it
  236. vi = sys.version_info
  237. self.address_family = address_family
  238. if USE_UNIX_SOCKETS and address_family == socket.AF_UNIX:
  239. # Unix sockets can't be bound if they already exist in the
  240. # filesystem. The convention of e.g. X11 is to unlink
  241. # before binding again.
  242. if os.path.exists(addr):
  243. try:
  244. os.unlink(addr)
  245. except OSError:
  246. logging.warning("Could not unlink socket %s", addr)
  247. # if python 2.5 and lower
  248. if vi[0] < 3 and vi[1] < 6:
  249. SocketServer.TCPServer.__init__(self, addr, requestHandler)
  250. else:
  251. SocketServer.TCPServer.__init__(self, addr, requestHandler,
  252. bind_and_activate)
  253. if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
  254. flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
  255. flags |= fcntl.FD_CLOEXEC
  256. fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
  257. class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher):
  258. def __init__(self, encoding=None):
  259. SimpleJSONRPCDispatcher.__init__(self, encoding)
  260. def handle_jsonrpc(self, request_text):
  261. response = self._marshaled_dispatch(request_text)
  262. print 'Content-Type: application/json-rpc'
  263. print 'Content-Length: %d' % len(response)
  264. print
  265. sys.stdout.write(response)
  266. handle_xmlrpc = handle_jsonrpc