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