ยปCore Development>Code coverage>Lib/xmlrpc/client.py

Python code coverage for Lib/xmlrpc/client.py

#countcontent
1n/a#
2n/a# XML-RPC CLIENT LIBRARY
3n/a# $Id$
4n/a#
5n/a# an XML-RPC client interface for Python.
6n/a#
7n/a# the marshalling and response parser code can also be used to
8n/a# implement XML-RPC servers.
9n/a#
10n/a# Notes:
11n/a# this version is designed to work with Python 2.1 or newer.
12n/a#
13n/a# History:
14n/a# 1999-01-14 fl Created
15n/a# 1999-01-15 fl Changed dateTime to use localtime
16n/a# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service
17n/a# 1999-01-19 fl Fixed array data element (from Skip Montanaro)
18n/a# 1999-01-21 fl Fixed dateTime constructor, etc.
19n/a# 1999-02-02 fl Added fault handling, handle empty sequences, etc.
20n/a# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro)
21n/a# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8)
22n/a# 2000-11-28 fl Changed boolean to check the truth value of its argument
23n/a# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches
24n/a# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1)
25n/a# 2001-03-28 fl Make sure response tuple is a singleton
26n/a# 2001-03-29 fl Don't require empty params element (from Nicholas Riley)
27n/a# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2)
28n/a# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
29n/a# 2001-09-03 fl Allow Transport subclass to override getparser
30n/a# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
31n/a# 2001-10-01 fl Remove containers from memo cache when done with them
32n/a# 2001-10-01 fl Use faster escape method (80% dumps speedup)
33n/a# 2001-10-02 fl More dumps microtuning
34n/a# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum)
35n/a# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
36n/a# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems)
37n/a# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
38n/a# 2002-03-17 fl Avoid buffered read when possible (from James Rucker)
39n/a# 2002-04-07 fl Added pythondoc comments
40n/a# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers
41n/a# 2002-05-15 fl Added error constants (from Andrew Kuchling)
42n/a# 2002-06-27 fl Merged with Python CVS version
43n/a# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby)
44n/a# 2003-01-22 sm Add support for the bool type
45n/a# 2003-02-27 gvr Remove apply calls
46n/a# 2003-04-24 sm Use cStringIO if available
47n/a# 2003-04-25 ak Add support for nil
48n/a# 2003-06-15 gn Add support for time.struct_time
49n/a# 2003-07-12 gp Correct marshalling of Faults
50n/a# 2003-10-31 mvl Add multicall support
51n/a# 2004-08-20 mvl Bump minimum supported Python version to 2.1
52n/a# 2014-12-02 ch/doko Add workaround for gzip bomb vulnerability
53n/a#
54n/a# Copyright (c) 1999-2002 by Secret Labs AB.
55n/a# Copyright (c) 1999-2002 by Fredrik Lundh.
56n/a#
57n/a# info@pythonware.com
58n/a# http://www.pythonware.com
59n/a#
60n/a# --------------------------------------------------------------------
61n/a# The XML-RPC client interface is
62n/a#
63n/a# Copyright (c) 1999-2002 by Secret Labs AB
64n/a# Copyright (c) 1999-2002 by Fredrik Lundh
65n/a#
66n/a# By obtaining, using, and/or copying this software and/or its
67n/a# associated documentation, you agree that you have read, understood,
68n/a# and will comply with the following terms and conditions:
69n/a#
70n/a# Permission to use, copy, modify, and distribute this software and
71n/a# its associated documentation for any purpose and without fee is
72n/a# hereby granted, provided that the above copyright notice appears in
73n/a# all copies, and that both that copyright notice and this permission
74n/a# notice appear in supporting documentation, and that the name of
75n/a# Secret Labs AB or the author not be used in advertising or publicity
76n/a# pertaining to distribution of the software without specific, written
77n/a# prior permission.
78n/a#
79n/a# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
80n/a# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
81n/a# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
82n/a# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
83n/a# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
84n/a# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
85n/a# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
86n/a# OF THIS SOFTWARE.
87n/a# --------------------------------------------------------------------
88n/a
89n/a"""
90n/aAn XML-RPC client interface for Python.
91n/a
92n/aThe marshalling and response parser code can also be used to
93n/aimplement XML-RPC servers.
94n/a
95n/aExported exceptions:
96n/a
97n/a Error Base class for client errors
98n/a ProtocolError Indicates an HTTP protocol error
99n/a ResponseError Indicates a broken response package
100n/a Fault Indicates an XML-RPC fault package
101n/a
102n/aExported classes:
103n/a
104n/a ServerProxy Represents a logical connection to an XML-RPC server
105n/a
106n/a MultiCall Executor of boxcared xmlrpc requests
107n/a DateTime dateTime wrapper for an ISO 8601 string or time tuple or
108n/a localtime integer value to generate a "dateTime.iso8601"
109n/a XML-RPC value
110n/a Binary binary data wrapper
111n/a
112n/a Marshaller Generate an XML-RPC params chunk from a Python data structure
113n/a Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
114n/a Transport Handles an HTTP transaction to an XML-RPC server
115n/a SafeTransport Handles an HTTPS transaction to an XML-RPC server
116n/a
117n/aExported constants:
118n/a
119n/a (none)
120n/a
121n/aExported functions:
122n/a
123n/a getparser Create instance of the fastest available parser & attach
124n/a to an unmarshalling object
125n/a dumps Convert an argument tuple or a Fault instance to an XML-RPC
126n/a request (or response, if the methodresponse option is used).
127n/a loads Convert an XML-RPC packet to unmarshalled data plus a method
128n/a name (None if not present).
129n/a"""
130n/a
131n/aimport base64
132n/aimport sys
133n/aimport time
134n/afrom datetime import datetime
135n/afrom decimal import Decimal
136n/aimport http.client
137n/aimport urllib.parse
138n/afrom xml.parsers import expat
139n/aimport errno
140n/afrom io import BytesIO
141n/atry:
142n/a import gzip
143n/aexcept ImportError:
144n/a gzip = None #python can be built without zlib/gzip support
145n/a
146n/a# --------------------------------------------------------------------
147n/a# Internal stuff
148n/a
149n/adef escape(s):
150n/a s = s.replace("&", "&")
151n/a s = s.replace("<", "&lt;")
152n/a return s.replace(">", "&gt;",)
153n/a
154n/a# used in User-Agent header sent
155n/a__version__ = '%d.%d' % sys.version_info[:2]
156n/a
157n/a# xmlrpc integer limits
158n/aMAXINT = 2**31-1
159n/aMININT = -2**31
160n/a
161n/a# --------------------------------------------------------------------
162n/a# Error constants (from Dan Libby's specification at
163n/a# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
164n/a
165n/a# Ranges of errors
166n/aPARSE_ERROR = -32700
167n/aSERVER_ERROR = -32600
168n/aAPPLICATION_ERROR = -32500
169n/aSYSTEM_ERROR = -32400
170n/aTRANSPORT_ERROR = -32300
171n/a
172n/a# Specific errors
173n/aNOT_WELLFORMED_ERROR = -32700
174n/aUNSUPPORTED_ENCODING = -32701
175n/aINVALID_ENCODING_CHAR = -32702
176n/aINVALID_XMLRPC = -32600
177n/aMETHOD_NOT_FOUND = -32601
178n/aINVALID_METHOD_PARAMS = -32602
179n/aINTERNAL_ERROR = -32603
180n/a
181n/a# --------------------------------------------------------------------
182n/a# Exceptions
183n/a
184n/a##
185n/a# Base class for all kinds of client-side errors.
186n/a
187n/aclass Error(Exception):
188n/a """Base class for client errors."""
189n/a def __str__(self):
190n/a return repr(self)
191n/a
192n/a##
193n/a# Indicates an HTTP-level protocol error. This is raised by the HTTP
194n/a# transport layer, if the server returns an error code other than 200
195n/a# (OK).
196n/a#
197n/a# @param url The target URL.
198n/a# @param errcode The HTTP error code.
199n/a# @param errmsg The HTTP error message.
200n/a# @param headers The HTTP header dictionary.
201n/a
202n/aclass ProtocolError(Error):
203n/a """Indicates an HTTP protocol error."""
204n/a def __init__(self, url, errcode, errmsg, headers):
205n/a Error.__init__(self)
206n/a self.url = url
207n/a self.errcode = errcode
208n/a self.errmsg = errmsg
209n/a self.headers = headers
210n/a def __repr__(self):
211n/a return (
212n/a "<%s for %s: %s %s>" %
213n/a (self.__class__.__name__, self.url, self.errcode, self.errmsg)
214n/a )
215n/a
216n/a##
217n/a# Indicates a broken XML-RPC response package. This exception is
218n/a# raised by the unmarshalling layer, if the XML-RPC response is
219n/a# malformed.
220n/a
221n/aclass ResponseError(Error):
222n/a """Indicates a broken response package."""
223n/a pass
224n/a
225n/a##
226n/a# Indicates an XML-RPC fault response package. This exception is
227n/a# raised by the unmarshalling layer, if the XML-RPC response contains
228n/a# a fault string. This exception can also be used as a class, to
229n/a# generate a fault XML-RPC message.
230n/a#
231n/a# @param faultCode The XML-RPC fault code.
232n/a# @param faultString The XML-RPC fault string.
233n/a
234n/aclass Fault(Error):
235n/a """Indicates an XML-RPC fault package."""
236n/a def __init__(self, faultCode, faultString, **extra):
237n/a Error.__init__(self)
238n/a self.faultCode = faultCode
239n/a self.faultString = faultString
240n/a def __repr__(self):
241n/a return "<%s %s: %r>" % (self.__class__.__name__,
242n/a self.faultCode, self.faultString)
243n/a
244n/a# --------------------------------------------------------------------
245n/a# Special values
246n/a
247n/a##
248n/a# Backwards compatibility
249n/a
250n/aboolean = Boolean = bool
251n/a
252n/a##
253n/a# Wrapper for XML-RPC DateTime values. This converts a time value to
254n/a# the format used by XML-RPC.
255n/a# <p>
256n/a# The value can be given as a datetime object, as a string in the
257n/a# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
258n/a# time.localtime()), or an integer value (as returned by time.time()).
259n/a# The wrapper uses time.localtime() to convert an integer to a time
260n/a# tuple.
261n/a#
262n/a# @param value The time, given as a datetime object, an ISO 8601 string,
263n/a# a time tuple, or an integer time value.
264n/a
265n/a
266n/a# Issue #13305: different format codes across platforms
267n/a_day0 = datetime(1, 1, 1)
268n/aif _day0.strftime('%Y') == '0001': # Mac OS X
269n/a def _iso8601_format(value):
270n/a return value.strftime("%Y%m%dT%H:%M:%S")
271n/aelif _day0.strftime('%4Y') == '0001': # Linux
272n/a def _iso8601_format(value):
273n/a return value.strftime("%4Y%m%dT%H:%M:%S")
274n/aelse:
275n/a def _iso8601_format(value):
276n/a return value.strftime("%Y%m%dT%H:%M:%S").zfill(17)
277n/adel _day0
278n/a
279n/a
280n/adef _strftime(value):
281n/a if isinstance(value, datetime):
282n/a return _iso8601_format(value)
283n/a
284n/a if not isinstance(value, (tuple, time.struct_time)):
285n/a if value == 0:
286n/a value = time.time()
287n/a value = time.localtime(value)
288n/a
289n/a return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
290n/a
291n/aclass DateTime:
292n/a """DateTime wrapper for an ISO 8601 string or time tuple or
293n/a localtime integer value to generate 'dateTime.iso8601' XML-RPC
294n/a value.
295n/a """
296n/a
297n/a def __init__(self, value=0):
298n/a if isinstance(value, str):
299n/a self.value = value
300n/a else:
301n/a self.value = _strftime(value)
302n/a
303n/a def make_comparable(self, other):
304n/a if isinstance(other, DateTime):
305n/a s = self.value
306n/a o = other.value
307n/a elif isinstance(other, datetime):
308n/a s = self.value
309n/a o = _iso8601_format(other)
310n/a elif isinstance(other, str):
311n/a s = self.value
312n/a o = other
313n/a elif hasattr(other, "timetuple"):
314n/a s = self.timetuple()
315n/a o = other.timetuple()
316n/a else:
317n/a otype = (hasattr(other, "__class__")
318n/a and other.__class__.__name__
319n/a or type(other))
320n/a raise TypeError("Can't compare %s and %s" %
321n/a (self.__class__.__name__, otype))
322n/a return s, o
323n/a
324n/a def __lt__(self, other):
325n/a s, o = self.make_comparable(other)
326n/a return s < o
327n/a
328n/a def __le__(self, other):
329n/a s, o = self.make_comparable(other)
330n/a return s <= o
331n/a
332n/a def __gt__(self, other):
333n/a s, o = self.make_comparable(other)
334n/a return s > o
335n/a
336n/a def __ge__(self, other):
337n/a s, o = self.make_comparable(other)
338n/a return s >= o
339n/a
340n/a def __eq__(self, other):
341n/a s, o = self.make_comparable(other)
342n/a return s == o
343n/a
344n/a def timetuple(self):
345n/a return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
346n/a
347n/a ##
348n/a # Get date/time value.
349n/a #
350n/a # @return Date/time value, as an ISO 8601 string.
351n/a
352n/a def __str__(self):
353n/a return self.value
354n/a
355n/a def __repr__(self):
356n/a return "<%s %r at %#x>" % (self.__class__.__name__, self.value, id(self))
357n/a
358n/a def decode(self, data):
359n/a self.value = str(data).strip()
360n/a
361n/a def encode(self, out):
362n/a out.write("<value><dateTime.iso8601>")
363n/a out.write(self.value)
364n/a out.write("</dateTime.iso8601></value>\n")
365n/a
366n/adef _datetime(data):
367n/a # decode xml element contents into a DateTime structure.
368n/a value = DateTime()
369n/a value.decode(data)
370n/a return value
371n/a
372n/adef _datetime_type(data):
373n/a return datetime.strptime(data, "%Y%m%dT%H:%M:%S")
374n/a
375n/a##
376n/a# Wrapper for binary data. This can be used to transport any kind
377n/a# of binary data over XML-RPC, using BASE64 encoding.
378n/a#
379n/a# @param data An 8-bit string containing arbitrary data.
380n/a
381n/aclass Binary:
382n/a """Wrapper for binary data."""
383n/a
384n/a def __init__(self, data=None):
385n/a if data is None:
386n/a data = b""
387n/a else:
388n/a if not isinstance(data, (bytes, bytearray)):
389n/a raise TypeError("expected bytes or bytearray, not %s" %
390n/a data.__class__.__name__)
391n/a data = bytes(data) # Make a copy of the bytes!
392n/a self.data = data
393n/a
394n/a ##
395n/a # Get buffer contents.
396n/a #
397n/a # @return Buffer contents, as an 8-bit string.
398n/a
399n/a def __str__(self):
400n/a return str(self.data, "latin-1") # XXX encoding?!
401n/a
402n/a def __eq__(self, other):
403n/a if isinstance(other, Binary):
404n/a other = other.data
405n/a return self.data == other
406n/a
407n/a def decode(self, data):
408n/a self.data = base64.decodebytes(data)
409n/a
410n/a def encode(self, out):
411n/a out.write("<value><base64>\n")
412n/a encoded = base64.encodebytes(self.data)
413n/a out.write(encoded.decode('ascii'))
414n/a out.write("</base64></value>\n")
415n/a
416n/adef _binary(data):
417n/a # decode xml element contents into a Binary structure
418n/a value = Binary()
419n/a value.decode(data)
420n/a return value
421n/a
422n/aWRAPPERS = (DateTime, Binary)
423n/a
424n/a# --------------------------------------------------------------------
425n/a# XML parsers
426n/a
427n/aclass ExpatParser:
428n/a # fast expat parser for Python 2.0 and later.
429n/a def __init__(self, target):
430n/a self._parser = parser = expat.ParserCreate(None, None)
431n/a self._target = target
432n/a parser.StartElementHandler = target.start
433n/a parser.EndElementHandler = target.end
434n/a parser.CharacterDataHandler = target.data
435n/a encoding = None
436n/a target.xml(encoding, None)
437n/a
438n/a def feed(self, data):
439n/a self._parser.Parse(data, 0)
440n/a
441n/a def close(self):
442n/a try:
443n/a parser = self._parser
444n/a except AttributeError:
445n/a pass
446n/a else:
447n/a del self._target, self._parser # get rid of circular references
448n/a parser.Parse(b"", True) # end of data
449n/a
450n/a# --------------------------------------------------------------------
451n/a# XML-RPC marshalling and unmarshalling code
452n/a
453n/a##
454n/a# XML-RPC marshaller.
455n/a#
456n/a# @param encoding Default encoding for 8-bit strings. The default
457n/a# value is None (interpreted as UTF-8).
458n/a# @see dumps
459n/a
460n/aclass Marshaller:
461n/a """Generate an XML-RPC params chunk from a Python data structure.
462n/a
463n/a Create a Marshaller instance for each set of parameters, and use
464n/a the "dumps" method to convert your data (represented as a tuple)
465n/a to an XML-RPC params chunk. To write a fault response, pass a
466n/a Fault instance instead. You may prefer to use the "dumps" module
467n/a function for this purpose.
468n/a """
469n/a
470n/a # by the way, if you don't understand what's going on in here,
471n/a # that's perfectly ok.
472n/a
473n/a def __init__(self, encoding=None, allow_none=False):
474n/a self.memo = {}
475n/a self.data = None
476n/a self.encoding = encoding
477n/a self.allow_none = allow_none
478n/a
479n/a dispatch = {}
480n/a
481n/a def dumps(self, values):
482n/a out = []
483n/a write = out.append
484n/a dump = self.__dump
485n/a if isinstance(values, Fault):
486n/a # fault instance
487n/a write("<fault>\n")
488n/a dump({'faultCode': values.faultCode,
489n/a 'faultString': values.faultString},
490n/a write)
491n/a write("</fault>\n")
492n/a else:
493n/a # parameter block
494n/a # FIXME: the xml-rpc specification allows us to leave out
495n/a # the entire <params> block if there are no parameters.
496n/a # however, changing this may break older code (including
497n/a # old versions of xmlrpclib.py), so this is better left as
498n/a # is for now. See @XMLRPC3 for more information. /F
499n/a write("<params>\n")
500n/a for v in values:
501n/a write("<param>\n")
502n/a dump(v, write)
503n/a write("</param>\n")
504n/a write("</params>\n")
505n/a result = "".join(out)
506n/a return result
507n/a
508n/a def __dump(self, value, write):
509n/a try:
510n/a f = self.dispatch[type(value)]
511n/a except KeyError:
512n/a # check if this object can be marshalled as a structure
513n/a if not hasattr(value, '__dict__'):
514n/a raise TypeError("cannot marshal %s objects" % type(value))
515n/a # check if this class is a sub-class of a basic type,
516n/a # because we don't know how to marshal these types
517n/a # (e.g. a string sub-class)
518n/a for type_ in type(value).__mro__:
519n/a if type_ in self.dispatch.keys():
520n/a raise TypeError("cannot marshal %s objects" % type(value))
521n/a # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
522n/a # for the p3yk merge, this should probably be fixed more neatly.
523n/a f = self.dispatch["_arbitrary_instance"]
524n/a f(self, value, write)
525n/a
526n/a def dump_nil (self, value, write):
527n/a if not self.allow_none:
528n/a raise TypeError("cannot marshal None unless allow_none is enabled")
529n/a write("<value><nil/></value>")
530n/a dispatch[type(None)] = dump_nil
531n/a
532n/a def dump_bool(self, value, write):
533n/a write("<value><boolean>")
534n/a write(value and "1" or "0")
535n/a write("</boolean></value>\n")
536n/a dispatch[bool] = dump_bool
537n/a
538n/a def dump_long(self, value, write):
539n/a if value > MAXINT or value < MININT:
540n/a raise OverflowError("int exceeds XML-RPC limits")
541n/a write("<value><int>")
542n/a write(str(int(value)))
543n/a write("</int></value>\n")
544n/a dispatch[int] = dump_long
545n/a
546n/a # backward compatible
547n/a dump_int = dump_long
548n/a
549n/a def dump_double(self, value, write):
550n/a write("<value><double>")
551n/a write(repr(value))
552n/a write("</double></value>\n")
553n/a dispatch[float] = dump_double
554n/a
555n/a def dump_unicode(self, value, write, escape=escape):
556n/a write("<value><string>")
557n/a write(escape(value))
558n/a write("</string></value>\n")
559n/a dispatch[str] = dump_unicode
560n/a
561n/a def dump_bytes(self, value, write):
562n/a write("<value><base64>\n")
563n/a encoded = base64.encodebytes(value)
564n/a write(encoded.decode('ascii'))
565n/a write("</base64></value>\n")
566n/a dispatch[bytes] = dump_bytes
567n/a dispatch[bytearray] = dump_bytes
568n/a
569n/a def dump_array(self, value, write):
570n/a i = id(value)
571n/a if i in self.memo:
572n/a raise TypeError("cannot marshal recursive sequences")
573n/a self.memo[i] = None
574n/a dump = self.__dump
575n/a write("<value><array><data>\n")
576n/a for v in value:
577n/a dump(v, write)
578n/a write("</data></array></value>\n")
579n/a del self.memo[i]
580n/a dispatch[tuple] = dump_array
581n/a dispatch[list] = dump_array
582n/a
583n/a def dump_struct(self, value, write, escape=escape):
584n/a i = id(value)
585n/a if i in self.memo:
586n/a raise TypeError("cannot marshal recursive dictionaries")
587n/a self.memo[i] = None
588n/a dump = self.__dump
589n/a write("<value><struct>\n")
590n/a for k, v in value.items():
591n/a write("<member>\n")
592n/a if not isinstance(k, str):
593n/a raise TypeError("dictionary key must be string")
594n/a write("<name>%s</name>\n" % escape(k))
595n/a dump(v, write)
596n/a write("</member>\n")
597n/a write("</struct></value>\n")
598n/a del self.memo[i]
599n/a dispatch[dict] = dump_struct
600n/a
601n/a def dump_datetime(self, value, write):
602n/a write("<value><dateTime.iso8601>")
603n/a write(_strftime(value))
604n/a write("</dateTime.iso8601></value>\n")
605n/a dispatch[datetime] = dump_datetime
606n/a
607n/a def dump_instance(self, value, write):
608n/a # check for special wrappers
609n/a if value.__class__ in WRAPPERS:
610n/a self.write = write
611n/a value.encode(self)
612n/a del self.write
613n/a else:
614n/a # store instance attributes as a struct (really?)
615n/a self.dump_struct(value.__dict__, write)
616n/a dispatch[DateTime] = dump_instance
617n/a dispatch[Binary] = dump_instance
618n/a # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
619n/a # for the p3yk merge, this should probably be fixed more neatly.
620n/a dispatch["_arbitrary_instance"] = dump_instance
621n/a
622n/a##
623n/a# XML-RPC unmarshaller.
624n/a#
625n/a# @see loads
626n/a
627n/aclass Unmarshaller:
628n/a """Unmarshal an XML-RPC response, based on incoming XML event
629n/a messages (start, data, end). Call close() to get the resulting
630n/a data structure.
631n/a
632n/a Note that this reader is fairly tolerant, and gladly accepts bogus
633n/a XML-RPC data without complaining (but not bogus XML).
634n/a """
635n/a
636n/a # and again, if you don't understand what's going on in here,
637n/a # that's perfectly ok.
638n/a
639n/a def __init__(self, use_datetime=False, use_builtin_types=False):
640n/a self._type = None
641n/a self._stack = []
642n/a self._marks = []
643n/a self._data = []
644n/a self._value = False
645n/a self._methodname = None
646n/a self._encoding = "utf-8"
647n/a self.append = self._stack.append
648n/a self._use_datetime = use_builtin_types or use_datetime
649n/a self._use_bytes = use_builtin_types
650n/a
651n/a def close(self):
652n/a # return response tuple and target method
653n/a if self._type is None or self._marks:
654n/a raise ResponseError()
655n/a if self._type == "fault":
656n/a raise Fault(**self._stack[0])
657n/a return tuple(self._stack)
658n/a
659n/a def getmethodname(self):
660n/a return self._methodname
661n/a
662n/a #
663n/a # event handlers
664n/a
665n/a def xml(self, encoding, standalone):
666n/a self._encoding = encoding
667n/a # FIXME: assert standalone == 1 ???
668n/a
669n/a def start(self, tag, attrs):
670n/a # prepare to handle this element
671n/a if ':' in tag:
672n/a tag = tag.split(':')[-1]
673n/a if tag == "array" or tag == "struct":
674n/a self._marks.append(len(self._stack))
675n/a self._data = []
676n/a if self._value and tag not in self.dispatch:
677n/a raise ResponseError("unknown tag %r" % tag)
678n/a self._value = (tag == "value")
679n/a
680n/a def data(self, text):
681n/a self._data.append(text)
682n/a
683n/a def end(self, tag):
684n/a # call the appropriate end tag handler
685n/a try:
686n/a f = self.dispatch[tag]
687n/a except KeyError:
688n/a if ':' not in tag:
689n/a return # unknown tag ?
690n/a try:
691n/a f = self.dispatch[tag.split(':')[-1]]
692n/a except KeyError:
693n/a return # unknown tag ?
694n/a return f(self, "".join(self._data))
695n/a
696n/a #
697n/a # accelerator support
698n/a
699n/a def end_dispatch(self, tag, data):
700n/a # dispatch data
701n/a try:
702n/a f = self.dispatch[tag]
703n/a except KeyError:
704n/a if ':' not in tag:
705n/a return # unknown tag ?
706n/a try:
707n/a f = self.dispatch[tag.split(':')[-1]]
708n/a except KeyError:
709n/a return # unknown tag ?
710n/a return f(self, data)
711n/a
712n/a #
713n/a # element decoders
714n/a
715n/a dispatch = {}
716n/a
717n/a def end_nil (self, data):
718n/a self.append(None)
719n/a self._value = 0
720n/a dispatch["nil"] = end_nil
721n/a
722n/a def end_boolean(self, data):
723n/a if data == "0":
724n/a self.append(False)
725n/a elif data == "1":
726n/a self.append(True)
727n/a else:
728n/a raise TypeError("bad boolean value")
729n/a self._value = 0
730n/a dispatch["boolean"] = end_boolean
731n/a
732n/a def end_int(self, data):
733n/a self.append(int(data))
734n/a self._value = 0
735n/a dispatch["i1"] = end_int
736n/a dispatch["i2"] = end_int
737n/a dispatch["i4"] = end_int
738n/a dispatch["i8"] = end_int
739n/a dispatch["int"] = end_int
740n/a dispatch["biginteger"] = end_int
741n/a
742n/a def end_double(self, data):
743n/a self.append(float(data))
744n/a self._value = 0
745n/a dispatch["double"] = end_double
746n/a dispatch["float"] = end_double
747n/a
748n/a def end_bigdecimal(self, data):
749n/a self.append(Decimal(data))
750n/a self._value = 0
751n/a dispatch["bigdecimal"] = end_bigdecimal
752n/a
753n/a def end_string(self, data):
754n/a if self._encoding:
755n/a data = data.decode(self._encoding)
756n/a self.append(data)
757n/a self._value = 0
758n/a dispatch["string"] = end_string
759n/a dispatch["name"] = end_string # struct keys are always strings
760n/a
761n/a def end_array(self, data):
762n/a mark = self._marks.pop()
763n/a # map arrays to Python lists
764n/a self._stack[mark:] = [self._stack[mark:]]
765n/a self._value = 0
766n/a dispatch["array"] = end_array
767n/a
768n/a def end_struct(self, data):
769n/a mark = self._marks.pop()
770n/a # map structs to Python dictionaries
771n/a dict = {}
772n/a items = self._stack[mark:]
773n/a for i in range(0, len(items), 2):
774n/a dict[items[i]] = items[i+1]
775n/a self._stack[mark:] = [dict]
776n/a self._value = 0
777n/a dispatch["struct"] = end_struct
778n/a
779n/a def end_base64(self, data):
780n/a value = Binary()
781n/a value.decode(data.encode("ascii"))
782n/a if self._use_bytes:
783n/a value = value.data
784n/a self.append(value)
785n/a self._value = 0
786n/a dispatch["base64"] = end_base64
787n/a
788n/a def end_dateTime(self, data):
789n/a value = DateTime()
790n/a value.decode(data)
791n/a if self._use_datetime:
792n/a value = _datetime_type(data)
793n/a self.append(value)
794n/a dispatch["dateTime.iso8601"] = end_dateTime
795n/a
796n/a def end_value(self, data):
797n/a # if we stumble upon a value element with no internal
798n/a # elements, treat it as a string element
799n/a if self._value:
800n/a self.end_string(data)
801n/a dispatch["value"] = end_value
802n/a
803n/a def end_params(self, data):
804n/a self._type = "params"
805n/a dispatch["params"] = end_params
806n/a
807n/a def end_fault(self, data):
808n/a self._type = "fault"
809n/a dispatch["fault"] = end_fault
810n/a
811n/a def end_methodName(self, data):
812n/a if self._encoding:
813n/a data = data.decode(self._encoding)
814n/a self._methodname = data
815n/a self._type = "methodName" # no params
816n/a dispatch["methodName"] = end_methodName
817n/a
818n/a## Multicall support
819n/a#
820n/a
821n/aclass _MultiCallMethod:
822n/a # some lesser magic to store calls made to a MultiCall object
823n/a # for batch execution
824n/a def __init__(self, call_list, name):
825n/a self.__call_list = call_list
826n/a self.__name = name
827n/a def __getattr__(self, name):
828n/a return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
829n/a def __call__(self, *args):
830n/a self.__call_list.append((self.__name, args))
831n/a
832n/aclass MultiCallIterator:
833n/a """Iterates over the results of a multicall. Exceptions are
834n/a raised in response to xmlrpc faults."""
835n/a
836n/a def __init__(self, results):
837n/a self.results = results
838n/a
839n/a def __getitem__(self, i):
840n/a item = self.results[i]
841n/a if type(item) == type({}):
842n/a raise Fault(item['faultCode'], item['faultString'])
843n/a elif type(item) == type([]):
844n/a return item[0]
845n/a else:
846n/a raise ValueError("unexpected type in multicall result")
847n/a
848n/aclass MultiCall:
849n/a """server -> an object used to boxcar method calls
850n/a
851n/a server should be a ServerProxy object.
852n/a
853n/a Methods can be added to the MultiCall using normal
854n/a method call syntax e.g.:
855n/a
856n/a multicall = MultiCall(server_proxy)
857n/a multicall.add(2,3)
858n/a multicall.get_address("Guido")
859n/a
860n/a To execute the multicall, call the MultiCall object e.g.:
861n/a
862n/a add_result, address = multicall()
863n/a """
864n/a
865n/a def __init__(self, server):
866n/a self.__server = server
867n/a self.__call_list = []
868n/a
869n/a def __repr__(self):
870n/a return "<%s at %#x>" % (self.__class__.__name__, id(self))
871n/a
872n/a __str__ = __repr__
873n/a
874n/a def __getattr__(self, name):
875n/a return _MultiCallMethod(self.__call_list, name)
876n/a
877n/a def __call__(self):
878n/a marshalled_list = []
879n/a for name, args in self.__call_list:
880n/a marshalled_list.append({'methodName' : name, 'params' : args})
881n/a
882n/a return MultiCallIterator(self.__server.system.multicall(marshalled_list))
883n/a
884n/a# --------------------------------------------------------------------
885n/a# convenience functions
886n/a
887n/aFastMarshaller = FastParser = FastUnmarshaller = None
888n/a
889n/a##
890n/a# Create a parser object, and connect it to an unmarshalling instance.
891n/a# This function picks the fastest available XML parser.
892n/a#
893n/a# return A (parser, unmarshaller) tuple.
894n/a
895n/adef getparser(use_datetime=False, use_builtin_types=False):
896n/a """getparser() -> parser, unmarshaller
897n/a
898n/a Create an instance of the fastest available parser, and attach it
899n/a to an unmarshalling object. Return both objects.
900n/a """
901n/a if FastParser and FastUnmarshaller:
902n/a if use_builtin_types:
903n/a mkdatetime = _datetime_type
904n/a mkbytes = base64.decodebytes
905n/a elif use_datetime:
906n/a mkdatetime = _datetime_type
907n/a mkbytes = _binary
908n/a else:
909n/a mkdatetime = _datetime
910n/a mkbytes = _binary
911n/a target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault)
912n/a parser = FastParser(target)
913n/a else:
914n/a target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
915n/a if FastParser:
916n/a parser = FastParser(target)
917n/a else:
918n/a parser = ExpatParser(target)
919n/a return parser, target
920n/a
921n/a##
922n/a# Convert a Python tuple or a Fault instance to an XML-RPC packet.
923n/a#
924n/a# @def dumps(params, **options)
925n/a# @param params A tuple or Fault instance.
926n/a# @keyparam methodname If given, create a methodCall request for
927n/a# this method name.
928n/a# @keyparam methodresponse If given, create a methodResponse packet.
929n/a# If used with a tuple, the tuple must be a singleton (that is,
930n/a# it must contain exactly one element).
931n/a# @keyparam encoding The packet encoding.
932n/a# @return A string containing marshalled data.
933n/a
934n/adef dumps(params, methodname=None, methodresponse=None, encoding=None,
935n/a allow_none=False):
936n/a """data [,options] -> marshalled data
937n/a
938n/a Convert an argument tuple or a Fault instance to an XML-RPC
939n/a request (or response, if the methodresponse option is used).
940n/a
941n/a In addition to the data object, the following options can be given
942n/a as keyword arguments:
943n/a
944n/a methodname: the method name for a methodCall packet
945n/a
946n/a methodresponse: true to create a methodResponse packet.
947n/a If this option is used with a tuple, the tuple must be
948n/a a singleton (i.e. it can contain only one element).
949n/a
950n/a encoding: the packet encoding (default is UTF-8)
951n/a
952n/a All byte strings in the data structure are assumed to use the
953n/a packet encoding. Unicode strings are automatically converted,
954n/a where necessary.
955n/a """
956n/a
957n/a assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
958n/a if isinstance(params, Fault):
959n/a methodresponse = 1
960n/a elif methodresponse and isinstance(params, tuple):
961n/a assert len(params) == 1, "response tuple must be a singleton"
962n/a
963n/a if not encoding:
964n/a encoding = "utf-8"
965n/a
966n/a if FastMarshaller:
967n/a m = FastMarshaller(encoding)
968n/a else:
969n/a m = Marshaller(encoding, allow_none)
970n/a
971n/a data = m.dumps(params)
972n/a
973n/a if encoding != "utf-8":
974n/a xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
975n/a else:
976n/a xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
977n/a
978n/a # standard XML-RPC wrappings
979n/a if methodname:
980n/a # a method call
981n/a data = (
982n/a xmlheader,
983n/a "<methodCall>\n"
984n/a "<methodName>", methodname, "</methodName>\n",
985n/a data,
986n/a "</methodCall>\n"
987n/a )
988n/a elif methodresponse:
989n/a # a method response, or a fault structure
990n/a data = (
991n/a xmlheader,
992n/a "<methodResponse>\n",
993n/a data,
994n/a "</methodResponse>\n"
995n/a )
996n/a else:
997n/a return data # return as is
998n/a return "".join(data)
999n/a
1000n/a##
1001n/a# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
1002n/a# represents a fault condition, this function raises a Fault exception.
1003n/a#
1004n/a# @param data An XML-RPC packet, given as an 8-bit string.
1005n/a# @return A tuple containing the unpacked data, and the method name
1006n/a# (None if not present).
1007n/a# @see Fault
1008n/a
1009n/adef loads(data, use_datetime=False, use_builtin_types=False):
1010n/a """data -> unmarshalled data, method name
1011n/a
1012n/a Convert an XML-RPC packet to unmarshalled data plus a method
1013n/a name (None if not present).
1014n/a
1015n/a If the XML-RPC packet represents a fault condition, this function
1016n/a raises a Fault exception.
1017n/a """
1018n/a p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
1019n/a p.feed(data)
1020n/a p.close()
1021n/a return u.close(), u.getmethodname()
1022n/a
1023n/a##
1024n/a# Encode a string using the gzip content encoding such as specified by the
1025n/a# Content-Encoding: gzip
1026n/a# in the HTTP header, as described in RFC 1952
1027n/a#
1028n/a# @param data the unencoded data
1029n/a# @return the encoded data
1030n/a
1031n/adef gzip_encode(data):
1032n/a """data -> gzip encoded data
1033n/a
1034n/a Encode data using the gzip content encoding as described in RFC 1952
1035n/a """
1036n/a if not gzip:
1037n/a raise NotImplementedError
1038n/a f = BytesIO()
1039n/a with gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) as gzf:
1040n/a gzf.write(data)
1041n/a return f.getvalue()
1042n/a
1043n/a##
1044n/a# Decode a string using the gzip content encoding such as specified by the
1045n/a# Content-Encoding: gzip
1046n/a# in the HTTP header, as described in RFC 1952
1047n/a#
1048n/a# @param data The encoded data
1049n/a# @keyparam max_decode Maximum bytes to decode (20MB default), use negative
1050n/a# values for unlimited decoding
1051n/a# @return the unencoded data
1052n/a# @raises ValueError if data is not correctly coded.
1053n/a# @raises ValueError if max gzipped payload length exceeded
1054n/a
1055n/adef gzip_decode(data, max_decode=20971520):
1056n/a """gzip encoded data -> unencoded data
1057n/a
1058n/a Decode data using the gzip content encoding as described in RFC 1952
1059n/a """
1060n/a if not gzip:
1061n/a raise NotImplementedError
1062n/a with gzip.GzipFile(mode="rb", fileobj=BytesIO(data)) as gzf:
1063n/a try:
1064n/a if max_decode < 0: # no limit
1065n/a decoded = gzf.read()
1066n/a else:
1067n/a decoded = gzf.read(max_decode + 1)
1068n/a except OSError:
1069n/a raise ValueError("invalid data")
1070n/a if max_decode >= 0 and len(decoded) > max_decode:
1071n/a raise ValueError("max gzipped payload length exceeded")
1072n/a return decoded
1073n/a
1074n/a##
1075n/a# Return a decoded file-like object for the gzip encoding
1076n/a# as described in RFC 1952.
1077n/a#
1078n/a# @param response A stream supporting a read() method
1079n/a# @return a file-like object that the decoded data can be read() from
1080n/a
1081n/aclass GzipDecodedResponse(gzip.GzipFile if gzip else object):
1082n/a """a file-like object to decode a response encoded with the gzip
1083n/a method, as described in RFC 1952.
1084n/a """
1085n/a def __init__(self, response):
1086n/a #response doesn't support tell() and read(), required by
1087n/a #GzipFile
1088n/a if not gzip:
1089n/a raise NotImplementedError
1090n/a self.io = BytesIO(response.read())
1091n/a gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
1092n/a
1093n/a def close(self):
1094n/a try:
1095n/a gzip.GzipFile.close(self)
1096n/a finally:
1097n/a self.io.close()
1098n/a
1099n/a
1100n/a# --------------------------------------------------------------------
1101n/a# request dispatcher
1102n/a
1103n/aclass _Method:
1104n/a # some magic to bind an XML-RPC method to an RPC server.
1105n/a # supports "nested" methods (e.g. examples.getStateName)
1106n/a def __init__(self, send, name):
1107n/a self.__send = send
1108n/a self.__name = name
1109n/a def __getattr__(self, name):
1110n/a return _Method(self.__send, "%s.%s" % (self.__name, name))
1111n/a def __call__(self, *args):
1112n/a return self.__send(self.__name, args)
1113n/a
1114n/a##
1115n/a# Standard transport class for XML-RPC over HTTP.
1116n/a# <p>
1117n/a# You can create custom transports by subclassing this method, and
1118n/a# overriding selected methods.
1119n/a
1120n/aclass Transport:
1121n/a """Handles an HTTP transaction to an XML-RPC server."""
1122n/a
1123n/a # client identifier (may be overridden)
1124n/a user_agent = "Python-xmlrpc/%s" % __version__
1125n/a
1126n/a #if true, we'll request gzip encoding
1127n/a accept_gzip_encoding = True
1128n/a
1129n/a # if positive, encode request using gzip if it exceeds this threshold
1130n/a # note that many server will get confused, so only use it if you know
1131n/a # that they can decode such a request
1132n/a encode_threshold = None #None = don't encode
1133n/a
1134n/a def __init__(self, use_datetime=False, use_builtin_types=False):
1135n/a self._use_datetime = use_datetime
1136n/a self._use_builtin_types = use_builtin_types
1137n/a self._connection = (None, None)
1138n/a self._extra_headers = []
1139n/a
1140n/a ##
1141n/a # Send a complete request, and parse the response.
1142n/a # Retry request if a cached connection has disconnected.
1143n/a #
1144n/a # @param host Target host.
1145n/a # @param handler Target PRC handler.
1146n/a # @param request_body XML-RPC request body.
1147n/a # @param verbose Debugging flag.
1148n/a # @return Parsed response.
1149n/a
1150n/a def request(self, host, handler, request_body, verbose=False):
1151n/a #retry request once if cached connection has gone cold
1152n/a for i in (0, 1):
1153n/a try:
1154n/a return self.single_request(host, handler, request_body, verbose)
1155n/a except http.client.RemoteDisconnected:
1156n/a if i:
1157n/a raise
1158n/a except OSError as e:
1159n/a if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED,
1160n/a errno.EPIPE):
1161n/a raise
1162n/a
1163n/a def single_request(self, host, handler, request_body, verbose=False):
1164n/a # issue XML-RPC request
1165n/a try:
1166n/a http_conn = self.send_request(host, handler, request_body, verbose)
1167n/a resp = http_conn.getresponse()
1168n/a if resp.status == 200:
1169n/a self.verbose = verbose
1170n/a return self.parse_response(resp)
1171n/a
1172n/a except Fault:
1173n/a raise
1174n/a except Exception:
1175n/a #All unexpected errors leave connection in
1176n/a # a strange state, so we clear it.
1177n/a self.close()
1178n/a raise
1179n/a
1180n/a #We got an error response.
1181n/a #Discard any response data and raise exception
1182n/a if resp.getheader("content-length", ""):
1183n/a resp.read()
1184n/a raise ProtocolError(
1185n/a host + handler,
1186n/a resp.status, resp.reason,
1187n/a dict(resp.getheaders())
1188n/a )
1189n/a
1190n/a
1191n/a ##
1192n/a # Create parser.
1193n/a #
1194n/a # @return A 2-tuple containing a parser and an unmarshaller.
1195n/a
1196n/a def getparser(self):
1197n/a # get parser and unmarshaller
1198n/a return getparser(use_datetime=self._use_datetime,
1199n/a use_builtin_types=self._use_builtin_types)
1200n/a
1201n/a ##
1202n/a # Get authorization info from host parameter
1203n/a # Host may be a string, or a (host, x509-dict) tuple; if a string,
1204n/a # it is checked for a "user:pw@host" format, and a "Basic
1205n/a # Authentication" header is added if appropriate.
1206n/a #
1207n/a # @param host Host descriptor (URL or (URL, x509 info) tuple).
1208n/a # @return A 3-tuple containing (actual host, extra headers,
1209n/a # x509 info). The header and x509 fields may be None.
1210n/a
1211n/a def get_host_info(self, host):
1212n/a
1213n/a x509 = {}
1214n/a if isinstance(host, tuple):
1215n/a host, x509 = host
1216n/a
1217n/a auth, host = urllib.parse.splituser(host)
1218n/a
1219n/a if auth:
1220n/a auth = urllib.parse.unquote_to_bytes(auth)
1221n/a auth = base64.encodebytes(auth).decode("utf-8")
1222n/a auth = "".join(auth.split()) # get rid of whitespace
1223n/a extra_headers = [
1224n/a ("Authorization", "Basic " + auth)
1225n/a ]
1226n/a else:
1227n/a extra_headers = []
1228n/a
1229n/a return host, extra_headers, x509
1230n/a
1231n/a ##
1232n/a # Connect to server.
1233n/a #
1234n/a # @param host Target host.
1235n/a # @return An HTTPConnection object
1236n/a
1237n/a def make_connection(self, host):
1238n/a #return an existing connection if possible. This allows
1239n/a #HTTP/1.1 keep-alive.
1240n/a if self._connection and host == self._connection[0]:
1241n/a return self._connection[1]
1242n/a # create a HTTP connection object from a host descriptor
1243n/a chost, self._extra_headers, x509 = self.get_host_info(host)
1244n/a self._connection = host, http.client.HTTPConnection(chost)
1245n/a return self._connection[1]
1246n/a
1247n/a ##
1248n/a # Clear any cached connection object.
1249n/a # Used in the event of socket errors.
1250n/a #
1251n/a def close(self):
1252n/a host, connection = self._connection
1253n/a if connection:
1254n/a self._connection = (None, None)
1255n/a connection.close()
1256n/a
1257n/a ##
1258n/a # Send HTTP request.
1259n/a #
1260n/a # @param host Host descriptor (URL or (URL, x509 info) tuple).
1261n/a # @param handler Targer RPC handler (a path relative to host)
1262n/a # @param request_body The XML-RPC request body
1263n/a # @param debug Enable debugging if debug is true.
1264n/a # @return An HTTPConnection.
1265n/a
1266n/a def send_request(self, host, handler, request_body, debug):
1267n/a connection = self.make_connection(host)
1268n/a headers = self._extra_headers[:]
1269n/a if debug:
1270n/a connection.set_debuglevel(1)
1271n/a if self.accept_gzip_encoding and gzip:
1272n/a connection.putrequest("POST", handler, skip_accept_encoding=True)
1273n/a headers.append(("Accept-Encoding", "gzip"))
1274n/a else:
1275n/a connection.putrequest("POST", handler)
1276n/a headers.append(("Content-Type", "text/xml"))
1277n/a headers.append(("User-Agent", self.user_agent))
1278n/a self.send_headers(connection, headers)
1279n/a self.send_content(connection, request_body)
1280n/a return connection
1281n/a
1282n/a ##
1283n/a # Send request headers.
1284n/a # This function provides a useful hook for subclassing
1285n/a #
1286n/a # @param connection httpConnection.
1287n/a # @param headers list of key,value pairs for HTTP headers
1288n/a
1289n/a def send_headers(self, connection, headers):
1290n/a for key, val in headers:
1291n/a connection.putheader(key, val)
1292n/a
1293n/a ##
1294n/a # Send request body.
1295n/a # This function provides a useful hook for subclassing
1296n/a #
1297n/a # @param connection httpConnection.
1298n/a # @param request_body XML-RPC request body.
1299n/a
1300n/a def send_content(self, connection, request_body):
1301n/a #optionally encode the request
1302n/a if (self.encode_threshold is not None and
1303n/a self.encode_threshold < len(request_body) and
1304n/a gzip):
1305n/a connection.putheader("Content-Encoding", "gzip")
1306n/a request_body = gzip_encode(request_body)
1307n/a
1308n/a connection.putheader("Content-Length", str(len(request_body)))
1309n/a connection.endheaders(request_body)
1310n/a
1311n/a ##
1312n/a # Parse response.
1313n/a #
1314n/a # @param file Stream.
1315n/a # @return Response tuple and target method.
1316n/a
1317n/a def parse_response(self, response):
1318n/a # read response data from httpresponse, and parse it
1319n/a # Check for new http response object, otherwise it is a file object.
1320n/a if hasattr(response, 'getheader'):
1321n/a if response.getheader("Content-Encoding", "") == "gzip":
1322n/a stream = GzipDecodedResponse(response)
1323n/a else:
1324n/a stream = response
1325n/a else:
1326n/a stream = response
1327n/a
1328n/a p, u = self.getparser()
1329n/a
1330n/a while 1:
1331n/a data = stream.read(1024)
1332n/a if not data:
1333n/a break
1334n/a if self.verbose:
1335n/a print("body:", repr(data))
1336n/a p.feed(data)
1337n/a
1338n/a if stream is not response:
1339n/a stream.close()
1340n/a p.close()
1341n/a
1342n/a return u.close()
1343n/a
1344n/a##
1345n/a# Standard transport class for XML-RPC over HTTPS.
1346n/a
1347n/aclass SafeTransport(Transport):
1348n/a """Handles an HTTPS transaction to an XML-RPC server."""
1349n/a
1350n/a def __init__(self, use_datetime=False, use_builtin_types=False, *,
1351n/a context=None):
1352n/a super().__init__(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
1353n/a self.context = context
1354n/a
1355n/a # FIXME: mostly untested
1356n/a
1357n/a def make_connection(self, host):
1358n/a if self._connection and host == self._connection[0]:
1359n/a return self._connection[1]
1360n/a
1361n/a if not hasattr(http.client, "HTTPSConnection"):
1362n/a raise NotImplementedError(
1363n/a "your version of http.client doesn't support HTTPS")
1364n/a # create a HTTPS connection object from a host descriptor
1365n/a # host may be a string, or a (host, x509-dict) tuple
1366n/a chost, self._extra_headers, x509 = self.get_host_info(host)
1367n/a self._connection = host, http.client.HTTPSConnection(chost,
1368n/a None, context=self.context, **(x509 or {}))
1369n/a return self._connection[1]
1370n/a
1371n/a##
1372n/a# Standard server proxy. This class establishes a virtual connection
1373n/a# to an XML-RPC server.
1374n/a# <p>
1375n/a# This class is available as ServerProxy and Server. New code should
1376n/a# use ServerProxy, to avoid confusion.
1377n/a#
1378n/a# @def ServerProxy(uri, **options)
1379n/a# @param uri The connection point on the server.
1380n/a# @keyparam transport A transport factory, compatible with the
1381n/a# standard transport class.
1382n/a# @keyparam encoding The default encoding used for 8-bit strings
1383n/a# (default is UTF-8).
1384n/a# @keyparam verbose Use a true value to enable debugging output.
1385n/a# (printed to standard output).
1386n/a# @see Transport
1387n/a
1388n/aclass ServerProxy:
1389n/a """uri [,options] -> a logical connection to an XML-RPC server
1390n/a
1391n/a uri is the connection point on the server, given as
1392n/a scheme://host/target.
1393n/a
1394n/a The standard implementation always supports the "http" scheme. If
1395n/a SSL socket support is available (Python 2.0), it also supports
1396n/a "https".
1397n/a
1398n/a If the target part and the slash preceding it are both omitted,
1399n/a "/RPC2" is assumed.
1400n/a
1401n/a The following options can be given as keyword arguments:
1402n/a
1403n/a transport: a transport factory
1404n/a encoding: the request encoding (default is UTF-8)
1405n/a
1406n/a All 8-bit strings passed to the server proxy are assumed to use
1407n/a the given encoding.
1408n/a """
1409n/a
1410n/a def __init__(self, uri, transport=None, encoding=None, verbose=False,
1411n/a allow_none=False, use_datetime=False, use_builtin_types=False,
1412n/a *, context=None):
1413n/a # establish a "logical" server connection
1414n/a
1415n/a # get the url
1416n/a type, uri = urllib.parse.splittype(uri)
1417n/a if type not in ("http", "https"):
1418n/a raise OSError("unsupported XML-RPC protocol")
1419n/a self.__host, self.__handler = urllib.parse.splithost(uri)
1420n/a if not self.__handler:
1421n/a self.__handler = "/RPC2"
1422n/a
1423n/a if transport is None:
1424n/a if type == "https":
1425n/a handler = SafeTransport
1426n/a extra_kwargs = {"context": context}
1427n/a else:
1428n/a handler = Transport
1429n/a extra_kwargs = {}
1430n/a transport = handler(use_datetime=use_datetime,
1431n/a use_builtin_types=use_builtin_types,
1432n/a **extra_kwargs)
1433n/a self.__transport = transport
1434n/a
1435n/a self.__encoding = encoding or 'utf-8'
1436n/a self.__verbose = verbose
1437n/a self.__allow_none = allow_none
1438n/a
1439n/a def __close(self):
1440n/a self.__transport.close()
1441n/a
1442n/a def __request(self, methodname, params):
1443n/a # call a method on the remote server
1444n/a
1445n/a request = dumps(params, methodname, encoding=self.__encoding,
1446n/a allow_none=self.__allow_none).encode(self.__encoding, 'xmlcharrefreplace')
1447n/a
1448n/a response = self.__transport.request(
1449n/a self.__host,
1450n/a self.__handler,
1451n/a request,
1452n/a verbose=self.__verbose
1453n/a )
1454n/a
1455n/a if len(response) == 1:
1456n/a response = response[0]
1457n/a
1458n/a return response
1459n/a
1460n/a def __repr__(self):
1461n/a return (
1462n/a "<%s for %s%s>" %
1463n/a (self.__class__.__name__, self.__host, self.__handler)
1464n/a )
1465n/a
1466n/a __str__ = __repr__
1467n/a
1468n/a def __getattr__(self, name):
1469n/a # magic method dispatcher
1470n/a return _Method(self.__request, name)
1471n/a
1472n/a # note: to call a remote object with a non-standard name, use
1473n/a # result getattr(server, "strange-python-name")(args)
1474n/a
1475n/a def __call__(self, attr):
1476n/a """A workaround to get special attributes on the ServerProxy
1477n/a without interfering with the magic __getattr__
1478n/a """
1479n/a if attr == "close":
1480n/a return self.__close
1481n/a elif attr == "transport":
1482n/a return self.__transport
1483n/a raise AttributeError("Attribute %r not found" % (attr,))
1484n/a
1485n/a def __enter__(self):
1486n/a return self
1487n/a
1488n/a def __exit__(self, *args):
1489n/a self.__close()
1490n/a
1491n/a# compatibility
1492n/a
1493n/aServer = ServerProxy
1494n/a
1495n/a# --------------------------------------------------------------------
1496n/a# test code
1497n/a
1498n/aif __name__ == "__main__":
1499n/a
1500n/a # simple test program (from the XML-RPC specification)
1501n/a
1502n/a # local server, available from Lib/xmlrpc/server.py
1503n/a server = ServerProxy("http://localhost:8000")
1504n/a
1505n/a try:
1506n/a print(server.currentTime.getCurrentTime())
1507n/a except Error as v:
1508n/a print("ERROR", v)
1509n/a
1510n/a multi = MultiCall(server)
1511n/a multi.getData()
1512n/a multi.pow(2,9)
1513n/a multi.add(1,2)
1514n/a try:
1515n/a for response in multi():
1516n/a print(response)
1517n/a except Error as v:
1518n/a print("ERROR", v)