Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions Doc/library/xmlrpc.client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Comment thread
cedk marked this conversation as resolved.
Outdated

.. 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
Expand Down
63 changes: 62 additions & 1 deletion Lib/test/test_xmlrpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1171,6 +1171,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
Comment thread
cedk marked this conversation as resolved.
Outdated
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):
Comment thread
cedk marked this conversation as resolved.
Outdated
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):
Expand Down Expand Up @@ -1397,7 +1458,7 @@ def test_main():
BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase,
SimpleServerTestCase, SimpleServerEncodingTestCase,
KeepaliveServerTestCase1, KeepaliveServerTestCase2,
GzipServerTestCase, GzipUtilTestCase,
GzipServerTestCase, GzipUtilTestCase, HeadersServerTestCase,
MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)

Expand Down
17 changes: 11 additions & 6 deletions Lib/xmlrpc/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []

##
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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.