ÿØÿà JFIF    ÿÛ „  ( %"1!%)+...383,7(-.+  -+++--++++---+-+-----+---------------+---+-++7-----ÿÀ  ß â" ÿÄ     ÿÄ H    !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ   ÿÄ %  !1AQa"23‘ÿÚ   ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6  öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ "Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷󲍷˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%ÌÁ²h´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ǍýʏTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆѪQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»& î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$˝Úsäÿ ÷Û #°xŸëí(l »ý3—¥5m! rt`†0~'j2(]S¦¦kv,ÚÇ l¦øJA£Šƒ J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉ䢍mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ­‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U  Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD é©¤&‡ïDbàÁôMÁ." ¤‡ú*õ'VŽ|¼´Úgllº¼klz[Æüï÷Aób‡Eÿ dÑ»Xx9ÃÜ£ÁT/`¼¸vI±Ýµ·Ë‚“G³þ*Ÿû´r|*}<¨îºœ @¦mÄ’M¹”.œ«Y–|6ÏU¤jç¥ÕÞqO ˜kDÆÁ¨5ÿ š;ÐЦ¦€GÙk \ –Þ=â¼=SͧµªS°ÚÍpÜãQűÀõ¬?ÃÁ1Ñ•õZà?hóœ€ L¦l{Y*K˜Ù›zc˜–ˆâ ø+¾ ­-Ök¥%ùEÜA'}ˆ><ÊIè“bpÍ/qÞâvoX€w,\úªò6Z[XdÒæ­@Ö—€$òJí#é>'°Ú ôª˜<)4ryÙ£|óAÅn5žêŸyÒäMÝ2{"}‰–¤l÷ûWX\l¾Á¸góÉOÔ /óñB¤f¸çñ[.P˜ZsÊË*ßT܈§QN¢’¡¨§V¼(Üù*eÕ“”5T¨‹Âê¥FŒã½Dü[8'Ò¥a…Ú¶k7a *•›¼'Ò·\8¨ª\@\õ¢¦íq+DÙrmÎ…_ªæ»ŠÓœ¡¯’Ré9MÅ×D™lælffc+ŒÑ,ý™ÿ ¯þǤ=Å’Á7µ÷ÚÛ/“Ü€ñýã¼àí¾ÕÑ+ƒ,uµMâÀÄbm:ÒÎPæ{˜Gz[ƒ¯«® KHà`ߨŠéí¯P8Aq.C‰ à€kòpj´kN¶qô€…Õ,ÜNŠª-­{Zö’æû44‰sŽè‰îVíRœÕm" 6?³D9¡ÇTíÅꋇ`4«¸ÝÁô ï’ýorqКÇZ«x4Žâéþuïf¹µö[P ,Q£éaX±`PÉÍZ ¸äYúg üAx ’6Lê‚xÝÓ*äQ  Ï’¨hÍ =²,6ï#rÃ<¯–£»ƒ‹,–ê•€ aÛsñ'%Æ"®ÛüìBᝠHÚ3ß°©$“XnœÖ’î2ËTeûìxîß ¦å¿çÉ ðK§þ{‘t‚Ϋ¬jéîZ[ ”š7L¥4VÚCE×]m¤Øy”ä4-dz£œ§¸x.*ãÊÊ b÷•h:©‡¦s`BTÁRû¾g⻩‹jø sF¢àJøFl‘È•Xᓁà~*j¯ +(ÚÕ6-£¯÷GŠØy‚<Ç’.F‹Hœw(+)ÜÜâÈzÄäT§FߘãÏ;DmVœ3Àu@mÚüXÝü•3B¨òÌÁÛ<·ÃÜ z,Ì@õÅ·d2]ü8s÷IôÞ¯^Ç9¢u„~ëAŸï4«M? K]­ÅàPl@s_ p:°¬ZR”´›JC[CS.h‹ƒïËœ«Æ]–÷ó‚wR×k7X‰k›‘´ù¦=¡«‰¨¨Â')—71ó’c‡Ðúµ `é.{§p¹ój\Ž{1h{o±Ý=áUÊïGÖŒõ–-BÄm+AZX¶¡ ïHðæ¥JmÙ;…䡟ˆ¦ ° äšiÉg«$üMk5¤L“’çÊvïâï ,=f“"íἊ5ô¬x6{ɏžID0e¸vçmi'︧ºð9$ò¹÷*£’9ÿ ²TÔ…×>JV¥}Œ}$p[bÔ®*[jzS*8 ”·T›Í–ñUîƒwo$áè=LT™ç—~ô·¤ÈÚ$榍q‰„+´kFm)ž‹©i–ËqÞŠ‰à¶ü( ‚•§ •°ò·‡#5ª•µÊ﯅¡X¨šÁ*F#TXJÊ ušJVÍ&=iÄs1‚3•'fý§5Ñ<=[íÞ­ PÚ;ѱÌ_~Ä££8rÞ ²w;’hDT°>ÈG¬8Á²ÚzŽ®ò®qZcqJêäÞ-ö[ܘbň±çb“ж31²n×iƒðÕ;1¶þÉ ªX‰,ßqÏ$>•î íZ¥Z 1{ç൵+ƒÕµ¥°T$§K]á»Ûï*·¤tMI’ÂZbŽÕiÒ˜}bÓ0£ª5›¨ [5Ž^ÝœWøÂÝh° ¢OWun£¤5 a2Z.G2³YL]jåtì”ä ÁÓ‘%"©<Ôúʰsº UZvä‡ÄiÆÒM .÷V·™ø#kèýiíÌ–ª)µT[)BˆõÑ xB¾B€ÖT¨.¥~ð@VĶr#¸ü*åZNDŽH;âi ],©£öØpù(šºãö¼T.uCê•4@ÿ GÕÛ)Cx›®0ø#:ÏðFÒbR\(€€Ä®fã4Þ‰Fä¯HXƒÅ,†öEÑÔÜ]Öv²?tLÃvBY£ú6Êu5ÅAQ³1‘’¬x–HŒÐ‡ ^ ¸KwJôÖŽ5×CÚ¨vÜ«/B0$×k°=ðbÇ(Ï)w±A†Á† 11Í=èQšµ626ŒÜ/`G«µ<}—-Ö7KEHÈÉðóȤmݱû±·ø«Snmá=“䫚mݱŸ¡¶~ó·“äUóJæúòB|E LêŽy´jDÔ$G¢þÐñ7óR8ýÒ…Ç› WVe#·Ÿ p·Fx~•ݤF÷0Èÿ K¯æS<6’¡WШ; ´ÿ ¥Êø\Òuî†åÝ–VNœkÒ7oòX¨Á­Ø÷FÎÑä±g÷ÿ M~Çî=p,X´ ÝÌÚÅ‹’ÃjÖ.ØöÏñ qïQ¤ÓZE†° =6·]܈ s¸>v•Ž^Ý\wq9r‰Î\¸¡kURÒ$­*‹Nq?Þª*!sŠÆ:TU_u±T+øX¡ ®¹¡,ÄâÃBTsÜ$Ø›4m椴zÜK]’’›Pƒ @€#â˜`é¹=I‡fiV•Ôî“nRm+µFPOhÍ0B£ €+¬5c v•:P'ÒyÎ ‰V~‚Ó†ÖuókDoh$å\*ö%Ю=£«…aȼ½÷Û.-½VŒŠ¼'lyî±1¬3ó#ÞE¿ÔS¤gV£m›=§\û"—WU¤ÚǼÿ ÂnÁGŒÃ ‚õN D³õNÚíŒÕ;HôyÄÈ©P¹Ä{:?R‘Ô¨âF÷ø£bÅó® JS|‚R÷ivýáâ€Æé¡è³´IئÑT!§˜•ت‚¬â@q€wnïCWÄ@JU€ê¯m6]Ï:£âx'+ÒðXvÓ¦Úm=–´7œ $ì“B£~p%ÕŸUþ« N@¼üï~w˜ñø5®—'Ôe»¤5ã//€ž~‰Tþ›Å7•#¤× Íö pÄ$ùeåì*«ÓŠEØWEÈsßg ¦ûvžSsLpºÊW–âµEWöˬH; ™!CYõZ ÃÄf æ#1W. \uWâ\,\Çf j’<qTbên›Î[vxx£ë 'ö¨1›˜ÀM¼Pÿ H)ƒêêŒA7s,|F“ 꺸k³9Ìö*ç®;Ö!Ö$Eiž•¹ÒÚ†ýóéÝû¾ÕS®ó$’NÝäŸz¤5r¦ãÄÃD÷Üø!°ø‡Ô&@m™Ì^Ãä­d q5Lnÿ N;.6½·N|#ä"1Nƒx“ã<3('&ñßt  ~ªu”1Tb㫨9ê–›–bìd$ߣ=#ÕãÒmU¯eí$EFù5ýYô櫨æì™Ç—±ssM]·á¿0ÕåJRÓªîiƒ+O58ÖñªŠÒx" \µâá¨i’¤i —Ö ” M+M¤ë9‚‰A¦°Qõ¾ßøK~¼Ã‘g…Ö´~÷Ï[3GUœÒ½#…kàÔ®Ò”‰³·dWV‰IP‰Ú8u¹”E ÖqLj¾êÕCBš{A^Âß;–¨`¯¬ìö ˼ ×tìø.tƐm*n¨y4o&Àx¥n¦×î‡aupáÛj8¿m›è¶ã!o½;ß0y^ý×^EÑ¿ÒjzŒ­)vÚÑnÄL …^ªô× ‡—‚3k Îý­hï]içå–îÏ*÷ñþ»Ô CÒjøjÍznˆ´ ¹#b'Fô‹ ‰v¥'’à'T´ƒHýÍ%M‰ ƒ&ÆÇŒï1 ‘ –Þ ‰i¬s žR-Ÿ kЬá¬7:þ 0ŒÅÒÕ/aÙ¬ÃÝ#Úøœ ©aiVc‰. ¹¦ãµ” ›Yg¦›ÆÎýº°f³7ƒhá·¸­}&D9¡ÂsÉÙÞèŠõØàC™¨ñbFC|´Ü(ŸƒÚÒ-%»'a Ì¿)ËÇn¿úÿ ÞŽX…4ÊÅH^ôΑí@ù¹Eh¶“L8Çjù ¼ÎåVªóR©Ï5uà V4lZß®=€xÖŸ–ÑÈ ÷”¨°¾__yM1tÉ?uÆþIkÄgæ@þ[¢†°XÃJ£j·:nkÅ¢u ‘}âGzö­/IµèЬ¼48q¦F°ŽR¼=ûì{´¯RýicS ÕÛ íNtÍÙï£,w4rêì®»~x(©Uñ§#Ñ&œÕ¤>ÎåÍÓ9’Ö{9eV­[Öjâ²ãu]˜å2›qÑšÕJç0€sÄ|Êëè0튔bÁ>“{×_F`Ø©ºê:µä,v¤ðfc1±"«ÔÍän1#=· Âøv~H½ÐßA¾¿Ü€Óš]Õ; I¾÷ç‚Qi†î¹9ywÔKG˜áñ zQY—§ÃÕZ07§X‚ Áh;ÁM)iÌCH-¯T‘ë|A0{Ò½LÚ–TâÖkÜ’dÀ“rmm»”جPF³ÖcbE§T€ÒxKºû’Ó®7±²(\4ŽÃ¸Uu@j™yĵ;³µ!Á¢b.W¤=mõ´êµK k ¸K^ÜÛ#p*Ü14qkZç5ïë †°5Ï%ÍÛ<Õ¤×Ô¥ê†C Õ´¼ú$ƒÖ“”]Ù¬qÞÚ[4©ý!ûÏ—Áb쳐XµA¬â~`›Çr¸8ìùÝ䫦<>ä÷«?xs´ÇÑ /á;¹øüÊÈÙà{"@Žïzâ¬[âß‚ U_<ÇŸ½4èN˜ú61®qŠu ¦þF£»äJ_ˆÙÎ~ ÞAã–݄ϗrŠD;xTž‘ô`É«…suãO`?³à™ô Lý#Íc5öoæØ‚y´´÷«ZR§<&JÇ+éâô´€i!Àˆ0æAoàðLèÖ-2ŸõW.’t^–(KÁmHµV@xÜÇy®Ñø­â^:Ú3w· 7½¹°ñ¸â¹®:',«Mœ—n­Á+Ãbš LÈ‘ÄnRÓÅœ%¦²‰¨ùQ:¤f‚ "PÕtô¸…cæl…&˜Ú˜Ôkv‹ž+vŠ,=¢v­6—Xy*¥t£«<™:“aîϲ=¦6rO]XI¿Œ÷¤zÚ­›¶ 6÷”w\d ü~v®ˆÌk«^m<ÿ ¢‰Õ\)ùºŽ;… lîÙÅEŠ®cѾ@vnMÏ,¼“ñ•ŽBxðÃzãÇç%3ˆ"}Ù•Åî> BÉú;Ò]V+P˜F_´ßé> Øše|ï‡ÄOmFæÇ ãqÞ$/xÐx­z`ï9"œÜij‚!7.\Td…9M‡•iŽ‹¾‘50ÞŽn¥ß4ÉôO ¹*í^QêËÜÇÌ8=ާs‰'ÂëÙ«á%Pú[O †ÅP¯Vsް.‰,kc¶ ¬A9n˜XÎ-ÞšN["¹QÕ‰ƒMýÁߺXJæÍaLj¾×Ãmã¾ãÚ uñÒþåQô¦¥ /ÄUx:‚ÍÜ’ Đ©ØÝ3V¨‰ÕnÐ6ó*óúK­«…c ¯U òhsý­jóÔj#,ímŒRµ«lbïUTŒÑ8†Ä0œÏr`ð¡¬É Ї ë"À² ™ 6¥ f¶ ¢ÚoܱԷ-<Àî)†a¶ž'Ú»¨TXqØæ¶÷YÄHy˜9ÈIW­YÀuMFë ºÏ’AqÌ4·/Ú †ô'i$øä­=Ä Ý|öK×40è|È6p‘0§)o¥ctî§H+CA-“ xØ|ÐXАç l8íºð3Ø:³¤¬KX¯UÿÙ # -*- test-case-name: twisted.web.test.test_xmlrpc -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details. """ Tests for XML-RPC support in L{twisted.web.xmlrpc}. """ from __future__ import division, absolute_import from twisted.python.compat import nativeString, networkString, NativeStringIO from io import BytesIO import datetime from twisted.trial import unittest from twisted.web import xmlrpc from twisted.web.xmlrpc import XMLRPC, payloadTemplate, addIntrospection from twisted.web.xmlrpc import _QueryFactory, withRequest, xmlrpclib from twisted.web import server, client, http, static from twisted.internet import reactor, defer from twisted.internet.error import ConnectionDone from twisted.python import failure from twisted.python.reflect import namedModule from twisted.test.proto_helpers import MemoryReactor, EventLoggingObserver from twisted.web.test.test_web import DummyRequest from twisted.logger import (globalLogPublisher, FilteringLogObserver, LogLevelFilterPredicate, LogLevel) try: namedModule('twisted.internet.ssl') except ImportError: sslSkip = "OpenSSL not present" else: sslSkip = None class AsyncXMLRPCTests(unittest.TestCase): """ Tests for L{XMLRPC}'s support of Deferreds. """ def setUp(self): self.request = DummyRequest(['']) self.request.method = 'POST' self.request.content = NativeStringIO( payloadTemplate % ('async', xmlrpclib.dumps(()))) result = self.result = defer.Deferred() class AsyncResource(XMLRPC): def xmlrpc_async(self): return result self.resource = AsyncResource() def test_deferredResponse(self): """ If an L{XMLRPC} C{xmlrpc_*} method returns a L{defer.Deferred}, the response to the request is the result of that L{defer.Deferred}. """ self.resource.render(self.request) self.assertEqual(self.request.written, []) self.result.callback("result") resp = xmlrpclib.loads(b"".join(self.request.written)) self.assertEqual(resp, (('result',), None)) self.assertEqual(self.request.finished, 1) def test_interruptedDeferredResponse(self): """ While waiting for the L{Deferred} returned by an L{XMLRPC} C{xmlrpc_*} method to fire, the connection the request was issued over may close. If this happens, neither C{write} nor C{finish} is called on the request. """ self.resource.render(self.request) self.request.processingFailed( failure.Failure(ConnectionDone("Simulated"))) self.result.callback("result") self.assertEqual(self.request.written, []) self.assertEqual(self.request.finished, 0) class TestRuntimeError(RuntimeError): pass class TestValueError(ValueError): pass class Test(XMLRPC): # If you add xmlrpc_ methods to this class, go change test_listMethods # below. FAILURE = 666 NOT_FOUND = 23 SESSION_EXPIRED = 42 def xmlrpc_echo(self, arg): return arg # the doc string is part of the test def xmlrpc_add(self, a, b): """ This function add two numbers. """ return a + b xmlrpc_add.signature = [['int', 'int', 'int'], ['double', 'double', 'double']] # the doc string is part of the test def xmlrpc_pair(self, string, num): """ This function puts the two arguments in an array. """ return [string, num] xmlrpc_pair.signature = [['array', 'string', 'int']] # the doc string is part of the test def xmlrpc_defer(self, x): """Help for defer.""" return defer.succeed(x) def xmlrpc_deferFail(self): return defer.fail(TestValueError()) # don't add a doc string, it's part of the test def xmlrpc_fail(self): raise TestRuntimeError def xmlrpc_fault(self): return xmlrpc.Fault(12, "hello") def xmlrpc_deferFault(self): return defer.fail(xmlrpc.Fault(17, "hi")) def xmlrpc_snowman(self, payload): """ Used to test that we can pass Unicode. """ snowman = u"\u2603" if snowman != payload: return xmlrpc.Fault(13, "Payload not unicode snowman") return snowman def xmlrpc_complex(self): return {"a": ["b", "c", 12, []], "D": "foo"} def xmlrpc_dict(self, map, key): return map[key] xmlrpc_dict.help = 'Help for dict.' @withRequest def xmlrpc_withRequest(self, request, other): """ A method decorated with L{withRequest} which can be called by a test to verify that the request object really is passed as an argument. """ return ( # as a proof that request is a request request.method + # plus proof other arguments are still passed along ' ' + other) def lookupProcedure(self, procedurePath): try: return XMLRPC.lookupProcedure(self, procedurePath) except xmlrpc.NoSuchFunction: if procedurePath.startswith("SESSION"): raise xmlrpc.Fault(self.SESSION_EXPIRED, "Session non-existent/expired.") else: raise class TestLookupProcedure(XMLRPC): """ This is a resource which customizes procedure lookup to be used by the tests of support for this customization. """ def echo(self, x): return x def lookupProcedure(self, procedureName): """ Lookup a procedure from a fixed set of choices, either I{echo} or I{system.listeMethods}. """ if procedureName == 'echo': return self.echo raise xmlrpc.NoSuchFunction( self.NOT_FOUND, 'procedure %s not found' % (procedureName,)) class TestListProcedures(XMLRPC): """ This is a resource which customizes procedure enumeration to be used by the tests of support for this customization. """ def listProcedures(self): """ Return a list of a single method this resource will claim to support. """ return ['foo'] class TestAuthHeader(Test): """ This is used to get the header info so that we can test authentication. """ def __init__(self): Test.__init__(self) self.request = None def render(self, request): self.request = request return Test.render(self, request) def xmlrpc_authinfo(self): return self.request.getUser(), self.request.getPassword() class TestQueryProtocol(xmlrpc.QueryProtocol): """ QueryProtocol for tests that saves headers received and sent, inside the factory. """ def connectionMade(self): self.factory.transport = self.transport xmlrpc.QueryProtocol.connectionMade(self) def handleHeader(self, key, val): self.factory.headers[key.lower()] = val def sendHeader(self, key, val): """ Keep sent headers so we can inspect them later. """ self.factory.sent_headers[key.lower()] = val xmlrpc.QueryProtocol.sendHeader(self, key, val) class TestQueryFactory(xmlrpc._QueryFactory): """ QueryFactory using L{TestQueryProtocol} for saving headers. """ protocol = TestQueryProtocol def __init__(self, *args, **kwargs): self.headers = {} self.sent_headers = {} xmlrpc._QueryFactory.__init__(self, *args, **kwargs) class TestQueryFactoryCancel(xmlrpc._QueryFactory): """ QueryFactory that saves a reference to the L{twisted.internet.interfaces.IConnector} to test connection lost. """ def startedConnecting(self, connector): self.connector = connector class XMLRPCTests(unittest.TestCase): def setUp(self): self.p = reactor.listenTCP(0, server.Site(Test()), interface="127.0.0.1") self.port = self.p.getHost().port self.factories = [] def tearDown(self): self.factories = [] return self.p.stopListening() def queryFactory(self, *args, **kwargs): """ Specific queryFactory for proxy that uses our custom L{TestQueryFactory}, and save factories. """ factory = TestQueryFactory(*args, **kwargs) self.factories.append(factory) return factory def proxy(self, factory=None): """ Return a new xmlrpc.Proxy for the test site created in setUp(), using the given factory as the queryFactory, or self.queryFactory if no factory is provided. """ p = xmlrpc.Proxy(networkString("http://127.0.0.1:%d/" % self.port)) if factory is None: p.queryFactory = self.queryFactory else: p.queryFactory = factory return p def test_results(self): inputOutput = [ ("add", (2, 3), 5), ("defer", ("a",), "a"), ("dict", ({"a": 1}, "a"), 1), ("pair", ("a", 1), ["a", 1]), ("snowman", (u"\u2603"), u"\u2603"), ("complex", (), {"a": ["b", "c", 12, []], "D": "foo"})] dl = [] for meth, args, outp in inputOutput: d = self.proxy().callRemote(meth, *args) d.addCallback(self.assertEqual, outp) dl.append(d) return defer.DeferredList(dl, fireOnOneErrback=True) def test_headers(self): """ Verify that headers sent from the client side and the ones we get back from the server side are correct. """ d = self.proxy().callRemote("snowman", u"\u2603") def check_server_headers(ing): self.assertEqual( self.factories[0].headers[b'content-type'], b'text/xml; charset=utf-8') self.assertEqual( self.factories[0].headers[b'content-length'], b'129') def check_client_headers(ign): self.assertEqual( self.factories[0].sent_headers[b'user-agent'], b'Twisted/XMLRPClib') self.assertEqual( self.factories[0].sent_headers[b'content-type'], b'text/xml; charset=utf-8') self.assertEqual( self.factories[0].sent_headers[b'content-length'], b'155') d.addCallback(check_server_headers) d.addCallback(check_client_headers) return d def test_errors(self): """ Verify that for each way a method exposed via XML-RPC can fail, the correct 'Content-type' header is set in the response and that the client-side Deferred is errbacked with an appropriate C{Fault} instance. """ logObserver = EventLoggingObserver() filtered = FilteringLogObserver( logObserver, [LogLevelFilterPredicate(defaultLogLevel=LogLevel.critical)] ) globalLogPublisher.addObserver(filtered) self.addCleanup(lambda: globalLogPublisher.removeObserver(filtered)) dl = [] for code, methodName in [(666, "fail"), (666, "deferFail"), (12, "fault"), (23, "noSuchMethod"), (17, "deferFault"), (42, "SESSION_TEST")]: d = self.proxy().callRemote(methodName) d = self.assertFailure(d, xmlrpc.Fault) d.addCallback(lambda exc, code=code: self.assertEqual(exc.faultCode, code)) dl.append(d) d = defer.DeferredList(dl, fireOnOneErrback=True) def cb(ign): for factory in self.factories: self.assertEqual(factory.headers[b'content-type'], b'text/xml; charset=utf-8') self.assertEquals(2, len(logObserver)) f1 = logObserver[0]["log_failure"].value f2 = logObserver[1]["log_failure"].value if isinstance(f1, TestValueError): self.assertIsInstance(f2, TestRuntimeError) else: self.assertIsInstance(f1, TestRuntimeError) self.assertIsInstance(f2, TestValueError) self.flushLoggedErrors(TestRuntimeError, TestValueError) d.addCallback(cb) return d def test_cancel(self): """ A deferred from the Proxy can be cancelled, disconnecting the L{twisted.internet.interfaces.IConnector}. """ def factory(*args, **kw): factory.f = TestQueryFactoryCancel(*args, **kw) return factory.f d = self.proxy(factory).callRemote('add', 2, 3) self.assertNotEqual(factory.f.connector.state, "disconnected") d.cancel() self.assertEqual(factory.f.connector.state, "disconnected") d = self.assertFailure(d, defer.CancelledError) return d def test_errorGet(self): """ A classic GET on the xml server should return a NOT_ALLOWED. """ agent = client.Agent(reactor) d = agent.request(b"GET", networkString("http://127.0.0.1:%d/" % (self.port,))) def checkResponse(response): self.assertEqual(response.code, http.NOT_ALLOWED) d.addCallback(checkResponse) return d def test_errorXMLContent(self): """ Test that an invalid XML input returns an L{xmlrpc.Fault}. """ agent = client.Agent(reactor) d = agent.request( uri=networkString("http://127.0.0.1:%d/" % (self.port,)), method=b"POST", bodyProducer=client.FileBodyProducer(BytesIO(b"foo"))) d.addCallback(client.readBody) def cb(result): self.assertRaises(xmlrpc.Fault, xmlrpclib.loads, result) d.addCallback(cb) return d def test_datetimeRoundtrip(self): """ If an L{xmlrpclib.DateTime} is passed as an argument to an XML-RPC call and then returned by the server unmodified, the result should be equal to the original object. """ when = xmlrpclib.DateTime() d = self.proxy().callRemote("echo", when) d.addCallback(self.assertEqual, when) return d def test_doubleEncodingError(self): """ If it is not possible to encode a response to the request (for example, because L{xmlrpclib.dumps} raises an exception when encoding a L{Fault}) the exception which prevents the response from being generated is logged and the request object is finished anyway. """ logObserver = EventLoggingObserver() filtered = FilteringLogObserver( logObserver, [LogLevelFilterPredicate(defaultLogLevel=LogLevel.critical)] ) globalLogPublisher.addObserver(filtered) self.addCleanup(lambda: globalLogPublisher.removeObserver(filtered)) d = self.proxy().callRemote("echo", "") # *Now* break xmlrpclib.dumps. Hopefully the client already used it. def fakeDumps(*args, **kwargs): raise RuntimeError("Cannot encode anything at all!") self.patch(xmlrpclib, 'dumps', fakeDumps) # It doesn't matter how it fails, so long as it does. Also, it happens # to fail with an implementation detail exception right now, not # something suitable as part of a public interface. d = self.assertFailure(d, Exception) def cbFailed(ignored): # The fakeDumps exception should have been logged. self.assertEquals(1, len(logObserver)) self.assertIsInstance( logObserver[0]["log_failure"].value, RuntimeError ) self.assertEqual(len(self.flushLoggedErrors(RuntimeError)), 1) d.addCallback(cbFailed) return d def test_closeConnectionAfterRequest(self): """ The connection to the web server is closed when the request is done. """ d = self.proxy().callRemote('echo', '') def responseDone(ignored): [factory] = self.factories self.assertFalse(factory.transport.connected) self.assertTrue(factory.transport.disconnected) return d.addCallback(responseDone) def test_tcpTimeout(self): """ For I{HTTP} URIs, L{xmlrpc.Proxy.callRemote} passes the value it received for the C{connectTimeout} parameter as the C{timeout} argument to the underlying connectTCP call. """ reactor = MemoryReactor() proxy = xmlrpc.Proxy(b"http://127.0.0.1:69", connectTimeout=2.0, reactor=reactor) proxy.callRemote("someMethod") self.assertEqual(reactor.tcpClients[0][3], 2.0) def test_sslTimeout(self): """ For I{HTTPS} URIs, L{xmlrpc.Proxy.callRemote} passes the value it received for the C{connectTimeout} parameter as the C{timeout} argument to the underlying connectSSL call. """ reactor = MemoryReactor() proxy = xmlrpc.Proxy(b"https://127.0.0.1:69", connectTimeout=3.0, reactor=reactor) proxy.callRemote("someMethod") self.assertEqual(reactor.sslClients[0][4], 3.0) test_sslTimeout.skip = sslSkip class XMLRPCProxyWithoutSlashTests(XMLRPCTests): """ Test with proxy that doesn't add a slash. """ def proxy(self, factory=None): p = xmlrpc.Proxy(networkString("http://127.0.0.1:%d" % self.port)) if factory is None: p.queryFactory = self.queryFactory else: p.queryFactory = factory return p class XMLRPCPublicLookupProcedureTests(unittest.TestCase): """ Tests for L{XMLRPC}'s support of subclasses which override C{lookupProcedure} and C{listProcedures}. """ def createServer(self, resource): self.p = reactor.listenTCP( 0, server.Site(resource), interface="127.0.0.1") self.addCleanup(self.p.stopListening) self.port = self.p.getHost().port self.proxy = xmlrpc.Proxy( networkString('http://127.0.0.1:%d' % self.port)) def test_lookupProcedure(self): """ A subclass of L{XMLRPC} can override C{lookupProcedure} to find procedures that are not defined using a C{xmlrpc_}-prefixed method name. """ self.createServer(TestLookupProcedure()) what = "hello" d = self.proxy.callRemote("echo", what) d.addCallback(self.assertEqual, what) return d def test_errors(self): """ A subclass of L{XMLRPC} can override C{lookupProcedure} to raise L{NoSuchFunction} to indicate that a requested method is not available to be called, signalling a fault to the XML-RPC client. """ self.createServer(TestLookupProcedure()) d = self.proxy.callRemote("xxxx", "hello") d = self.assertFailure(d, xmlrpc.Fault) return d def test_listMethods(self): """ A subclass of L{XMLRPC} can override C{listProcedures} to define Overriding listProcedures should prevent introspection from being broken. """ resource = TestListProcedures() addIntrospection(resource) self.createServer(resource) d = self.proxy.callRemote("system.listMethods") def listed(procedures): # The list will also include other introspection procedures added by # addIntrospection. We just want to see "foo" from our customized # listProcedures. self.assertIn('foo', procedures) d.addCallback(listed) return d class SerializationConfigMixin: """ Mixin which defines a couple tests which should pass when a particular flag is passed to L{XMLRPC}. These are not meant to be exhaustive serialization tests, since L{xmlrpclib} does all of the actual serialization work. They are just meant to exercise a few codepaths to make sure we are calling into xmlrpclib correctly. @ivar flagName: A C{str} giving the name of the flag which must be passed to L{XMLRPC} to allow the tests to pass. Subclasses should set this. @ivar value: A value which the specified flag will allow the serialization of. Subclasses should set this. """ def setUp(self): """ Create a new XML-RPC server with C{allowNone} set to C{True}. """ kwargs = {self.flagName: True} self.p = reactor.listenTCP( 0, server.Site(Test(**kwargs)), interface="127.0.0.1") self.addCleanup(self.p.stopListening) self.port = self.p.getHost().port self.proxy = xmlrpc.Proxy( networkString("http://127.0.0.1:%d/" % (self.port,)), **kwargs) def test_roundtripValue(self): """ C{self.value} can be round-tripped over an XMLRPC method call/response. """ d = self.proxy.callRemote('defer', self.value) d.addCallback(self.assertEqual, self.value) return d def test_roundtripNestedValue(self): """ A C{dict} which contains C{self.value} can be round-tripped over an XMLRPC method call/response. """ d = self.proxy.callRemote('defer', {'a': self.value}) d.addCallback(self.assertEqual, {'a': self.value}) return d class XMLRPCAllowNoneTests(SerializationConfigMixin, unittest.TestCase): """ Tests for passing L{None} when the C{allowNone} flag is set. """ flagName = "allowNone" value = None class XMLRPCUseDateTimeTests(SerializationConfigMixin, unittest.TestCase): """ Tests for passing a C{datetime.datetime} instance when the C{useDateTime} flag is set. """ flagName = "useDateTime" value = datetime.datetime(2000, 12, 28, 3, 45, 59) class XMLRPCAuthenticatedTests(XMLRPCTests): """ Test with authenticated proxy. We run this with the same input/output as above. """ user = b"username" password = b"asecret" def setUp(self): self.p = reactor.listenTCP(0, server.Site(TestAuthHeader()), interface="127.0.0.1") self.port = self.p.getHost().port self.factories = [] def test_authInfoInURL(self): url = "http://%s:%s@127.0.0.1:%d/" % ( nativeString(self.user), nativeString(self.password), self.port) p = xmlrpc.Proxy(networkString(url)) d = p.callRemote("authinfo") d.addCallback(self.assertEqual, [self.user, self.password]) return d def test_explicitAuthInfo(self): p = xmlrpc.Proxy(networkString("http://127.0.0.1:%d/" % ( self.port,)), self.user, self.password) d = p.callRemote("authinfo") d.addCallback(self.assertEqual, [self.user, self.password]) return d def test_longPassword(self): """ C{QueryProtocol} uses the C{base64.b64encode} function to encode user name and password in the I{Authorization} header, so that it doesn't embed new lines when using long inputs. """ longPassword = self.password * 40 p = xmlrpc.Proxy(networkString("http://127.0.0.1:%d/" % ( self.port,)), self.user, longPassword) d = p.callRemote("authinfo") d.addCallback(self.assertEqual, [self.user, longPassword]) return d def test_explicitAuthInfoOverride(self): p = xmlrpc.Proxy(networkString("http://wrong:info@127.0.0.1:%d/" % ( self.port,)), self.user, self.password) d = p.callRemote("authinfo") d.addCallback(self.assertEqual, [self.user, self.password]) return d class XMLRPCIntrospectionTests(XMLRPCTests): def setUp(self): xmlrpc = Test() addIntrospection(xmlrpc) self.p = reactor.listenTCP(0, server.Site(xmlrpc),interface="127.0.0.1") self.port = self.p.getHost().port self.factories = [] def test_listMethods(self): def cbMethods(meths): meths.sort() self.assertEqual( meths, ['add', 'complex', 'defer', 'deferFail', 'deferFault', 'dict', 'echo', 'fail', 'fault', 'pair', 'snowman', 'system.listMethods', 'system.methodHelp', 'system.methodSignature', 'withRequest']) d = self.proxy().callRemote("system.listMethods") d.addCallback(cbMethods) return d def test_methodHelp(self): inputOutputs = [ ("defer", "Help for defer."), ("fail", ""), ("dict", "Help for dict.")] dl = [] for meth, expected in inputOutputs: d = self.proxy().callRemote("system.methodHelp", meth) d.addCallback(self.assertEqual, expected) dl.append(d) return defer.DeferredList(dl, fireOnOneErrback=True) def test_methodSignature(self): inputOutputs = [ ("defer", ""), ("add", [['int', 'int', 'int'], ['double', 'double', 'double']]), ("pair", [['array', 'string', 'int']])] dl = [] for meth, expected in inputOutputs: d = self.proxy().callRemote("system.methodSignature", meth) d.addCallback(self.assertEqual, expected) dl.append(d) return defer.DeferredList(dl, fireOnOneErrback=True) class XMLRPCClientErrorHandlingTests(unittest.TestCase): """ Test error handling on the xmlrpc client. """ def setUp(self): self.resource = static.Data( b"This text is not a valid XML-RPC response.", b"text/plain") self.resource.isLeaf = True self.port = reactor.listenTCP(0, server.Site(self.resource), interface='127.0.0.1') def tearDown(self): return self.port.stopListening() def test_erroneousResponse(self): """ Test that calling the xmlrpc client on a static http server raises an exception. """ proxy = xmlrpc.Proxy(networkString("http://127.0.0.1:%d/" % (self.port.getHost().port,))) return self.assertFailure(proxy.callRemote("someMethod"), ValueError) class QueryFactoryParseResponseTests(unittest.TestCase): """ Test the behaviour of L{_QueryFactory.parseResponse}. """ def setUp(self): # The _QueryFactory that we are testing. We don't care about any # of the constructor parameters. self.queryFactory = _QueryFactory( path=None, host=None, method='POST', user=None, password=None, allowNone=False, args=()) # An XML-RPC response that will parse without raising an error. self.goodContents = xmlrpclib.dumps(('',)) # An 'XML-RPC response' that will raise a parsing error. self.badContents = 'invalid xml' # A dummy 'reason' to pass to clientConnectionLost. We don't care # what it is. self.reason = failure.Failure(ConnectionDone()) def test_parseResponseCallbackSafety(self): """ We can safely call L{_QueryFactory.clientConnectionLost} as a callback of L{_QueryFactory.parseResponse}. """ d = self.queryFactory.deferred # The failure mode is that this callback raises an AlreadyCalled # error. We have to add it now so that it gets called synchronously # and triggers the race condition. d.addCallback(self.queryFactory.clientConnectionLost, self.reason) self.queryFactory.parseResponse(self.goodContents) return d def test_parseResponseErrbackSafety(self): """ We can safely call L{_QueryFactory.clientConnectionLost} as an errback of L{_QueryFactory.parseResponse}. """ d = self.queryFactory.deferred # The failure mode is that this callback raises an AlreadyCalled # error. We have to add it now so that it gets called synchronously # and triggers the race condition. d.addErrback(self.queryFactory.clientConnectionLost, self.reason) self.queryFactory.parseResponse(self.badContents) return d def test_badStatusErrbackSafety(self): """ We can safely call L{_QueryFactory.clientConnectionLost} as an errback of L{_QueryFactory.badStatus}. """ d = self.queryFactory.deferred # The failure mode is that this callback raises an AlreadyCalled # error. We have to add it now so that it gets called synchronously # and triggers the race condition. d.addErrback(self.queryFactory.clientConnectionLost, self.reason) self.queryFactory.badStatus('status', 'message') return d def test_parseResponseWithoutData(self): """ Some server can send a response without any data: L{_QueryFactory.parseResponse} should catch the error and call the result errback. """ content = """ """ d = self.queryFactory.deferred self.queryFactory.parseResponse(content) return self.assertFailure(d, IndexError) class XMLRPCWithRequestTests(unittest.TestCase): def setUp(self): self.resource = Test() def test_withRequest(self): """ When an XML-RPC method is called and the implementation is decorated with L{withRequest}, the request object is passed as the first argument. """ request = DummyRequest('/RPC2') request.method = "POST" request.content = NativeStringIO(xmlrpclib.dumps( ("foo",), 'withRequest')) def valid(n, request): data = xmlrpclib.loads(request.written[0]) self.assertEqual(data, (('POST foo',), None)) d = request.notifyFinish().addCallback(valid, request) self.resource.render_POST(request) return d