--- /dev/null
+import base64\r
+import datetime\r
+import sys\r
+import time\r
+import unittest\r
+import xmlrpclib\r
+import SimpleXMLRPCServer\r
+import mimetools\r
+import httplib\r
+import socket\r
+import StringIO\r
+import os\r
+import re\r
+from test import test_support\r
+\r
+try:\r
+ import threading\r
+except ImportError:\r
+ threading = None\r
+\r
+try:\r
+ unicode\r
+except NameError:\r
+ have_unicode = False\r
+else:\r
+ have_unicode = True\r
+\r
+alist = [{'astring': 'foo@bar.baz.spam',\r
+ 'afloat': 7283.43,\r
+ 'anint': 2**20,\r
+ 'ashortlong': 2L,\r
+ 'anotherlist': ['.zyx.41'],\r
+ 'abase64': xmlrpclib.Binary("my dog has fleas"),\r
+ 'boolean': xmlrpclib.False,\r
+ 'unicode': u'\u4000\u6000\u8000',\r
+ u'ukey\u4000': 'regular value',\r
+ 'datetime1': xmlrpclib.DateTime('20050210T11:41:23'),\r
+ 'datetime2': xmlrpclib.DateTime(\r
+ (2005, 02, 10, 11, 41, 23, 0, 1, -1)),\r
+ 'datetime3': xmlrpclib.DateTime(\r
+ datetime.datetime(2005, 02, 10, 11, 41, 23)),\r
+ }]\r
+\r
+class XMLRPCTestCase(unittest.TestCase):\r
+\r
+ def test_dump_load(self):\r
+ self.assertEqual(alist,\r
+ xmlrpclib.loads(xmlrpclib.dumps((alist,)))[0][0])\r
+\r
+ def test_dump_bare_datetime(self):\r
+ # This checks that an unwrapped datetime.date object can be handled\r
+ # by the marshalling code. This can't be done via test_dump_load()\r
+ # since with use_datetime set to 1 the unmarshaller would create\r
+ # datetime objects for the 'datetime[123]' keys as well\r
+ dt = datetime.datetime(2005, 02, 10, 11, 41, 23)\r
+ s = xmlrpclib.dumps((dt,))\r
+ (newdt,), m = xmlrpclib.loads(s, use_datetime=1)\r
+ self.assertEqual(newdt, dt)\r
+ self.assertEqual(m, None)\r
+\r
+ (newdt,), m = xmlrpclib.loads(s, use_datetime=0)\r
+ self.assertEqual(newdt, xmlrpclib.DateTime('20050210T11:41:23'))\r
+\r
+ def test_datetime_before_1900(self):\r
+ # same as before but with a date before 1900\r
+ dt = datetime.datetime(1, 02, 10, 11, 41, 23)\r
+ s = xmlrpclib.dumps((dt,))\r
+ (newdt,), m = xmlrpclib.loads(s, use_datetime=1)\r
+ self.assertEqual(newdt, dt)\r
+ self.assertEqual(m, None)\r
+\r
+ (newdt,), m = xmlrpclib.loads(s, use_datetime=0)\r
+ self.assertEqual(newdt, xmlrpclib.DateTime('00010210T11:41:23'))\r
+\r
+ def test_cmp_datetime_DateTime(self):\r
+ now = datetime.datetime.now()\r
+ dt = xmlrpclib.DateTime(now.timetuple())\r
+ self.assertTrue(dt == now)\r
+ self.assertTrue(now == dt)\r
+ then = now + datetime.timedelta(seconds=4)\r
+ self.assertTrue(then >= dt)\r
+ self.assertTrue(dt < then)\r
+\r
+ def test_bug_1164912 (self):\r
+ d = xmlrpclib.DateTime()\r
+ ((new_d,), dummy) = xmlrpclib.loads(xmlrpclib.dumps((d,),\r
+ methodresponse=True))\r
+ self.assertIsInstance(new_d.value, str)\r
+\r
+ # Check that the output of dumps() is still an 8-bit string\r
+ s = xmlrpclib.dumps((new_d,), methodresponse=True)\r
+ self.assertIsInstance(s, str)\r
+\r
+ def test_newstyle_class(self):\r
+ class T(object):\r
+ pass\r
+ t = T()\r
+ t.x = 100\r
+ t.y = "Hello"\r
+ ((t2,), dummy) = xmlrpclib.loads(xmlrpclib.dumps((t,)))\r
+ self.assertEqual(t2, t.__dict__)\r
+\r
+ def test_dump_big_long(self):\r
+ self.assertRaises(OverflowError, xmlrpclib.dumps, (2L**99,))\r
+\r
+ def test_dump_bad_dict(self):\r
+ self.assertRaises(TypeError, xmlrpclib.dumps, ({(1,2,3): 1},))\r
+\r
+ def test_dump_recursive_seq(self):\r
+ l = [1,2,3]\r
+ t = [3,4,5,l]\r
+ l.append(t)\r
+ self.assertRaises(TypeError, xmlrpclib.dumps, (l,))\r
+\r
+ def test_dump_recursive_dict(self):\r
+ d = {'1':1, '2':1}\r
+ t = {'3':3, 'd':d}\r
+ d['t'] = t\r
+ self.assertRaises(TypeError, xmlrpclib.dumps, (d,))\r
+\r
+ def test_dump_big_int(self):\r
+ if sys.maxint > 2L**31-1:\r
+ self.assertRaises(OverflowError, xmlrpclib.dumps,\r
+ (int(2L**34),))\r
+\r
+ xmlrpclib.dumps((xmlrpclib.MAXINT, xmlrpclib.MININT))\r
+ self.assertRaises(OverflowError, xmlrpclib.dumps, (xmlrpclib.MAXINT+1,))\r
+ self.assertRaises(OverflowError, xmlrpclib.dumps, (xmlrpclib.MININT-1,))\r
+\r
+ def dummy_write(s):\r
+ pass\r
+\r
+ m = xmlrpclib.Marshaller()\r
+ m.dump_int(xmlrpclib.MAXINT, dummy_write)\r
+ m.dump_int(xmlrpclib.MININT, dummy_write)\r
+ self.assertRaises(OverflowError, m.dump_int, xmlrpclib.MAXINT+1, dummy_write)\r
+ self.assertRaises(OverflowError, m.dump_int, xmlrpclib.MININT-1, dummy_write)\r
+\r
+\r
+ def test_dump_none(self):\r
+ value = alist + [None]\r
+ arg1 = (alist + [None],)\r
+ strg = xmlrpclib.dumps(arg1, allow_none=True)\r
+ self.assertEqual(value,\r
+ xmlrpclib.loads(strg)[0][0])\r
+ self.assertRaises(TypeError, xmlrpclib.dumps, (arg1,))\r
+\r
+ def test_default_encoding_issues(self):\r
+ # SF bug #1115989: wrong decoding in '_stringify'\r
+ utf8 = """<?xml version='1.0' encoding='iso-8859-1'?>\r
+ <params>\r
+ <param><value>\r
+ <string>abc \x95</string>\r
+ </value></param>\r
+ <param><value>\r
+ <struct>\r
+ <member>\r
+ <name>def \x96</name>\r
+ <value><string>ghi \x97</string></value>\r
+ </member>\r
+ </struct>\r
+ </value></param>\r
+ </params>\r
+ """\r
+\r
+ # sys.setdefaultencoding() normally doesn't exist after site.py is\r
+ # loaded. Import a temporary fresh copy to get access to it\r
+ # but then restore the original copy to avoid messing with\r
+ # other potentially modified sys module attributes\r
+ old_encoding = sys.getdefaultencoding()\r
+ with test_support.CleanImport('sys'):\r
+ import sys as temp_sys\r
+ temp_sys.setdefaultencoding("iso-8859-1")\r
+ try:\r
+ (s, d), m = xmlrpclib.loads(utf8)\r
+ finally:\r
+ temp_sys.setdefaultencoding(old_encoding)\r
+\r
+ items = d.items()\r
+ if have_unicode:\r
+ self.assertEqual(s, u"abc \x95")\r
+ self.assertIsInstance(s, unicode)\r
+ self.assertEqual(items, [(u"def \x96", u"ghi \x97")])\r
+ self.assertIsInstance(items[0][0], unicode)\r
+ self.assertIsInstance(items[0][1], unicode)\r
+ else:\r
+ self.assertEqual(s, "abc \xc2\x95")\r
+ self.assertEqual(items, [("def \xc2\x96", "ghi \xc2\x97")])\r
+\r
+\r
+class HelperTestCase(unittest.TestCase):\r
+ def test_escape(self):\r
+ self.assertEqual(xmlrpclib.escape("a&b"), "a&b")\r
+ self.assertEqual(xmlrpclib.escape("a<b"), "a<b")\r
+ self.assertEqual(xmlrpclib.escape("a>b"), "a>b")\r
+\r
+class FaultTestCase(unittest.TestCase):\r
+ def test_repr(self):\r
+ f = xmlrpclib.Fault(42, 'Test Fault')\r
+ self.assertEqual(repr(f), "<Fault 42: 'Test Fault'>")\r
+ self.assertEqual(repr(f), str(f))\r
+\r
+ def test_dump_fault(self):\r
+ f = xmlrpclib.Fault(42, 'Test Fault')\r
+ s = xmlrpclib.dumps((f,))\r
+ (newf,), m = xmlrpclib.loads(s)\r
+ self.assertEqual(newf, {'faultCode': 42, 'faultString': 'Test Fault'})\r
+ self.assertEqual(m, None)\r
+\r
+ s = xmlrpclib.Marshaller().dumps(f)\r
+ self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, s)\r
+\r
+\r
+class DateTimeTestCase(unittest.TestCase):\r
+ def test_default(self):\r
+ t = xmlrpclib.DateTime()\r
+\r
+ def test_time(self):\r
+ d = 1181399930.036952\r
+ t = xmlrpclib.DateTime(d)\r
+ self.assertEqual(str(t), time.strftime("%Y%m%dT%H:%M:%S", time.localtime(d)))\r
+\r
+ def test_time_tuple(self):\r
+ d = (2007,6,9,10,38,50,5,160,0)\r
+ t = xmlrpclib.DateTime(d)\r
+ self.assertEqual(str(t), '20070609T10:38:50')\r
+\r
+ def test_time_struct(self):\r
+ d = time.localtime(1181399930.036952)\r
+ t = xmlrpclib.DateTime(d)\r
+ self.assertEqual(str(t), time.strftime("%Y%m%dT%H:%M:%S", d))\r
+\r
+ def test_datetime_datetime(self):\r
+ d = datetime.datetime(2007,1,2,3,4,5)\r
+ t = xmlrpclib.DateTime(d)\r
+ self.assertEqual(str(t), '20070102T03:04:05')\r
+\r
+ def test_repr(self):\r
+ d = datetime.datetime(2007,1,2,3,4,5)\r
+ t = xmlrpclib.DateTime(d)\r
+ val ="<DateTime '20070102T03:04:05' at %x>" % id(t)\r
+ self.assertEqual(repr(t), val)\r
+\r
+ def test_decode(self):\r
+ d = ' 20070908T07:11:13 '\r
+ t1 = xmlrpclib.DateTime()\r
+ t1.decode(d)\r
+ tref = xmlrpclib.DateTime(datetime.datetime(2007,9,8,7,11,13))\r
+ self.assertEqual(t1, tref)\r
+\r
+ t2 = xmlrpclib._datetime(d)\r
+ self.assertEqual(t1, tref)\r
+\r
+class BinaryTestCase(unittest.TestCase):\r
+ def test_default(self):\r
+ t = xmlrpclib.Binary()\r
+ self.assertEqual(str(t), '')\r
+\r
+ def test_string(self):\r
+ d = '\x01\x02\x03abc123\xff\xfe'\r
+ t = xmlrpclib.Binary(d)\r
+ self.assertEqual(str(t), d)\r
+\r
+ def test_decode(self):\r
+ d = '\x01\x02\x03abc123\xff\xfe'\r
+ de = base64.encodestring(d)\r
+ t1 = xmlrpclib.Binary()\r
+ t1.decode(de)\r
+ self.assertEqual(str(t1), d)\r
+\r
+ t2 = xmlrpclib._binary(de)\r
+ self.assertEqual(str(t2), d)\r
+\r
+\r
+ADDR = PORT = URL = None\r
+\r
+# The evt is set twice. First when the server is ready to serve.\r
+# Second when the server has been shutdown. The user must clear\r
+# the event after it has been set the first time to catch the second set.\r
+def http_server(evt, numrequests, requestHandler=None):\r
+ class TestInstanceClass:\r
+ def div(self, x, y):\r
+ return x // y\r
+\r
+ def _methodHelp(self, name):\r
+ if name == 'div':\r
+ return 'This is the div function'\r
+\r
+ def my_function():\r
+ '''This is my function'''\r
+ return True\r
+\r
+ class MyXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):\r
+ def get_request(self):\r
+ # Ensure the socket is always non-blocking. On Linux, socket\r
+ # attributes are not inherited like they are on *BSD and Windows.\r
+ s, port = self.socket.accept()\r
+ s.setblocking(True)\r
+ return s, port\r
+\r
+ if not requestHandler:\r
+ requestHandler = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler\r
+ serv = MyXMLRPCServer(("localhost", 0), requestHandler,\r
+ logRequests=False, bind_and_activate=False)\r
+ try:\r
+ serv.socket.settimeout(3)\r
+ serv.server_bind()\r
+ global ADDR, PORT, URL\r
+ ADDR, PORT = serv.socket.getsockname()\r
+ #connect to IP address directly. This avoids socket.create_connection()\r
+ #trying to connect to to "localhost" using all address families, which\r
+ #causes slowdown e.g. on vista which supports AF_INET6. The server listens\r
+ #on AF_INET only.\r
+ URL = "http://%s:%d"%(ADDR, PORT)\r
+ serv.server_activate()\r
+ serv.register_introspection_functions()\r
+ serv.register_multicall_functions()\r
+ serv.register_function(pow)\r
+ serv.register_function(lambda x,y: x+y, 'add')\r
+ serv.register_function(my_function)\r
+ serv.register_instance(TestInstanceClass())\r
+ evt.set()\r
+\r
+ # handle up to 'numrequests' requests\r
+ while numrequests > 0:\r
+ serv.handle_request()\r
+ numrequests -= 1\r
+\r
+ except socket.timeout:\r
+ pass\r
+ finally:\r
+ serv.socket.close()\r
+ PORT = None\r
+ evt.set()\r
+\r
+def http_multi_server(evt, numrequests, requestHandler=None):\r
+ class TestInstanceClass:\r
+ def div(self, x, y):\r
+ return x // y\r
+\r
+ def _methodHelp(self, name):\r
+ if name == 'div':\r
+ return 'This is the div function'\r
+\r
+ def my_function():\r
+ '''This is my function'''\r
+ return True\r
+\r
+ class MyXMLRPCServer(SimpleXMLRPCServer.MultiPathXMLRPCServer):\r
+ def get_request(self):\r
+ # Ensure the socket is always non-blocking. On Linux, socket\r
+ # attributes are not inherited like they are on *BSD and Windows.\r
+ s, port = self.socket.accept()\r
+ s.setblocking(True)\r
+ return s, port\r
+\r
+ if not requestHandler:\r
+ requestHandler = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler\r
+ class MyRequestHandler(requestHandler):\r
+ rpc_paths = []\r
+\r
+ serv = MyXMLRPCServer(("localhost", 0), MyRequestHandler,\r
+ logRequests=False, bind_and_activate=False)\r
+ serv.socket.settimeout(3)\r
+ serv.server_bind()\r
+ try:\r
+ global ADDR, PORT, URL\r
+ ADDR, PORT = serv.socket.getsockname()\r
+ #connect to IP address directly. This avoids socket.create_connection()\r
+ #trying to connect to to "localhost" using all address families, which\r
+ #causes slowdown e.g. on vista which supports AF_INET6. The server listens\r
+ #on AF_INET only.\r
+ URL = "http://%s:%d"%(ADDR, PORT)\r
+ serv.server_activate()\r
+ paths = ["/foo", "/foo/bar"]\r
+ for path in paths:\r
+ d = serv.add_dispatcher(path, SimpleXMLRPCServer.SimpleXMLRPCDispatcher())\r
+ d.register_introspection_functions()\r
+ d.register_multicall_functions()\r
+ serv.get_dispatcher(paths[0]).register_function(pow)\r
+ serv.get_dispatcher(paths[1]).register_function(lambda x,y: x+y, 'add')\r
+ evt.set()\r
+\r
+ # handle up to 'numrequests' requests\r
+ while numrequests > 0:\r
+ serv.handle_request()\r
+ numrequests -= 1\r
+\r
+ except socket.timeout:\r
+ pass\r
+ finally:\r
+ serv.socket.close()\r
+ PORT = None\r
+ evt.set()\r
+\r
+# This function prevents errors like:\r
+# <ProtocolError for localhost:57527/RPC2: 500 Internal Server Error>\r
+def is_unavailable_exception(e):\r
+ '''Returns True if the given ProtocolError is the product of a server-side\r
+ exception caused by the 'temporarily unavailable' response sometimes\r
+ given by operations on non-blocking sockets.'''\r
+\r
+ # sometimes we get a -1 error code and/or empty headers\r
+ try:\r
+ if e.errcode == -1 or e.headers is None:\r
+ return True\r
+ exc_mess = e.headers.get('X-exception')\r
+ except AttributeError:\r
+ # Ignore socket.errors here.\r
+ exc_mess = str(e)\r
+\r
+ if exc_mess and 'temporarily unavailable' in exc_mess.lower():\r
+ return True\r
+\r
+ return False\r
+\r
+@unittest.skipUnless(threading, 'Threading required for this test.')\r
+class BaseServerTestCase(unittest.TestCase):\r
+ requestHandler = None\r
+ request_count = 1\r
+ threadFunc = staticmethod(http_server)\r
+\r
+ def setUp(self):\r
+ # enable traceback reporting\r
+ SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True\r
+\r
+ self.evt = threading.Event()\r
+ # start server thread to handle requests\r
+ serv_args = (self.evt, self.request_count, self.requestHandler)\r
+ threading.Thread(target=self.threadFunc, args=serv_args).start()\r
+\r
+ # wait for the server to be ready\r
+ self.evt.wait(10)\r
+ self.evt.clear()\r
+\r
+ def tearDown(self):\r
+ # wait on the server thread to terminate\r
+ self.evt.wait(10)\r
+\r
+ # disable traceback reporting\r
+ SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False\r
+\r
+# NOTE: The tests in SimpleServerTestCase will ignore failures caused by\r
+# "temporarily unavailable" exceptions raised in SimpleXMLRPCServer. This\r
+# condition occurs infrequently on some platforms, frequently on others, and\r
+# is apparently caused by using SimpleXMLRPCServer with a non-blocking socket\r
+# If the server class is updated at some point in the future to handle this\r
+# situation more gracefully, these tests should be modified appropriately.\r
+\r
+class SimpleServerTestCase(BaseServerTestCase):\r
+ def test_simple1(self):\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_nonascii(self):\r
+ start_string = 'P\N{LATIN SMALL LETTER Y WITH CIRCUMFLEX}t'\r
+ end_string = 'h\N{LATIN SMALL LETTER O WITH HORN}n'\r
+\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ self.assertEqual(p.add(start_string, end_string),\r
+ start_string + end_string)\r
+ except (xmlrpclib.ProtocolError, socket.error) as e:\r
+ # ignore failures due to non-blocking socket unavailable errors.\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+\r
+ # [ch] The test 404 is causing lots of false alarms.\r
+ def XXXtest_404(self):\r
+ # send POST with httplib, it should return 404 header and\r
+ # 'Not Found' message.\r
+ conn = httplib.HTTPConnection(ADDR, PORT)\r
+ conn.request('POST', '/this-is-not-valid')\r
+ response = conn.getresponse()\r
+ conn.close()\r
+\r
+ self.assertEqual(response.status, 404)\r
+ self.assertEqual(response.reason, 'Not Found')\r
+\r
+ def test_introspection1(self):\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ meth = p.system.listMethods()\r
+ expected_methods = set(['pow', 'div', 'my_function', 'add',\r
+ 'system.listMethods', 'system.methodHelp',\r
+ 'system.methodSignature', 'system.multicall'])\r
+ self.assertEqual(set(meth), expected_methods)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_introspection2(self):\r
+ try:\r
+ # test _methodHelp()\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ divhelp = p.system.methodHelp('div')\r
+ self.assertEqual(divhelp, 'This is the div function')\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ @unittest.skipIf(sys.flags.optimize >= 2,\r
+ "Docstrings are omitted with -O2 and above")\r
+ def test_introspection3(self):\r
+ try:\r
+ # test native doc\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ myfunction = p.system.methodHelp('my_function')\r
+ self.assertEqual(myfunction, 'This is my function')\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_introspection4(self):\r
+ # the SimpleXMLRPCServer doesn't support signatures, but\r
+ # at least check that we can try making the call\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ divsig = p.system.methodSignature('div')\r
+ self.assertEqual(divsig, 'signatures not supported')\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_multicall(self):\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ multicall = xmlrpclib.MultiCall(p)\r
+ multicall.add(2,3)\r
+ multicall.pow(6,8)\r
+ multicall.div(127,42)\r
+ add_result, pow_result, div_result = multicall()\r
+ self.assertEqual(add_result, 2+3)\r
+ self.assertEqual(pow_result, 6**8)\r
+ self.assertEqual(div_result, 127//42)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_non_existing_multicall(self):\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ multicall = xmlrpclib.MultiCall(p)\r
+ multicall.this_is_not_exists()\r
+ result = multicall()\r
+\r
+ # result.results contains;\r
+ # [{'faultCode': 1, 'faultString': '<type \'exceptions.Exception\'>:'\r
+ # 'method "this_is_not_exists" is not supported'>}]\r
+\r
+ self.assertEqual(result.results[0]['faultCode'], 1)\r
+ self.assertEqual(result.results[0]['faultString'],\r
+ '<type \'exceptions.Exception\'>:method "this_is_not_exists" '\r
+ 'is not supported')\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_dotted_attribute(self):\r
+ # Raises an AttributeError because private methods are not allowed.\r
+ self.assertRaises(AttributeError,\r
+ SimpleXMLRPCServer.resolve_dotted_attribute, str, '__add')\r
+\r
+ self.assertTrue(SimpleXMLRPCServer.resolve_dotted_attribute(str, 'title'))\r
+ # Get the test to run faster by sending a request with test_simple1.\r
+ # This avoids waiting for the socket timeout.\r
+ self.test_simple1()\r
+\r
+class MultiPathServerTestCase(BaseServerTestCase):\r
+ threadFunc = staticmethod(http_multi_server)\r
+ request_count = 2\r
+ def test_path1(self):\r
+ p = xmlrpclib.ServerProxy(URL+"/foo")\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertRaises(xmlrpclib.Fault, p.add, 6, 8)\r
+ def test_path2(self):\r
+ p = xmlrpclib.ServerProxy(URL+"/foo/bar")\r
+ self.assertEqual(p.add(6,8), 6+8)\r
+ self.assertRaises(xmlrpclib.Fault, p.pow, 6, 8)\r
+\r
+#A test case that verifies that a server using the HTTP/1.1 keep-alive mechanism\r
+#does indeed serve subsequent requests on the same connection\r
+class BaseKeepaliveServerTestCase(BaseServerTestCase):\r
+ #a request handler that supports keep-alive and logs requests into a\r
+ #class variable\r
+ class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):\r
+ parentClass = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler\r
+ protocol_version = 'HTTP/1.1'\r
+ myRequests = []\r
+ def handle(self):\r
+ self.myRequests.append([])\r
+ self.reqidx = len(self.myRequests)-1\r
+ return self.parentClass.handle(self)\r
+ def handle_one_request(self):\r
+ result = self.parentClass.handle_one_request(self)\r
+ self.myRequests[self.reqidx].append(self.raw_requestline)\r
+ return result\r
+\r
+ requestHandler = RequestHandler\r
+ def setUp(self):\r
+ #clear request log\r
+ self.RequestHandler.myRequests = []\r
+ return BaseServerTestCase.setUp(self)\r
+\r
+#A test case that verifies that a server using the HTTP/1.1 keep-alive mechanism\r
+#does indeed serve subsequent requests on the same connection\r
+class KeepaliveServerTestCase1(BaseKeepaliveServerTestCase):\r
+ def test_two(self):\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ #do three requests.\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+\r
+ #they should have all been handled by a single request handler\r
+ self.assertEqual(len(self.RequestHandler.myRequests), 1)\r
+\r
+ #check that we did at least two (the third may be pending append\r
+ #due to thread scheduling)\r
+ self.assertGreaterEqual(len(self.RequestHandler.myRequests[-1]), 2)\r
+\r
+#test special attribute access on the serverproxy, through the __call__\r
+#function.\r
+class KeepaliveServerTestCase2(BaseKeepaliveServerTestCase):\r
+ #ask for two keepalive requests to be handled.\r
+ request_count=2\r
+\r
+ def test_close(self):\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ #do some requests with close.\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ p("close")() #this should trigger a new keep-alive request\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+\r
+ #they should have all been two request handlers, each having logged at least\r
+ #two complete requests\r
+ self.assertEqual(len(self.RequestHandler.myRequests), 2)\r
+ self.assertGreaterEqual(len(self.RequestHandler.myRequests[-1]), 2)\r
+ self.assertGreaterEqual(len(self.RequestHandler.myRequests[-2]), 2)\r
+\r
+ def test_transport(self):\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ #do some requests with close.\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ p("transport").close() #same as above, really.\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ self.assertEqual(len(self.RequestHandler.myRequests), 2)\r
+\r
+#A test case that verifies that gzip encoding works in both directions\r
+#(for a request and the response)\r
+class GzipServerTestCase(BaseServerTestCase):\r
+ #a request handler that supports keep-alive and logs requests into a\r
+ #class variable\r
+ class RequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):\r
+ parentClass = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler\r
+ protocol_version = 'HTTP/1.1'\r
+\r
+ def do_POST(self):\r
+ #store content of last request in class\r
+ self.__class__.content_length = int(self.headers["content-length"])\r
+ return self.parentClass.do_POST(self)\r
+ requestHandler = RequestHandler\r
+\r
+ class Transport(xmlrpclib.Transport):\r
+ #custom transport, stores the response length for our perusal\r
+ fake_gzip = False\r
+ def parse_response(self, response):\r
+ self.response_length=int(response.getheader("content-length", 0))\r
+ return xmlrpclib.Transport.parse_response(self, response)\r
+\r
+ def send_content(self, connection, body):\r
+ if self.fake_gzip:\r
+ #add a lone gzip header to induce decode error remotely\r
+ connection.putheader("Content-Encoding", "gzip")\r
+ return xmlrpclib.Transport.send_content(self, connection, body)\r
+\r
+ def setUp(self):\r
+ BaseServerTestCase.setUp(self)\r
+\r
+ def test_gzip_request(self):\r
+ t = self.Transport()\r
+ t.encode_threshold = None\r
+ p = xmlrpclib.ServerProxy(URL, transport=t)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ a = self.RequestHandler.content_length\r
+ t.encode_threshold = 0 #turn on request encoding\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ b = self.RequestHandler.content_length\r
+ self.assertTrue(a>b)\r
+\r
+ def test_bad_gzip_request(self):\r
+ t = self.Transport()\r
+ t.encode_threshold = None\r
+ t.fake_gzip = True\r
+ p = xmlrpclib.ServerProxy(URL, transport=t)\r
+ cm = self.assertRaisesRegexp(xmlrpclib.ProtocolError,\r
+ re.compile(r"\b400\b"))\r
+ with cm:\r
+ p.pow(6, 8)\r
+\r
+ def test_gsip_response(self):\r
+ t = self.Transport()\r
+ p = xmlrpclib.ServerProxy(URL, transport=t)\r
+ old = self.requestHandler.encode_threshold\r
+ self.requestHandler.encode_threshold = None #no encoding\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ a = t.response_length\r
+ self.requestHandler.encode_threshold = 0 #always encode\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ b = t.response_length\r
+ self.requestHandler.encode_threshold = old\r
+ self.assertTrue(a>b)\r
+\r
+#Test special attributes of the ServerProxy object\r
+class ServerProxyTestCase(unittest.TestCase):\r
+ def setUp(self):\r
+ unittest.TestCase.setUp(self)\r
+ if threading:\r
+ self.url = URL\r
+ else:\r
+ # Without threading, http_server() and http_multi_server() will not\r
+ # be executed and URL is still equal to None. 'http://' is a just\r
+ # enough to choose the scheme (HTTP)\r
+ self.url = 'http://'\r
+\r
+ def test_close(self):\r
+ p = xmlrpclib.ServerProxy(self.url)\r
+ self.assertEqual(p('close')(), None)\r
+\r
+ def test_transport(self):\r
+ t = xmlrpclib.Transport()\r
+ p = xmlrpclib.ServerProxy(self.url, transport=t)\r
+ self.assertEqual(p('transport'), t)\r
+\r
+# This is a contrived way to make a failure occur on the server side\r
+# in order to test the _send_traceback_header flag on the server\r
+class FailingMessageClass(mimetools.Message):\r
+ def __getitem__(self, key):\r
+ key = key.lower()\r
+ if key == 'content-length':\r
+ return 'I am broken'\r
+ return mimetools.Message.__getitem__(self, key)\r
+\r
+\r
+@unittest.skipUnless(threading, 'Threading required for this test.')\r
+class FailingServerTestCase(unittest.TestCase):\r
+ def setUp(self):\r
+ self.evt = threading.Event()\r
+ # start server thread to handle requests\r
+ serv_args = (self.evt, 1)\r
+ threading.Thread(target=http_server, args=serv_args).start()\r
+\r
+ # wait for the server to be ready\r
+ self.evt.wait()\r
+ self.evt.clear()\r
+\r
+ def tearDown(self):\r
+ # wait on the server thread to terminate\r
+ self.evt.wait()\r
+ # reset flag\r
+ SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = False\r
+ # reset message class\r
+ SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = mimetools.Message\r
+\r
+ def test_basic(self):\r
+ # check that flag is false by default\r
+ flagval = SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header\r
+ self.assertEqual(flagval, False)\r
+\r
+ # enable traceback reporting\r
+ SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True\r
+\r
+ # test a call that shouldn't fail just as a smoke test\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ self.assertEqual(p.pow(6,8), 6**8)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e):\r
+ # protocol error; provide additional information in test output\r
+ self.fail("%s\n%s" % (e, getattr(e, "headers", "")))\r
+\r
+ def test_fail_no_info(self):\r
+ # use the broken message class\r
+ SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass\r
+\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ p.pow(6,8)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e) and hasattr(e, "headers"):\r
+ # The two server-side error headers shouldn't be sent back in this case\r
+ self.assertTrue(e.headers.get("X-exception") is None)\r
+ self.assertTrue(e.headers.get("X-traceback") is None)\r
+ else:\r
+ self.fail('ProtocolError not raised')\r
+\r
+ def test_fail_with_info(self):\r
+ # use the broken message class\r
+ SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.MessageClass = FailingMessageClass\r
+\r
+ # Check that errors in the server send back exception/traceback\r
+ # info when flag is set\r
+ SimpleXMLRPCServer.SimpleXMLRPCServer._send_traceback_header = True\r
+\r
+ try:\r
+ p = xmlrpclib.ServerProxy(URL)\r
+ p.pow(6,8)\r
+ except (xmlrpclib.ProtocolError, socket.error), e:\r
+ # ignore failures due to non-blocking socket 'unavailable' errors\r
+ if not is_unavailable_exception(e) and hasattr(e, "headers"):\r
+ # We should get error info in the response\r
+ expected_err = "invalid literal for int() with base 10: 'I am broken'"\r
+ self.assertEqual(e.headers.get("x-exception"), expected_err)\r
+ self.assertTrue(e.headers.get("x-traceback") is not None)\r
+ else:\r
+ self.fail('ProtocolError not raised')\r
+\r
+class CGIHandlerTestCase(unittest.TestCase):\r
+ def setUp(self):\r
+ self.cgi = SimpleXMLRPCServer.CGIXMLRPCRequestHandler()\r
+\r
+ def tearDown(self):\r
+ self.cgi = None\r
+\r
+ def test_cgi_get(self):\r
+ with test_support.EnvironmentVarGuard() as env:\r
+ env['REQUEST_METHOD'] = 'GET'\r
+ # if the method is GET and no request_text is given, it runs handle_get\r
+ # get sysout output\r
+ with test_support.captured_stdout() as data_out:\r
+ self.cgi.handle_request()\r
+\r
+ # parse Status header\r
+ data_out.seek(0)\r
+ handle = data_out.read()\r
+ status = handle.split()[1]\r
+ message = ' '.join(handle.split()[2:4])\r
+\r
+ self.assertEqual(status, '400')\r
+ self.assertEqual(message, 'Bad Request')\r
+\r
+\r
+ def test_cgi_xmlrpc_response(self):\r
+ data = """<?xml version='1.0'?>\r
+ <methodCall>\r
+ <methodName>test_method</methodName>\r
+ <params>\r
+ <param>\r
+ <value><string>foo</string></value>\r
+ </param>\r
+ <param>\r
+ <value><string>bar</string></value>\r
+ </param>\r
+ </params>\r
+ </methodCall>\r
+ """\r
+\r
+ with test_support.EnvironmentVarGuard() as env, \\r
+ test_support.captured_stdout() as data_out, \\r
+ test_support.captured_stdin() as data_in:\r
+ data_in.write(data)\r
+ data_in.seek(0)\r
+ env['CONTENT_LENGTH'] = str(len(data))\r
+ self.cgi.handle_request()\r
+ data_out.seek(0)\r
+\r
+ # will respond exception, if so, our goal is achieved ;)\r
+ handle = data_out.read()\r
+\r
+ # start with 44th char so as not to get http header, we just need only xml\r
+ self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, handle[44:])\r
+\r
+ # Also test the content-length returned by handle_request\r
+ # Using the same test method inorder to avoid all the datapassing\r
+ # boilerplate code.\r
+ # Test for bug: http://bugs.python.org/issue5040\r
+\r
+ content = handle[handle.find("<?xml"):]\r
+\r
+ self.assertEqual(\r
+ int(re.search('Content-Length: (\d+)', handle).group(1)),\r
+ len(content))\r
+\r
+\r
+class FakeSocket:\r
+\r
+ def __init__(self):\r
+ self.data = StringIO.StringIO()\r
+\r
+ def send(self, buf):\r
+ self.data.write(buf)\r
+ return len(buf)\r
+\r
+ def sendall(self, buf):\r
+ self.data.write(buf)\r
+\r
+ def getvalue(self):\r
+ return self.data.getvalue()\r
+\r
+ def makefile(self, x='r', y=-1):\r
+ raise RuntimeError\r
+\r
+ def close(self):\r
+ pass\r
+\r
+class FakeTransport(xmlrpclib.Transport):\r
+ """A Transport instance that records instead of sending a request.\r
+\r
+ This class replaces the actual socket used by httplib with a\r
+ FakeSocket object that records the request. It doesn't provide a\r
+ response.\r
+ """\r
+\r
+ def make_connection(self, host):\r
+ conn = xmlrpclib.Transport.make_connection(self, host)\r
+ conn.sock = self.fake_socket = FakeSocket()\r
+ return conn\r
+\r
+class TransportSubclassTestCase(unittest.TestCase):\r
+\r
+ def issue_request(self, transport_class):\r
+ """Return an HTTP request made via transport_class."""\r
+ transport = transport_class()\r
+ proxy = xmlrpclib.ServerProxy("http://example.com/",\r
+ transport=transport)\r
+ try:\r
+ proxy.pow(6, 8)\r
+ except RuntimeError:\r
+ return transport.fake_socket.getvalue()\r
+ return None\r
+\r
+ def test_custom_user_agent(self):\r
+ class TestTransport(FakeTransport):\r
+\r
+ def send_user_agent(self, conn):\r
+ xmlrpclib.Transport.send_user_agent(self, conn)\r
+ conn.putheader("X-Test", "test_custom_user_agent")\r
+\r
+ req = self.issue_request(TestTransport)\r
+ self.assertIn("X-Test: test_custom_user_agent\r\n", req)\r
+\r
+ def test_send_host(self):\r
+ class TestTransport(FakeTransport):\r
+\r
+ def send_host(self, conn, host):\r
+ xmlrpclib.Transport.send_host(self, conn, host)\r
+ conn.putheader("X-Test", "test_send_host")\r
+\r
+ req = self.issue_request(TestTransport)\r
+ self.assertIn("X-Test: test_send_host\r\n", req)\r
+\r
+ def test_send_request(self):\r
+ class TestTransport(FakeTransport):\r
+\r
+ def send_request(self, conn, url, body):\r
+ xmlrpclib.Transport.send_request(self, conn, url, body)\r
+ conn.putheader("X-Test", "test_send_request")\r
+\r
+ req = self.issue_request(TestTransport)\r
+ self.assertIn("X-Test: test_send_request\r\n", req)\r
+\r
+ def test_send_content(self):\r
+ class TestTransport(FakeTransport):\r
+\r
+ def send_content(self, conn, body):\r
+ conn.putheader("X-Test", "test_send_content")\r
+ xmlrpclib.Transport.send_content(self, conn, body)\r
+\r
+ req = self.issue_request(TestTransport)\r
+ self.assertIn("X-Test: test_send_content\r\n", req)\r
+\r
+@test_support.reap_threads\r
+def test_main():\r
+ xmlrpc_tests = [XMLRPCTestCase, HelperTestCase, DateTimeTestCase,\r
+ BinaryTestCase, FaultTestCase, TransportSubclassTestCase]\r
+ xmlrpc_tests.append(SimpleServerTestCase)\r
+ xmlrpc_tests.append(KeepaliveServerTestCase1)\r
+ xmlrpc_tests.append(KeepaliveServerTestCase2)\r
+ try:\r
+ import gzip\r
+ xmlrpc_tests.append(GzipServerTestCase)\r
+ except ImportError:\r
+ pass #gzip not supported in this build\r
+ xmlrpc_tests.append(MultiPathServerTestCase)\r
+ xmlrpc_tests.append(ServerProxyTestCase)\r
+ xmlrpc_tests.append(FailingServerTestCase)\r
+ xmlrpc_tests.append(CGIHandlerTestCase)\r
+\r
+ test_support.run_unittest(*xmlrpc_tests)\r
+\r
+if __name__ == "__main__":\r
+ test_main()\r