From c1b073a630bb731de18bb17afb2b8b1388b92a72 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Mon, 29 Mar 2021 16:39:31 +0300 Subject: [PATCH] bpo-43433: Preserve query and fragment in the URL of the server in ServerProxy. (GH-25057) --- Lib/test/test_xmlrpc.py | 40 ++++++++++++++++++- Lib/xmlrpc/client.py | 6 ++- .../2021-03-28-23-50-20.bpo-43433.so9j5G.rst | 2 + 3 files changed, 45 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2021-03-28-23-50-20.bpo-43433.so9j5G.rst diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index c54aeb10945..a9f67466071 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -698,11 +698,16 @@ def http_multi_server(evt, numrequests, requestHandler=None): #on AF_INET only. URL = "http://%s:%d"%(ADDR, PORT) serv.server_activate() - paths = ["/foo", "/foo/bar"] + paths = [ + "/foo", "/foo/bar", + "/foo?k=v", "/foo#frag", "/foo?k=v#frag", + "", "/", "/RPC2", "?k=v", "#frag", + ] for path in paths: d = serv.add_dispatcher(path, xmlrpc.server.SimpleXMLRPCDispatcher()) d.register_introspection_functions() d.register_multicall_functions() + d.register_function(lambda p=path: p, 'test') serv.get_dispatcher(paths[0]).register_function(pow) serv.get_dispatcher(paths[1]).register_function(lambda x,y: x+y, 'add') serv.add_dispatcher("/is/broken", BrokenDispatcher()) @@ -1018,6 +1023,39 @@ class MultiPathServerTestCase(BaseServerTestCase): p = xmlrpclib.ServerProxy(URL+"/is/broken") self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + def test_invalid_path(self): + p = xmlrpclib.ServerProxy(URL+"/invalid") + self.assertRaises(xmlrpclib.Fault, p.add, 6, 8) + + def test_path_query_fragment(self): + p = xmlrpclib.ServerProxy(URL+"/foo?k=v#frag") + self.assertEqual(p.test(), "/foo?k=v#frag") + + def test_path_fragment(self): + p = xmlrpclib.ServerProxy(URL+"/foo#frag") + self.assertEqual(p.test(), "/foo#frag") + + def test_path_query(self): + p = xmlrpclib.ServerProxy(URL+"/foo?k=v") + self.assertEqual(p.test(), "/foo?k=v") + + def test_empty_path(self): + p = xmlrpclib.ServerProxy(URL) + self.assertEqual(p.test(), "/RPC2") + + def test_root_path(self): + p = xmlrpclib.ServerProxy(URL + "/") + self.assertEqual(p.test(), "/") + + def test_empty_path_query(self): + p = xmlrpclib.ServerProxy(URL + "?k=v") + self.assertEqual(p.test(), "?k=v") + + def test_empty_path_fragment(self): + p = xmlrpclib.ServerProxy(URL + "#frag") + self.assertEqual(p.test(), "#frag") + + #A test case that verifies that a server using the HTTP/1.1 keep-alive mechanism #does indeed serve subsequent requests on the same connection class BaseKeepaliveServerTestCase(BaseServerTestCase): diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index d15d60d2937..9e7449c88df 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -1421,11 +1421,13 @@ class ServerProxy: # establish a "logical" server connection # get the url - p = urllib.parse.urlparse(uri) + p = urllib.parse.urlsplit(uri) if p.scheme not in ("http", "https"): raise OSError("unsupported XML-RPC protocol") self.__host = p.netloc - self.__handler = p.path or "/RPC2" + self.__handler = urllib.parse.urlunsplit(["", "", *p[2:]]) + if not self.__handler: + self.__handler = "/RPC2" if transport is None: if p.scheme == "https": diff --git a/Misc/NEWS.d/next/Library/2021-03-28-23-50-20.bpo-43433.so9j5G.rst b/Misc/NEWS.d/next/Library/2021-03-28-23-50-20.bpo-43433.so9j5G.rst new file mode 100644 index 00000000000..2f67e316a5e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2021-03-28-23-50-20.bpo-43433.so9j5G.rst @@ -0,0 +1,2 @@ +:class:`xmlrpc.client.ServerProxy` no longer ignores query and fragment in +the URL of the server.