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 otype = (hasattr(other, "__class__") 317 and other.__class__.__name__ 318 or type(other)) 319 raise TypeError("Can't compare %s and %s" % 320 (self.__class__.__name__, otype)) 321 return s, o 322 323 def __lt__(self, other): 324 s, o = self.make_comparable(other) 325 return s < o 326 327 def __le__(self, other): 328 s, o = self.make_comparable(other) 329 return s <= o 330 331 def __gt__(self, other): 332 s, o = self.make_comparable(other) 333 return s > o 334 335 def __ge__(self, other): 336 s, o = self.make_comparable(other) 337 return s >= o 338 339 def __eq__(self, other): 340 s, o = self.make_comparable(other) 341 return s == o 342 343 def timetuple(self): 344 return time.strptime(self.value, "%Y%m%dT%H:%M:%S") 345 346 ## 347 # Get date/time value. 348 # 349 # @return Date/time value, as an ISO 8601 string. 350 351 def __str__(self): 352 return self.value 353 354 def __repr__(self): 355 return "<%s %r at %#x>" % (self.__class__.__name__, self.value, id(self)) 356 357 def decode(self, data): 358 self.value = str(data).strip() 359 360 def encode(self, out): 361 out.write("<value><dateTime.iso8601>") 362 out.write(self.value) 363 out.write("</dateTime.iso8601></value>\n") 364 365def _datetime(data): 366 # decode xml element contents into a DateTime structure. 367 value = DateTime() 368 value.decode(data) 369 return value 370 371def _datetime_type(data): 372 return datetime.strptime(data, "%Y%m%dT%H:%M:%S") 373 374## 375# Wrapper for binary data. This can be used to transport any kind 376# of binary data over XML-RPC, using BASE64 encoding. 377# 378# @param data An 8-bit string containing arbitrary data. 379 380class Binary: 381 """Wrapper for binary data.""" 382 383 def __init__(self, data=None): 384 if data is None: 385 data = b"" 386 else: 387 if not isinstance(data, (bytes, bytearray)): 388 raise TypeError("expected bytes or bytearray, not %s" % 389 data.__class__.__name__) 390 data = bytes(data) # Make a copy of the bytes! 391 self.data = data 392 393 ## 394 # Get buffer contents. 395 # 396 # @return Buffer contents, as an 8-bit string. 397 398 def __str__(self): 399 return str(self.data, "latin-1") # XXX encoding?! 400 401 def __eq__(self, other): 402 if isinstance(other, Binary): 403 other = other.data 404 return self.data == other 405 406 def decode(self, data): 407 self.data = base64.decodebytes(data) 408 409 def encode(self, out): 410 out.write("<value><base64>\n") 411 encoded = base64.encodebytes(self.data) 412 out.write(encoded.decode('ascii')) 413 out.write("</base64></value>\n") 414 415def _binary(data): 416 # decode xml element contents into a Binary structure 417 value = Binary() 418 value.decode(data) 419 return value 420 421WRAPPERS = (DateTime, Binary) 422 423# -------------------------------------------------------------------- 424# XML parsers 425 426class ExpatParser: 427 # fast expat parser for Python 2.0 and later. 428 def __init__(self, target): 429 self._parser = parser = expat.ParserCreate(None, None) 430 self._target = target 431 parser.StartElementHandler = target.start 432 parser.EndElementHandler = target.end 433 parser.CharacterDataHandler = target.data 434 encoding = None 435 target.xml(encoding, None) 436 437 def feed(self, data): 438 self._parser.Parse(data, 0) 439 440 def close(self): 441 try: 442 parser = self._parser 443 except AttributeError: 444 pass 445 else: 446 del self._target, self._parser # get rid of circular references 447 parser.Parse(b"", True) # end of data 448 449# -------------------------------------------------------------------- 450# XML-RPC marshalling and unmarshalling code 451 452## 453# XML-RPC marshaller. 454# 455# @param encoding Default encoding for 8-bit strings. The default 456# value is None (interpreted as UTF-8). 457# @see dumps 458 459class Marshaller: 460 """Generate an XML-RPC params chunk from a Python data structure. 461 462 Create a Marshaller instance for each set of parameters, and use 463 the "dumps" method to convert your data (represented as a tuple) 464 to an XML-RPC params chunk. To write a fault response, pass a 465 Fault instance instead. You may prefer to use the "dumps" module 466 function for this purpose. 467 """ 468 469 # by the way, if you don't understand what's going on in here, 470 # that's perfectly ok. 471 472 def __init__(self, encoding=None, allow_none=False): 473 self.memo = {} 474 self.data = None 475 self.encoding = encoding 476 self.allow_none = allow_none 477 478 dispatch = {} 479 480 def dumps(self, values): 481 out = [] 482 write = out.append 483 dump = self.__dump 484 if isinstance(values, Fault): 485 # fault instance 486 write("<fault>\n") 487 dump({'faultCode': values.faultCode, 488 'faultString': values.faultString}, 489 write) 490 write("</fault>\n") 491 else: 492 # parameter block 493 # FIXME: the xml-rpc specification allows us to leave out 494 # the entire <params> block if there are no parameters. 495 # however, changing this may break older code (including 496 # old versions of xmlrpclib.py), so this is better left as 497 # is for now. See @XMLRPC3 for more information. /F 498 write("<params>\n") 499 for v in values: 500 write("<param>\n") 501 dump(v, write) 502 write("</param>\n") 503 write("</params>\n") 504 result = "".join(out) 505 return result 506 507 def __dump(self, value, write): 508 try: 509 f = self.dispatch[type(value)] 510 except KeyError: 511 # check if this object can be marshalled as a structure 512 if not hasattr(value, '__dict__'): 513 raise TypeError("cannot marshal %s objects" % type(value)) 514 # check if this class is a sub-class of a basic type, 515 # because we don't know how to marshal these types 516 # (e.g. a string sub-class) 517 for type_ in type(value).__mro__: 518 if type_ in self.dispatch.keys(): 519 raise TypeError("cannot marshal %s objects" % type(value)) 520 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix 521 # for the p3yk merge, this should probably be fixed more neatly. 522 f = self.dispatch["_arbitrary_instance"] 523 f(self, value, write) 524 525 def dump_nil (self, value, write): 526 if not self.allow_none: 527 raise TypeError("cannot marshal None unless allow_none is enabled") 528 write("<value><nil/></value>") 529 dispatch[type(None)] = dump_nil 530 531 def dump_bool(self, value, write): 532 write("<value><boolean>") 533 write(value and "1" or "0") 534 write("</boolean></value>\n") 535 dispatch[bool] = dump_bool 536 537 def dump_long(self, value, write): 538 if value > MAXINT or value < MININT: 539 raise OverflowError("int exceeds XML-RPC limits") 540 write("<value><int>") 541 write(str(int(value))) 542 write("</int></value>\n") 543 dispatch[int] = dump_long 544 545 # backward compatible 546 dump_int = dump_long 547 548 def dump_double(self, value, write): 549 write("<value><double>") 550 write(repr(value)) 551 write("</double></value>\n") 552 dispatch[float] = dump_double 553 554 def dump_unicode(self, value, write, escape=escape): 555 write("<value><string>") 556 write(escape(value)) 557 write("</string></value>\n") 558 dispatch[str] = dump_unicode 559 560 def dump_bytes(self, value, write): 561 write("<value><base64>\n") 562 encoded = base64.encodebytes(value) 563 write(encoded.decode('ascii')) 564 write("</base64></value>\n") 565 dispatch[bytes] = dump_bytes 566 dispatch[bytearray] = dump_bytes 567 568 def dump_array(self, value, write): 569 i = id(value) 570 if i in self.memo: 571 raise TypeError("cannot marshal recursive sequences") 572 self.memo[i] = None 573 dump = self.__dump 574 write("<value><array><data>\n") 575 for v in value: 576 dump(v, write) 577 write("</data></array></value>\n") 578 del self.memo[i] 579 dispatch[tuple] = dump_array 580 dispatch[list] = dump_array 581 582 def dump_struct(self, value, write, escape=escape): 583 i = id(value) 584 if i in self.memo: 585 raise TypeError("cannot marshal recursive dictionaries") 586 self.memo[i] = None 587 dump = self.__dump 588 write("<value><struct>\n") 589 for k, v in value.items(): 590 write("<member>\n") 591 if not isinstance(k, str): 592 raise TypeError("dictionary key must be string") 593 write("<name>%s</name>\n" % escape(k)) 594 dump(v, write) 595 write("</member>\n") 596 write("</struct></value>\n") 597 del self.memo[i] 598 dispatch[dict] = dump_struct 599 600 def dump_datetime(self, value, write): 601 write("<value><dateTime.iso8601>") 602 write(_strftime(value)) 603 write("</dateTime.iso8601></value>\n") 604 dispatch[datetime] = dump_datetime 605 606 def dump_instance(self, value, write): 607 # check for special wrappers 608 if value.__class__ in WRAPPERS: 609 self.write = write 610 value.encode(self) 611 del self.write 612 else: 613 # store instance attributes as a struct (really?) 614 self.dump_struct(value.__dict__, write) 615 dispatch[DateTime] = dump_instance 616 dispatch[Binary] = dump_instance 617 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix 618 # for the p3yk merge, this should probably be fixed more neatly. 619 dispatch["_arbitrary_instance"] = dump_instance 620 621## 622# XML-RPC unmarshaller. 623# 624# @see loads 625 626class Unmarshaller: 627 """Unmarshal an XML-RPC response, based on incoming XML event 628 messages (start, data, end). Call close() to get the resulting 629 data structure. 630 631 Note that this reader is fairly tolerant, and gladly accepts bogus 632 XML-RPC data without complaining (but not bogus XML). 633 """ 634 635 # and again, if you don't understand what's going on in here, 636 # that's perfectly ok. 637 638 def __init__(self, use_datetime=False, use_builtin_types=False): 639 self._type = None 640 self._stack = [] 641 self._marks = [] 642 self._data = [] 643 self._value = False 644 self._methodname = None 645 self._encoding = "utf-8" 646 self.append = self._stack.append 647 self._use_datetime = use_builtin_types or use_datetime 648 self._use_bytes = use_builtin_types 649 650 def close(self): 651 # return response tuple and target method 652 if self._type is None or self._marks: 653 raise ResponseError() 654 if self._type == "fault": 655 raise Fault(**self._stack[0]) 656 return tuple(self._stack) 657 658 def getmethodname(self): 659 return self._methodname 660 661 # 662 # event handlers 663 664 def xml(self, encoding, standalone): 665 self._encoding = encoding 666 # FIXME: assert standalone == 1 ??? 667 668 def start(self, tag, attrs): 669 # prepare to handle this element 670 if ':' in tag: 671 tag = tag.split(':')[-1] 672 if tag == "array" or tag == "struct": 673 self._marks.append(len(self._stack)) 674 self._data = [] 675 if self._value and tag not in self.dispatch: 676 raise ResponseError("unknown tag %r" % tag) 677 self._value = (tag == "value") 678 679 def data(self, text): 680 self._data.append(text) 681 682 def end(self, tag): 683 # call the appropriate end tag handler 684 try: 685 f = self.dispatch[tag] 686 except KeyError: 687 if ':' not in tag: 688 return # unknown tag ? 689 try: 690 f = self.dispatch[tag.split(':')[-1]] 691 except KeyError: 692 return # unknown tag ? 693 return f(self, "".join(self._data)) 694 695 # 696 # accelerator support 697 698 def end_dispatch(self, tag, data): 699 # dispatch data 700 try: 701 f = self.dispatch[tag] 702 except KeyError: 703 if ':' not in tag: 704 return # unknown tag ? 705 try: 706 f = self.dispatch[tag.split(':')[-1]] 707 except KeyError: 708 return # unknown tag ? 709 return f(self, data) 710 711 # 712 # element decoders 713 714 dispatch = {} 715 716 def end_nil (self, data): 717 self.append(None) 718 self._value = 0 719 dispatch["nil"] = end_nil 720 721 def end_boolean(self, data): 722 if data == "0": 723 self.append(False) 724 elif data == "1": 725 self.append(True) 726 else: 727 raise TypeError("bad boolean value") 728 self._value = 0 729 dispatch["boolean"] = end_boolean 730 731 def end_int(self, data): 732 self.append(int(data)) 733 self._value = 0 734 dispatch["i1"] = end_int 735 dispatch["i2"] = end_int 736 dispatch["i4"] = end_int 737 dispatch["i8"] = end_int 738 dispatch["int"] = end_int 739 dispatch["biginteger"] = end_int 740 741 def end_double(self, data): 742 self.append(float(data)) 743 self._value = 0 744 dispatch["double"] = end_double 745 dispatch["float"] = end_double 746 747 def end_bigdecimal(self, data): 748 self.append(Decimal(data)) 749 self._value = 0 750 dispatch["bigdecimal"] = end_bigdecimal 751 752 def end_string(self, data): 753 if self._encoding: 754 data = data.decode(self._encoding) 755 self.append(data) 756 self._value = 0 757 dispatch["string"] = end_string 758 dispatch["name"] = end_string # struct keys are always strings 759 760 def end_array(self, data): 761 mark = self._marks.pop() 762 # map arrays to Python lists 763 self._stack[mark:] = [self._stack[mark:]] 764 self._value = 0 765 dispatch["array"] = end_array 766 767 def end_struct(self, data): 768 mark = self._marks.pop() 769 # map structs to Python dictionaries 770 dict = {} 771 items = self._stack[mark:] 772 for i in range(0, len(items), 2): 773 dict[items[i]] = items[i+1] 774 self._stack[mark:] = [dict] 775 self._value = 0 776 dispatch["struct"] = end_struct 777 778 def end_base64(self, data): 779 value = Binary() 780 value.decode(data.encode("ascii")) 781 if self._use_bytes: 782 value = value.data 783 self.append(value) 784 self._value = 0 785 dispatch["base64"] = end_base64 786 787 def end_dateTime(self, data): 788 value = DateTime() 789 value.decode(data) 790 if self._use_datetime: 791 value = _datetime_type(data) 792 self.append(value) 793 dispatch["dateTime.iso8601"] = end_dateTime 794 795 def end_value(self, data): 796 # if we stumble upon a value element with no internal 797 # elements, treat it as a string element 798 if self._value: 799 self.end_string(data) 800 dispatch["value"] = end_value 801 802 def end_params(self, data): 803 self._type = "params" 804 dispatch["params"] = end_params 805 806 def end_fault(self, data): 807 self._type = "fault" 808 dispatch["fault"] = end_fault 809 810 def end_methodName(self, data): 811 if self._encoding: 812 data = data.decode(self._encoding) 813 self._methodname = data 814 self._type = "methodName" # no params 815 dispatch["methodName"] = end_methodName 816 817## Multicall support 818# 819 820class _MultiCallMethod: 821 # some lesser magic to store calls made to a MultiCall object 822 # for batch execution 823 def __init__(self, call_list, name): 824 self.__call_list = call_list 825 self.__name = name 826 def __getattr__(self, name): 827 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) 828 def __call__(self, *args): 829 self.__call_list.append((self.__name, args)) 830 831class MultiCallIterator: 832 """Iterates over the results of a multicall. Exceptions are 833 raised in response to xmlrpc faults.""" 834 835 def __init__(self, results): 836 self.results = results 837 838 def __getitem__(self, i): 839 item = self.results[i] 840 if type(item) == type({}): 841 raise Fault(item['faultCode'], item['faultString']) 842 elif type(item) == type([]): 843 return item[0] 844 else: 845 raise ValueError("unexpected type in multicall result") 846 847class MultiCall: 848 """server -> an object used to boxcar method calls 849 850 server should be a ServerProxy object. 851 852 Methods can be added to the MultiCall using normal 853 method call syntax e.g.: 854 855 multicall = MultiCall(server_proxy) 856 multicall.add(2,3) 857 multicall.get_address("Guido") 858 859 To execute the multicall, call the MultiCall object e.g.: 860 861 add_result, address = multicall() 862 """ 863 864 def __init__(self, server): 865 self.__server = server 866 self.__call_list = [] 867 868 def __repr__(self): 869 return "<%s at %#x>" % (self.__class__.__name__, id(self)) 870 871 def __getattr__(self, name): 872 return _MultiCallMethod(self.__call_list, name) 873 874 def __call__(self): 875 marshalled_list = [] 876 for name, args in self.__call_list: 877 marshalled_list.append({'methodName' : name, 'params' : args}) 878 879 return MultiCallIterator(self.__server.system.multicall(marshalled_list)) 880 881# -------------------------------------------------------------------- 882# convenience functions 883 884FastMarshaller = FastParser = FastUnmarshaller = None 885 886## 887# Create a parser object, and connect it to an unmarshalling instance. 888# This function picks the fastest available XML parser. 889# 890# return A (parser, unmarshaller) tuple. 891 892def getparser(use_datetime=False, use_builtin_types=False): 893 """getparser() -> parser, unmarshaller 894 895 Create an instance of the fastest available parser, and attach it 896 to an unmarshalling object. Return both objects. 897 """ 898 if FastParser and FastUnmarshaller: 899 if use_builtin_types: 900 mkdatetime = _datetime_type 901 mkbytes = base64.decodebytes 902 elif use_datetime: 903 mkdatetime = _datetime_type 904 mkbytes = _binary 905 else: 906 mkdatetime = _datetime 907 mkbytes = _binary 908 target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) 909 parser = FastParser(target) 910 else: 911 target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) 912 if FastParser: 913 parser = FastParser(target) 914 else: 915 parser = ExpatParser(target) 916 return parser, target 917 918## 919# Convert a Python tuple or a Fault instance to an XML-RPC packet. 920# 921# @def dumps(params, **options) 922# @param params A tuple or Fault instance. 923# @keyparam methodname If given, create a methodCall request for 924# this method name. 925# @keyparam methodresponse If given, create a methodResponse packet. 926# If used with a tuple, the tuple must be a singleton (that is, 927# it must contain exactly one element). 928# @keyparam encoding The packet encoding. 929# @return A string containing marshalled data. 930 931def dumps(params, methodname=None, methodresponse=None, encoding=None, 932 allow_none=False): 933 """data [,options] -> marshalled data 934 935 Convert an argument tuple or a Fault instance to an XML-RPC 936 request (or response, if the methodresponse option is used). 937 938 In addition to the data object, the following options can be given 939 as keyword arguments: 940 941 methodname: the method name for a methodCall packet 942 943 methodresponse: true to create a methodResponse packet. 944 If this option is used with a tuple, the tuple must be 945 a singleton (i.e. it can contain only one element). 946 947 encoding: the packet encoding (default is UTF-8) 948 949 All byte strings in the data structure are assumed to use the 950 packet encoding. Unicode strings are automatically converted, 951 where necessary. 952 """ 953 954 assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" 955 if isinstance(params, Fault): 956 methodresponse = 1 957 elif methodresponse and isinstance(params, tuple): 958 assert len(params) == 1, "response tuple must be a singleton" 959 960 if not encoding: 961 encoding = "utf-8" 962 963 if FastMarshaller: 964 m = FastMarshaller(encoding) 965 else: 966 m = Marshaller(encoding, allow_none) 967 968 data = m.dumps(params) 969 970 if encoding != "utf-8": 971 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding) 972 else: 973 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default 974 975 # standard XML-RPC wrappings 976 if methodname: 977 # a method call 978 data = ( 979 xmlheader, 980 "<methodCall>\n" 981 "<methodName>", methodname, "</methodName>\n", 982 data, 983 "</methodCall>\n" 984 ) 985 elif methodresponse: 986 # a method response, or a fault structure 987 data = ( 988 xmlheader, 989 "<methodResponse>\n", 990 data, 991 "</methodResponse>\n" 992 ) 993 else: 994 return data # return as is 995 return "".join(data) 996 997## 998# Convert an XML-RPC packet to a Python object. If the XML-RPC packet 999# represents a fault condition, this function raises a Fault exception. 1000# 1001# @param data An XML-RPC packet, given as an 8-bit string. 1002# @return A tuple containing the unpacked data, and the method name 1003# (None if not present). 1004# @see Fault 1005 1006def loads(data, use_datetime=False, use_builtin_types=False): 1007 """data -> unmarshalled data, method name 1008 1009 Convert an XML-RPC packet to unmarshalled data plus a method 1010 name (None if not present). 1011 1012 If the XML-RPC packet represents a fault condition, this function 1013 raises a Fault exception. 1014 """ 1015 p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) 1016 p.feed(data) 1017 p.close() 1018 return u.close(), u.getmethodname() 1019 1020## 1021# Encode a string using the gzip content encoding such as specified by the 1022# Content-Encoding: gzip 1023# in the HTTP header, as described in RFC 1952 1024# 1025# @param data the unencoded data 1026# @return the encoded data 1027 1028def gzip_encode(data): 1029 """data -> gzip encoded data 1030 1031 Encode data using the gzip content encoding as described in RFC 1952 1032 """ 1033 if not gzip: 1034 raise NotImplementedError 1035 f = BytesIO() 1036 with gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) as gzf: 1037 gzf.write(data) 1038 return f.getvalue() 1039 1040## 1041# Decode a string using the gzip content encoding such as specified by the 1042# Content-Encoding: gzip 1043# in the HTTP header, as described in RFC 1952 1044# 1045# @param data The encoded data 1046# @keyparam max_decode Maximum bytes to decode (20 MiB default), use negative 1047# values for unlimited decoding 1048# @return the unencoded data 1049# @raises ValueError if data is not correctly coded. 1050# @raises ValueError if max gzipped payload length exceeded 1051 1052def gzip_decode(data, max_decode=20971520): 1053 """gzip encoded data -> unencoded data 1054 1055 Decode data using the gzip content encoding as described in RFC 1952 1056 """ 1057 if not gzip: 1058 raise NotImplementedError 1059 with gzip.GzipFile(mode="rb", fileobj=BytesIO(data)) as gzf: 1060 try: 1061 if max_decode < 0: # no limit 1062 decoded = gzf.read() 1063 else: 1064 decoded = gzf.read(max_decode + 1) 1065 except OSError: 1066 raise ValueError("invalid data") 1067 if max_decode >= 0 and len(decoded) > max_decode: 1068 raise ValueError("max gzipped payload length exceeded") 1069 return decoded 1070 1071## 1072# Return a decoded file-like object for the gzip encoding 1073# as described in RFC 1952. 1074# 1075# @param response A stream supporting a read() method 1076# @return a file-like object that the decoded data can be read() from 1077 1078class GzipDecodedResponse(gzip.GzipFile if gzip else object): 1079 """a file-like object to decode a response encoded with the gzip 1080 method, as described in RFC 1952. 1081 """ 1082 def __init__(self, response): 1083 #response doesn't support tell() and read(), required by 1084 #GzipFile 1085 if not gzip: 1086 raise NotImplementedError 1087 self.io = BytesIO(response.read()) 1088 gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io) 1089 1090 def close(self): 1091 try: 1092 gzip.GzipFile.close(self) 1093 finally: 1094 self.io.close() 1095 1096 1097# -------------------------------------------------------------------- 1098# request dispatcher 1099 1100class _Method: 1101 # some magic to bind an XML-RPC method to an RPC server. 1102 # supports "nested" methods (e.g. examples.getStateName) 1103 def __init__(self, send, name): 1104 self.__send = send 1105 self.__name = name 1106 def __getattr__(self, name): 1107 return _Method(self.__send, "%s.%s" % (self.__name, name)) 1108 def __call__(self, *args): 1109 return self.__send(self.__name, args) 1110 1111## 1112# Standard transport class for XML-RPC over HTTP. 1113# <p> 1114# You can create custom transports by subclassing this method, and 1115# overriding selected methods. 1116 1117class Transport: 1118 """Handles an HTTP transaction to an XML-RPC server.""" 1119 1120 # client identifier (may be overridden) 1121 user_agent = "Python-xmlrpc/%s" % __version__ 1122 1123 #if true, we'll request gzip encoding 1124 accept_gzip_encoding = True 1125 1126 # if positive, encode request using gzip if it exceeds this threshold 1127 # note that many servers will get confused, so only use it if you know 1128 # that they can decode such a request 1129 encode_threshold = None #None = don't encode 1130 1131 def __init__(self, use_datetime=False, use_builtin_types=False, 1132 *, headers=()): 1133 self._use_datetime = use_datetime 1134 self._use_builtin_types = use_builtin_types 1135 self._connection = (None, None) 1136 self._headers = list(headers) 1137 self._extra_headers = [] 1138 1139 ## 1140 # Send a complete request, and parse the response. 1141 # Retry request if a cached connection has disconnected. 1142 # 1143 # @param host Target host. 1144 # @param handler Target PRC handler. 1145 # @param request_body XML-RPC request body. 1146 # @param verbose Debugging flag. 1147 # @return Parsed response. 1148 1149 def request(self, host, handler, request_body, verbose=False): 1150 #retry request once if cached connection has gone cold 1151 for i in (0, 1): 1152 try: 1153 return self.single_request(host, handler, request_body, verbose) 1154 except http.client.RemoteDisconnected: 1155 if i: 1156 raise 1157 except OSError as e: 1158 if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, 1159 errno.EPIPE): 1160 raise 1161 1162 def single_request(self, host, handler, request_body, verbose=False): 1163 # issue XML-RPC request 1164 try: 1165 http_conn = self.send_request(host, handler, request_body, verbose) 1166 resp = http_conn.getresponse() 1167 if resp.status == 200: 1168 self.verbose = verbose 1169 return self.parse_response(resp) 1170 1171 except Fault: 1172 raise 1173 except Exception: 1174 #All unexpected errors leave connection in 1175 # a strange state, so we clear it. 1176 self.close() 1177 raise 1178 1179 #We got an error response. 1180 #Discard any response data and raise exception 1181 if resp.getheader("content-length", ""): 1182 resp.read() 1183 raise ProtocolError( 1184 host + handler, 1185 resp.status, resp.reason, 1186 dict(resp.getheaders()) 1187 ) 1188 1189 1190 ## 1191 # Create parser. 1192 # 1193 # @return A 2-tuple containing a parser and an unmarshaller. 1194 1195 def getparser(self): 1196 # get parser and unmarshaller 1197 return getparser(use_datetime=self._use_datetime, 1198 use_builtin_types=self._use_builtin_types) 1199 1200 ## 1201 # Get authorization info from host parameter 1202 # Host may be a string, or a (host, x509-dict) tuple; if a string, 1203 # it is checked for a "user:pw@host" format, and a "Basic 1204 # Authentication" header is added if appropriate. 1205 # 1206 # @param host Host descriptor (URL or (URL, x509 info) tuple). 1207 # @return A 3-tuple containing (actual host, extra headers, 1208 # x509 info). The header and x509 fields may be None. 1209 1210 def get_host_info(self, host): 1211 1212 x509 = {} 1213 if isinstance(host, tuple): 1214 host, x509 = host 1215 1216 auth, host = urllib.parse._splituser(host) 1217 1218 if auth: 1219 auth = urllib.parse.unquote_to_bytes(auth) 1220 auth = base64.encodebytes(auth).decode("utf-8") 1221 auth = "".join(auth.split()) # get rid of whitespace 1222 extra_headers = [ 1223 ("Authorization", "Basic " + auth) 1224 ] 1225 else: 1226 extra_headers = [] 1227 1228 return host, extra_headers, x509 1229 1230 ## 1231 # Connect to server. 1232 # 1233 # @param host Target host. 1234 # @return An HTTPConnection object 1235 1236 def make_connection(self, host): 1237 #return an existing connection if possible. This allows 1238 #HTTP/1.1 keep-alive. 1239 if self._connection and host == self._connection[0]: 1240 return self._connection[1] 1241 # create a HTTP connection object from a host descriptor 1242 chost, self._extra_headers, x509 = self.get_host_info(host) 1243 self._connection = host, http.client.HTTPConnection(chost) 1244 return self._connection[1] 1245 1246 ## 1247 # Clear any cached connection object. 1248 # Used in the event of socket errors. 1249 # 1250 def close(self): 1251 host, connection = self._connection 1252 if connection: 1253 self._connection = (None, None) 1254 connection.close() 1255 1256 ## 1257 # Send HTTP request. 1258 # 1259 # @param host Host descriptor (URL or (URL, x509 info) tuple). 1260 # @param handler Target RPC handler (a path relative to host) 1261 # @param request_body The XML-RPC request body 1262 # @param debug Enable debugging if debug is true. 1263 # @return An HTTPConnection. 1264 1265 def send_request(self, host, handler, request_body, debug): 1266 connection = self.make_connection(host) 1267 headers = self._headers + self._extra_headers 1268 if debug: 1269 connection.set_debuglevel(1) 1270 if self.accept_gzip_encoding and gzip: 1271 connection.putrequest("POST", handler, skip_accept_encoding=True) 1272 headers.append(("Accept-Encoding", "gzip")) 1273 else: 1274 connection.putrequest("POST", handler) 1275 headers.append(("Content-Type", "text/xml")) 1276 headers.append(("User-Agent", self.user_agent)) 1277 self.send_headers(connection, headers) 1278 self.send_content(connection, request_body) 1279 return connection 1280 1281 ## 1282 # Send request headers. 1283 # This function provides a useful hook for subclassing 1284 # 1285 # @param connection httpConnection. 1286 # @param headers list of key,value pairs for HTTP headers 1287 1288 def send_headers(self, connection, headers): 1289 for key, val in headers: 1290 connection.putheader(key, val) 1291 1292 ## 1293 # Send request body. 1294 # This function provides a useful hook for subclassing 1295 # 1296 # @param connection httpConnection. 1297 # @param request_body XML-RPC request body. 1298 1299 def send_content(self, connection, request_body): 1300 #optionally encode the request 1301 if (self.encode_threshold is not None and 1302 self.encode_threshold < len(request_body) and 1303 gzip): 1304 connection.putheader("Content-Encoding", "gzip") 1305 request_body = gzip_encode(request_body) 1306 1307 connection.putheader("Content-Length", str(len(request_body))) 1308 connection.endheaders(request_body) 1309 1310 ## 1311 # Parse response. 1312 # 1313 # @param file Stream. 1314 # @return Response tuple and target method. 1315 1316 def parse_response(self, response): 1317 # read response data from httpresponse, and parse it 1318 # Check for new http response object, otherwise it is a file object. 1319 if hasattr(response, 'getheader'): 1320 if response.getheader("Content-Encoding", "") == "gzip": 1321 stream = GzipDecodedResponse(response) 1322 else: 1323 stream = response 1324 else: 1325 stream = response 1326 1327 p, u = self.getparser() 1328 1329 while 1: 1330 data = stream.read(1024) 1331 if not data: 1332 break 1333 if self.verbose: 1334 print("body:", repr(data)) 1335 p.feed(data) 1336 1337 if stream is not response: 1338 stream.close() 1339 p.close() 1340 1341 return u.close() 1342 1343## 1344# Standard transport class for XML-RPC over HTTPS. 1345 1346class SafeTransport(Transport): 1347 """Handles an HTTPS transaction to an XML-RPC server.""" 1348 1349 def __init__(self, use_datetime=False, use_builtin_types=False, 1350 *, headers=(), context=None): 1351 super().__init__(use_datetime=use_datetime, 1352 use_builtin_types=use_builtin_types, 1353 headers=headers) 1354 self.context = context 1355 1356 # FIXME: mostly untested 1357 1358 def make_connection(self, host): 1359 if self._connection and host == self._connection[0]: 1360 return self._connection[1] 1361 1362 if not hasattr(http.client, "HTTPSConnection"): 1363 raise NotImplementedError( 1364 "your version of http.client doesn't support HTTPS") 1365 # create a HTTPS connection object from a host descriptor 1366 # host may be a string, or a (host, x509-dict) tuple 1367 chost, self._extra_headers, x509 = self.get_host_info(host) 1368 self._connection = host, http.client.HTTPSConnection(chost, 1369 None, context=self.context, **(x509 or {})) 1370 return self._connection[1] 1371 1372## 1373# Standard server proxy. This class establishes a virtual connection 1374# to an XML-RPC server. 1375# <p> 1376# This class is available as ServerProxy and Server. New code should 1377# use ServerProxy, to avoid confusion. 1378# 1379# @def ServerProxy(uri, **options) 1380# @param uri The connection point on the server. 1381# @keyparam transport A transport factory, compatible with the 1382# standard transport class. 1383# @keyparam encoding The default encoding used for 8-bit strings 1384# (default is UTF-8). 1385# @keyparam verbose Use a true value to enable debugging output. 1386# (printed to standard output). 1387# @see Transport 1388 1389class ServerProxy: 1390 """uri [,options] -> a logical connection to an XML-RPC server 1391 1392 uri is the connection point on the server, given as 1393 scheme://host/target. 1394 1395 The standard implementation always supports the "http" scheme. If 1396 SSL socket support is available (Python 2.0), it also supports 1397 "https". 1398 1399 If the target part and the slash preceding it are both omitted, 1400 "/RPC2" is assumed. 1401 1402 The following options can be given as keyword arguments: 1403 1404 transport: a transport factory 1405 encoding: the request encoding (default is UTF-8) 1406 1407 All 8-bit strings passed to the server proxy are assumed to use 1408 the given encoding. 1409 """ 1410 1411 def __init__(self, uri, transport=None, encoding=None, verbose=False, 1412 allow_none=False, use_datetime=False, use_builtin_types=False, 1413 *, headers=(), context=None): 1414 # establish a "logical" server connection 1415 1416 # get the url 1417 type, uri = urllib.parse._splittype(uri) 1418 if type not in ("http", "https"): 1419 raise OSError("unsupported XML-RPC protocol") 1420 self.__host, self.__handler = urllib.parse._splithost(uri) 1421 if not self.__handler: 1422 self.__handler = "/RPC2" 1423 1424 if transport is None: 1425 if type == "https": 1426 handler = SafeTransport 1427 extra_kwargs = {"context": context} 1428 else: 1429 handler = Transport 1430 extra_kwargs = {} 1431 transport = handler(use_datetime=use_datetime, 1432 use_builtin_types=use_builtin_types, 1433 headers=headers, 1434 **extra_kwargs) 1435 self.__transport = transport 1436 1437 self.__encoding = encoding or 'utf-8' 1438 self.__verbose = verbose 1439 self.__allow_none = allow_none 1440 1441 def __close(self): 1442 self.__transport.close() 1443 1444 def __request(self, methodname, params): 1445 # call a method on the remote server 1446 1447 request = dumps(params, methodname, encoding=self.__encoding, 1448 allow_none=self.__allow_none).encode(self.__encoding, 'xmlcharrefreplace') 1449 1450 response = self.__transport.request( 1451 self.__host, 1452 self.__handler, 1453 request, 1454 verbose=self.__verbose 1455 ) 1456 1457 if len(response) == 1: 1458 response = response[0] 1459 1460 return response 1461 1462 def __repr__(self): 1463 return ( 1464 "<%s for %s%s>" % 1465 (self.__class__.__name__, self.__host, self.__handler) 1466 ) 1467 1468 def __getattr__(self, name): 1469 # magic method dispatcher 1470 return _Method(self.__request, name) 1471 1472 # note: to call a remote object with a non-standard name, use 1473 # result getattr(server, "strange-python-name")(args) 1474 1475 def __call__(self, attr): 1476 """A workaround to get special attributes on the ServerProxy 1477 without interfering with the magic __getattr__ 1478 """ 1479 if attr == "close": 1480 return self.__close 1481 elif attr == "transport": 1482 return self.__transport 1483 raise AttributeError("Attribute %r not found" % (attr,)) 1484 1485 def __enter__(self): 1486 return self 1487 1488 def __exit__(self, *args): 1489 self.__close() 1490 1491# compatibility 1492 1493Server = ServerProxy 1494 1495# -------------------------------------------------------------------- 1496# test code 1497 1498if __name__ == "__main__": 1499 1500 # simple test program (from the XML-RPC specification) 1501 1502 # local server, available from Lib/xmlrpc/server.py 1503 server = ServerProxy("http://localhost:8000") 1504 1505 try: 1506 print(server.currentTime.getCurrentTime()) 1507 except Error as v: 1508 print("ERROR", v) 1509 1510 multi = MultiCall(server) 1511 multi.getData() 1512 multi.pow(2,9) 1513 multi.add(1,2) 1514 try: 1515 for response in multi(): 1516 print(response) 1517 except Error as v: 1518 print("ERROR", v) 1519