mirror of
https://github.com/python/cpython
synced 2024-09-18 20:21:42 +00:00
bpo-35153: Add headers parameter to xmlrpc.client.ServerProxy (GH-10308)
Allow to add HTTP headers to XML-RPC requests sent to the server.
This commit is contained in:
parent
513e9b4425
commit
beda52ed36
|
@ -34,10 +34,7 @@ between conformable Python objects and XML on the wire.
|
||||||
|
|
||||||
.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
|
.. class:: ServerProxy(uri, transport=None, encoding=None, verbose=False, \
|
||||||
allow_none=False, use_datetime=False, \
|
allow_none=False, use_datetime=False, \
|
||||||
use_builtin_types=False, *, context=None)
|
use_builtin_types=False, *, headers=(), context=None)
|
||||||
|
|
||||||
.. versionchanged:: 3.3
|
|
||||||
The *use_builtin_types* flag was added.
|
|
||||||
|
|
||||||
A :class:`ServerProxy` instance is an object that manages communication with a
|
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
|
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.
|
presented as :class:`bytes` objects; this flag is false by default.
|
||||||
:class:`datetime.datetime`, :class:`bytes` and :class:`bytearray` objects
|
:class:`datetime.datetime`, :class:`bytes` and :class:`bytearray` objects
|
||||||
may be passed to calls.
|
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
|
The obsolete *use_datetime* flag is similar to *use_builtin_types* but it
|
||||||
applies only to date/time values.
|
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
|
Both the HTTP and HTTPS transports support the URL syntax extension for HTTP
|
||||||
Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass``
|
Basic Authentication: ``http://user:pass@host:port/path``. The ``user:pass``
|
||||||
portion will be base64-encoded as an HTTP 'Authorization' header, and sent to
|
portion will be base64-encoded as an HTTP 'Authorization' header, and sent to
|
||||||
|
|
|
@ -1170,6 +1170,67 @@ def test_gzip_decode_limit(self):
|
||||||
xmlrpclib.gzip_decode(encoded, max_decode=-1)
|
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
|
#Test special attributes of the ServerProxy object
|
||||||
class ServerProxyTestCase(unittest.TestCase):
|
class ServerProxyTestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -1396,7 +1457,7 @@ def test_main():
|
||||||
BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase,
|
BinaryTestCase, FaultTestCase, UseBuiltinTypesTestCase,
|
||||||
SimpleServerTestCase, SimpleServerEncodingTestCase,
|
SimpleServerTestCase, SimpleServerEncodingTestCase,
|
||||||
KeepaliveServerTestCase1, KeepaliveServerTestCase2,
|
KeepaliveServerTestCase1, KeepaliveServerTestCase2,
|
||||||
GzipServerTestCase, GzipUtilTestCase,
|
GzipServerTestCase, GzipUtilTestCase, HeadersServerTestCase,
|
||||||
MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
|
MultiPathServerTestCase, ServerProxyTestCase, FailingServerTestCase,
|
||||||
CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
|
CGIHandlerTestCase, SimpleXMLRPCDispatcherTestCase)
|
||||||
|
|
||||||
|
|
|
@ -1131,10 +1131,12 @@ class Transport:
|
||||||
# that they can decode such a request
|
# that they can decode such a request
|
||||||
encode_threshold = None #None = don't encode
|
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_datetime = use_datetime
|
||||||
self._use_builtin_types = use_builtin_types
|
self._use_builtin_types = use_builtin_types
|
||||||
self._connection = (None, None)
|
self._connection = (None, None)
|
||||||
|
self._headers = list(headers)
|
||||||
self._extra_headers = []
|
self._extra_headers = []
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1265,7 +1267,7 @@ def close(self):
|
||||||
|
|
||||||
def send_request(self, host, handler, request_body, debug):
|
def send_request(self, host, handler, request_body, debug):
|
||||||
connection = self.make_connection(host)
|
connection = self.make_connection(host)
|
||||||
headers = self._extra_headers[:]
|
headers = self._headers + self._extra_headers
|
||||||
if debug:
|
if debug:
|
||||||
connection.set_debuglevel(1)
|
connection.set_debuglevel(1)
|
||||||
if self.accept_gzip_encoding and gzip:
|
if self.accept_gzip_encoding and gzip:
|
||||||
|
@ -1347,9 +1349,11 @@ def parse_response(self, response):
|
||||||
class SafeTransport(Transport):
|
class SafeTransport(Transport):
|
||||||
"""Handles an HTTPS transaction to an XML-RPC server."""
|
"""Handles an HTTPS transaction to an XML-RPC server."""
|
||||||
|
|
||||||
def __init__(self, use_datetime=False, use_builtin_types=False, *,
|
def __init__(self, use_datetime=False, use_builtin_types=False,
|
||||||
context=None):
|
*, headers=(), context=None):
|
||||||
super().__init__(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
|
super().__init__(use_datetime=use_datetime,
|
||||||
|
use_builtin_types=use_builtin_types,
|
||||||
|
headers=headers)
|
||||||
self.context = context
|
self.context = context
|
||||||
|
|
||||||
# FIXME: mostly untested
|
# FIXME: mostly untested
|
||||||
|
@ -1409,7 +1413,7 @@ class ServerProxy:
|
||||||
|
|
||||||
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||||
allow_none=False, use_datetime=False, use_builtin_types=False,
|
allow_none=False, use_datetime=False, use_builtin_types=False,
|
||||||
*, context=None):
|
*, headers=(), context=None):
|
||||||
# establish a "logical" server connection
|
# establish a "logical" server connection
|
||||||
|
|
||||||
# get the url
|
# get the url
|
||||||
|
@ -1429,6 +1433,7 @@ def __init__(self, uri, transport=None, encoding=None, verbose=False,
|
||||||
extra_kwargs = {}
|
extra_kwargs = {}
|
||||||
transport = handler(use_datetime=use_datetime,
|
transport = handler(use_datetime=use_datetime,
|
||||||
use_builtin_types=use_builtin_types,
|
use_builtin_types=use_builtin_types,
|
||||||
|
headers=headers,
|
||||||
**extra_kwargs)
|
**extra_kwargs)
|
||||||
self.__transport = transport
|
self.__transport = transport
|
||||||
|
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in a new issue