Skip to content

Instantly share code, notes, and snippets.

@lvh
Created February 27, 2014 11:25
Show Gist options
  • Save lvh/9248425 to your computer and use it in GitHub Desktop.
Save lvh/9248425 to your computer and use it in GitHub Desktop.
from json import dumps, loads
from twisted.internet.defer import inlineCallbacks
from twisted.protocols.basic import NetstringReceiver
from twisted.python import log
class JSONRPCReceiver(NetstringReceiver):
def stringReceived(self, string):
try:
request = loads(string)
except ValueError:
err = self._formatError(-32700, "Parse error")
self._sendResponse(None, error=err)
self._requestReceived(request)
def _requestReceived(self, request):
"""Validate and respond to the request.
"""
if request.get("jsonrpc") != "2.0":
err = self._formatError(-32600, "Invalid Request")
self._sendResponse(request.get("id"), error=err)
return
try:
requestId = request["id"]
except KeyError:
err = self._formatError(-32600, "Invalid Request")
self._sendResponse(None, error=err)
return
try:
method = self._getMethod(request["method"])
except KeyError:
err = self._formatError(-32601, "Method not found")
self._sendResponse(requestId, error=err)
return
kwargs = request.get("params", {})
self._call(requestId, method, kwargs)
@inlineCallbacks
def _call(self, requestId, method, kwargs):
log.msg("JSONRPC call {} for {!r}: {}, {}"
.format(requestId, self.transport.getPeer(), method, kwargs))
try:
result = yield method(**kwargs)
except TypeError as e: # Can also catch spurious TypeErrors...
log.err(e)
err = self._formatError(-32602, "Invalid params")
self._sendResponse(requestId, error=err)
else:
self._sendResponse(requestId, result=result)
def _getMethod(self, methodName):
"""Gets a method.
"""
return getattr(self, "jsonrpc_" + methodName)
def _formatError(self, code, message, data=None):
log.msg("JSONRPC error for {!r}: {}, {}, {}"
.format(self.transport.getPeer(), code, message, data))
return {"code": code, "message": message, "data": data}
def _sendResponse(self, requestId, result=None, error=None):
response = {"jsonrpc": "2.0", "id": requestId}
if result is not None:
response["result"] = result
elif error is not None:
response["error"] = error
else:
raise RuntimeError("Must pass exactly one of [result, error]")
encoded = dumps(response)
self.sendString(encoded)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment