Changeset 836
- Timestamp:
- 11/26/05 13:08:27
- Files:
-
- trunk/cherrypy/filters/xmlrpcfilter.py (modified) (5 diffs)
- trunk/cherrypy/test/test_xmlrpc_filter.py (modified) (7 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/cherrypy/filters/xmlrpcfilter.py
r814 r836 80 80 ###################################################################### 81 81 82 83 import sys 82 84 import xmlrpclib 83 85 … … 108 110 def testValidityOfRequest(self): 109 111 # test if the content-length was sent 110 result = False 111 if cherrypy.request.headerMap.has_key('Content-Length'): 112 length = cherrypy.request.headerMap.get('Content-Length', 0) 113 if length is None or length == "": length = 0 114 result = int(length) > 0 115 ct = 'text/xml' 116 if cherrypy.request.headerMap.has_key('Content-Type'): 117 ct = cherrypy.request.headerMap.get('Content-Type', 'text/xml').lower() 118 if ct is None or ct == "": ct = 'text/xml' 119 result = result and ct in ['text/xml'] 120 return result 112 length = cherrypy.request.headerMap.get('Content-Length') or 0 113 ct = cherrypy.request.headerMap.get('Content-Type') or 'text/xml' 114 return int(length) > 0 and ct.lower() in ['text/xml'] 121 115 122 116 def beforeRequestBody(self): … … 126 120 request.xmlRpcFilterOn = cherrypy.config.get('xmlRpcFilter.on', False) 127 121 if not request.xmlRpcFilterOn: 128 return True122 return 129 123 130 124 request.isRPC = self.testValidityOfRequest() 131 125 if not request.isRPC: 132 # used for debugging or more info 133 # print 'not a valid xmlrpc call' 134 return # break this if it's not for this filter!! 126 return 135 127 136 128 request.processRequestBody = False 137 dataLength = int(request.headerMap.get('Content-Length' , 0))129 dataLength = int(request.headerMap.get('Content-Length') or 0) 138 130 data = request.rfile.read(dataLength) 139 131 try: … … 158 150 """This is a variation of main() from _cphttptools. 159 151 160 The reason it is redone here is because we don't want161 cherrypy.response.body = iterable(body) - we want to use162 whatever real value the user returned from their callable163 to reach the xmlrpcfilter unchanged."""152 It is redone here because: 153 1. we want to handle responses of any type 154 2. we need to pass our own paramList 155 """ 164 156 165 157 if (not cherrypy.config.get('xmlRpcFilter.on', False) … … 168 160 169 161 path = cherrypy.request.objectPath 170 page_handler, object_path, virtual_path = cherrypy.request.mapPathToObject(path) 171 172 # Remove "root" from object_path and join it to get objectPath 173 cherrypy.request.objectPath = '/' + '/'.join(object_path[1:]) 174 args = virtual_path + cherrypy.request.paramList 175 body = page_handler(*args, **cherrypy.request.paramMap) 176 cherrypy.response.body = body 177 178 def beforeFinalize(self): 179 """ Called before finalizing output """ 180 if (not cherrypy.config.get('xmlRpcFilter.on', False) 181 or not getattr(cherrypy.request, 'isRPC', False)): 182 return 183 184 encoding = cherrypy.config.get('xmlRpcFilter.encoding', 'utf-8') 185 162 while True: 163 try: 164 page_handler, object_path, virtual_path = cherrypy.request.mapPathToObject(path) 165 166 # Decode any leftover %2F in the virtual_path atoms. 167 virtual_path = [x.replace("%2F", "/") for x in virtual_path] 168 169 # Remove "root" from object_path and join it to get objectPath 170 self.objectPath = '/' + '/'.join(object_path[1:]) 171 args = virtual_path + cherrypy.request.paramList 172 body = page_handler(*args, **cherrypy.request.paramMap) 173 # Don't form an iterable like CP core main() does 174 ## cherrypy.response.body = iterable(body) 175 break 176 except cherrypy.InternalRedirect, x: 177 # Try again with the new path 178 path = x.path 179 186 180 # See xmlrpclib documentation 187 181 # Python's None value cannot be used in standard XML-RPC; 188 182 # to allow using it via an extension, provide a true value for allow_none. 189 cherrypy.response.body = [xmlrpclib.dumps( 190 (cherrypy.response.body,), 191 methodresponse=1, 192 encoding=encoding, 193 allow_none=0)] 194 cherrypy.response.headerMap['Content-Type'] = 'text/xml' 195 cherrypy.response.headerMap['Content-Length'] = len(cherrypy.response.body[0]) 196 197 def beforeErrorResponse(self): 198 try: 199 if (not cherrypy.config.get('xmlRpcFilter.on', False) 200 or not getattr(cherrypy.request, 'isRPC', False)): 201 return 202 import sys 203 # Since we got here because of an exception, let's get its error message if any 204 message = str(sys.exc_info()[1]) 205 body = ''.join([chunk for chunk in message]) 206 cherrypy.response.body = [xmlrpclib.dumps(xmlrpclib.Fault(1, body))] 207 cherrypy.response.headerMap['Content-Type'] = 'text/xml' 208 cherrypy.response.headerMap['Content-Length'] = len(cherrypy.response.body[0]) 209 except: 210 pass 211 183 encoding = cherrypy.config.get('xmlRpcFilter.encoding', 'utf-8') 184 body = xmlrpclib.dumps((body,), methodresponse=1, 185 encoding=encoding, allow_none=0) 186 self.respond(body) 187 212 188 def afterErrorResponse(self): 213 189 if (not cherrypy.config.get('xmlRpcFilter.on', False) 214 190 or not getattr(cherrypy.request, 'isRPC', False)): 215 191 return 192 193 # Since we got here because of an exception, 194 # let's get its error message if any 195 body = str(sys.exc_info()[1]) 196 body = xmlrpclib.dumps(xmlrpclib.Fault(1, body)) 197 self.respond(body) 198 199 def respond(self, body): 216 200 # The XML-RPC spec (http://www.xmlrpc.com/spec) says: 217 201 # "Unless there's a lower-level error, always return 200 OK." 218 # However if arrived here we do have a status set to 500 then 219 # it means we got an error we didn't want to trap explicitely 220 # so let's assume it's part of the "lower-level error" defined above. 221 if cherrypy.response.status[:3] != '500': 222 cherrypy.response.status = '200 OK' 202 # Since Python's xmlrpclib interprets a non-200 response 203 # as a "Protocol Error", we'll just return 200 every time. 204 response = cherrypy.response 205 response.status = '200 OK' 206 response.body = [body] 207 response.headerMap['Content-Type'] = 'text/xml' 208 response.headerMap['Content-Length'] = len(body) 209 trunk/cherrypy/test/test_xmlrpc_filter.py
r804 r836 10 10 index.exposed = True 11 11 12 12 13 13 class XmlRpc: 14 14 def return_single_item_list(self): … … 55 55 cherrypy.root.xmlrpc = XmlRpc() 56 56 57 57 58 import helper 58 59 59 60 cherrypy.config.update({ 60 61 'global': {'server.logToScreen': False, … … 64 65 'server.socketPort': helper.CPWebCase.PORT, 65 66 }, 66 '/xmlrpc': 67 {'xmlRpcFilter.on':True} 68 }) 67 '/xmlrpc': {'xmlRpcFilter.on':True}, 68 }) 69 69 70 70 71 71 class ServerlessProxy(object): 72 72 """An xmlrpc proxy for the test suite's 'serverless' tests.""" … … 74 74 self._webcase = webcase 75 75 self.url = url 76 76 77 77 def __getattr__(self, attr, *args): 78 78 def xmlrpc_method(*args): … … 81 81 cl = len(body) 82 82 headers = [('Content-Type', 'text/xml'), ('Content-Length', str(cl))] 83 self._webcase.getPage(self.url, headers=headers, method="POST", body=body) 84 return xmlrpclib.loads("".join(self._webcase.body))[0][0] 83 self._webcase.getPage(self.url, headers=headers, 84 method="POST", body=body) 85 p, u = xmlrpclib.getparser() 86 for line in self._webcase.body: 87 p.feed(line) 88 p.close() 89 response = u.close() 90 if len(response) == 1: 91 response = response[0] 92 return response 85 93 return xmlrpc_method 86 94 … … 88 96 class XmlRpcFilterTest(helper.CPWebCase): 89 97 def testXmlRpcFilter(self): 90 98 91 99 # load the appropriate xmlrpc proxy 92 if cherrypy.server.httpserver is None: 93 proxy = ServerlessProxy(self, 'http://localhost:%s/xmlrpc/' % (self.PORT)) 100 url = 'http://localhost:%s/xmlrpc/' % (self.PORT) 101 if cherrypy.server.httpserver is None: 102 proxy = ServerlessProxy(self, url) 94 103 else: 95 proxy = xmlrpclib.ServerProxy( 'http://localhost:%s/xmlrpc/' % (self.PORT))96 104 proxy = xmlrpclib.ServerProxy(url) 105 97 106 # begin the tests ... 98 self.assertEqual(proxy.return_single_item_list(), 99 [42] 100 ) 101 self.assertNotEqual(proxy.return_single_item_list(), 102 'one bazillion' 103 ) 104 self.assertEqual(proxy.return_string(), 105 "here is a string" 106 ) 107 self.assertEqual(proxy.return_tuple(), 108 list(('here', 'is', 1, 'tuple')) 109 ) 110 self.assertEqual(proxy.return_dict(), 111 {'a': 1, 'c': 3, 'b': 2} 112 ) 107 self.assertEqual(proxy.return_single_item_list(), [42]) 108 self.assertNotEqual(proxy.return_single_item_list(), 'one bazillion') 109 self.assertEqual(proxy.return_string(), "here is a string") 110 self.assertEqual(proxy.return_tuple(), list(('here', 'is', 1, 'tuple'))) 111 self.assertEqual(proxy.return_dict(), {'a': 1, 'c': 3, 'b': 2}) 113 112 self.assertEqual(proxy.return_composite(), 114 [{'a': 1, 'z': 26}, 'hi', ['welcome', 'friend']] 115 ) 116 self.assertEqual(proxy.return_int(), 117 42 118 ) 119 self.assertEqual(proxy.return_float(), 120 3.14 121 ) 113 [{'a': 1, 'z': 26}, 'hi', ['welcome', 'friend']]) 114 self.assertEqual(proxy.return_int(), 42) 115 self.assertEqual(proxy.return_float(), 3.14) 122 116 self.assertEqual(proxy.return_datetime(), 123 xmlrpclib.DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1)) 124 ) 125 self.assertEqual(proxy.return_boolean(), 126 True 127 ) 128 self.assertEqual(proxy.test_argument_passing(22), 129 22 * 2 130 ) 117 xmlrpclib.DateTime((2003, 10, 7, 8, 1, 0, 1, 280, -1))) 118 self.assertEqual(proxy.return_boolean(), True) 119 self.assertEqual(proxy.test_argument_passing(22), 22 * 2) 120 121 # Test an error in the page handler (should raise an xmlrpclib.Fault) 122 ignore = helper.webtest.ignored_exceptions 123 ignore.append(TypeError) 124 try: 125 try: 126 proxy.test_argument_passing({}) 127 except Exception, x: 128 self.assertEqual(x.__class__, xmlrpclib.Fault) 129 self.assertEqual(x.faultString, ("unsupported operand type(s) " 130 "for *: 'dict' and 'int'")) 131 else: 132 self.fail("Expected xmlrpclib.Fault") 133 finally: 134 ignore.pop() 131 135 132 136 … … 135 139 serverClass = _cpwsgi.WSGIServer 136 140 helper.testmain(serverClass) 137 141

