mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 1 | """\ |
| 2 | RPC request handler Django. Exposed RPC interface functions should be |
| 3 | defined in rpc_interface.py. |
| 4 | """ |
| 5 | |
| 6 | __author__ = 'showard@google.com (Steve Howard)' |
| 7 | |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 8 | import traceback, pydoc, re, urllib |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 9 | from frontend.afe.json_rpc import serviceHandler |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 10 | from frontend.afe import rpc_utils |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 11 | |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 12 | |
| 13 | class RpcMethodHolder(object): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 14 | 'Dummy class to hold RPC interface methods as attributes.' |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 15 | |
| 16 | |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 17 | class RpcHandler(object): |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 18 | def __init__(self, rpc_interface_modules, document_module=None): |
| 19 | self._rpc_methods = RpcMethodHolder() |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 20 | self._dispatcher = serviceHandler.ServiceHandler(self._rpc_methods) |
mbligh | e8819cd | 2008-02-15 16:48:40 +0000 | [diff] [blame] | 21 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 22 | # store all methods from interface modules |
| 23 | for module in rpc_interface_modules: |
| 24 | self._grab_methods_from(module) |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 25 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 26 | # get documentation for rpc_interface we can send back to the |
| 27 | # user |
| 28 | if document_module is None: |
| 29 | document_module = rpc_interface_modules[0] |
| 30 | self.html_doc = pydoc.html.document(document_module) |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 31 | |
| 32 | |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 33 | def get_rpc_documentation(self): |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 34 | return rpc_utils.rpc_http_response(self.html_doc) |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 35 | |
| 36 | |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 37 | def raw_request_data(self, request): |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 38 | if request.method == 'POST': |
| 39 | return request.raw_post_data |
| 40 | return urllib.unquote(request.META['QUERY_STRING']) |
| 41 | |
| 42 | |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 43 | def execute_request(self, json_request): |
| 44 | return self._dispatcher.handleRequest(json_request) |
| 45 | |
| 46 | |
| 47 | def decode_request(self, json_request): |
| 48 | return self._dispatcher.translateRequest(json_request) |
| 49 | |
| 50 | |
| 51 | def dispatch_request(self, decoded_request): |
| 52 | return self._dispatcher.dispatchRequest(decoded_request) |
| 53 | |
| 54 | |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 55 | def handle_rpc_request(self, request): |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 56 | json_request = self.raw_request_data(request) |
| 57 | result = self.execute_request(json_request) |
| 58 | return rpc_utils.raw_http_response(result) |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 59 | |
| 60 | |
| 61 | def handle_jsonp_rpc_request(self, request): |
| 62 | request_data = request.GET['request'] |
| 63 | callback_name = request.GET['callback'] |
| 64 | # callback_name must be a simple identifier |
| 65 | assert re.search(r'^\w+$', callback_name) |
| 66 | |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 67 | result = self.execute_request(request_data) |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 68 | padded_result = '%s(%s)' % (callback_name, result) |
showard | 3d6ae11 | 2009-05-02 00:45:48 +0000 | [diff] [blame^] | 69 | return rpc_utils.raw_http_response(padded_result, |
| 70 | content_type='text/javascript') |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 71 | |
| 72 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 73 | @staticmethod |
| 74 | def _allow_keyword_args(f): |
| 75 | """\ |
| 76 | Decorator to allow a function to take keyword args even though |
| 77 | the RPC layer doesn't support that. The decorated function |
| 78 | assumes its last argument is a dictionary of keyword args and |
| 79 | passes them to the original function as keyword args. |
| 80 | """ |
| 81 | def new_fn(*args): |
| 82 | assert args |
| 83 | keyword_args = args[-1] |
| 84 | args = args[:-1] |
| 85 | return f(*args, **keyword_args) |
| 86 | new_fn.func_name = f.func_name |
| 87 | return new_fn |
showard | 7c78528 | 2008-05-29 19:45:12 +0000 | [diff] [blame] | 88 | |
| 89 | |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 90 | def _grab_methods_from(self, module): |
| 91 | for name in dir(module): |
| 92 | attribute = getattr(module, name) |
| 93 | if not callable(attribute): |
| 94 | continue |
showard | ef6fe02 | 2009-03-27 20:55:16 +0000 | [diff] [blame] | 95 | decorated_function = RpcHandler._allow_keyword_args(attribute) |
jadmanski | 0afbb63 | 2008-06-06 21:10:57 +0000 | [diff] [blame] | 96 | setattr(self._rpc_methods, name, decorated_function) |