[Python-checkins] bpo-35153: Add headers parameter to xmlrpc.client.ServerProxy (GH-10308)

Victor Stinner webhook-mailer at python.org
Tue Feb 19 11:18:56 EST 2019


https://github.com/python/cpython/commit/beda52ed36e701e45f22903fc4d3bec0d085b25b
commit: beda52ed36e701e45f22903fc4d3bec0d085b25b
branch: master
author: Cédric Krier <cedk at users.noreply.github.com>
committer: Victor Stinner <vstinner at redhat.com>
date: 2019-02-19T17:18:50+01:00
summary:

bpo-35153: Add headers parameter to xmlrpc.client.ServerProxy (GH-10308)

Allow to add HTTP headers to XML-RPC requests sent to the server.

files:
A Misc/NEWS.d/next/Library/2018-11-03-12-38-03.bpo-35153.009pdF.rst
M Doc/library/xmlrpc.client.rst
M Lib/test/test_xmlrpc.py
M Lib/xmlrpc/client.py

diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst
index 27d92e324722..32403819531a 100644
--- a/Doc/library/xmlrpc.client.rst
+++ b/Doc/library/xmlrpc.client.rst
@@ -34,10 +34,7 @@ between conformable Python objects and XML on the wire.
 
 .. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
                        allow_none=False, use_datetime=False, \
-                       use_builtin_types=False, *, context=None)
-
-   .. versionchanged:: 3.3
-      The *use_builtin_types* flag was added.
+                       use_builtin_types=False, *, headers=(), context=None)
 
    A :class:`ServerProxy` instance is an object that manages communication with a
    remote XML-RPC server.  The required first argument is a URI (Uniform Resource
@@ -59,9 +56,18 @@ between conformable Python objects and XML on the wire.
    presented as :class:`bytes` objects; this flag is false by default.
    :class:`datetime.datetime`, :class:`bytes` and :class:`bytearray` objects
    may be passed to calls.
+   The *headers* parameter is an optional sequence of HTTP headers to send with
+   each request, expressed as a sequence of 2-tuples representing the header
+   name and value. (e.g. `[('Header-Name', 'value')]`).
    The obsolete *use_datetime* flag is similar to *use_builtin_types* but it
    applies only to date/time values.
 
+.. versionchanged:: 3.3
+    The *use_builtin_types* flag was added.
+
+.. versionchanged:: 3.8
+    The *headers* parameter was added.
+
    Both the HTTP and HTTPS transports support the URL syntax extension for HTTP
    Basic Authentication: ``http://user:pass@host:port/path``.  The  ``user:pass``
    portion will be base64-encoded as an HTTP 'Authorization' header, and sent to
diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
index 32263f7f0b3b..916e9c49a97c 100644
--- a/Lib/test/test_xmlrpc.py
+++ b/Lib/test/test_xmlrpc.py
@@ -1170,6 +1170,67 @@ def test_gzip_decode_limit(self):
         xmlrpclib.gzip_decode(encoded, max_decode=-1)
 
 
+class HeadersServerTestCase(BaseServerTestCase):
+    class RequestHandler(xmlrpc.server.SimpleXMLRPCRequestHandler):
+        test_headers = None
+
+        def do_POST(self):
+            self.__class__.test_headers = self.headers
+            return super().do_POST()
+    requestHandler = RequestHandler
+    standard_headers = [
+        'Host', 'Accept-Encoding', 'Content-Type', 'User-Agent',
+        'Content-Length']
+
+    def setUp(self):
+        self.RequestHandler.test_headers = None
+        return super().setUp()
+
+    def assertContainsAdditionalHeaders(self, headers, additional):
+        expected_keys = sorted(self.standard_headers + list(additional.keys()))
+        self.assertListEqual(sorted(headers.keys()), expected_keys)
+
+        for key, value in additional.items():
+            self.assertEqual(headers.get(key), value)
+
+    def test_header(self):
+        p = xmlrpclib.ServerProxy(URL, headers=[('X-Test', 'foo')])
+        self.assertEqual(p.pow(6, 8), 6**8)
+
+        headers = self.RequestHandler.test_headers
+        self.assertContainsAdditionalHeaders(headers, {'X-Test': 'foo'})
+
+    def test_header_many(self):
+        p = xmlrpclib.ServerProxy(
+            URL, headers=[('X-Test', 'foo'), ('X-Test-Second', 'bar')])
+        self.assertEqual(p.pow(6, 8), 6**8)
+
+        headers = self.RequestHandler.test_headers
+        self.assertContainsAdditionalHeaders(
+            headers, {'X-Test': 'foo', 'X-Test-Second': 'bar'})
+
+    def test_header_empty(self):
+        p = xmlrpclib.ServerProxy(URL, headers=[])
+        self.assertEqual(p.pow(6, 8), 6**8)
+
+        headers = self.RequestHandler.test_headers
+        self.assertContainsAdditionalHeaders(headers, {})
+
+    def test_header_tuple(self):
+        p = xmlrpclib.ServerProxy(URL, headers=(('X-Test', 'foo'),))
+        self.assertEqual(p.pow(6, 8), 6**8)
+
+        headers = self.RequestHandler.test_headers
+        self.assertContainsAdditionalHeaders(headers, {'X-Test': 'foo'})
+
+    def test_header_items(self):
+        p = xmlrpclib.ServerProxy(URL, headers={'X-Test': 'foo'}.items())
+        self.assertEqual(p.pow(6, 8), 6**8)
+
+        headers = self.RequestHandler.test_headers
+        self.assertContainsAdditionalHeaders(headers, {'X-Test': 'foo'})
+
+
 #Test special attributes of the ServerProxy object
 class ServerProxyTestCase(unittest.TestCase):
     def setUp(self):
@@ -1396,7 +1457,7 @@ def test_main():
             BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase,
             SimpleServerTestCase, SimpleServerEncodingTestCase,
             KeepaliveServerTestCase1, KeepaliveServerTestCase2,
-            GzipServerTestCase, GzipUtilTestCase,
+            GzipServerTestCase, GzipUtilTestCase, HeadersServerTestCase,
             MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
             CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
 
diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py
index ddab76ffbe83..a0e923a20322 100644
--- a/Lib/xmlrpc/client.py
+++ b/Lib/xmlrpc/client.py
@@ -1131,10 +1131,12 @@ class Transport:
     # that they can decode such a request
     encode_threshold = None #None = don't encode
 
-    def __init__(self, use_datetime=False, use_builtin_types=False):
+    def __init__(self, use_datetime=False, use_builtin_types=False,
+                 *, headers=()):
         self._use_datetime = use_datetime
         self._use_builtin_types = use_builtin_types
         self._connection = (None, None)
+        self._headers = list(headers)
         self._extra_headers = []
 
     ##
@@ -1265,7 +1267,7 @@ def close(self):
 
     def send_request(self, host, handler, request_body, debug):
         connection = self.make_connection(host)
-        headers = self._extra_headers[:]
+        headers = self._headers + self._extra_headers
         if debug:
             connection.set_debuglevel(1)
         if self.accept_gzip_encoding and gzip:
@@ -1347,9 +1349,11 @@ def parse_response(self, response):
 class SafeTransport(Transport):
     """Handles an HTTPS transaction to an XML-RPC server."""
 
-    def __init__(self, use_datetime=False, use_builtin_types=False, *,
-                 context=None):
-        super().__init__(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
+    def __init__(self, use_datetime=False, use_builtin_types=False,
+                 *, headers=(), context=None):
+        super().__init__(use_datetime=use_datetime,
+                         use_builtin_types=use_builtin_types,
+                         headers=headers)
         self.context = context
 
     # FIXME: mostly untested
@@ -1409,7 +1413,7 @@ class ServerProxy:
 
     def __init__(self, uri, transport=None, encoding=None, verbose=False,
                  allow_none=False, use_datetime=False, use_builtin_types=False,
-                 *, context=None):
+                 *, headers=(), context=None):
         # establish a "logical" server connection
 
         # get the url
@@ -1429,6 +1433,7 @@ def __init__(self, uri, transport=None, encoding=None, verbose=False,
                 extra_kwargs = {}
             transport = handler(use_datetime=use_datetime,
                                 use_builtin_types=use_builtin_types,
+                                headers=headers,
                                 **extra_kwargs)
         self.__transport = transport
 
diff --git a/Misc/NEWS.d/next/Library/2018-11-03-12-38-03.bpo-35153.009pdF.rst b/Misc/NEWS.d/next/Library/2018-11-03-12-38-03.bpo-35153.009pdF.rst
new file mode 100644
index 000000000000..7926024a67bb
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-11-03-12-38-03.bpo-35153.009pdF.rst
@@ -0,0 +1,3 @@
+Add *headers* optional keyword-only parameter to
+:class:`xmlrpc.client.ServerProxy`, :class:`xmlrpc.client.Transport` and
+:class:`xmlrpc.client.SafeTransport`.  Patch by Cédric Krier.



More information about the Python-checkins mailing list