Skip to content

Commit 669c136

Browse files
author
catchjosh
committed
Oops -- NOW adding the new jsonrpclib/ files (jsonclass, config, history, __init__)
git-svn-id: http://jsonrpclib.googlecode.com/svn/trunk@13 ae587032-bbab-11de-869a-473eb4776397
1 parent ccb16ea commit 669c136

File tree

6 files changed

+892
-0
lines changed

6 files changed

+892
-0
lines changed

jsonrpclib/SimpleJSONRPCServer.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
import jsonrpclib
2+
from jsonrpclib import Fault
3+
import SimpleXMLRPCServer
4+
import SocketServer
5+
import types
6+
import traceback
7+
import fcntl
8+
import sys
9+
10+
def get_version(request):
11+
if type(request) not in (types.ListType, types.DictType):
12+
return None
13+
if type(request) is types.ListType:
14+
if len(request) == 0:
15+
return None
16+
if 'jsonrpc' not in request[0].keys():
17+
return None
18+
return '2.0'
19+
# must be a dict
20+
if 'jsonrpc' in request.keys():
21+
return 2.0
22+
if 'id' in request.keys():
23+
return 1.0
24+
return None
25+
26+
class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
27+
28+
def __init__(self, encoding=None):
29+
SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,
30+
allow_none=True,
31+
encoding=encoding)
32+
33+
def _marshaled_dispatch(self, data, dispatch_method = None):
34+
response = None
35+
try:
36+
request = jsonrpclib.loads(data)
37+
except:
38+
fault = Fault(-32600, 'Request %s invalid.' % data)
39+
response = fault.response()
40+
return response
41+
version = get_version(request)
42+
if not version:
43+
fault = Fault(-32600, 'Request %s invalid.' % data)
44+
response = fault.response()
45+
return response
46+
if type(request) is types.ListType:
47+
# This SHOULD be a batch, by spec
48+
responses = []
49+
for req_entry in request:
50+
resp_entry = self._marshaled_single_dispatch(req_entry)
51+
if resp_entry is not None:
52+
responses.append(resp_entry)
53+
response = '[%s]' % ','.join(responses)
54+
else:
55+
response = self._marshaled_single_dispatch(request)
56+
return response
57+
58+
def _marshaled_single_dispatch(self, request):
59+
# TODO - Use the multiprocessing and skip the response if
60+
# it is a notification
61+
method = request['method']
62+
params = request['params']
63+
# Put in support for custom dispatcher here
64+
# (See SimpleXMLRPCServer._marshaled_dispatch)
65+
try:
66+
response = self._dispatch(method, params)
67+
except:
68+
exc_type, exc_value, exc_tb = sys.exc_info()
69+
fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
70+
return fault.response()
71+
if 'id' not in request.keys() or request['id'] == None:
72+
# It's a notification
73+
return None
74+
try:
75+
response = jsonrpclib.dumps(response,
76+
methodresponse=True,
77+
rpcid=request['id']
78+
)
79+
return response
80+
except:
81+
exc_type, exc_value, exc_tb = sys.exc_info()
82+
fault = Fault(-32603, '%s:%s' % (exc_type, exc_value))
83+
return fault.response()
84+
85+
def _dispatch(self, method, params):
86+
func = None
87+
try:
88+
func = self.funcs[method]
89+
except KeyError:
90+
if self.instance is not None:
91+
if hasattr(self.instance, '_dispatch'):
92+
return self.instance._dispatch(method, params)
93+
else:
94+
try:
95+
func = resolve_dotted_attribute(
96+
self.instance,
97+
method,
98+
True
99+
)
100+
except AttributeError:
101+
pass
102+
if func is not None:
103+
try:
104+
if type(params) is types.ListType:
105+
response = func(*params)
106+
else:
107+
response = func(**params)
108+
return response
109+
except TypeError:
110+
return Fault(-32602, 'Invalid parameters.')
111+
except:
112+
err_lines = traceback.format_exc().splitlines()
113+
trace_string = '%s | %s' % (err_lines[-3], err_lines[-1])
114+
fault = jsonrpclib.Fault(-32603, 'Server error: %s' %
115+
trace_string)
116+
return fault
117+
else:
118+
return Fault(-32601, 'Method %s not supported.' % method)
119+
120+
class SimpleJSONRPCRequestHandler(
121+
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
122+
123+
def do_POST(self):
124+
if not self.is_rpc_path_valid():
125+
self.report_404()
126+
return
127+
try:
128+
max_chunk_size = 10*1024*1024
129+
size_remaining = int(self.headers["content-length"])
130+
L = []
131+
while size_remaining:
132+
chunk_size = min(size_remaining, max_chunk_size)
133+
L.append(self.rfile.read(chunk_size))
134+
size_remaining -= len(L[-1])
135+
data = ''.join(L)
136+
response = self.server._marshaled_dispatch(data)
137+
self.send_response(200)
138+
except Exception, e:
139+
self.send_response(500)
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' % trace_string)
143+
response = fault.response()
144+
if response == None:
145+
response = ''
146+
self.send_header("Content-type", "application/json-rpc")
147+
self.send_header("Content-length", str(len(response)))
148+
self.end_headers()
149+
self.wfile.write(response)
150+
self.wfile.flush()
151+
self.connection.shutdown(1)
152+
153+
class SimpleJSONRPCServer(SocketServer.TCPServer,
154+
SimpleJSONRPCDispatcher):
155+
156+
allow_reuse_address = True
157+
158+
def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler,
159+
logRequests=True, encoding=None, bind_and_activate=True):
160+
self.logRequests = logRequests
161+
SimpleJSONRPCDispatcher.__init__(self, encoding)
162+
SocketServer.TCPServer.__init__(self, addr, requestHandler,
163+
bind_and_activate)
164+
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
165+
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
166+
flags |= fcntl.FD_CLOEXEC
167+
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
168+
169+
class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher):
170+
171+
def __init__(self, encoding=None):
172+
SimpleJSONRPCDispatcher.__init__(self, encoding)
173+
174+
def handle_jsonrpc(self, request_text):
175+
response = self._marshaled_dispatch(request_text)
176+
print 'Content-Type: application/json-rpc'
177+
print 'Content-Length: %d' % len(response)
178+
print
179+
sys.stdout.write(response)
180+
181+
handle_xmlrpc = handle_jsonrpc
182+
183+
if __name__ == '__main__':
184+
print 'Running JSON-RPC server on port 8000'
185+
server = SimpleJSONRPCServer(("localhost", 8000))
186+
server.register_function(pow)
187+
server.register_function(lambda x,y: x+y, 'add')
188+
server.serve_forever()

jsonrpclib/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from jsonrpclib import *
2+
from config import config
3+
from history import history

jsonrpclib/config.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import sys
2+
3+
class LocalClasses(dict):
4+
def add(self, cls):
5+
self[cls.__name__] = cls
6+
7+
class Config(object):
8+
"""
9+
This is pretty much used exclusively for the 'jsonclass'
10+
functionality... set use_jsonclass to False to turn it off.
11+
You can change serialize_method and ignore_attribute, or use
12+
the local_classes.add(class) to include "local" classes.
13+
"""
14+
use_jsonclass = True
15+
# Change to False to keep __jsonclass__ entries raw.
16+
serialize_method = '_serialize'
17+
# The serialize_method should be a string that references the
18+
# method on a custom class object which is responsible for
19+
# returning a tuple of the constructor arguments and a dict of
20+
# attributes.
21+
ignore_attribute = '_ignore'
22+
# The ignore attribute should be a string that references the
23+
# attribute on a custom class object which holds strings and / or
24+
# references of the attributes the class translator should ignore.
25+
classes = LocalClasses()
26+
# The list of classes to use for jsonclass translation.
27+
version = 2.0
28+
# Version of the JSON-RPC spec to support
29+
user_agent = 'jsonrpclib/0.1 (Python %s)' % \
30+
'.'.join([str(ver) for ver in sys.version_info[0:3]])
31+
# User agent to use for calls.
32+
33+
config = Config

jsonrpclib/history.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
class History(object):
3+
"""
4+
This holds all the response and request objects for a
5+
session. A server using this should call "clear" after
6+
each request cycle in order to keep it from clogging
7+
memory.
8+
"""
9+
requests = []
10+
responses = []
11+
12+
def add_response(self, response_obj):
13+
self.responses.append(response_obj)
14+
15+
def add_request(self, request_obj):
16+
self.requests.append(request_obj)
17+
18+
@property
19+
def request(self):
20+
if len(self.requests) == 0:
21+
return None
22+
else:
23+
return self.requests[-1]
24+
25+
@property
26+
def response(self):
27+
if len(self.responses) == 0:
28+
return None
29+
else:
30+
return self.responses[-1]
31+
32+
def clear(self):
33+
del self.requests[:]
34+
del self.responses[:]
35+
36+
history = History()

0 commit comments

Comments
 (0)