Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

message.py 209KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000
  1. ###############################################################################
  2. #
  3. # The MIT License (MIT)
  4. #
  5. # Copyright (c) typedef int GmbH
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy
  8. # of this software and associated documentation files (the "Software"), to deal
  9. # in the Software without restriction, including without limitation the rights
  10. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. # copies of the Software, and to permit persons to whom the Software is
  12. # furnished to do so, subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in
  15. # all copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. # THE SOFTWARE.
  24. #
  25. ###############################################################################
  26. import re
  27. import binascii
  28. import textwrap
  29. from pprint import pformat
  30. from typing import Any, Dict, Optional
  31. import autobahn
  32. from autobahn.util import hlval
  33. from autobahn.wamp.exception import ProtocolError, InvalidUriError
  34. from autobahn.wamp.role import ROLE_NAME_TO_CLASS
  35. try:
  36. import cbor2
  37. import flatbuffers
  38. from autobahn.wamp import message_fbs
  39. except ImportError:
  40. _HAS_WAMP_FLATBUFFERS = False
  41. else:
  42. _HAS_WAMP_FLATBUFFERS = True
  43. __all__ = ('Message',
  44. 'Hello',
  45. 'Welcome',
  46. 'Abort',
  47. 'Challenge',
  48. 'Authenticate',
  49. 'Goodbye',
  50. 'Error',
  51. 'Publish',
  52. 'Published',
  53. 'Subscribe',
  54. 'Subscribed',
  55. 'Unsubscribe',
  56. 'Unsubscribed',
  57. 'Event',
  58. 'Call',
  59. 'Cancel',
  60. 'Result',
  61. 'Register',
  62. 'Registered',
  63. 'Unregister',
  64. 'Unregistered',
  65. 'Invocation',
  66. 'Interrupt',
  67. 'Yield',
  68. 'check_or_raise_uri',
  69. 'check_or_raise_realm_name',
  70. 'check_or_raise_id',
  71. 'check_or_raise_extra',
  72. 'is_valid_enc_algo',
  73. 'is_valid_enc_serializer',
  74. 'identify_realm_name_category',
  75. 'PAYLOAD_ENC_CRYPTO_BOX',
  76. 'PAYLOAD_ENC_MQTT',
  77. 'PAYLOAD_ENC_STANDARD_IDENTIFIERS')
  78. # all realm names in Autobahn/Crossbar.io must match this
  79. _URI_PAT_REALM_NAME = re.compile(r"^[A-Za-z][A-Za-z\d_\-@\.]{2,254}$")
  80. # if Ethereum addresses are enabled, realm names which are "0x" prefixed Ethereum addresses are also valid
  81. _URI_PAT_REALM_NAME_ETH = re.compile(r"^0x([A-Fa-f\d]{40})$")
  82. # realms names might also specifically match ENS URIs
  83. _URI_PAT_REALM_NAME_ENS = re.compile(r"^([a-z\d_\-@\.]{2,250})\.eth$")
  84. # since WAMP recommends using reverse dotted notation, reverse ENS names can be checked with this pattern
  85. _URI_PAT_REALM_NAME_ENS_REVERSE = re.compile(r"^eth\.([a-z\d_\-@\.]{2,250})$")
  86. # strict URI check allowing empty URI components
  87. _URI_PAT_STRICT_EMPTY = re.compile(r"^(([\da-z_]+\.)|\.)*([\da-z_]+)?$")
  88. # loose URI check allowing empty URI components
  89. _URI_PAT_LOOSE_EMPTY = re.compile(r"^(([^\s\.#]+\.)|\.)*([^\s\.#]+)?$")
  90. # strict URI check disallowing empty URI components
  91. _URI_PAT_STRICT_NON_EMPTY = re.compile(r"^([\da-z_]+\.)*([\da-z_]+)$")
  92. # loose URI check disallowing empty URI components
  93. _URI_PAT_LOOSE_NON_EMPTY = re.compile(r"^([^\s\.#]+\.)*([^\s\.#]+)$")
  94. # strict URI check disallowing empty URI components in all but the last component
  95. _URI_PAT_STRICT_LAST_EMPTY = re.compile(r"^([\da-z_]+\.)*([\da-z_]*)$")
  96. # loose URI check disallowing empty URI components in all but the last component
  97. _URI_PAT_LOOSE_LAST_EMPTY = re.compile(r"^([^\s\.#]+\.)*([^\s\.#]*)$")
  98. # custom (=implementation specific) WAMP attributes (used in WAMP message details/options)
  99. _CUSTOM_ATTRIBUTE = re.compile(r"^x_([a-z][\da-z_]+)?$")
  100. # Value for algo attribute in end-to-end encrypted messages using cryptobox, which
  101. # is a scheme based on Curve25519, SHA512, Salsa20 and Poly1305.
  102. # See: http://cr.yp.to/highspeed/coolnacl-20120725.pdf
  103. PAYLOAD_ENC_CRYPTO_BOX = 'cryptobox'
  104. # Payload transparency identifier for MQTT payloads (which are arbitrary binary).
  105. PAYLOAD_ENC_MQTT = 'mqtt'
  106. # Payload transparency identifier for XBR payloads
  107. PAYLOAD_ENC_XBR = 'xbr'
  108. # Payload transparency algorithm identifiers from the WAMP spec.
  109. PAYLOAD_ENC_STANDARD_IDENTIFIERS = [PAYLOAD_ENC_CRYPTO_BOX, PAYLOAD_ENC_MQTT, PAYLOAD_ENC_XBR]
  110. # Payload transparency serializer identifiers from the WAMP spec.
  111. PAYLOAD_ENC_STANDARD_SERIALIZERS = ['json', 'msgpack', 'cbor', 'ubjson', 'flatbuffers']
  112. ENC_ALGO_NONE = 0
  113. ENC_ALGO_CRYPTOBOX = 1
  114. ENC_ALGO_MQTT = 2
  115. ENC_ALGO_XBR = 3
  116. ENC_ALGOS = {
  117. ENC_ALGO_NONE: 'null',
  118. ENC_ALGO_CRYPTOBOX: 'cryptobox',
  119. ENC_ALGO_MQTT: 'mqtt',
  120. ENC_ALGO_XBR: 'xbr',
  121. }
  122. ENC_ALGOS_FROMSTR = {key: value for value, key in ENC_ALGOS.items()}
  123. ENC_SER_NONE = 0
  124. ENC_SER_JSON = 1
  125. ENC_SER_MSGPACK = 2
  126. ENC_SER_CBOR = 3
  127. ENC_SER_UBJSON = 4
  128. ENC_SER_OPAQUE = 5
  129. ENC_SER_FLATBUFFERS = 6
  130. ENC_SERS = {
  131. ENC_SER_NONE: 'null',
  132. ENC_SER_JSON: 'json',
  133. ENC_SER_MSGPACK: 'msgpack',
  134. ENC_SER_CBOR: 'cbor',
  135. ENC_SER_UBJSON: 'ubjson',
  136. ENC_SER_OPAQUE: 'opaque',
  137. ENC_SER_FLATBUFFERS: 'flatbuffers',
  138. }
  139. ENC_SERS_FROMSTR = {key: value for value, key in ENC_SERS.items()}
  140. def is_valid_enc_algo(enc_algo):
  141. """
  142. For WAMP payload transparency mode, check if the provided ``enc_algo``
  143. identifier in the WAMP message is a valid one.
  144. Currently defined standard identifiers are:
  145. * ``"cryptobox"``
  146. * ``"mqtt"``
  147. * ``"xbr"``
  148. Users can select arbitrary identifiers too, but these MUST start with ``"x_"``.
  149. :param enc_algo: The payload transparency algorithm identifier to check.
  150. :type enc_algo: str
  151. :returns: Returns ``True`` if and only if the payload transparency
  152. algorithm identifier is valid.
  153. :rtype: bool
  154. """
  155. return type(enc_algo) == str and (enc_algo in PAYLOAD_ENC_STANDARD_IDENTIFIERS or _CUSTOM_ATTRIBUTE.match(enc_algo))
  156. def is_valid_enc_serializer(enc_serializer):
  157. """
  158. For WAMP payload transparency mode, check if the provided ``enc_serializer``
  159. identifier in the WAMP message is a valid one.
  160. Currently, the only standard defined identifier are
  161. * ``"json"``
  162. * ``"msgpack"``
  163. * ``"cbor"``
  164. * ``"ubjson"``
  165. * ``"flatbuffers"``
  166. Users can select arbitrary identifiers too, but these MUST start with ``"x_"``.
  167. :param enc_serializer: The payload transparency serializer identifier to check.
  168. :type enc_serializer: str
  169. :returns: Returns ``True`` if and only if the payload transparency
  170. serializer identifier is valid.
  171. :rtype: bool
  172. """
  173. return type(enc_serializer) == str and (enc_serializer in PAYLOAD_ENC_STANDARD_SERIALIZERS or _CUSTOM_ATTRIBUTE.match(enc_serializer))
  174. def b2a(data, max_len=40):
  175. if type(data) == str:
  176. s = data
  177. elif type(data) == bytes:
  178. s = binascii.b2a_hex(data).decode('ascii')
  179. elif data is None:
  180. s = '-'
  181. else:
  182. s = '{}'.format(data)
  183. if len(s) > max_len:
  184. return s[:max_len] + '..'
  185. else:
  186. return s
  187. def identify_realm_name_category(value: Any) -> Optional[str]:
  188. """
  189. Identify the real name category of the given value:
  190. * ``"standalone"``: A normal, standalone WAMP realm name, e.g. ``"realm1"``.
  191. * ``"eth"``: An Ethereum address, e.g. ``"0xe59C7418403CF1D973485B36660728a5f4A8fF9c"``.
  192. * ``"ens"``: An Ethereum ENS name, e.g. ``"wamp-proto.eth"``.
  193. * ``"reverse_ens"``: An Ethereum ENS name in reverse notation, e.g. ``"eth.wamp-proto"``.
  194. * ``None``: The value is not a WAMP realm name.
  195. :param value: The value for which to identify realm name category.
  196. :return: The category identified, one of ``["standalone", "eth", "ens", "reverse-ens"]``
  197. or ``None``.
  198. """
  199. if type(value) != str:
  200. return None
  201. if _URI_PAT_REALM_NAME.match(value):
  202. if _URI_PAT_REALM_NAME_ENS.match(value):
  203. return 'ens'
  204. elif _URI_PAT_REALM_NAME_ENS_REVERSE.match(value):
  205. return 'reverse_ens'
  206. else:
  207. return 'standalone'
  208. elif _URI_PAT_REALM_NAME_ETH.match(value):
  209. return 'eth'
  210. else:
  211. return None
  212. def check_or_raise_uri(value: Any, message: str = "WAMP message invalid", strict: bool = False,
  213. allow_empty_components: bool = False, allow_last_empty: bool = False,
  214. allow_none: bool = False) -> str:
  215. """
  216. Check a value for being a valid WAMP URI.
  217. If the value is not a valid WAMP URI is invalid, raises :class:`autobahn.wamp.exception.InvalidUriError`,
  218. otherwise returns the value.
  219. :param value: The value to check.
  220. :param message: Prefix for message in exception raised when value is invalid.
  221. :param strict: If ``True``, do a strict check on the URI (the WAMP spec SHOULD behavior).
  222. :param allow_empty_components: If ``True``, allow empty URI components (for pattern based
  223. subscriptions and registrations).
  224. :param allow_last_empty: If ``True``, allow the last URI component to be empty (for prefix based
  225. subscriptions and registrations).
  226. :param allow_none: If ``True``, allow ``None`` for URIs.
  227. :returns: The URI value (if valid).
  228. :raises: instance of :class:`autobahn.wamp.exception.InvalidUriError`
  229. """
  230. if value is None:
  231. if allow_none:
  232. return
  233. else:
  234. raise InvalidUriError("{0}: URI cannot be null".format(message))
  235. if type(value) != str:
  236. if not (value is None and allow_none):
  237. raise InvalidUriError("{0}: invalid type {1} for URI".format(message, type(value)))
  238. if strict:
  239. if allow_last_empty:
  240. pat = _URI_PAT_STRICT_LAST_EMPTY
  241. elif allow_empty_components:
  242. pat = _URI_PAT_STRICT_EMPTY
  243. else:
  244. pat = _URI_PAT_STRICT_NON_EMPTY
  245. else:
  246. if allow_last_empty:
  247. pat = _URI_PAT_LOOSE_LAST_EMPTY
  248. elif allow_empty_components:
  249. pat = _URI_PAT_LOOSE_EMPTY
  250. else:
  251. pat = _URI_PAT_LOOSE_NON_EMPTY
  252. if not pat.match(value):
  253. raise InvalidUriError('{0}: invalid value "{1}" for URI (did not match pattern "{2}" with options strict={3}, allow_empty_components={4}, allow_last_empty={5}, allow_none={6})'.format(message, value, pat.pattern, strict, allow_empty_components, allow_last_empty, allow_none))
  254. else:
  255. return value
  256. def check_or_raise_realm_name(value, message="WAMP message invalid", allow_eth=True):
  257. """
  258. Check a value for being a valid WAMP URI.
  259. If the value is not a valid WAMP URI is invalid, raises :class:`autobahn.wamp.exception.InvalidUriError`,
  260. otherwise returns the value.
  261. :param value: The value to check, e.g. ``"realm1"`` or ``"com.example.myapp"`` or ``"eth.example"``.
  262. :param message: Prefix for message in exception raised when value is invalid.
  263. :param allow_eth: If ``True``, allow Ethereum addresses as realm names,
  264. e.g. ``"0xe59C7418403CF1D973485B36660728a5f4A8fF9c"``.
  265. :returns: The URI value (if valid).
  266. :raises: instance of :class:`autobahn.wamp.exception.InvalidUriError`
  267. """
  268. if value is None:
  269. raise InvalidUriError("{0}: realm name cannot be null".format(message))
  270. if type(value) != str:
  271. raise InvalidUriError("{0}: invalid type {1} for realm name".format(message, type(value)))
  272. if allow_eth:
  273. if _URI_PAT_REALM_NAME.match(value) or _URI_PAT_REALM_NAME_ETH.match(value):
  274. return value
  275. else:
  276. raise InvalidUriError(
  277. '{0}: invalid value "{1}" for realm name (did not match patterns '
  278. '"{2}" or "{3}")'.format(message, value,
  279. _URI_PAT_REALM_NAME.pattern,
  280. _URI_PAT_REALM_NAME_ETH.pattern))
  281. else:
  282. if _URI_PAT_REALM_NAME.match(value):
  283. return value
  284. else:
  285. raise InvalidUriError(
  286. '{0}: invalid value "{1}" for realm name (did not match pattern '
  287. '"{2}")'.format(message, value,
  288. _URI_PAT_REALM_NAME.pattern))
  289. def check_or_raise_id(value: Any, message: str = "WAMP message invalid") -> int:
  290. """
  291. Check a value for being a valid WAMP ID.
  292. If the value is not a valid WAMP ID, raises :class:`autobahn.wamp.exception.ProtocolError`,
  293. otherwise return the value.
  294. :param value: The value to check.
  295. :param message: Prefix for message in exception raised when value is invalid.
  296. :returns: The ID value (if valid).
  297. :raises: instance of :class:`autobahn.wamp.exception.ProtocolError`
  298. """
  299. if type(value) != int:
  300. raise ProtocolError("{0}: invalid type {1} for ID".format(message, type(value)))
  301. # the value 0 for WAMP IDs is possible in certain WAMP messages, e.g. UNREGISTERED with
  302. # router revocation signaling!
  303. if value < 0 or value > 9007199254740992: # 2**53
  304. raise ProtocolError("{0}: invalid value {1} for ID".format(message, value))
  305. return value
  306. def check_or_raise_extra(value: Any, message: str = "WAMP message invalid") -> Dict[str, Any]:
  307. """
  308. Check a value for being a valid WAMP extra dictionary.
  309. If the value is not a valid WAMP extra dictionary, raises :class:`autobahn.wamp.exception.ProtocolError`,
  310. otherwise return the value.
  311. :param value: The value to check.
  312. :param message: Prefix for message in exception raised when value is invalid.
  313. :returns: The extra dictionary (if valid).
  314. :raises: instance of :class:`autobahn.wamp.exception.ProtocolError`
  315. """
  316. if type(value) != dict:
  317. raise ProtocolError("{0}: invalid type {1} for WAMP extra".format(message, type(value)))
  318. for k in value.keys():
  319. if not isinstance(k, str):
  320. raise ProtocolError("{0}: invalid type {1} for key in WAMP extra ('{2}')".format(message, type(k), k))
  321. return value
  322. def _validate_kwargs(kwargs, message="WAMP message invalid"):
  323. """
  324. Check a value for being a valid WAMP kwargs dictionary.
  325. If the value is not a valid WAMP kwargs dictionary,
  326. raises :class:`autobahn.wamp.exception.ProtocolError`.
  327. Otherwise return the kwargs.
  328. The WAMP spec requires that the keys in kwargs are proper
  329. strings (unicode), not bytes. Note that the WAMP spec
  330. says nothing about keys in application payload. Key in the
  331. latter can be potentially of other type (if that is really
  332. wanted).
  333. :param kwargs: The keyword arguments to check.
  334. :type kwargs: dict
  335. :param message: Prefix for message in exception raised when
  336. value is invalid.
  337. :type message: str
  338. :returns: The kwargs dictionary (if valid).
  339. :rtype: dict
  340. :raises: instance of
  341. :class:`autobahn.wamp.exception.ProtocolError`
  342. """
  343. if kwargs is not None:
  344. if type(kwargs) != dict:
  345. raise ProtocolError("{0}: invalid type {1} for WAMP kwargs".format(message, type(kwargs)))
  346. for k in kwargs.keys():
  347. if not isinstance(k, str):
  348. raise ProtocolError("{0}: invalid type {1} for key in WAMP kwargs ('{2}')".format(message, type(k), k))
  349. return kwargs
  350. class Message(object):
  351. """
  352. WAMP message base class.
  353. .. note:: This is not supposed to be instantiated, but subclassed only.
  354. """
  355. MESSAGE_TYPE = None
  356. """
  357. WAMP message type code.
  358. """
  359. __slots__ = (
  360. '_from_fbs',
  361. '_serialized',
  362. '_correlation_id',
  363. '_correlation_uri',
  364. '_correlation_is_anchor',
  365. '_correlation_is_last',
  366. '_router_internal',
  367. )
  368. def __init__(self, from_fbs=None):
  369. # only filled in case this object has flatbuffers underlying
  370. self._from_fbs = from_fbs
  371. # serialization cache: mapping from ISerializer instances to serialized bytes
  372. self._serialized = {}
  373. # user attributes for message correlation (mainly for message tracing)
  374. self._correlation_id = None
  375. self._correlation_uri = None
  376. self._correlation_is_anchor = None
  377. self._correlation_is_last = None
  378. # non-serialized 'internal' attributes (used by Crossbar router)
  379. self._router_internal = None
  380. @property
  381. def correlation_id(self):
  382. return self._correlation_id
  383. @correlation_id.setter
  384. def correlation_id(self, value):
  385. assert(value is None or type(value) == str)
  386. self._correlation_id = value
  387. @property
  388. def correlation_uri(self):
  389. return self._correlation_uri
  390. @correlation_uri.setter
  391. def correlation_uri(self, value):
  392. assert(value is None or type(value) == str)
  393. self._correlation_uri = value
  394. @property
  395. def correlation_is_anchor(self):
  396. return self._correlation_is_anchor
  397. @correlation_is_anchor.setter
  398. def correlation_is_anchor(self, value):
  399. assert(value is None or type(value) == bool)
  400. self._correlation_is_anchor = value
  401. @property
  402. def correlation_is_last(self):
  403. return self._correlation_is_last
  404. @correlation_is_last.setter
  405. def correlation_is_last(self, value):
  406. assert(value is None or type(value) == bool)
  407. self._correlation_is_last = value
  408. def __eq__(self, other):
  409. """
  410. Compare this message to another message for equality.
  411. :param other: The other message to compare with.
  412. :type other: obj
  413. :returns: ``True`` iff the messages are equal.
  414. :rtype: bool
  415. """
  416. if not isinstance(other, self.__class__):
  417. return False
  418. # we only want the actual message data attributes (not eg _serialize)
  419. for k in self.__slots__:
  420. if k not in ['_serialized',
  421. '_correlation_id',
  422. '_correlation_uri',
  423. '_correlation_is_anchor',
  424. '_correlation_is_last'] and not k.startswith('_'):
  425. if not getattr(self, k) == getattr(other, k):
  426. return False
  427. return True
  428. def __ne__(self, other):
  429. """
  430. Compare this message to another message for inequality.
  431. :param other: The other message to compare with.
  432. :type other: obj
  433. :returns: ``True`` iff the messages are not equal.
  434. :rtype: bool
  435. """
  436. return not self.__eq__(other)
  437. def __str__(self) -> str:
  438. return '{}\n{}'.format(hlval(self.__class__.__name__.upper() + '::', color='blue', bold=True),
  439. hlval(textwrap.indent(pformat(self.marshal()), ' '), color='blue', bold=False))
  440. @staticmethod
  441. def parse(wmsg):
  442. """
  443. Factory method that parses a unserialized raw message (as returned byte
  444. :func:`autobahn.interfaces.ISerializer.unserialize`) into an instance
  445. of this class.
  446. :returns: An instance of this class.
  447. :rtype: obj
  448. """
  449. raise NotImplementedError()
  450. def marshal(self):
  451. raise NotImplementedError()
  452. @staticmethod
  453. def cast(buf):
  454. raise NotImplementedError()
  455. def build(self, builder):
  456. raise NotImplementedError()
  457. def uncache(self):
  458. """
  459. Resets the serialization cache.
  460. """
  461. self._serialized = {}
  462. def serialize(self, serializer):
  463. """
  464. Serialize this object into a wire level bytes representation and cache
  465. the resulting bytes. If the cache already contains an entry for the given
  466. serializer, return the cached representation directly.
  467. :param serializer: The wire level serializer to use.
  468. :type serializer: An instance that implements :class:`autobahn.interfaces.ISerializer`
  469. :returns: The serialized bytes.
  470. :rtype: bytes
  471. """
  472. # only serialize if not cached ..
  473. if serializer not in self._serialized:
  474. if serializer.NAME == 'flatbuffers':
  475. # flatbuffers get special treatment ..
  476. builder = flatbuffers.Builder(1024)
  477. # this is the core method writing out this message (self) to a (new) flatbuffer
  478. # FIXME: implement this method for all classes derived from Message
  479. obj = self.build(builder)
  480. builder.Finish(obj)
  481. buf = builder.Output()
  482. self._serialized[serializer] = bytes(buf)
  483. else:
  484. # all other serializers first marshal() the object and then serialize the latter
  485. self._serialized[serializer] = serializer.serialize(self.marshal())
  486. # cache is filled now: return serialized, cached bytes
  487. return self._serialized[serializer]
  488. class Hello(Message):
  489. """
  490. A WAMP ``HELLO`` message.
  491. Format: ``[HELLO, Realm|uri, Details|dict]``
  492. """
  493. MESSAGE_TYPE = 1
  494. """
  495. The WAMP message code for this type of message.
  496. """
  497. __slots__ = (
  498. 'realm',
  499. 'roles',
  500. 'authmethods',
  501. 'authid',
  502. 'authrole',
  503. 'authextra',
  504. 'resumable',
  505. 'resume_session',
  506. 'resume_token',
  507. )
  508. def __init__(self,
  509. realm,
  510. roles,
  511. authmethods=None,
  512. authid=None,
  513. authrole=None,
  514. authextra=None,
  515. resumable=None,
  516. resume_session=None,
  517. resume_token=None):
  518. """
  519. :param realm: The URI of the WAMP realm to join.
  520. :type realm: str
  521. :param roles: The WAMP session roles and features to announce.
  522. :type roles: dict of :class:`autobahn.wamp.role.RoleFeatures`
  523. :param authmethods: The authentication methods to announce.
  524. :type authmethods: list of str or None
  525. :param authid: The authentication ID to announce.
  526. :type authid: str or None
  527. :param authrole: The authentication role to announce.
  528. :type authrole: str or None
  529. :param authextra: Application-specific "extra data" to be forwarded to the client.
  530. :type authextra: dict or None
  531. :param resumable: Whether the client wants this to be a session that can be later resumed.
  532. :type resumable: bool or None
  533. :param resume_session: The session the client would like to resume.
  534. :type resume_session: int or None
  535. :param resume_token: The secure authorisation token to resume the session.
  536. :type resume_token: str or None
  537. """
  538. assert(realm is None or isinstance(realm, str))
  539. assert(type(roles) == dict)
  540. assert(len(roles) > 0)
  541. for role in roles:
  542. assert(role in ['subscriber', 'publisher', 'caller', 'callee'])
  543. assert(isinstance(roles[role], autobahn.wamp.role.ROLE_NAME_TO_CLASS[role]))
  544. if authmethods:
  545. assert(type(authmethods) == list)
  546. for authmethod in authmethods:
  547. assert(type(authmethod) == str)
  548. assert(authid is None or type(authid) == str)
  549. assert(authrole is None or type(authrole) == str)
  550. assert(authextra is None or type(authextra) == dict)
  551. assert(resumable is None or type(resumable) == bool)
  552. assert(resume_session is None or type(resume_session) == int)
  553. assert(resume_token is None or type(resume_token) == str)
  554. Message.__init__(self)
  555. self.realm = realm
  556. self.roles = roles
  557. self.authmethods = authmethods
  558. self.authid = authid
  559. self.authrole = authrole
  560. self.authextra = authextra
  561. self.resumable = resumable
  562. self.resume_session = resume_session
  563. self.resume_token = resume_token
  564. @staticmethod
  565. def parse(wmsg):
  566. """
  567. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  568. :param wmsg: The unserialized raw message.
  569. :type wmsg: list
  570. :returns: An instance of this class.
  571. """
  572. # this should already be verified by WampSerializer.unserialize
  573. assert(len(wmsg) > 0 and wmsg[0] == Hello.MESSAGE_TYPE)
  574. if len(wmsg) != 3:
  575. raise ProtocolError("invalid message length {0} for HELLO".format(len(wmsg)))
  576. realm = check_or_raise_uri(wmsg[1], "'realm' in HELLO", allow_none=True)
  577. details = check_or_raise_extra(wmsg[2], "'details' in HELLO")
  578. roles = {}
  579. if 'roles' not in details:
  580. raise ProtocolError("missing mandatory roles attribute in options in HELLO")
  581. details_roles = check_or_raise_extra(details['roles'], "'roles' in 'details' in HELLO")
  582. if len(details_roles) == 0:
  583. raise ProtocolError("empty 'roles' in 'details' in HELLO")
  584. for role in details_roles:
  585. if role not in ['subscriber', 'publisher', 'caller', 'callee']:
  586. raise ProtocolError("invalid role '{0}' in 'roles' in 'details' in HELLO".format(role))
  587. role_cls = ROLE_NAME_TO_CLASS[role]
  588. details_role = check_or_raise_extra(details_roles[role], "role '{0}' in 'roles' in 'details' in HELLO".format(role))
  589. if 'features' in details_role:
  590. check_or_raise_extra(details_role['features'], "'features' in role '{0}' in 'roles' in 'details' in HELLO".format(role))
  591. role_features = role_cls(**details_role['features'])
  592. else:
  593. role_features = role_cls()
  594. roles[role] = role_features
  595. authmethods = None
  596. if 'authmethods' in details:
  597. details_authmethods = details['authmethods']
  598. if type(details_authmethods) != list:
  599. raise ProtocolError("invalid type {0} for 'authmethods' detail in HELLO".format(type(details_authmethods)))
  600. for auth_method in details_authmethods:
  601. if type(auth_method) != str:
  602. raise ProtocolError("invalid type {0} for item in 'authmethods' detail in HELLO".format(type(auth_method)))
  603. authmethods = details_authmethods
  604. authid = None
  605. if 'authid' in details:
  606. details_authid = details['authid']
  607. if type(details_authid) != str:
  608. raise ProtocolError("invalid type {0} for 'authid' detail in HELLO".format(type(details_authid)))
  609. authid = details_authid
  610. authrole = None
  611. if 'authrole' in details:
  612. details_authrole = details['authrole']
  613. if type(details_authrole) != str:
  614. raise ProtocolError("invalid type {0} for 'authrole' detail in HELLO".format(type(details_authrole)))
  615. authrole = details_authrole
  616. authextra = None
  617. if 'authextra' in details:
  618. details_authextra = details['authextra']
  619. if type(details_authextra) != dict:
  620. raise ProtocolError("invalid type {0} for 'authextra' detail in HELLO".format(type(details_authextra)))
  621. authextra = details_authextra
  622. resumable = None
  623. if 'resumable' in details:
  624. resumable = details['resumable']
  625. if type(resumable) != bool:
  626. raise ProtocolError("invalid type {0} for 'resumable' detail in HELLO".format(type(resumable)))
  627. resume_session = None
  628. if 'resume-session' in details:
  629. resume_session = details['resume-session']
  630. if type(resume_session) != int:
  631. raise ProtocolError("invalid type {0} for 'resume-session' detail in HELLO".format(type(resume_session)))
  632. resume_token = None
  633. if 'resume-token' in details:
  634. resume_token = details['resume-token']
  635. if type(resume_token) != str:
  636. raise ProtocolError("invalid type {0} for 'resume-token' detail in HELLO".format(type(resume_token)))
  637. else:
  638. if resume_session:
  639. raise ProtocolError("resume-token must be provided if resume-session is provided in HELLO")
  640. obj = Hello(realm, roles, authmethods, authid, authrole, authextra, resumable, resume_session, resume_token)
  641. return obj
  642. def marshal(self):
  643. """
  644. Marshal this object into a raw message for subsequent serialization to bytes.
  645. :returns: The serialized raw message.
  646. :rtype: list
  647. """
  648. details = {'roles': {}}
  649. for role in self.roles.values():
  650. details['roles'][role.ROLE] = {}
  651. for feature in role.__dict__:
  652. if not feature.startswith('_') and feature != 'ROLE' and getattr(role, feature) is not None:
  653. if 'features' not in details['roles'][role.ROLE]:
  654. details['roles'][role.ROLE] = {'features': {}}
  655. details['roles'][role.ROLE]['features'][feature] = getattr(role, feature)
  656. if self.authmethods is not None:
  657. details['authmethods'] = self.authmethods
  658. if self.authid is not None:
  659. details['authid'] = self.authid
  660. if self.authrole is not None:
  661. details['authrole'] = self.authrole
  662. if self.authextra is not None:
  663. details['authextra'] = self.authextra
  664. if self.resumable is not None:
  665. details['resumable'] = self.resumable
  666. if self.resume_session is not None:
  667. details['resume-session'] = self.resume_session
  668. if self.resume_token is not None:
  669. details['resume-token'] = self.resume_token
  670. return [Hello.MESSAGE_TYPE, self.realm, details]
  671. class Welcome(Message):
  672. """
  673. A WAMP ``WELCOME`` message.
  674. Format: ``[WELCOME, Session|id, Details|dict]``
  675. """
  676. MESSAGE_TYPE = 2
  677. """
  678. The WAMP message code for this type of message.
  679. """
  680. __slots__ = (
  681. 'session',
  682. 'roles',
  683. 'realm',
  684. 'authid',
  685. 'authrole',
  686. 'authmethod',
  687. 'authprovider',
  688. 'authextra',
  689. 'resumed',
  690. 'resumable',
  691. 'resume_token',
  692. 'custom',
  693. )
  694. def __init__(self,
  695. session,
  696. roles,
  697. realm=None,
  698. authid=None,
  699. authrole=None,
  700. authmethod=None,
  701. authprovider=None,
  702. authextra=None,
  703. resumed=None,
  704. resumable=None,
  705. resume_token=None,
  706. custom=None):
  707. """
  708. :param session: The WAMP session ID the other peer is assigned.
  709. :type session: int
  710. :param roles: The WAMP roles to announce.
  711. :type roles: dict of :class:`autobahn.wamp.role.RoleFeatures`
  712. :param realm: The effective realm the session is joined on.
  713. :type realm: str or None
  714. :param authid: The authentication ID assigned.
  715. :type authid: str or None
  716. :param authrole: The authentication role assigned.
  717. :type authrole: str or None
  718. :param authmethod: The authentication method in use.
  719. :type authmethod: str or None
  720. :param authprovider: The authentication provided in use.
  721. :type authprovider: str or None
  722. :param authextra: Application-specific "extra data" to be forwarded to the client.
  723. :type authextra: arbitrary or None
  724. :param resumed: Whether the session is a resumed one.
  725. :type resumed: bool or None
  726. :param resumable: Whether this session can be resumed later.
  727. :type resumable: bool or None
  728. :param resume_token: The secure authorisation token to resume the session.
  729. :type resume_token: str or None
  730. :param custom: Implementation-specific "custom attributes" (`x_my_impl_attribute`) to be set.
  731. :type custom: dict or None
  732. """
  733. assert(type(session) == int)
  734. assert(type(roles) == dict)
  735. assert(len(roles) > 0)
  736. for role in roles:
  737. assert(role in ['broker', 'dealer'])
  738. assert(isinstance(roles[role], autobahn.wamp.role.ROLE_NAME_TO_CLASS[role]))
  739. assert(realm is None or type(realm) == str)
  740. assert(authid is None or type(authid) == str)
  741. assert(authrole is None or type(authrole) == str)
  742. assert(authmethod is None or type(authmethod) == str)
  743. assert(authprovider is None or type(authprovider) == str)
  744. assert(authextra is None or type(authextra) == dict)
  745. assert(resumed is None or type(resumed) == bool)
  746. assert(resumable is None or type(resumable) == bool)
  747. assert(resume_token is None or type(resume_token) == str)
  748. assert(custom is None or type(custom) == dict)
  749. if custom:
  750. for k in custom:
  751. assert(_CUSTOM_ATTRIBUTE.match(k))
  752. Message.__init__(self)
  753. self.session = session
  754. self.roles = roles
  755. self.realm = realm
  756. self.authid = authid
  757. self.authrole = authrole
  758. self.authmethod = authmethod
  759. self.authprovider = authprovider
  760. self.authextra = authextra
  761. self.resumed = resumed
  762. self.resumable = resumable
  763. self.resume_token = resume_token
  764. self.custom = custom or {}
  765. @staticmethod
  766. def parse(wmsg):
  767. """
  768. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  769. :param wmsg: The unserialized raw message.
  770. :type wmsg: list
  771. :returns: An instance of this class.
  772. """
  773. # this should already be verified by WampSerializer.unserialize
  774. assert(len(wmsg) > 0 and wmsg[0] == Welcome.MESSAGE_TYPE)
  775. if len(wmsg) != 3:
  776. raise ProtocolError("invalid message length {0} for WELCOME".format(len(wmsg)))
  777. session = check_or_raise_id(wmsg[1], "'session' in WELCOME")
  778. details = check_or_raise_extra(wmsg[2], "'details' in WELCOME")
  779. # FIXME: tigher value checking (types, URIs etc)
  780. realm = details.get('realm', None)
  781. authid = details.get('authid', None)
  782. authrole = details.get('authrole', None)
  783. authmethod = details.get('authmethod', None)
  784. authprovider = details.get('authprovider', None)
  785. authextra = details.get('authextra', None)
  786. resumed = None
  787. if 'resumed' in details:
  788. resumed = details['resumed']
  789. if not type(resumed) == bool:
  790. raise ProtocolError("invalid type {0} for 'resumed' detail in WELCOME".format(type(resumed)))
  791. resumable = None
  792. if 'resumable' in details:
  793. resumable = details['resumable']
  794. if not type(resumable) == bool:
  795. raise ProtocolError("invalid type {0} for 'resumable' detail in WELCOME".format(type(resumable)))
  796. resume_token = None
  797. if 'resume_token' in details:
  798. resume_token = details['resume_token']
  799. if not type(resume_token) == str:
  800. raise ProtocolError("invalid type {0} for 'resume_token' detail in WELCOME".format(type(resume_token)))
  801. elif resumable:
  802. raise ProtocolError("resume_token required when resumable is given in WELCOME")
  803. roles = {}
  804. if 'roles' not in details:
  805. raise ProtocolError("missing mandatory roles attribute in options in WELCOME")
  806. details_roles = check_or_raise_extra(details['roles'], "'roles' in 'details' in WELCOME")
  807. if len(details_roles) == 0:
  808. raise ProtocolError("empty 'roles' in 'details' in WELCOME")
  809. for role in details_roles:
  810. if role not in ['broker', 'dealer']:
  811. raise ProtocolError("invalid role '{0}' in 'roles' in 'details' in WELCOME".format(role))
  812. role_cls = ROLE_NAME_TO_CLASS[role]
  813. details_role = check_or_raise_extra(details_roles[role], "role '{0}' in 'roles' in 'details' in WELCOME".format(role))
  814. if 'features' in details_role:
  815. check_or_raise_extra(details_role['features'], "'features' in role '{0}' in 'roles' in 'details' in WELCOME".format(role))
  816. role_features = role_cls(**details_roles[role]['features'])
  817. else:
  818. role_features = role_cls()
  819. roles[role] = role_features
  820. custom = {}
  821. for k in details:
  822. if _CUSTOM_ATTRIBUTE.match(k):
  823. custom[k] = details[k]
  824. obj = Welcome(session, roles, realm, authid, authrole, authmethod, authprovider, authextra, resumed, resumable, resume_token, custom)
  825. return obj
  826. def marshal(self):
  827. """
  828. Marshal this object into a raw message for subsequent serialization to bytes.
  829. :returns: The serialized raw message.
  830. :rtype: list
  831. """
  832. details = {}
  833. details.update(self.custom)
  834. if self.realm:
  835. details['realm'] = self.realm
  836. if self.authid:
  837. details['authid'] = self.authid
  838. if self.authrole:
  839. details['authrole'] = self.authrole
  840. if self.authrole:
  841. details['authmethod'] = self.authmethod
  842. if self.authprovider:
  843. details['authprovider'] = self.authprovider
  844. if self.authextra:
  845. details['authextra'] = self.authextra
  846. if self.resumed:
  847. details['resumed'] = self.resumed
  848. if self.resumable:
  849. details['resumable'] = self.resumable
  850. if self.resume_token:
  851. details['resume_token'] = self.resume_token
  852. details['roles'] = {}
  853. for role in self.roles.values():
  854. details['roles'][role.ROLE] = {}
  855. for feature in role.__dict__:
  856. if not feature.startswith('_') and feature != 'ROLE' and getattr(role, feature) is not None:
  857. if 'features' not in details['roles'][role.ROLE]:
  858. details['roles'][role.ROLE] = {'features': {}}
  859. details['roles'][role.ROLE]['features'][feature] = getattr(role, feature)
  860. return [Welcome.MESSAGE_TYPE, self.session, details]
  861. class Abort(Message):
  862. """
  863. A WAMP ``ABORT`` message.
  864. Format: ``[ABORT, Details|dict, Reason|uri]``
  865. """
  866. MESSAGE_TYPE = 3
  867. """
  868. The WAMP message code for this type of message.
  869. """
  870. __slots__ = (
  871. 'reason',
  872. 'message',
  873. )
  874. def __init__(self, reason, message=None):
  875. """
  876. :param reason: WAMP or application error URI for aborting reason.
  877. :type reason: str
  878. :param message: Optional human-readable closing message, e.g. for logging purposes.
  879. :type message: str or None
  880. """
  881. assert(type(reason) == str)
  882. assert(message is None or type(message) == str)
  883. Message.__init__(self)
  884. self.reason = reason
  885. self.message = message
  886. @staticmethod
  887. def parse(wmsg):
  888. """
  889. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  890. :param wmsg: The unserialized raw message.
  891. :type wmsg: list
  892. :returns: An instance of this class.
  893. """
  894. # this should already be verified by WampSerializer.unserialize
  895. assert(len(wmsg) > 0 and wmsg[0] == Abort.MESSAGE_TYPE)
  896. if len(wmsg) != 3:
  897. raise ProtocolError("invalid message length {0} for ABORT".format(len(wmsg)))
  898. details = check_or_raise_extra(wmsg[1], "'details' in ABORT")
  899. reason = check_or_raise_uri(wmsg[2], "'reason' in ABORT")
  900. message = None
  901. if 'message' in details:
  902. details_message = details['message']
  903. if type(details_message) != str:
  904. raise ProtocolError("invalid type {0} for 'message' detail in ABORT".format(type(details_message)))
  905. message = details_message
  906. obj = Abort(reason, message)
  907. return obj
  908. def marshal(self):
  909. """
  910. Marshal this object into a raw message for subsequent serialization to bytes.
  911. :returns: The serialized raw message.
  912. :rtype: list
  913. """
  914. details = {}
  915. if self.message:
  916. details['message'] = self.message
  917. return [Abort.MESSAGE_TYPE, details, self.reason]
  918. class Challenge(Message):
  919. """
  920. A WAMP ``CHALLENGE`` message.
  921. Format: ``[CHALLENGE, Method|string, Extra|dict]``
  922. """
  923. MESSAGE_TYPE = 4
  924. """
  925. The WAMP message code for this type of message.
  926. """
  927. __slots__ = (
  928. 'method',
  929. 'extra',
  930. )
  931. def __init__(self, method, extra=None):
  932. """
  933. :param method: The authentication method.
  934. :type method: str
  935. :param extra: Authentication method specific information.
  936. :type extra: dict or None
  937. """
  938. assert(type(method) == str)
  939. assert(extra is None or type(extra) == dict)
  940. Message.__init__(self)
  941. self.method = method
  942. self.extra = extra or {}
  943. @staticmethod
  944. def parse(wmsg):
  945. """
  946. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  947. :param wmsg: The unserialized raw message.
  948. :type wmsg: list
  949. :returns: An instance of this class.
  950. """
  951. # this should already be verified by WampSerializer.unserialize
  952. assert(len(wmsg) > 0 and wmsg[0] == Challenge.MESSAGE_TYPE)
  953. if len(wmsg) != 3:
  954. raise ProtocolError("invalid message length {0} for CHALLENGE".format(len(wmsg)))
  955. method = wmsg[1]
  956. if type(method) != str:
  957. raise ProtocolError("invalid type {0} for 'method' in CHALLENGE".format(type(method)))
  958. extra = check_or_raise_extra(wmsg[2], "'extra' in CHALLENGE")
  959. obj = Challenge(method, extra)
  960. return obj
  961. def marshal(self):
  962. """
  963. Marshal this object into a raw message for subsequent serialization to bytes.
  964. :returns: The serialized raw message.
  965. :rtype: list
  966. """
  967. return [Challenge.MESSAGE_TYPE, self.method, self.extra]
  968. class Authenticate(Message):
  969. """
  970. A WAMP ``AUTHENTICATE`` message.
  971. Format: ``[AUTHENTICATE, Signature|string, Extra|dict]``
  972. """
  973. MESSAGE_TYPE = 5
  974. """
  975. The WAMP message code for this type of message.
  976. """
  977. __slots__ = (
  978. 'signature',
  979. 'extra',
  980. )
  981. def __init__(self, signature, extra=None):
  982. """
  983. :param signature: The signature for the authentication challenge.
  984. :type signature: str
  985. :param extra: Authentication method specific information.
  986. :type extra: dict or None
  987. """
  988. assert(type(signature) == str)
  989. assert(extra is None or type(extra) == dict)
  990. Message.__init__(self)
  991. self.signature = signature
  992. self.extra = extra or {}
  993. @staticmethod
  994. def parse(wmsg):
  995. """
  996. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  997. :param wmsg: The unserialized raw message.
  998. :type wmsg: list
  999. :returns: An instance of this class.
  1000. """
  1001. # this should already be verified by WampSerializer.unserialize
  1002. assert(len(wmsg) > 0 and wmsg[0] == Authenticate.MESSAGE_TYPE)
  1003. if len(wmsg) != 3:
  1004. raise ProtocolError("invalid message length {0} for AUTHENTICATE".format(len(wmsg)))
  1005. signature = wmsg[1]
  1006. if type(signature) != str:
  1007. raise ProtocolError("invalid type {0} for 'signature' in AUTHENTICATE".format(type(signature)))
  1008. extra = check_or_raise_extra(wmsg[2], "'extra' in AUTHENTICATE")
  1009. obj = Authenticate(signature, extra)
  1010. return obj
  1011. def marshal(self):
  1012. """
  1013. Marshal this object into a raw message for subsequent serialization to bytes.
  1014. :returns: The serialized raw message.
  1015. :rtype: list
  1016. """
  1017. return [Authenticate.MESSAGE_TYPE, self.signature, self.extra]
  1018. class Goodbye(Message):
  1019. """
  1020. A WAMP ``GOODBYE`` message.
  1021. Format: ``[GOODBYE, Details|dict, Reason|uri]``
  1022. """
  1023. MESSAGE_TYPE = 6
  1024. """
  1025. The WAMP message code for this type of message.
  1026. """
  1027. DEFAULT_REASON = "wamp.close.normal"
  1028. """
  1029. Default WAMP closing reason.
  1030. """
  1031. __slots__ = (
  1032. 'reason',
  1033. 'message',
  1034. 'resumable',
  1035. )
  1036. def __init__(self, reason=DEFAULT_REASON, message=None, resumable=None):
  1037. """
  1038. :param reason: Optional WAMP or application error URI for closing reason.
  1039. :type reason: str
  1040. :param message: Optional human-readable closing message, e.g. for logging purposes.
  1041. :type message: str or None
  1042. :param resumable: From the server: Whether the session is able to be resumed (true) or destroyed (false). From the client: Whether it should be resumable (true) or destroyed (false).
  1043. :type resumable: bool or None
  1044. """
  1045. assert(type(reason) == str)
  1046. assert(message is None or type(message) == str)
  1047. assert(resumable is None or type(resumable) == bool)
  1048. Message.__init__(self)
  1049. self.reason = reason
  1050. self.message = message
  1051. self.resumable = resumable
  1052. @staticmethod
  1053. def parse(wmsg):
  1054. """
  1055. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  1056. :param wmsg: The unserialized raw message.
  1057. :type wmsg: list
  1058. :returns: An instance of this class.
  1059. """
  1060. # this should already be verified by WampSerializer.unserialize
  1061. assert(len(wmsg) > 0 and wmsg[0] == Goodbye.MESSAGE_TYPE)
  1062. if len(wmsg) != 3:
  1063. raise ProtocolError("invalid message length {0} for GOODBYE".format(len(wmsg)))
  1064. details = check_or_raise_extra(wmsg[1], "'details' in GOODBYE")
  1065. reason = check_or_raise_uri(wmsg[2], "'reason' in GOODBYE")
  1066. message = None
  1067. resumable = None
  1068. if 'message' in details:
  1069. details_message = details['message']
  1070. if type(details_message) != str:
  1071. raise ProtocolError("invalid type {0} for 'message' detail in GOODBYE".format(type(details_message)))
  1072. message = details_message
  1073. if 'resumable' in details:
  1074. resumable = details['resumable']
  1075. if type(resumable) != bool:
  1076. raise ProtocolError("invalid type {0} for 'resumable' detail in GOODBYE".format(type(resumable)))
  1077. obj = Goodbye(reason=reason,
  1078. message=message,
  1079. resumable=resumable)
  1080. return obj
  1081. def marshal(self):
  1082. """
  1083. Marshal this object into a raw message for subsequent serialization to bytes.
  1084. :returns: The serialized raw message.
  1085. :rtype: list
  1086. """
  1087. details = {}
  1088. if self.message:
  1089. details['message'] = self.message
  1090. if self.resumable:
  1091. details['resumable'] = self.resumable
  1092. return [Goodbye.MESSAGE_TYPE, details, self.reason]
  1093. class Error(Message):
  1094. """
  1095. A WAMP ``ERROR`` message.
  1096. Formats:
  1097. * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri]``
  1098. * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list]``
  1099. * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Arguments|list, ArgumentsKw|dict]``
  1100. * ``[ERROR, REQUEST.Type|int, REQUEST.Request|id, Details|dict, Error|uri, Payload|binary]``
  1101. """
  1102. MESSAGE_TYPE = 8
  1103. """
  1104. The WAMP message code for this type of message.
  1105. """
  1106. __slots__ = (
  1107. 'request_type',
  1108. 'request',
  1109. 'error',
  1110. 'args',
  1111. 'kwargs',
  1112. 'payload',
  1113. 'enc_algo',
  1114. 'enc_key',
  1115. 'enc_serializer',
  1116. 'callee',
  1117. 'callee_authid',
  1118. 'callee_authrole',
  1119. 'forward_for',
  1120. )
  1121. def __init__(self,
  1122. request_type,
  1123. request,
  1124. error,
  1125. args=None,
  1126. kwargs=None,
  1127. payload=None,
  1128. enc_algo=None,
  1129. enc_key=None,
  1130. enc_serializer=None,
  1131. callee=None,
  1132. callee_authid=None,
  1133. callee_authrole=None,
  1134. forward_for=None):
  1135. """
  1136. :param request_type: The WAMP message type code for the original request.
  1137. :type request_type: int
  1138. :param request: The WAMP request ID of the original request (`Call`, `Subscribe`, ...) this error occurred for.
  1139. :type request: int
  1140. :param error: The WAMP or application error URI for the error that occurred.
  1141. :type error: str
  1142. :param args: Positional values for application-defined exception.
  1143. Must be serializable using any serializers in use.
  1144. :type args: list or None
  1145. :param kwargs: Keyword values for application-defined exception.
  1146. Must be serializable using any serializers in use.
  1147. :type kwargs: dict or None
  1148. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  1149. :type payload: bytes or None
  1150. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  1151. :type enc_algo: str or None
  1152. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  1153. :type enc_key: str or None
  1154. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  1155. :type enc_serializer: str or None
  1156. :param callee: The WAMP session ID of the effective callee that responded with the error. Only filled if callee is disclosed.
  1157. :type callee: None or int
  1158. :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed.
  1159. :type callee_authid: None or unicode
  1160. :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed.
  1161. :type callee_authrole: None or unicode
  1162. :param forward_for: When this Error is forwarded for a client/callee (or from an intermediary router).
  1163. :type forward_for: list[dict]
  1164. """
  1165. assert(type(request_type) == int)
  1166. assert(type(request) == int)
  1167. assert(type(error) == str)
  1168. assert(args is None or type(args) in [list, tuple])
  1169. assert(kwargs is None or type(kwargs) == dict)
  1170. assert(payload is None or type(payload) == bytes)
  1171. assert(payload is None or (payload is not None and args is None and kwargs is None))
  1172. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  1173. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  1174. assert(enc_key is None or type(enc_key) == str)
  1175. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  1176. assert(callee is None or type(callee) == int)
  1177. assert(callee_authid is None or type(callee_authid) == str)
  1178. assert(callee_authrole is None or type(callee_authrole) == str)
  1179. assert(forward_for is None or type(forward_for) == list)
  1180. if forward_for:
  1181. for ff in forward_for:
  1182. assert type(ff) == dict
  1183. assert 'session' in ff and type(ff['session']) == int
  1184. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  1185. assert 'authrole' in ff and type(ff['authrole']) == str
  1186. Message.__init__(self)
  1187. self.request_type = request_type
  1188. self.request = request
  1189. self.error = error
  1190. self.args = args
  1191. self.kwargs = _validate_kwargs(kwargs)
  1192. self.payload = payload
  1193. # payload transparency related knobs
  1194. self.enc_algo = enc_algo
  1195. self.enc_key = enc_key
  1196. self.enc_serializer = enc_serializer
  1197. # effective callee that responded with the error
  1198. self.callee = callee
  1199. self.callee_authid = callee_authid
  1200. self.callee_authrole = callee_authrole
  1201. # message forwarding
  1202. self.forward_for = forward_for
  1203. @staticmethod
  1204. def parse(wmsg):
  1205. """
  1206. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  1207. :param wmsg: The unserialized raw message.
  1208. :type wmsg: list
  1209. :returns: An instance of this class.
  1210. """
  1211. # this should already be verified by WampSerializer.unserialize
  1212. assert(len(wmsg) > 0 and wmsg[0] == Error.MESSAGE_TYPE)
  1213. if len(wmsg) not in (5, 6, 7):
  1214. raise ProtocolError("invalid message length {0} for ERROR".format(len(wmsg)))
  1215. request_type = wmsg[1]
  1216. if type(request_type) != int:
  1217. raise ProtocolError("invalid type {0} for 'request_type' in ERROR".format(request_type))
  1218. if request_type not in [Subscribe.MESSAGE_TYPE,
  1219. Unsubscribe.MESSAGE_TYPE,
  1220. Publish.MESSAGE_TYPE,
  1221. Register.MESSAGE_TYPE,
  1222. Unregister.MESSAGE_TYPE,
  1223. Call.MESSAGE_TYPE,
  1224. Invocation.MESSAGE_TYPE]:
  1225. raise ProtocolError("invalid value {0} for 'request_type' in ERROR".format(request_type))
  1226. request = check_or_raise_id(wmsg[2], "'request' in ERROR")
  1227. details = check_or_raise_extra(wmsg[3], "'details' in ERROR")
  1228. error = check_or_raise_uri(wmsg[4], "'error' in ERROR")
  1229. args = None
  1230. kwargs = None
  1231. payload = None
  1232. enc_algo = None
  1233. enc_key = None
  1234. enc_serializer = None
  1235. callee = None
  1236. callee_authid = None
  1237. callee_authrole = None
  1238. forward_for = None
  1239. if len(wmsg) == 6 and type(wmsg[5]) == bytes:
  1240. payload = wmsg[5]
  1241. enc_algo = details.get('enc_algo', None)
  1242. if enc_algo and not is_valid_enc_algo(enc_algo):
  1243. raise ProtocolError("invalid value {0} for 'enc_algo' detail in EVENT".format(enc_algo))
  1244. enc_key = details.get('enc_key', None)
  1245. if enc_key and type(enc_key) != str:
  1246. raise ProtocolError("invalid type {0} for 'enc_key' detail in EVENT".format(type(enc_key)))
  1247. enc_serializer = details.get('enc_serializer', None)
  1248. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  1249. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in EVENT".format(enc_serializer))
  1250. else:
  1251. if len(wmsg) > 5:
  1252. args = wmsg[5]
  1253. if args is not None and type(args) != list:
  1254. raise ProtocolError("invalid type {0} for 'args' in ERROR".format(type(args)))
  1255. if len(wmsg) > 6:
  1256. kwargs = wmsg[6]
  1257. if type(kwargs) != dict:
  1258. raise ProtocolError("invalid type {0} for 'kwargs' in ERROR".format(type(kwargs)))
  1259. if 'callee' in details:
  1260. detail_callee = details['callee']
  1261. if type(detail_callee) != int:
  1262. raise ProtocolError("invalid type {0} for 'callee' detail in ERROR".format(type(detail_callee)))
  1263. callee = detail_callee
  1264. if 'callee_authid' in details:
  1265. detail_callee_authid = details['callee_authid']
  1266. if type(detail_callee_authid) != str:
  1267. raise ProtocolError("invalid type {0} for 'callee_authid' detail in ERROR".format(type(detail_callee_authid)))
  1268. callee_authid = detail_callee_authid
  1269. if 'callee_authrole' in details:
  1270. detail_callee_authrole = details['callee_authrole']
  1271. if type(detail_callee_authrole) != str:
  1272. raise ProtocolError("invalid type {0} for 'callee_authrole' detail in ERROR".format(type(detail_callee_authrole)))
  1273. callee_authrole = detail_callee_authrole
  1274. if 'forward_for' in details:
  1275. forward_for = details['forward_for']
  1276. valid = False
  1277. if type(forward_for) == list:
  1278. for ff in forward_for:
  1279. if type(ff) != dict:
  1280. break
  1281. if 'session' not in ff or type(ff['session']) != int:
  1282. break
  1283. if 'authid' not in ff or type(ff['authid']) != str:
  1284. break
  1285. if 'authrole' not in ff or type(ff['authrole']) != str:
  1286. break
  1287. valid = True
  1288. if not valid:
  1289. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in ERROR")
  1290. obj = Error(request_type,
  1291. request,
  1292. error,
  1293. args=args,
  1294. kwargs=kwargs,
  1295. payload=payload,
  1296. enc_algo=enc_algo,
  1297. enc_key=enc_key,
  1298. enc_serializer=enc_serializer,
  1299. callee=callee,
  1300. callee_authid=callee_authid,
  1301. callee_authrole=callee_authrole,
  1302. forward_for=forward_for)
  1303. return obj
  1304. def marshal(self):
  1305. """
  1306. Marshal this object into a raw message for subsequent serialization to bytes.
  1307. :returns: The serialized raw message.
  1308. :rtype: list
  1309. """
  1310. details = {}
  1311. if self.callee is not None:
  1312. details['callee'] = self.callee
  1313. if self.callee_authid is not None:
  1314. details['callee_authid'] = self.callee_authid
  1315. if self.callee_authrole is not None:
  1316. details['callee_authrole'] = self.callee_authrole
  1317. if self.forward_for is not None:
  1318. details['forward_for'] = self.forward_for
  1319. if self.payload:
  1320. if self.enc_algo is not None:
  1321. details['enc_algo'] = self.enc_algo
  1322. if self.enc_key is not None:
  1323. details['enc_key'] = self.enc_key
  1324. if self.enc_serializer is not None:
  1325. details['enc_serializer'] = self.enc_serializer
  1326. return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.payload]
  1327. else:
  1328. if self.kwargs:
  1329. return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.args, self.kwargs]
  1330. elif self.args:
  1331. return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error, self.args]
  1332. else:
  1333. return [self.MESSAGE_TYPE, self.request_type, self.request, details, self.error]
  1334. class Publish(Message):
  1335. """
  1336. A WAMP ``PUBLISH`` message.
  1337. Formats:
  1338. * ``[PUBLISH, Request|id, Options|dict, Topic|uri]``
  1339. * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list]``
  1340. * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Arguments|list, ArgumentsKw|dict]``
  1341. * ``[PUBLISH, Request|id, Options|dict, Topic|uri, Payload|binary]``
  1342. """
  1343. MESSAGE_TYPE = 16
  1344. """
  1345. The WAMP message code for this type of message.
  1346. """
  1347. __slots__ = (
  1348. # uint64 (key)
  1349. '_request',
  1350. # string (required, uri)
  1351. '_topic',
  1352. # [uint8]
  1353. '_args',
  1354. # [uint8]
  1355. '_kwargs',
  1356. # [uint8]
  1357. '_payload',
  1358. # Payload => uint8
  1359. '_enc_algo',
  1360. # Serializer => uint8
  1361. '_enc_serializer',
  1362. # [uint8]
  1363. '_enc_key',
  1364. # bool
  1365. '_acknowledge',
  1366. # bool
  1367. '_exclude_me',
  1368. # [uint64]
  1369. '_exclude',
  1370. # [string] (principal)
  1371. '_exclude_authid',
  1372. # [string] (principal)
  1373. '_exclude_authrole',
  1374. # [uint64]
  1375. '_eligible',
  1376. # [string] (principal)
  1377. '_eligible_authid',
  1378. # [string] (principal)
  1379. '_eligible_authrole',
  1380. # bool
  1381. '_retain',
  1382. # string
  1383. '_transaction_hash',
  1384. # [Principal]
  1385. '_forward_for',
  1386. )
  1387. def __init__(self,
  1388. request=None,
  1389. topic=None,
  1390. args=None,
  1391. kwargs=None,
  1392. payload=None,
  1393. acknowledge=None,
  1394. exclude_me=None,
  1395. exclude=None,
  1396. exclude_authid=None,
  1397. exclude_authrole=None,
  1398. eligible=None,
  1399. eligible_authid=None,
  1400. eligible_authrole=None,
  1401. retain=None,
  1402. transaction_hash=None,
  1403. enc_algo=None,
  1404. enc_key=None,
  1405. enc_serializer=None,
  1406. forward_for=None,
  1407. from_fbs=None):
  1408. """
  1409. :param request: The WAMP request ID of this request.
  1410. :type request: int
  1411. :param topic: The WAMP or application URI of the PubSub topic the event should
  1412. be published to.
  1413. :type topic: str
  1414. :param args: Positional values for application-defined event payload.
  1415. Must be serializable using any serializers in use.
  1416. :type args: list or tuple or None
  1417. :param kwargs: Keyword values for application-defined event payload.
  1418. Must be serializable using any serializers in use.
  1419. :type kwargs: dict or None
  1420. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  1421. :type payload: bytes or None
  1422. :param acknowledge: If True, acknowledge the publication with a success or
  1423. error response.
  1424. :type acknowledge: bool or None
  1425. :param exclude_me: If ``True``, exclude the publisher from receiving the event, even
  1426. if he is subscribed (and eligible).
  1427. :type exclude_me: bool or None
  1428. :param exclude: List of WAMP session IDs to exclude from receiving this event.
  1429. :type exclude: list of int or None
  1430. :param exclude_authid: List of WAMP authids to exclude from receiving this event.
  1431. :type exclude_authid: list of str or None
  1432. :param exclude_authrole: List of WAMP authroles to exclude from receiving this event.
  1433. :type exclude_authrole: list of str or None
  1434. :param eligible: List of WAMP session IDs eligible to receive this event.
  1435. :type eligible: list of int or None
  1436. :param eligible_authid: List of WAMP authids eligible to receive this event.
  1437. :type eligible_authid: list of str or None
  1438. :param eligible_authrole: List of WAMP authroles eligible to receive this event.
  1439. :type eligible_authrole: list of str or None
  1440. :param retain: If ``True``, request the broker retain this event.
  1441. :type retain: bool or None
  1442. :param transaction_hash: An application provided transaction hash for the published event, which may
  1443. be used in the router to throttle or deduplicate the events on the topic. See the discussion
  1444. `here <https://github.com/wamp-proto/wamp-proto/issues/391#issuecomment-998577967>`_.
  1445. :type transaction_hash: str
  1446. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  1447. :type enc_algo: str or None
  1448. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  1449. :type enc_key: str or None
  1450. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  1451. :type enc_serializer: str or None or None
  1452. :param forward_for: When this Call is forwarded for a client (or from an intermediary router).
  1453. :type forward_for: list[dict]
  1454. """
  1455. assert(request is None or type(request) == int)
  1456. assert(topic is None or type(topic) == str)
  1457. assert(args is None or type(args) in [list, tuple, str, bytes])
  1458. assert(kwargs is None or type(kwargs) in [dict, str, bytes])
  1459. assert(payload is None or type(payload) == bytes)
  1460. assert(payload is None or (payload is not None and args is None and kwargs is None))
  1461. assert(acknowledge is None or type(acknowledge) == bool)
  1462. assert(retain is None or type(retain) == bool)
  1463. assert(transaction_hash is None or type(transaction_hash) == str)
  1464. # publisher exlusion and black-/whitelisting
  1465. assert(exclude_me is None or type(exclude_me) == bool)
  1466. assert(exclude is None or type(exclude) == list)
  1467. if exclude:
  1468. for sessionid in exclude:
  1469. assert(type(sessionid) == int)
  1470. assert(exclude_authid is None or type(exclude_authid) == list)
  1471. if exclude_authid:
  1472. for authid in exclude_authid:
  1473. assert(type(authid) == str)
  1474. assert(exclude_authrole is None or type(exclude_authrole) == list)
  1475. if exclude_authrole:
  1476. for authrole in exclude_authrole:
  1477. assert(type(authrole) == str)
  1478. assert(eligible is None or type(eligible) == list)
  1479. if eligible:
  1480. for sessionid in eligible:
  1481. assert(type(sessionid) == int)
  1482. assert(eligible_authid is None or type(eligible_authid) == list)
  1483. if eligible_authid:
  1484. for authid in eligible_authid:
  1485. assert(type(authid) == str)
  1486. assert(eligible_authrole is None or type(eligible_authrole) == list)
  1487. if eligible_authrole:
  1488. for authrole in eligible_authrole:
  1489. assert(type(authrole) == str)
  1490. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  1491. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  1492. assert(enc_key is None or type(enc_key) == str)
  1493. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  1494. assert(forward_for is None or type(forward_for) == list)
  1495. if forward_for:
  1496. for ff in forward_for:
  1497. assert type(ff) == dict
  1498. assert 'session' in ff and type(ff['session']) == int
  1499. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  1500. assert 'authrole' in ff and type(ff['authrole']) == str
  1501. Message.__init__(self, from_fbs=from_fbs)
  1502. self._request = request
  1503. self._topic = topic
  1504. self._args = args
  1505. self._kwargs = _validate_kwargs(kwargs)
  1506. self._payload = payload
  1507. self._acknowledge = acknowledge
  1508. # publisher exlusion and black-/whitelisting
  1509. self._exclude_me = exclude_me
  1510. self._exclude = exclude
  1511. self._exclude_authid = exclude_authid
  1512. self._exclude_authrole = exclude_authrole
  1513. self._eligible = eligible
  1514. self._eligible_authid = eligible_authid
  1515. self._eligible_authrole = eligible_authrole
  1516. # event retention
  1517. self._retain = retain
  1518. # application provided transaction hash for event
  1519. self._transaction_hash = transaction_hash
  1520. # payload transparency related knobs
  1521. self._enc_algo = enc_algo
  1522. self._enc_key = enc_key
  1523. self._enc_serializer = enc_serializer
  1524. # message forwarding
  1525. self._forward_for = forward_for
  1526. def __eq__(self, other):
  1527. if not isinstance(other, self.__class__):
  1528. return False
  1529. if not Message.__eq__(self, other):
  1530. return False
  1531. if other.request != self.request:
  1532. return False
  1533. if other.topic != self.topic:
  1534. return False
  1535. if other.args != self.args:
  1536. return False
  1537. if other.kwargs != self.kwargs:
  1538. return False
  1539. if other.payload != self.payload:
  1540. return False
  1541. if other.acknowledge != self.acknowledge:
  1542. return False
  1543. if other.exclude_me != self.exclude_me:
  1544. return False
  1545. if other.exclude != self.exclude:
  1546. return False
  1547. if other.exclude_authid != self.exclude_authid:
  1548. return False
  1549. if other.exclude_authrole != self.exclude_authrole:
  1550. return False
  1551. if other.eligible != self.eligible:
  1552. return False
  1553. if other.eligible_authid != self.eligible_authid:
  1554. return False
  1555. if other.eligible_authrole != self.eligible_authrole:
  1556. return False
  1557. if other.retain != self.retain:
  1558. return False
  1559. if other.transaction_hash != self.transaction_hash:
  1560. return False
  1561. if other.enc_algo != self.enc_algo:
  1562. return False
  1563. if other.enc_key != self.enc_key:
  1564. return False
  1565. if other.enc_serializer != self.enc_serializer:
  1566. return False
  1567. if other.forward_for != self.forward_for:
  1568. return False
  1569. return True
  1570. def __ne__(self, other):
  1571. return not self.__eq__(other)
  1572. @property
  1573. def request(self):
  1574. if self._request is None and self._from_fbs:
  1575. self._request = self._from_fbs.Request()
  1576. return self._request
  1577. @request.setter
  1578. def request(self, value):
  1579. assert(value is None or type(value) == int)
  1580. self._request = value
  1581. @property
  1582. def topic(self):
  1583. if self._topic is None and self._from_fbs:
  1584. s = self._from_fbs.Topic()
  1585. if s:
  1586. self._topic = s.decode('utf8')
  1587. return self._topic
  1588. @topic.setter
  1589. def topic(self, value):
  1590. assert value is None or type(value) == str
  1591. self._topic = value
  1592. @property
  1593. def args(self):
  1594. if self._args is None and self._from_fbs:
  1595. if self._from_fbs.ArgsLength():
  1596. self._args = cbor2.loads(bytes(self._from_fbs.ArgsAsBytes()))
  1597. return self._args
  1598. @args.setter
  1599. def args(self, value):
  1600. assert(value is None or type(value) in [list, tuple])
  1601. self._args = value
  1602. @property
  1603. def kwargs(self):
  1604. if self._kwargs is None and self._from_fbs:
  1605. if self._from_fbs.KwargsLength():
  1606. self._kwargs = cbor2.loads(bytes(self._from_fbs.KwargsAsBytes()))
  1607. return self._kwargs
  1608. @kwargs.setter
  1609. def kwargs(self, value):
  1610. assert(value is None or type(value) == dict)
  1611. self._kwargs = value
  1612. @property
  1613. def payload(self):
  1614. if self._payload is None and self._from_fbs:
  1615. if self._from_fbs.PayloadLength():
  1616. self._payload = self._from_fbs.PayloadAsBytes()
  1617. return self._payload
  1618. @payload.setter
  1619. def payload(self, value):
  1620. assert value is None or type(value) == bytes
  1621. self._payload = value
  1622. @property
  1623. def acknowledge(self):
  1624. if self._acknowledge is None and self._from_fbs:
  1625. acknowledge = self._from_fbs.Acknowledge()
  1626. if acknowledge:
  1627. self._acknowledge = acknowledge
  1628. return self._acknowledge
  1629. @acknowledge.setter
  1630. def acknowledge(self, value):
  1631. assert value is None or type(value) == bool
  1632. self._acknowledge = value
  1633. @property
  1634. def exclude_me(self):
  1635. if self._exclude_me is None and self._from_fbs:
  1636. exclude_me = self._from_fbs.ExcludeMe()
  1637. if exclude_me is False:
  1638. self._exclude_me = exclude_me
  1639. return self._exclude_me
  1640. @exclude_me.setter
  1641. def exclude_me(self, value):
  1642. assert value is None or type(value) == bool
  1643. self._exclude_me = value
  1644. @property
  1645. def exclude(self):
  1646. if self._exclude is None and self._from_fbs:
  1647. if self._from_fbs.ExcludeLength():
  1648. exclude = []
  1649. for j in range(self._from_fbs.ExcludeLength()):
  1650. exclude.append(self._from_fbs.Exclude(j))
  1651. self._exclude = exclude
  1652. return self._exclude
  1653. @exclude.setter
  1654. def exclude(self, value):
  1655. assert value is None or type(value) == list
  1656. if value:
  1657. for x in value:
  1658. assert type(x) == int
  1659. self._exclude = value
  1660. @property
  1661. def exclude_authid(self):
  1662. if self._exclude_authid is None and self._from_fbs:
  1663. if self._from_fbs.ExcludeAuthidLength():
  1664. exclude_authid = []
  1665. for j in range(self._from_fbs.ExcludeAuthidLength()):
  1666. exclude_authid.append(self._from_fbs.ExcludeAuthid(j).decode('utf8'))
  1667. self._exclude_authid = exclude_authid
  1668. return self._exclude_authid
  1669. @exclude_authid.setter
  1670. def exclude_authid(self, value):
  1671. assert value is None or type(value) == list
  1672. if value:
  1673. for x in value:
  1674. assert type(x) == str
  1675. self._exclude_authid = value
  1676. @property
  1677. def exclude_authrole(self):
  1678. if self._exclude_authrole is None and self._from_fbs:
  1679. if self._from_fbs.ExcludeAuthroleLength():
  1680. exclude_authrole = []
  1681. for j in range(self._from_fbs.ExcludeAuthroleLength()):
  1682. exclude_authrole.append(self._from_fbs.ExcludeAuthrole(j).decode('utf8'))
  1683. self._exclude_authrole = exclude_authrole
  1684. return self._exclude_authrole
  1685. @exclude_authrole.setter
  1686. def exclude_authrole(self, value):
  1687. assert value is None or type(value) == list
  1688. if value:
  1689. for x in value:
  1690. assert type(x) == str
  1691. self._exclude_authrole = value
  1692. @property
  1693. def eligible(self):
  1694. if self._eligible is None and self._from_fbs:
  1695. if self._from_fbs.EligibleLength():
  1696. eligible = []
  1697. for j in range(self._from_fbs.EligibleLength()):
  1698. eligible.append(self._from_fbs.Eligible(j))
  1699. self._eligible = eligible
  1700. return self._eligible
  1701. @eligible.setter
  1702. def eligible(self, value):
  1703. assert value is None or type(value) == list
  1704. if value:
  1705. for x in value:
  1706. assert type(x) == int
  1707. self._eligible = value
  1708. @property
  1709. def eligible_authid(self):
  1710. if self._eligible_authid is None and self._from_fbs:
  1711. if self._from_fbs.EligibleAuthidLength():
  1712. eligible_authid = []
  1713. for j in range(self._from_fbs.EligibleAuthidLength()):
  1714. eligible_authid.append(self._from_fbs.EligibleAuthid(j).decode('utf8'))
  1715. self._eligible_authid = eligible_authid
  1716. return self._eligible_authid
  1717. @eligible_authid.setter
  1718. def eligible_authid(self, value):
  1719. assert value is None or type(value) == list
  1720. if value:
  1721. for x in value:
  1722. assert type(x) == str
  1723. self._eligible_authid = value
  1724. @property
  1725. def eligible_authrole(self):
  1726. if self._eligible_authrole is None and self._from_fbs:
  1727. if self._from_fbs.EligibleAuthroleLength():
  1728. eligible_authrole = []
  1729. for j in range(self._from_fbs.EligibleAuthroleLength()):
  1730. eligible_authrole.append(self._from_fbs.EligibleAuthrole(j).decode('utf8'))
  1731. self._eligible_authrole = eligible_authrole
  1732. return self._eligible_authrole
  1733. @eligible_authrole.setter
  1734. def eligible_authrole(self, value):
  1735. assert value is None or type(value) == list
  1736. if value:
  1737. for x in value:
  1738. assert type(x) == str
  1739. self._eligible_authrole = value
  1740. @property
  1741. def retain(self):
  1742. if self._retain is None and self._from_fbs:
  1743. retain = self._from_fbs.Retain()
  1744. if retain:
  1745. self._retain = retain
  1746. return self._retain
  1747. @retain.setter
  1748. def retain(self, value):
  1749. assert value is None or type(value) == bool
  1750. self._retain = value
  1751. @property
  1752. def transaction_hash(self):
  1753. if self._transaction_hash is None and self._from_fbs:
  1754. s = self._from_fbs.TransactionHash()
  1755. if s:
  1756. self._transaction_hash = s.decode('utf8')
  1757. return self._transaction_hash
  1758. @transaction_hash.setter
  1759. def transaction_hash(self, value):
  1760. assert value is None or type(value) == str
  1761. self._transaction_hash = value
  1762. @property
  1763. def enc_algo(self):
  1764. if self._enc_algo is None and self._from_fbs:
  1765. enc_algo = self._from_fbs.EncAlgo()
  1766. if enc_algo:
  1767. self._enc_algo = enc_algo
  1768. return self._enc_algo
  1769. @enc_algo.setter
  1770. def enc_algo(self, value):
  1771. assert value is None or value in [ENC_ALGO_CRYPTOBOX, ENC_ALGO_MQTT, ENC_ALGO_XBR]
  1772. self._enc_algo = value
  1773. @property
  1774. def enc_key(self):
  1775. if self._enc_key is None and self._from_fbs:
  1776. if self._from_fbs.EncKeyLength():
  1777. self._enc_key = self._from_fbs.EncKeyAsBytes()
  1778. return self._enc_key
  1779. @enc_key.setter
  1780. def enc_key(self, value):
  1781. assert value is None or type(value) == bytes
  1782. self._enc_key = value
  1783. @property
  1784. def enc_serializer(self):
  1785. if self._enc_serializer is None and self._from_fbs:
  1786. enc_serializer = self._from_fbs.EncSerializer()
  1787. if enc_serializer:
  1788. self._enc_serializer = enc_serializer
  1789. return self._enc_serializer
  1790. @enc_serializer.setter
  1791. def enc_serializer(self, value):
  1792. assert value is None or value in [ENC_SER_JSON, ENC_SER_MSGPACK, ENC_SER_CBOR, ENC_SER_UBJSON]
  1793. self._enc_serializer = value
  1794. @property
  1795. def forward_for(self):
  1796. # FIXME
  1797. return self._forward_for
  1798. @forward_for.setter
  1799. def forward_for(self, value):
  1800. # FIXME
  1801. self._forward_for = value
  1802. @staticmethod
  1803. def cast(buf):
  1804. return Publish(from_fbs=message_fbs.Publish.GetRootAsPublish(buf, 0))
  1805. def build(self, builder):
  1806. args = self.args
  1807. if args:
  1808. args = builder.CreateByteVector(cbor2.dumps(args))
  1809. kwargs = self.kwargs
  1810. if kwargs:
  1811. kwargs = builder.CreateByteVector(cbor2.dumps(kwargs))
  1812. payload = self.payload
  1813. if payload:
  1814. payload = builder.CreateByteVector(payload)
  1815. topic = self.topic
  1816. if topic:
  1817. topic = builder.CreateString(topic)
  1818. transaction_hash = self.transaction_hash
  1819. if transaction_hash:
  1820. transaction_hash = builder.CreateString(transaction_hash)
  1821. enc_key = self.enc_key
  1822. if enc_key:
  1823. enc_key = builder.CreateByteVector(enc_key)
  1824. # exclude: [int]
  1825. exclude = self.exclude
  1826. if exclude:
  1827. message_fbs.PublishGen.PublishStartExcludeAuthidVector(builder, len(exclude))
  1828. for session in reversed(exclude):
  1829. builder.PrependUint64(session)
  1830. exclude = builder.EndVector(len(exclude))
  1831. # exclude_authid: [string]
  1832. exclude_authid = self.exclude_authid
  1833. if exclude_authid:
  1834. _exclude_authid = []
  1835. for authid in exclude_authid:
  1836. _exclude_authid.append(builder.CreateString(authid))
  1837. message_fbs.PublishGen.PublishStartExcludeAuthidVector(builder, len(_exclude_authid))
  1838. for o in reversed(_exclude_authid):
  1839. builder.PrependUOffsetTRelative(o)
  1840. exclude_authid = builder.EndVector(len(_exclude_authid))
  1841. # exclude_authrole: [string]
  1842. exclude_authrole = self.exclude_authrole
  1843. if exclude_authid:
  1844. _exclude_authrole = []
  1845. for authrole in exclude_authrole:
  1846. _exclude_authrole.append(builder.CreateString(authrole))
  1847. message_fbs.PublishGen.PublishStartExcludeAuthroleVector(builder, len(_exclude_authrole))
  1848. for o in reversed(_exclude_authrole):
  1849. builder.PrependUOffsetTRelative(o)
  1850. exclude_authrole = builder.EndVector(len(_exclude_authrole))
  1851. # eligible: [int]
  1852. eligible = self.eligible
  1853. if eligible:
  1854. message_fbs.PublishGen.PublishStartEligibleAuthidVector(builder, len(eligible))
  1855. for session in reversed(eligible):
  1856. builder.PrependUint64(session)
  1857. eligible = builder.EndVector(len(eligible))
  1858. # eligible_authid: [string]
  1859. eligible_authid = self.eligible_authid
  1860. if eligible_authid:
  1861. _eligible_authid = []
  1862. for authid in eligible_authid:
  1863. _eligible_authid.append(builder.CreateString(authid))
  1864. message_fbs.PublishGen.PublishStartEligibleAuthidVector(builder, len(_eligible_authid))
  1865. for o in reversed(_eligible_authid):
  1866. builder.PrependUOffsetTRelative(o)
  1867. eligible_authid = builder.EndVector(len(_eligible_authid))
  1868. # eligible_authrole: [string]
  1869. eligible_authrole = self.eligible_authrole
  1870. if eligible_authrole:
  1871. _eligible_authrole = []
  1872. for authrole in eligible_authrole:
  1873. _eligible_authrole.append(builder.CreateString(authrole))
  1874. message_fbs.PublishGen.PublishStartEligibleAuthroleVector(builder, len(_eligible_authrole))
  1875. for o in reversed(_eligible_authrole):
  1876. builder.PrependUOffsetTRelative(o)
  1877. eligible_authrole = builder.EndVector(len(_eligible_authrole))
  1878. # now start and build a new object ..
  1879. message_fbs.PublishGen.PublishStart(builder)
  1880. if self.request is not None:
  1881. message_fbs.PublishGen.PublishAddRequest(builder, self.request)
  1882. if topic:
  1883. message_fbs.PublishGen.PublishAddTopic(builder, topic)
  1884. if args:
  1885. message_fbs.PublishGen.PublishAddArgs(builder, args)
  1886. if kwargs:
  1887. message_fbs.PublishGen.PublishAddKwargs(builder, kwargs)
  1888. if payload:
  1889. message_fbs.PublishGen.PublishAddPayload(builder, payload)
  1890. if self.enc_algo:
  1891. message_fbs.PublishGen.PublishAddEncAlgo(builder, self.enc_algo)
  1892. if self.enc_serializer:
  1893. message_fbs.PublishGen.PublishAddEncSerializer(builder, self.enc_serializer)
  1894. if enc_key:
  1895. message_fbs.PublishGen.PublishAddEncKey(builder, enc_key)
  1896. if self.acknowledge is not None:
  1897. message_fbs.PublishGen.PublishAddAcknowledge(builder, self.acknowledge)
  1898. if self.exclude_me is not None:
  1899. message_fbs.PublishGen.PublishAddExcludeMe(builder, self.exclude_me)
  1900. if exclude:
  1901. message_fbs.PublishGen.PublishAddExclude(builder, exclude)
  1902. if exclude_authid:
  1903. message_fbs.PublishGen.PublishAddExcludeAuthid(builder, exclude_authid)
  1904. if exclude_authrole:
  1905. message_fbs.PublishGen.PublishAddExcludeAuthrole(builder, exclude_authrole)
  1906. if eligible:
  1907. message_fbs.PublishGen.PublishAddEligible(builder, eligible)
  1908. if eligible_authid:
  1909. message_fbs.PublishGen.PublishAddEligibleAuthid(builder, eligible_authid)
  1910. if eligible_authrole:
  1911. message_fbs.PublishGen.PublishAddEligibleAuthrole(builder, eligible_authrole)
  1912. if self.retain is not None:
  1913. message_fbs.PublishGen.PublishAddRetain(builder, self.retain)
  1914. if transaction_hash is not None:
  1915. message_fbs.PublishGen.PublishAddTransactionHash(builder, self.transaction_hash)
  1916. # FIXME: add forward_for
  1917. msg = message_fbs.PublishGen.PublishEnd(builder)
  1918. message_fbs.Message.MessageStart(builder)
  1919. message_fbs.Message.MessageAddMsgType(builder, message_fbs.MessageType.PUBLISH)
  1920. message_fbs.Message.MessageAddMsg(builder, msg)
  1921. union_msg = message_fbs.Message.MessageEnd(builder)
  1922. return union_msg
  1923. @staticmethod
  1924. def parse(wmsg):
  1925. """
  1926. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  1927. :param wmsg: The unserialized raw message.
  1928. :type wmsg: list
  1929. :returns: An instance of this class.
  1930. """
  1931. # this should already be verified by WampSerializer.unserialize
  1932. assert(len(wmsg) > 0 and wmsg[0] == Publish.MESSAGE_TYPE)
  1933. if len(wmsg) not in (4, 5, 6):
  1934. raise ProtocolError("invalid message length {0} for PUBLISH".format(len(wmsg)))
  1935. request = check_or_raise_id(wmsg[1], "'request' in PUBLISH")
  1936. options = check_or_raise_extra(wmsg[2], "'options' in PUBLISH")
  1937. topic = check_or_raise_uri(wmsg[3], "'topic' in PUBLISH")
  1938. args = None
  1939. kwargs = None
  1940. payload = None
  1941. if len(wmsg) == 5 and type(wmsg[4]) in [str, bytes]:
  1942. payload = wmsg[4]
  1943. enc_algo = options.get('enc_algo', None)
  1944. if enc_algo and not is_valid_enc_algo(enc_algo):
  1945. raise ProtocolError("invalid value {0} for 'enc_algo' option in PUBLISH".format(enc_algo))
  1946. enc_key = options.get('enc_key', None)
  1947. if enc_key and type(enc_key) != str:
  1948. raise ProtocolError("invalid type {0} for 'enc_key' option in PUBLISH".format(type(enc_key)))
  1949. enc_serializer = options.get('enc_serializer', None)
  1950. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  1951. raise ProtocolError("invalid value {0} for 'enc_serializer' option in PUBLISH".format(enc_serializer))
  1952. else:
  1953. if len(wmsg) > 4:
  1954. args = wmsg[4]
  1955. if type(args) not in [list, str, bytes]:
  1956. raise ProtocolError("invalid type {0} for 'args' in PUBLISH".format(type(args)))
  1957. if len(wmsg) > 5:
  1958. kwargs = wmsg[5]
  1959. if type(kwargs) not in [dict, str, bytes]:
  1960. raise ProtocolError("invalid type {0} for 'kwargs' in PUBLISH".format(type(kwargs)))
  1961. enc_algo = None
  1962. enc_key = None
  1963. enc_serializer = None
  1964. acknowledge = None
  1965. exclude_me = None
  1966. exclude = None
  1967. exclude_authid = None
  1968. exclude_authrole = None
  1969. eligible = None
  1970. eligible_authid = None
  1971. eligible_authrole = None
  1972. retain = None
  1973. transaction_hash = None
  1974. forward_for = None
  1975. if 'acknowledge' in options:
  1976. option_acknowledge = options['acknowledge']
  1977. if type(option_acknowledge) != bool:
  1978. raise ProtocolError("invalid type {0} for 'acknowledge' option in PUBLISH".format(type(option_acknowledge)))
  1979. acknowledge = option_acknowledge
  1980. if 'exclude_me' in options:
  1981. option_exclude_me = options['exclude_me']
  1982. if type(option_exclude_me) != bool:
  1983. raise ProtocolError("invalid type {0} for 'exclude_me' option in PUBLISH".format(type(option_exclude_me)))
  1984. exclude_me = option_exclude_me
  1985. if 'exclude' in options:
  1986. option_exclude = options['exclude']
  1987. if type(option_exclude) != list:
  1988. raise ProtocolError("invalid type {0} for 'exclude' option in PUBLISH".format(type(option_exclude)))
  1989. for _sessionid in option_exclude:
  1990. if type(_sessionid) != int:
  1991. raise ProtocolError("invalid type {0} for value in 'exclude' option in PUBLISH".format(type(_sessionid)))
  1992. exclude = option_exclude
  1993. if 'exclude_authid' in options:
  1994. option_exclude_authid = options['exclude_authid']
  1995. if type(option_exclude_authid) != list:
  1996. raise ProtocolError("invalid type {0} for 'exclude_authid' option in PUBLISH".format(type(option_exclude_authid)))
  1997. for _authid in option_exclude_authid:
  1998. if type(_authid) != str:
  1999. raise ProtocolError("invalid type {0} for value in 'exclude_authid' option in PUBLISH".format(type(_authid)))
  2000. exclude_authid = option_exclude_authid
  2001. if 'exclude_authrole' in options:
  2002. option_exclude_authrole = options['exclude_authrole']
  2003. if type(option_exclude_authrole) != list:
  2004. raise ProtocolError("invalid type {0} for 'exclude_authrole' option in PUBLISH".format(type(option_exclude_authrole)))
  2005. for _authrole in option_exclude_authrole:
  2006. if type(_authrole) != str:
  2007. raise ProtocolError("invalid type {0} for value in 'exclude_authrole' option in PUBLISH".format(type(_authrole)))
  2008. exclude_authrole = option_exclude_authrole
  2009. if 'eligible' in options:
  2010. option_eligible = options['eligible']
  2011. if type(option_eligible) != list:
  2012. raise ProtocolError("invalid type {0} for 'eligible' option in PUBLISH".format(type(option_eligible)))
  2013. for sessionId in option_eligible:
  2014. if type(sessionId) != int:
  2015. raise ProtocolError("invalid type {0} for value in 'eligible' option in PUBLISH".format(type(sessionId)))
  2016. eligible = option_eligible
  2017. if 'eligible_authid' in options:
  2018. option_eligible_authid = options['eligible_authid']
  2019. if type(option_eligible_authid) != list:
  2020. raise ProtocolError("invalid type {0} for 'eligible_authid' option in PUBLISH".format(type(option_eligible_authid)))
  2021. for _authid in option_eligible_authid:
  2022. if type(_authid) != str:
  2023. raise ProtocolError("invalid type {0} for value in 'eligible_authid' option in PUBLISH".format(type(_authid)))
  2024. eligible_authid = option_eligible_authid
  2025. if 'eligible_authrole' in options:
  2026. option_eligible_authrole = options['eligible_authrole']
  2027. if type(option_eligible_authrole) != list:
  2028. raise ProtocolError("invalid type {0} for 'eligible_authrole' option in PUBLISH".format(type(option_eligible_authrole)))
  2029. for _authrole in option_eligible_authrole:
  2030. if type(_authrole) != str:
  2031. raise ProtocolError("invalid type {0} for value in 'eligible_authrole' option in PUBLISH".format(type(_authrole)))
  2032. eligible_authrole = option_eligible_authrole
  2033. if 'retain' in options:
  2034. retain = options['retain']
  2035. if type(retain) != bool:
  2036. raise ProtocolError("invalid type {0} for 'retain' option in PUBLISH".format(type(retain)))
  2037. if 'transaction_hash' in options:
  2038. transaction_hash = options['transaction_hash']
  2039. if type(transaction_hash) != str:
  2040. raise ProtocolError("invalid type {0} for 'transaction_hash' option in PUBLISH".format(type(transaction_hash)))
  2041. if 'forward_for' in options:
  2042. forward_for = options['forward_for']
  2043. valid = False
  2044. if type(forward_for) == list:
  2045. for ff in forward_for:
  2046. if type(ff) != dict:
  2047. break
  2048. if 'session' not in ff or type(ff['session']) != int:
  2049. break
  2050. if 'authid' not in ff or type(ff['authid']) != str:
  2051. break
  2052. if 'authrole' not in ff or type(ff['authrole']) != str:
  2053. break
  2054. valid = True
  2055. if not valid:
  2056. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in PUBLISH")
  2057. obj = Publish(request,
  2058. topic,
  2059. args=args,
  2060. kwargs=kwargs,
  2061. payload=payload,
  2062. acknowledge=acknowledge,
  2063. exclude_me=exclude_me,
  2064. exclude=exclude,
  2065. exclude_authid=exclude_authid,
  2066. exclude_authrole=exclude_authrole,
  2067. eligible=eligible,
  2068. eligible_authid=eligible_authid,
  2069. eligible_authrole=eligible_authrole,
  2070. retain=retain,
  2071. transaction_hash=transaction_hash,
  2072. enc_algo=enc_algo,
  2073. enc_key=enc_key,
  2074. enc_serializer=enc_serializer,
  2075. forward_for=forward_for)
  2076. return obj
  2077. def marshal_options(self):
  2078. options = {}
  2079. if self.acknowledge is not None:
  2080. options['acknowledge'] = self.acknowledge
  2081. if self.exclude_me is not None:
  2082. options['exclude_me'] = self.exclude_me
  2083. if self.exclude is not None:
  2084. options['exclude'] = self.exclude
  2085. if self.exclude_authid is not None:
  2086. options['exclude_authid'] = self.exclude_authid
  2087. if self.exclude_authrole is not None:
  2088. options['exclude_authrole'] = self.exclude_authrole
  2089. if self.eligible is not None:
  2090. options['eligible'] = self.eligible
  2091. if self.eligible_authid is not None:
  2092. options['eligible_authid'] = self.eligible_authid
  2093. if self.eligible_authrole is not None:
  2094. options['eligible_authrole'] = self.eligible_authrole
  2095. if self.retain is not None:
  2096. options['retain'] = self.retain
  2097. if self.transaction_hash is not None:
  2098. options['transaction_hash'] = self.transaction_hash
  2099. if self.payload:
  2100. if self.enc_algo is not None:
  2101. options['enc_algo'] = self.enc_algo
  2102. if self.enc_key is not None:
  2103. options['enc_key'] = self.enc_key
  2104. if self.enc_serializer is not None:
  2105. options['enc_serializer'] = self.enc_serializer
  2106. if self.forward_for is not None:
  2107. options['forward_for'] = self.forward_for
  2108. return options
  2109. def marshal(self):
  2110. """
  2111. Marshal this object into a raw message for subsequent serialization to bytes.
  2112. :returns: The serialized raw message.
  2113. :rtype: list
  2114. """
  2115. options = self.marshal_options()
  2116. if self.payload:
  2117. return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.payload]
  2118. else:
  2119. if self.kwargs:
  2120. return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.args, self.kwargs]
  2121. elif self.args:
  2122. return [Publish.MESSAGE_TYPE, self.request, options, self.topic, self.args]
  2123. else:
  2124. return [Publish.MESSAGE_TYPE, self.request, options, self.topic]
  2125. class Published(Message):
  2126. """
  2127. A WAMP ``PUBLISHED`` message.
  2128. Format: ``[PUBLISHED, PUBLISH.Request|id, Publication|id]``
  2129. """
  2130. MESSAGE_TYPE = 17
  2131. """
  2132. The WAMP message code for this type of message.
  2133. """
  2134. __slots__ = (
  2135. 'request',
  2136. 'publication',
  2137. )
  2138. def __init__(self, request, publication):
  2139. """
  2140. :param request: The request ID of the original `PUBLISH` request.
  2141. :type request: int
  2142. :param publication: The publication ID for the published event.
  2143. :type publication: int
  2144. """
  2145. assert(type(request) == int)
  2146. assert(type(publication) == int)
  2147. Message.__init__(self)
  2148. self.request = request
  2149. self.publication = publication
  2150. @staticmethod
  2151. def parse(wmsg):
  2152. """
  2153. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2154. :param wmsg: The unserialized raw message.
  2155. :type wmsg: list
  2156. :returns: An instance of this class.
  2157. """
  2158. # this should already be verified by WampSerializer.unserialize
  2159. assert(len(wmsg) > 0 and wmsg[0] == Published.MESSAGE_TYPE)
  2160. if len(wmsg) != 3:
  2161. raise ProtocolError("invalid message length {0} for PUBLISHED".format(len(wmsg)))
  2162. request = check_or_raise_id(wmsg[1], "'request' in PUBLISHED")
  2163. publication = check_or_raise_id(wmsg[2], "'publication' in PUBLISHED")
  2164. obj = Published(request, publication)
  2165. return obj
  2166. def marshal(self):
  2167. """
  2168. Marshal this object into a raw message for subsequent serialization to bytes.
  2169. :returns: The serialized raw message.
  2170. :rtype: list
  2171. """
  2172. return [Published.MESSAGE_TYPE, self.request, self.publication]
  2173. class Subscribe(Message):
  2174. """
  2175. A WAMP ``SUBSCRIBE`` message.
  2176. Format: ``[SUBSCRIBE, Request|id, Options|dict, Topic|uri]``
  2177. """
  2178. MESSAGE_TYPE = 32
  2179. """
  2180. The WAMP message code for this type of message.
  2181. """
  2182. MATCH_EXACT = 'exact'
  2183. MATCH_PREFIX = 'prefix'
  2184. MATCH_WILDCARD = 'wildcard'
  2185. __slots__ = (
  2186. 'request',
  2187. 'topic',
  2188. 'match',
  2189. 'get_retained',
  2190. 'forward_for',
  2191. )
  2192. def __init__(self,
  2193. request,
  2194. topic,
  2195. match=None,
  2196. get_retained=None,
  2197. forward_for=None):
  2198. """
  2199. :param request: The WAMP request ID of this request.
  2200. :type request: int
  2201. :param topic: The WAMP or application URI of the PubSub topic to subscribe to.
  2202. :type topic: str
  2203. :param match: The topic matching method to be used for the subscription.
  2204. :type match: str
  2205. :param get_retained: Whether the client wants the retained message we may have along with the subscription.
  2206. :type get_retained: bool or None
  2207. :param forward_for: When this Subscribe is forwarded over a router-to-router link,
  2208. or via an intermediary router.
  2209. :type forward_for: list[dict]
  2210. """
  2211. assert(type(request) == int)
  2212. assert(type(topic) == str)
  2213. assert(match is None or type(match) == str)
  2214. assert(match is None or match in [Subscribe.MATCH_EXACT, Subscribe.MATCH_PREFIX, Subscribe.MATCH_WILDCARD])
  2215. assert(get_retained is None or type(get_retained) is bool)
  2216. assert(forward_for is None or type(forward_for) == list)
  2217. if forward_for:
  2218. for ff in forward_for:
  2219. assert type(ff) == dict
  2220. assert 'session' in ff and type(ff['session']) == int
  2221. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  2222. assert 'authrole' in ff and type(ff['authrole']) == str
  2223. Message.__init__(self)
  2224. self.request = request
  2225. self.topic = topic
  2226. self.match = match or Subscribe.MATCH_EXACT
  2227. self.get_retained = get_retained
  2228. self.forward_for = forward_for
  2229. @staticmethod
  2230. def parse(wmsg):
  2231. """
  2232. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2233. :param wmsg: The unserialized raw message.
  2234. :type wmsg: list
  2235. :returns: An instance of this class.
  2236. """
  2237. # this should already be verified by WampSerializer.unserialize
  2238. assert(len(wmsg) > 0 and wmsg[0] == Subscribe.MESSAGE_TYPE)
  2239. if len(wmsg) != 4:
  2240. raise ProtocolError("invalid message length {0} for SUBSCRIBE".format(len(wmsg)))
  2241. request = check_or_raise_id(wmsg[1], "'request' in SUBSCRIBE")
  2242. options = check_or_raise_extra(wmsg[2], "'options' in SUBSCRIBE")
  2243. topic = check_or_raise_uri(wmsg[3], "'topic' in SUBSCRIBE", allow_empty_components=True)
  2244. match = Subscribe.MATCH_EXACT
  2245. get_retained = None
  2246. forward_for = None
  2247. if 'match' in options:
  2248. option_match = options['match']
  2249. if type(option_match) != str:
  2250. raise ProtocolError("invalid type {0} for 'match' option in SUBSCRIBE".format(type(option_match)))
  2251. if option_match not in [Subscribe.MATCH_EXACT, Subscribe.MATCH_PREFIX, Subscribe.MATCH_WILDCARD]:
  2252. raise ProtocolError("invalid value {0} for 'match' option in SUBSCRIBE".format(option_match))
  2253. match = option_match
  2254. if 'get_retained' in options:
  2255. get_retained = options['get_retained']
  2256. if type(get_retained) != bool:
  2257. raise ProtocolError("invalid type {0} for 'get_retained' option in SUBSCRIBE".format(type(get_retained)))
  2258. if 'forward_for' in options:
  2259. forward_for = options['forward_for']
  2260. valid = False
  2261. if type(forward_for) == list:
  2262. for ff in forward_for:
  2263. if type(ff) != dict:
  2264. break
  2265. if 'session' not in ff or type(ff['session']) != int:
  2266. break
  2267. if 'authid' not in ff or type(ff['authid']) != str:
  2268. break
  2269. if 'authrole' not in ff or type(ff['authrole']) != str:
  2270. break
  2271. valid = True
  2272. if not valid:
  2273. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in SUBSCRIBE")
  2274. obj = Subscribe(request, topic, match=match, get_retained=get_retained, forward_for=forward_for)
  2275. return obj
  2276. def marshal_options(self):
  2277. options = {}
  2278. if self.match and self.match != Subscribe.MATCH_EXACT:
  2279. options['match'] = self.match
  2280. if self.get_retained is not None:
  2281. options['get_retained'] = self.get_retained
  2282. if self.forward_for is not None:
  2283. options['forward_for'] = self.forward_for
  2284. return options
  2285. def marshal(self):
  2286. """
  2287. Marshal this object into a raw message for subsequent serialization to bytes.
  2288. :returns: The serialized raw message.
  2289. :rtype: list
  2290. """
  2291. return [Subscribe.MESSAGE_TYPE, self.request, self.marshal_options(), self.topic]
  2292. class Subscribed(Message):
  2293. """
  2294. A WAMP ``SUBSCRIBED`` message.
  2295. Format: ``[SUBSCRIBED, SUBSCRIBE.Request|id, Subscription|id]``
  2296. """
  2297. MESSAGE_TYPE = 33
  2298. """
  2299. The WAMP message code for this type of message.
  2300. """
  2301. __slots__ = (
  2302. 'request',
  2303. 'subscription',
  2304. )
  2305. def __init__(self, request, subscription):
  2306. """
  2307. :param request: The request ID of the original ``SUBSCRIBE`` request.
  2308. :type request: int
  2309. :param subscription: The subscription ID for the subscribed topic (or topic pattern).
  2310. :type subscription: int
  2311. """
  2312. assert(type(request) == int)
  2313. assert(type(subscription) == int)
  2314. Message.__init__(self)
  2315. self.request = request
  2316. self.subscription = subscription
  2317. @staticmethod
  2318. def parse(wmsg):
  2319. """
  2320. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2321. :param wmsg: The unserialized raw message.
  2322. :type wmsg: list
  2323. :returns: An instance of this class.
  2324. """
  2325. # this should already be verified by WampSerializer.unserialize
  2326. assert(len(wmsg) > 0 and wmsg[0] == Subscribed.MESSAGE_TYPE)
  2327. if len(wmsg) != 3:
  2328. raise ProtocolError("invalid message length {0} for SUBSCRIBED".format(len(wmsg)))
  2329. request = check_or_raise_id(wmsg[1], "'request' in SUBSCRIBED")
  2330. subscription = check_or_raise_id(wmsg[2], "'subscription' in SUBSCRIBED")
  2331. obj = Subscribed(request, subscription)
  2332. return obj
  2333. def marshal(self):
  2334. """
  2335. Marshal this object into a raw message for subsequent serialization to bytes.
  2336. :returns: The serialized raw message.
  2337. :rtype: list
  2338. """
  2339. return [Subscribed.MESSAGE_TYPE, self.request, self.subscription]
  2340. class Unsubscribe(Message):
  2341. """
  2342. A WAMP ``UNSUBSCRIBE`` message.
  2343. Formats:
  2344. * ``[UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id]``
  2345. * ``[UNSUBSCRIBE, Request|id, SUBSCRIBED.Subscription|id, Options|dict]``
  2346. """
  2347. MESSAGE_TYPE = 34
  2348. """
  2349. The WAMP message code for this type of message.
  2350. """
  2351. __slots__ = (
  2352. 'request',
  2353. 'subscription',
  2354. 'forward_for',
  2355. )
  2356. def __init__(self, request, subscription, forward_for=None):
  2357. """
  2358. :param request: The WAMP request ID of this request.
  2359. :type request: int
  2360. :param subscription: The subscription ID for the subscription to unsubscribe from.
  2361. :type subscription: int
  2362. :param forward_for: When this Unsubscribe is forwarded over a router-to-router link,
  2363. or via an intermediary router.
  2364. :type forward_for: list[dict]
  2365. """
  2366. assert(type(request) == int)
  2367. assert(type(subscription) == int)
  2368. if forward_for:
  2369. for ff in forward_for:
  2370. assert type(ff) == dict
  2371. assert 'session' in ff and type(ff['session']) == int
  2372. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  2373. assert 'authrole' in ff and type(ff['authrole']) == str
  2374. Message.__init__(self)
  2375. self.request = request
  2376. self.subscription = subscription
  2377. self.forward_for = forward_for
  2378. @staticmethod
  2379. def parse(wmsg):
  2380. """
  2381. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2382. :param wmsg: The unserialized raw message.
  2383. :type wmsg: list
  2384. :returns: An instance of this class.
  2385. """
  2386. # this should already be verified by WampSerializer.unserialize
  2387. assert(len(wmsg) > 0 and wmsg[0] == Unsubscribe.MESSAGE_TYPE)
  2388. if len(wmsg) not in [3, 4]:
  2389. raise ProtocolError("invalid message length {0} for WAMP UNSUBSCRIBE".format(len(wmsg)))
  2390. request = check_or_raise_id(wmsg[1], "'request' in UNSUBSCRIBE")
  2391. subscription = check_or_raise_id(wmsg[2], "'subscription' in UNSUBSCRIBE")
  2392. options = None
  2393. if len(wmsg) > 3:
  2394. options = check_or_raise_extra(wmsg[3], "'options' in UNSUBSCRIBE")
  2395. forward_for = None
  2396. if options and 'forward_for' in options:
  2397. forward_for = options['forward_for']
  2398. valid = False
  2399. if type(forward_for) == list:
  2400. for ff in forward_for:
  2401. if type(ff) != dict:
  2402. break
  2403. if 'session' not in ff or type(ff['session']) != int:
  2404. break
  2405. if 'authid' not in ff or type(ff['authid']) != str:
  2406. break
  2407. if 'authrole' not in ff or type(ff['authrole']) != str:
  2408. break
  2409. valid = True
  2410. if not valid:
  2411. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in UNSUBSCRIBE")
  2412. obj = Unsubscribe(request, subscription, forward_for=forward_for)
  2413. return obj
  2414. def marshal(self):
  2415. """
  2416. Marshal this object into a raw message for subsequent serialization to bytes.
  2417. :returns: The serialized raw message.
  2418. :rtype: list
  2419. """
  2420. if self.forward_for:
  2421. options = {
  2422. 'forward_for': self.forward_for,
  2423. }
  2424. return [Unsubscribe.MESSAGE_TYPE, self.request, self.subscription, options]
  2425. else:
  2426. return [Unsubscribe.MESSAGE_TYPE, self.request, self.subscription]
  2427. class Unsubscribed(Message):
  2428. """
  2429. A WAMP ``UNSUBSCRIBED`` message.
  2430. Formats:
  2431. * ``[UNSUBSCRIBED, UNSUBSCRIBE.Request|id]``
  2432. * ``[UNSUBSCRIBED, UNSUBSCRIBE.Request|id, Details|dict]``
  2433. """
  2434. MESSAGE_TYPE = 35
  2435. """
  2436. The WAMP message code for this type of message.
  2437. """
  2438. __slots__ = (
  2439. 'request',
  2440. 'subscription',
  2441. 'reason',
  2442. )
  2443. def __init__(self, request, subscription=None, reason=None):
  2444. """
  2445. :param request: The request ID of the original ``UNSUBSCRIBE`` request or
  2446. ``0`` is router triggered unsubscribe ("router revocation signaling").
  2447. :type request: int
  2448. :param subscription: If unsubscribe was actively triggered by router, the ID
  2449. of the subscription revoked.
  2450. :type subscription: int or None
  2451. :param reason: The reason (an URI) for an active (router initiated) revocation.
  2452. :type reason: str or None.
  2453. """
  2454. assert(type(request) == int)
  2455. assert(subscription is None or type(subscription) == int)
  2456. assert(reason is None or type(reason) == str)
  2457. assert((request != 0 and subscription is None) or (request == 0 and subscription != 0))
  2458. Message.__init__(self)
  2459. self.request = request
  2460. self.subscription = subscription
  2461. self.reason = reason
  2462. @staticmethod
  2463. def parse(wmsg):
  2464. """
  2465. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2466. :param wmsg: The unserialized raw message.
  2467. :type wmsg: list
  2468. :returns: An instance of this class.
  2469. """
  2470. # this should already be verified by WampSerializer.unserialize
  2471. assert(len(wmsg) > 0 and wmsg[0] == Unsubscribed.MESSAGE_TYPE)
  2472. if len(wmsg) not in [2, 3]:
  2473. raise ProtocolError("invalid message length {0} for UNSUBSCRIBED".format(len(wmsg)))
  2474. request = check_or_raise_id(wmsg[1], "'request' in UNSUBSCRIBED")
  2475. subscription = None
  2476. reason = None
  2477. if len(wmsg) > 2:
  2478. details = check_or_raise_extra(wmsg[2], "'details' in UNSUBSCRIBED")
  2479. if "subscription" in details:
  2480. details_subscription = details["subscription"]
  2481. if type(details_subscription) != int:
  2482. raise ProtocolError("invalid type {0} for 'subscription' detail in UNSUBSCRIBED".format(type(details_subscription)))
  2483. subscription = details_subscription
  2484. if "reason" in details:
  2485. reason = check_or_raise_uri(details["reason"], "'reason' in UNSUBSCRIBED")
  2486. obj = Unsubscribed(request, subscription, reason)
  2487. return obj
  2488. def marshal(self):
  2489. """
  2490. Marshal this object into a raw message for subsequent serialization to bytes.
  2491. :returns: The serialized raw message.
  2492. :rtype: list
  2493. """
  2494. if self.reason is not None or self.subscription is not None:
  2495. details = {}
  2496. if self.reason is not None:
  2497. details["reason"] = self.reason
  2498. if self.subscription is not None:
  2499. details["subscription"] = self.subscription
  2500. return [Unsubscribed.MESSAGE_TYPE, self.request, details]
  2501. else:
  2502. return [Unsubscribed.MESSAGE_TYPE, self.request]
  2503. class Event(Message):
  2504. """
  2505. A WAMP ``EVENT`` message.
  2506. Formats:
  2507. * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict]``
  2508. * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list]``
  2509. * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Arguments|list, PUBLISH.ArgumentsKw|dict]``
  2510. * ``[EVENT, SUBSCRIBED.Subscription|id, PUBLISHED.Publication|id, Details|dict, PUBLISH.Payload|binary]``
  2511. """
  2512. MESSAGE_TYPE = 36
  2513. """
  2514. The WAMP message code for this type of message.
  2515. """
  2516. __slots__ = (
  2517. # uint64
  2518. '_subscription',
  2519. # uint64
  2520. '_publication',
  2521. # [uint8]
  2522. '_args',
  2523. # [uint8]
  2524. '_kwargs',
  2525. # [uint8]
  2526. '_payload',
  2527. # Payload => uint8
  2528. '_enc_algo',
  2529. # Serializer => uint8
  2530. '_enc_serializer',
  2531. # [uint8]
  2532. '_enc_key',
  2533. # uint64
  2534. '_publisher',
  2535. # string (principal)
  2536. '_publisher_authid',
  2537. # string (principal)
  2538. '_publisher_authrole',
  2539. # string (uri)
  2540. '_topic',
  2541. # bool
  2542. '_retained',
  2543. # string
  2544. '_transaction_hash',
  2545. # bool - FIXME: rename to "acknowledge"
  2546. '_x_acknowledged_delivery',
  2547. # [Principal]
  2548. '_forward_for',
  2549. )
  2550. def __init__(self, subscription=None, publication=None, args=None, kwargs=None, payload=None,
  2551. publisher=None, publisher_authid=None, publisher_authrole=None, topic=None,
  2552. retained=None, transaction_hash=None, x_acknowledged_delivery=None,
  2553. enc_algo=None, enc_key=None, enc_serializer=None, forward_for=None,
  2554. from_fbs=None):
  2555. """
  2556. :param subscription: The subscription ID this event is dispatched under.
  2557. :type subscription: int
  2558. :param publication: The publication ID of the dispatched event.
  2559. :type publication: int
  2560. :param args: Positional values for application-defined exception.
  2561. Must be serializable using any serializers in use.
  2562. :type args: list or tuple or None
  2563. :param kwargs: Keyword values for application-defined exception.
  2564. Must be serializable using any serializers in use.
  2565. :type kwargs: dict or None
  2566. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  2567. :type payload: bytes or None
  2568. :param publisher: The WAMP session ID of the publisher. Only filled if publisher is disclosed.
  2569. :type publisher: None or int
  2570. :param publisher_authid: The WAMP authid of the publisher. Only filled if publisher is disclosed.
  2571. :type publisher_authid: None or unicode
  2572. :param publisher_authrole: The WAMP authrole of the publisher. Only filled if publisher is disclosed.
  2573. :type publisher_authrole: None or unicode
  2574. :param topic: For pattern-based subscriptions, the event MUST contain the actual topic published to.
  2575. :type topic: str or None
  2576. :param retained: Whether the message was retained by the broker on the topic, rather than just published.
  2577. :type retained: bool or None
  2578. :param transaction_hash: An application provided transaction hash for the originating call, which may
  2579. be used in the router to throttle or deduplicate the calls on the procedure. See the discussion
  2580. `here <https://github.com/wamp-proto/wamp-proto/issues/391#issuecomment-998577967>`_.
  2581. :type transaction_hash: str
  2582. :param x_acknowledged_delivery: Whether this Event should be acknowledged.
  2583. :type x_acknowledged_delivery: bool or None
  2584. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  2585. :type enc_algo: str or None
  2586. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  2587. :type enc_key: str or None
  2588. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  2589. :type enc_serializer: str or None
  2590. :param forward_for: When this Event is forwarded for a client (or from an intermediary router).
  2591. :type forward_for: list[dict]
  2592. """
  2593. assert(subscription is None or type(subscription) == int)
  2594. assert(publication is None or type(publication) == int)
  2595. assert(args is None or type(args) in [list, tuple])
  2596. assert(kwargs is None or type(kwargs) == dict)
  2597. assert(payload is None or type(payload) == bytes)
  2598. assert(payload is None or (payload is not None and args is None and kwargs is None))
  2599. assert(publisher is None or type(publisher) == int)
  2600. assert(publisher_authid is None or type(publisher_authid) == str)
  2601. assert(publisher_authrole is None or type(publisher_authrole) == str)
  2602. assert(topic is None or type(topic) == str)
  2603. assert(retained is None or type(retained) == bool)
  2604. assert(transaction_hash is None or type(transaction_hash) == str)
  2605. assert(x_acknowledged_delivery is None or type(x_acknowledged_delivery) == bool)
  2606. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  2607. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  2608. assert(enc_key is None or type(enc_key) == str)
  2609. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  2610. assert(forward_for is None or type(forward_for) == list)
  2611. if forward_for:
  2612. for ff in forward_for:
  2613. assert type(ff) == dict
  2614. assert 'session' in ff and type(ff['session']) == int
  2615. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  2616. assert 'authrole' in ff and type(ff['authrole']) == str
  2617. Message.__init__(self, from_fbs=from_fbs)
  2618. self._subscription = subscription
  2619. self._publication = publication
  2620. self._args = args
  2621. self._kwargs = _validate_kwargs(kwargs)
  2622. self._payload = payload
  2623. self._publisher = publisher
  2624. self._publisher_authid = publisher_authid
  2625. self._publisher_authrole = publisher_authrole
  2626. self._topic = topic
  2627. self._retained = retained
  2628. self._transaction_hash = transaction_hash
  2629. self._x_acknowledged_delivery = x_acknowledged_delivery
  2630. self._enc_algo = enc_algo
  2631. self._enc_key = enc_key
  2632. self._enc_serializer = enc_serializer
  2633. self._forward_for = forward_for
  2634. def __eq__(self, other):
  2635. if not isinstance(other, self.__class__):
  2636. return False
  2637. if not Message.__eq__(self, other):
  2638. return False
  2639. if other.subscription != self.subscription:
  2640. return False
  2641. if other.publication != self.publication:
  2642. return False
  2643. if other.args != self.args:
  2644. return False
  2645. if other.kwargs != self.kwargs:
  2646. return False
  2647. if other.payload != self.payload:
  2648. return False
  2649. if other.publisher != self.publisher:
  2650. return False
  2651. if other.publisher_authid != self.publisher_authid:
  2652. return False
  2653. if other.publisher_authrole != self.publisher_authrole:
  2654. return False
  2655. if other.topic != self.topic:
  2656. return False
  2657. if other.retained != self.retained:
  2658. return False
  2659. if other.transaction_hash != self.transaction_hash:
  2660. return False
  2661. if other.x_acknowledged_delivery != self.x_acknowledged_delivery:
  2662. return False
  2663. if other.enc_algo != self.enc_algo:
  2664. return False
  2665. if other.enc_key != self.enc_key:
  2666. return False
  2667. if other.enc_serializer != self.enc_serializer:
  2668. return False
  2669. if other.forward_for != self.forward_for:
  2670. return False
  2671. return True
  2672. def __ne__(self, other):
  2673. return not self.__eq__(other)
  2674. @property
  2675. def subscription(self):
  2676. if self._subscription is None and self._from_fbs:
  2677. self._subscription = self._from_fbs.Subscription()
  2678. return self._subscription
  2679. @subscription.setter
  2680. def subscription(self, value):
  2681. assert(value is None or type(value) == int)
  2682. self._subscription = value
  2683. @property
  2684. def publication(self):
  2685. if self._publication is None and self._from_fbs:
  2686. self._publication = self._from_fbs.Publication()
  2687. return self._publication
  2688. @publication.setter
  2689. def publication(self, value):
  2690. assert(value is None or type(value) == int)
  2691. self._publication = value
  2692. @property
  2693. def args(self):
  2694. if self._args is None and self._from_fbs:
  2695. if self._from_fbs.ArgsLength():
  2696. self._args = cbor2.loads(bytes(self._from_fbs.ArgsAsBytes()))
  2697. return self._args
  2698. @args.setter
  2699. def args(self, value):
  2700. assert(value is None or type(value) in [list, tuple])
  2701. self._args = value
  2702. @property
  2703. def kwargs(self):
  2704. if self._kwargs is None and self._from_fbs:
  2705. if self._from_fbs.KwargsLength():
  2706. self._kwargs = cbor2.loads(bytes(self._from_fbs.KwargsAsBytes()))
  2707. return self._kwargs
  2708. @kwargs.setter
  2709. def kwargs(self, value):
  2710. assert(value is None or type(value) == dict)
  2711. self._kwargs = value
  2712. @property
  2713. def payload(self):
  2714. if self._payload is None and self._from_fbs:
  2715. if self._from_fbs.PayloadLength():
  2716. self._payload = self._from_fbs.PayloadAsBytes()
  2717. return self._payload
  2718. @payload.setter
  2719. def payload(self, value):
  2720. assert value is None or type(value) == bytes
  2721. self._payload = value
  2722. @property
  2723. def publisher(self):
  2724. if self._publisher is None and self._from_fbs:
  2725. publisher = self._from_fbs.Publisher()
  2726. if publisher:
  2727. self._publisher = publisher
  2728. return self._publisher
  2729. @publisher.setter
  2730. def publisher(self, value):
  2731. assert value is None or type(value) == int
  2732. self._publisher = value
  2733. @property
  2734. def publisher_authid(self):
  2735. if self._publisher_authid is None and self._from_fbs:
  2736. s = self._from_fbs.PublisherAuthid()
  2737. if s:
  2738. self._publisher_authid = s.decode('utf8')
  2739. return self._publisher_authid
  2740. @publisher_authid.setter
  2741. def publisher_authid(self, value):
  2742. assert value is None or type(value) == str
  2743. self._publisher_authid = value
  2744. @property
  2745. def publisher_authrole(self):
  2746. if self._publisher_authrole is None and self._from_fbs:
  2747. s = self._from_fbs.PublisherAuthrole()
  2748. if s:
  2749. self._publisher_authrole = s.decode('utf8')
  2750. return self._publisher_authrole
  2751. @publisher_authrole.setter
  2752. def publisher_authrole(self, value):
  2753. assert value is None or type(value) == str
  2754. self._publisher_authrole = value
  2755. @property
  2756. def topic(self):
  2757. if self._topic is None and self._from_fbs:
  2758. s = self._from_fbs.Topic()
  2759. if s:
  2760. self._topic = s.decode('utf8')
  2761. return self._topic
  2762. @topic.setter
  2763. def topic(self, value):
  2764. assert value is None or type(value) == str
  2765. self._topic = value
  2766. @property
  2767. def retained(self):
  2768. if self._retained is None and self._from_fbs:
  2769. self._retained = self._from_fbs.Retained()
  2770. return self._retained
  2771. @retained.setter
  2772. def retained(self, value):
  2773. assert value is None or type(value) == bool
  2774. self._retained = value
  2775. @property
  2776. def transaction_hash(self):
  2777. if self._transaction_hash is None and self._from_fbs:
  2778. s = self._from_fbs.TransactionHash()
  2779. if s:
  2780. self._transaction_hash = s.decode('utf8')
  2781. return self._transaction_hash
  2782. @transaction_hash.setter
  2783. def transaction_hash(self, value):
  2784. assert value is None or type(value) == str
  2785. self._transaction_hash = value
  2786. @property
  2787. def x_acknowledged_delivery(self):
  2788. if self._x_acknowledged_delivery is None and self._from_fbs:
  2789. x_acknowledged_delivery = self._from_fbs.Acknowledge()
  2790. if x_acknowledged_delivery:
  2791. self._x_acknowledged_delivery = x_acknowledged_delivery
  2792. return self._x_acknowledged_delivery
  2793. @x_acknowledged_delivery.setter
  2794. def x_acknowledged_delivery(self, value):
  2795. assert value is None or type(value) == bool
  2796. self._x_acknowledged_delivery = value
  2797. @property
  2798. def enc_algo(self):
  2799. if self._enc_algo is None and self._from_fbs:
  2800. enc_algo = self._from_fbs.EncAlgo()
  2801. if enc_algo:
  2802. self._enc_algo = enc_algo
  2803. return self._enc_algo
  2804. @enc_algo.setter
  2805. def enc_algo(self, value):
  2806. assert value is None or value in [ENC_ALGO_CRYPTOBOX, ENC_ALGO_MQTT, ENC_ALGO_XBR]
  2807. self._enc_algo = value
  2808. @property
  2809. def enc_key(self):
  2810. if self._enc_key is None and self._from_fbs:
  2811. if self._from_fbs.EncKeyLength():
  2812. self._enc_key = self._from_fbs.EncKeyAsBytes()
  2813. return self._enc_key
  2814. @enc_key.setter
  2815. def enc_key(self, value):
  2816. assert value is None or type(value) == bytes
  2817. self._enc_key = value
  2818. @property
  2819. def enc_serializer(self):
  2820. if self._enc_serializer is None and self._from_fbs:
  2821. enc_serializer = self._from_fbs.EncSerializer()
  2822. if enc_serializer:
  2823. self._enc_serializer = enc_serializer
  2824. return self._enc_serializer
  2825. @enc_serializer.setter
  2826. def enc_serializer(self, value):
  2827. assert value is None or value in [ENC_SER_JSON, ENC_SER_MSGPACK, ENC_SER_CBOR, ENC_SER_UBJSON]
  2828. self._enc_serializer = value
  2829. @property
  2830. def forward_for(self):
  2831. # FIXME
  2832. return self._forward_for
  2833. @forward_for.setter
  2834. def forward_for(self, value):
  2835. # FIXME
  2836. self._forward_for = value
  2837. @staticmethod
  2838. def cast(buf):
  2839. return Event(from_fbs=message_fbs.Event.GetRootAsEvent(buf, 0))
  2840. def build(self, builder):
  2841. args = self.args
  2842. if args:
  2843. args = builder.CreateByteVector(cbor2.dumps(args))
  2844. kwargs = self.kwargs
  2845. if kwargs:
  2846. kwargs = builder.CreateByteVector(cbor2.dumps(kwargs))
  2847. payload = self.payload
  2848. if payload:
  2849. payload = builder.CreateByteVector(payload)
  2850. publisher_authid = self.publisher_authid
  2851. if publisher_authid:
  2852. publisher_authid = builder.CreateString(publisher_authid)
  2853. publisher_authrole = self.publisher_authrole
  2854. if publisher_authrole:
  2855. publisher_authrole = builder.CreateString(publisher_authrole)
  2856. topic = self.topic
  2857. if topic:
  2858. topic = builder.CreateString(topic)
  2859. transaction_hash = self.transaction_hash
  2860. if transaction_hash:
  2861. transaction_hash = builder.CreateString(transaction_hash)
  2862. enc_key = self.enc_key
  2863. if enc_key:
  2864. enc_key = builder.CreateByteVector(enc_key)
  2865. message_fbs.EventGen.EventStart(builder)
  2866. if self.subscription:
  2867. message_fbs.EventGen.EventAddSubscription(builder, self.subscription)
  2868. if self.publication:
  2869. message_fbs.EventGen.EventAddPublication(builder, self.publication)
  2870. if args:
  2871. message_fbs.EventGen.EventAddArgs(builder, args)
  2872. if kwargs:
  2873. message_fbs.EventGen.EventAddKwargs(builder, kwargs)
  2874. if payload:
  2875. message_fbs.EventGen.EventAddPayload(builder, payload)
  2876. if self.publisher:
  2877. message_fbs.EventGen.EventAddPublisher(builder, self.publisher)
  2878. if publisher_authid:
  2879. message_fbs.EventGen.EventAddPublisherAuthid(builder, publisher_authid)
  2880. if publisher_authrole:
  2881. message_fbs.EventGen.EventAddPublisherAuthrole(builder, publisher_authrole)
  2882. if topic:
  2883. message_fbs.EventGen.EventAddTopic(builder, topic)
  2884. if self.retained is not None:
  2885. message_fbs.EventGen.EventAddRetained(builder, self.retained)
  2886. if transaction_hash is not None:
  2887. message_fbs.EventGen.EventAddTransactionHash(builder, transaction_hash)
  2888. if self.x_acknowledged_delivery is not None:
  2889. message_fbs.EventGen.EventAddAcknowledge(builder, self.x_acknowledged_delivery)
  2890. if self.enc_algo:
  2891. message_fbs.EventGen.EventAddEncAlgo(builder, self.enc_algo)
  2892. if enc_key:
  2893. message_fbs.EventGen.EventAddEncKey(builder, enc_key)
  2894. if self.enc_serializer:
  2895. message_fbs.EventGen.EventAddEncSerializer(builder, self.enc_serializer)
  2896. # FIXME: add forward_for
  2897. msg = message_fbs.EventGen.EventEnd(builder)
  2898. message_fbs.Message.MessageStart(builder)
  2899. message_fbs.Message.MessageAddMsgType(builder, message_fbs.MessageType.EVENT)
  2900. message_fbs.Message.MessageAddMsg(builder, msg)
  2901. union_msg = message_fbs.Message.MessageEnd(builder)
  2902. return union_msg
  2903. @staticmethod
  2904. def parse(wmsg):
  2905. """
  2906. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  2907. :param wmsg: The unserialized raw message.
  2908. :type wmsg: list
  2909. :returns: An instance of this class.
  2910. """
  2911. # this should already be verified by WampSerializer.unserialize
  2912. assert(len(wmsg) > 0 and wmsg[0] == Event.MESSAGE_TYPE)
  2913. if len(wmsg) not in (4, 5, 6):
  2914. raise ProtocolError("invalid message length {0} for EVENT".format(len(wmsg)))
  2915. subscription = check_or_raise_id(wmsg[1], "'subscription' in EVENT")
  2916. publication = check_or_raise_id(wmsg[2], "'publication' in EVENT")
  2917. details = check_or_raise_extra(wmsg[3], "'details' in EVENT")
  2918. args = None
  2919. kwargs = None
  2920. payload = None
  2921. enc_algo = None
  2922. enc_key = None
  2923. enc_serializer = None
  2924. if len(wmsg) == 5 and type(wmsg[4]) == bytes:
  2925. payload = wmsg[4]
  2926. enc_algo = details.get('enc_algo', None)
  2927. if enc_algo and not is_valid_enc_algo(enc_algo):
  2928. raise ProtocolError("invalid value {0} for 'enc_algo' detail in EVENT".format(enc_algo))
  2929. enc_key = details.get('enc_key', None)
  2930. if enc_key and type(enc_key) != str:
  2931. raise ProtocolError("invalid type {0} for 'enc_key' detail in EVENT".format(type(enc_key)))
  2932. enc_serializer = details.get('enc_serializer', None)
  2933. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  2934. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in EVENT".format(enc_serializer))
  2935. else:
  2936. if len(wmsg) > 4:
  2937. args = wmsg[4]
  2938. if args is not None and type(args) != list:
  2939. raise ProtocolError("invalid type {0} for 'args' in EVENT".format(type(args)))
  2940. if len(wmsg) > 5:
  2941. kwargs = wmsg[5]
  2942. if type(kwargs) != dict:
  2943. raise ProtocolError("invalid type {0} for 'kwargs' in EVENT".format(type(kwargs)))
  2944. publisher = None
  2945. publisher_authid = None
  2946. publisher_authrole = None
  2947. topic = None
  2948. retained = None
  2949. transaction_hash = None
  2950. forward_for = None
  2951. x_acknowledged_delivery = None
  2952. if 'publisher' in details:
  2953. detail_publisher = details['publisher']
  2954. if type(detail_publisher) != int:
  2955. raise ProtocolError("invalid type {0} for 'publisher' detail in EVENT".format(type(detail_publisher)))
  2956. publisher = detail_publisher
  2957. if 'publisher_authid' in details:
  2958. detail_publisher_authid = details['publisher_authid']
  2959. if type(detail_publisher_authid) != str:
  2960. raise ProtocolError("invalid type {0} for 'publisher_authid' detail in EVENT".format(type(detail_publisher_authid)))
  2961. publisher_authid = detail_publisher_authid
  2962. if 'publisher_authrole' in details:
  2963. detail_publisher_authrole = details['publisher_authrole']
  2964. if type(detail_publisher_authrole) != str:
  2965. raise ProtocolError("invalid type {0} for 'publisher_authrole' detail in EVENT".format(type(detail_publisher_authrole)))
  2966. publisher_authrole = detail_publisher_authrole
  2967. if 'topic' in details:
  2968. detail_topic = details['topic']
  2969. if type(detail_topic) != str:
  2970. raise ProtocolError("invalid type {0} for 'topic' detail in EVENT".format(type(detail_topic)))
  2971. topic = detail_topic
  2972. if 'retained' in details:
  2973. retained = details['retained']
  2974. if type(retained) != bool:
  2975. raise ProtocolError("invalid type {0} for 'retained' detail in EVENT".format(type(retained)))
  2976. if 'transaction_hash' in details:
  2977. detail_transaction_hash = details['transaction_hash']
  2978. if type(detail_transaction_hash) != str:
  2979. raise ProtocolError("invalid type {0} for 'transaction_hash' detail in EVENT".format(type(detail_transaction_hash)))
  2980. transaction_hash = detail_transaction_hash
  2981. if 'x_acknowledged_delivery' in details:
  2982. x_acknowledged_delivery = details['x_acknowledged_delivery']
  2983. if type(x_acknowledged_delivery) != bool:
  2984. raise ProtocolError("invalid type {0} for 'x_acknowledged_delivery' detail in EVENT".format(type(x_acknowledged_delivery)))
  2985. if 'forward_for' in details:
  2986. forward_for = details['forward_for']
  2987. valid = False
  2988. if type(forward_for) == list:
  2989. for ff in forward_for:
  2990. if type(ff) != dict:
  2991. break
  2992. if 'session' not in ff or type(ff['session']) != int:
  2993. break
  2994. if 'authid' not in ff or type(ff['authid']) != str:
  2995. break
  2996. if 'authrole' not in ff or type(ff['authrole']) != str:
  2997. break
  2998. valid = True
  2999. if not valid:
  3000. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in EVENT")
  3001. obj = Event(subscription,
  3002. publication,
  3003. args=args,
  3004. kwargs=kwargs,
  3005. payload=payload,
  3006. publisher=publisher,
  3007. publisher_authid=publisher_authid,
  3008. publisher_authrole=publisher_authrole,
  3009. topic=topic,
  3010. retained=retained,
  3011. transaction_hash=transaction_hash,
  3012. x_acknowledged_delivery=x_acknowledged_delivery,
  3013. enc_algo=enc_algo,
  3014. enc_key=enc_key,
  3015. enc_serializer=enc_serializer,
  3016. forward_for=forward_for)
  3017. return obj
  3018. def marshal(self):
  3019. """
  3020. Marshal this object into a raw message for subsequent serialization to bytes.
  3021. :returns: The serialized raw message.
  3022. :rtype: list
  3023. """
  3024. details = {}
  3025. if self.publisher is not None:
  3026. details['publisher'] = self.publisher
  3027. if self.publisher_authid is not None:
  3028. details['publisher_authid'] = self.publisher_authid
  3029. if self.publisher_authrole is not None:
  3030. details['publisher_authrole'] = self.publisher_authrole
  3031. if self.topic is not None:
  3032. details['topic'] = self.topic
  3033. if self.retained is not None:
  3034. details['retained'] = self.retained
  3035. if self.transaction_hash is not None:
  3036. details['transaction_hash'] = self.transaction_hash
  3037. if self.x_acknowledged_delivery is not None:
  3038. details['x_acknowledged_delivery'] = self.x_acknowledged_delivery
  3039. if self.forward_for is not None:
  3040. details['forward_for'] = self.forward_for
  3041. if self.payload:
  3042. if self.enc_algo is not None:
  3043. details['enc_algo'] = self.enc_algo
  3044. if self.enc_key is not None:
  3045. details['enc_key'] = self.enc_key
  3046. if self.enc_serializer is not None:
  3047. details['enc_serializer'] = self.enc_serializer
  3048. return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.payload]
  3049. else:
  3050. if self.kwargs:
  3051. return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.args, self.kwargs]
  3052. elif self.args:
  3053. return [Event.MESSAGE_TYPE, self.subscription, self.publication, details, self.args]
  3054. else:
  3055. return [Event.MESSAGE_TYPE, self.subscription, self.publication, details]
  3056. class EventReceived(Message):
  3057. """
  3058. A WAMP ``EVENT_RECEIVED`` message.
  3059. Format: ``[EVENT_RECEIVED, EVENT.Publication|id]``
  3060. """
  3061. # NOTE: Implementation-specific message! Should be 37 on ratification.
  3062. MESSAGE_TYPE = 337
  3063. """
  3064. The WAMP message code for this type of message.
  3065. """
  3066. __slots__ = (
  3067. 'publication',
  3068. )
  3069. def __init__(self, publication):
  3070. """
  3071. :param publication: The publication ID for the sent event.
  3072. :type publication: int
  3073. """
  3074. assert(type(publication) == int)
  3075. Message.__init__(self)
  3076. self.publication = publication
  3077. @staticmethod
  3078. def parse(wmsg):
  3079. """
  3080. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3081. :param wmsg: The unserialized raw message.
  3082. :type wmsg: list
  3083. :returns: An instance of this class.
  3084. """
  3085. # this should already be verified by WampSerializer.unserialize
  3086. assert(len(wmsg) > 0 and wmsg[0] == EventReceived.MESSAGE_TYPE)
  3087. if len(wmsg) != 2:
  3088. raise ProtocolError("invalid message length {0} for EVENT_RECEIVED".format(len(wmsg)))
  3089. publication = check_or_raise_id(wmsg[1], "'publication' in EVENT_RECEIVED")
  3090. obj = EventReceived(publication)
  3091. return obj
  3092. def marshal(self):
  3093. """
  3094. Marshal this object into a raw message for subsequent serialization to bytes.
  3095. :returns: The serialized raw message.
  3096. :rtype: list
  3097. """
  3098. return [EventReceived.MESSAGE_TYPE, self.publication]
  3099. class Call(Message):
  3100. """
  3101. A WAMP ``CALL`` message.
  3102. Formats:
  3103. * ``[CALL, Request|id, Options|dict, Procedure|uri]``
  3104. * ``[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list]``
  3105. * ``[CALL, Request|id, Options|dict, Procedure|uri, Arguments|list, ArgumentsKw|dict]``
  3106. * ``[CALL, Request|id, Options|dict, Procedure|uri, Payload|binary]``
  3107. """
  3108. MESSAGE_TYPE = 48
  3109. """
  3110. The WAMP message code for this type of message.
  3111. """
  3112. __slots__ = (
  3113. 'request',
  3114. 'procedure',
  3115. 'args',
  3116. 'kwargs',
  3117. 'payload',
  3118. 'timeout',
  3119. 'receive_progress',
  3120. 'transaction_hash',
  3121. 'enc_algo',
  3122. 'enc_key',
  3123. 'enc_serializer',
  3124. 'caller',
  3125. 'caller_authid',
  3126. 'caller_authrole',
  3127. 'forward_for',
  3128. )
  3129. def __init__(self,
  3130. request,
  3131. procedure,
  3132. args=None,
  3133. kwargs=None,
  3134. payload=None,
  3135. timeout=None,
  3136. receive_progress=None,
  3137. transaction_hash=None,
  3138. enc_algo=None,
  3139. enc_key=None,
  3140. enc_serializer=None,
  3141. caller=None,
  3142. caller_authid=None,
  3143. caller_authrole=None,
  3144. forward_for=None):
  3145. """
  3146. :param request: The WAMP request ID of this request.
  3147. :type request: int
  3148. :param procedure: The WAMP or application URI of the procedure which should be called.
  3149. :type procedure: str
  3150. :param args: Positional values for application-defined call arguments.
  3151. Must be serializable using any serializers in use.
  3152. :type args: list or tuple or None
  3153. :param kwargs: Keyword values for application-defined call arguments.
  3154. Must be serializable using any serializers in use.
  3155. :type kwargs: dict or None
  3156. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  3157. :type payload: bytes or None
  3158. :param timeout: If present, let the callee automatically cancel
  3159. the call after this ms.
  3160. :type timeout: int or None
  3161. :param receive_progress: If ``True``, indicates that the caller wants to receive
  3162. progressive call results.
  3163. :type receive_progress: bool or None
  3164. :param transaction_hash: An application provided transaction hash for the originating call, which may
  3165. be used in the router to throttle or deduplicate the calls on the procedure. See the discussion
  3166. `here <https://github.com/wamp-proto/wamp-proto/issues/391#issuecomment-998577967>`_.
  3167. :type transaction_hash: str
  3168. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  3169. :type enc_algo: str or None
  3170. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  3171. :type enc_key: str or None
  3172. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  3173. :type enc_serializer: str or None
  3174. :param caller: The WAMP session ID of the caller. Only filled if caller is disclosed.
  3175. :type caller: None or int
  3176. :param caller_authid: The WAMP authid of the caller. Only filled if caller is disclosed.
  3177. :type caller_authid: None or unicode
  3178. :param caller_authrole: The WAMP authrole of the caller. Only filled if caller is disclosed.
  3179. :type caller_authrole: None or unicode
  3180. :param forward_for: When this Publish is forwarded for a client (or from an intermediary router).
  3181. :type forward_for: list[dict]
  3182. """
  3183. assert(type(request) == int)
  3184. assert(type(procedure) == str)
  3185. assert(args is None or type(args) in [list, tuple])
  3186. assert(kwargs is None or type(kwargs) == dict)
  3187. assert(payload is None or type(payload) == bytes)
  3188. assert(payload is None or (payload is not None and args is None and kwargs is None))
  3189. assert(timeout is None or type(timeout) == int)
  3190. assert(receive_progress is None or type(receive_progress) == bool)
  3191. assert(transaction_hash is None or type(transaction_hash) == str)
  3192. # payload transparency related knobs
  3193. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  3194. assert(enc_key is None or type(enc_key) == str)
  3195. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  3196. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  3197. assert(caller is None or type(caller) == int)
  3198. assert(caller_authid is None or type(caller_authid) == str)
  3199. assert(caller_authrole is None or type(caller_authrole) == str)
  3200. assert(forward_for is None or type(forward_for) == list)
  3201. if forward_for:
  3202. for ff in forward_for:
  3203. assert type(ff) == dict
  3204. assert 'session' in ff and type(ff['session']) == int
  3205. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  3206. assert 'authrole' in ff and type(ff['authrole']) == str
  3207. Message.__init__(self)
  3208. self.request = request
  3209. self.procedure = procedure
  3210. self.args = args
  3211. self.kwargs = _validate_kwargs(kwargs)
  3212. self.payload = payload
  3213. self.timeout = timeout
  3214. self.receive_progress = receive_progress
  3215. self.transaction_hash = transaction_hash
  3216. # payload transparency related knobs
  3217. self.enc_algo = enc_algo
  3218. self.enc_key = enc_key
  3219. self.enc_serializer = enc_serializer
  3220. # message forwarding
  3221. self.caller = caller
  3222. self.caller_authid = caller_authid
  3223. self.caller_authrole = caller_authrole
  3224. self.forward_for = forward_for
  3225. @staticmethod
  3226. def parse(wmsg):
  3227. """
  3228. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3229. :param wmsg: The unserialized raw message.
  3230. :type wmsg: list
  3231. :returns: An instance of this class.
  3232. """
  3233. # this should already be verified by WampSerializer.unserialize
  3234. assert(len(wmsg) > 0 and wmsg[0] == Call.MESSAGE_TYPE)
  3235. if len(wmsg) not in (4, 5, 6):
  3236. raise ProtocolError("invalid message length {0} for CALL".format(len(wmsg)))
  3237. request = check_or_raise_id(wmsg[1], "'request' in CALL")
  3238. options = check_or_raise_extra(wmsg[2], "'options' in CALL")
  3239. procedure = check_or_raise_uri(wmsg[3], "'procedure' in CALL")
  3240. args = None
  3241. kwargs = None
  3242. payload = None
  3243. enc_algo = None
  3244. enc_key = None
  3245. enc_serializer = None
  3246. if len(wmsg) == 5 and type(wmsg[4]) in [str, bytes]:
  3247. payload = wmsg[4]
  3248. enc_algo = options.get('enc_algo', None)
  3249. if enc_algo and not is_valid_enc_algo(enc_algo):
  3250. raise ProtocolError("invalid value {0} for 'enc_algo' detail in CALL".format(enc_algo))
  3251. enc_key = options.get('enc_key', None)
  3252. if enc_key and type(enc_key) != str:
  3253. raise ProtocolError("invalid type {0} for 'enc_key' detail in CALL".format(type(enc_key)))
  3254. enc_serializer = options.get('enc_serializer', None)
  3255. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  3256. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in CALL".format(enc_serializer))
  3257. else:
  3258. if len(wmsg) > 4:
  3259. args = wmsg[4]
  3260. if args is not None and type(args) != list:
  3261. raise ProtocolError("invalid type {0} for 'args' in CALL".format(type(args)))
  3262. if len(wmsg) > 5:
  3263. kwargs = wmsg[5]
  3264. if type(kwargs) != dict:
  3265. raise ProtocolError("invalid type {0} for 'kwargs' in CALL".format(type(kwargs)))
  3266. timeout = None
  3267. receive_progress = None
  3268. transaction_hash = None
  3269. caller = None
  3270. caller_authid = None
  3271. caller_authrole = None
  3272. forward_for = None
  3273. if 'timeout' in options:
  3274. option_timeout = options['timeout']
  3275. if type(option_timeout) != int:
  3276. raise ProtocolError("invalid type {0} for 'timeout' option in CALL".format(type(option_timeout)))
  3277. if option_timeout < 0:
  3278. raise ProtocolError("invalid value {0} for 'timeout' option in CALL".format(option_timeout))
  3279. timeout = option_timeout
  3280. if 'receive_progress' in options:
  3281. option_receive_progress = options['receive_progress']
  3282. if type(option_receive_progress) != bool:
  3283. raise ProtocolError("invalid type {0} for 'receive_progress' option in CALL".format(type(option_receive_progress)))
  3284. receive_progress = option_receive_progress
  3285. if 'transaction_hash' in options:
  3286. option_transaction_hash = options['transaction_hash']
  3287. if type(option_transaction_hash) != str:
  3288. raise ProtocolError("invalid type {0} for 'transaction_hash' detail in CALL".format(type(option_transaction_hash)))
  3289. transaction_hash = option_transaction_hash
  3290. if 'caller' in options:
  3291. option_caller = options['caller']
  3292. if type(option_caller) != int:
  3293. raise ProtocolError("invalid type {0} for 'caller' detail in CALL".format(type(option_caller)))
  3294. caller = option_caller
  3295. if 'caller_authid' in options:
  3296. option_caller_authid = options['caller_authid']
  3297. if type(option_caller_authid) != str:
  3298. raise ProtocolError("invalid type {0} for 'caller_authid' detail in CALL".format(type(option_caller_authid)))
  3299. caller_authid = option_caller_authid
  3300. if 'caller_authrole' in options:
  3301. option_caller_authrole = options['caller_authrole']
  3302. if type(option_caller_authrole) != str:
  3303. raise ProtocolError("invalid type {0} for 'caller_authrole' detail in CALL".format(type(option_caller_authrole)))
  3304. caller_authrole = option_caller_authrole
  3305. if 'forward_for' in options:
  3306. forward_for = options['forward_for']
  3307. valid = False
  3308. if type(forward_for) == list:
  3309. for ff in forward_for:
  3310. if type(ff) != dict:
  3311. break
  3312. if 'session' not in ff or type(ff['session']) != int:
  3313. break
  3314. if 'authid' not in ff or type(ff['authid']) != str:
  3315. break
  3316. if 'authrole' not in ff or type(ff['authrole']) != str:
  3317. break
  3318. valid = True
  3319. if not valid:
  3320. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in CALL")
  3321. obj = Call(request,
  3322. procedure,
  3323. args=args,
  3324. kwargs=kwargs,
  3325. payload=payload,
  3326. timeout=timeout,
  3327. receive_progress=receive_progress,
  3328. transaction_hash=transaction_hash,
  3329. enc_algo=enc_algo,
  3330. enc_key=enc_key,
  3331. enc_serializer=enc_serializer,
  3332. caller=caller,
  3333. caller_authid=caller_authid,
  3334. caller_authrole=caller_authrole,
  3335. forward_for=forward_for)
  3336. return obj
  3337. def marshal_options(self):
  3338. options = {}
  3339. if self.timeout is not None:
  3340. options['timeout'] = self.timeout
  3341. if self.receive_progress is not None:
  3342. options['receive_progress'] = self.receive_progress
  3343. if self.transaction_hash is not None:
  3344. options['transaction_hash'] = self.transaction_hash
  3345. if self.payload:
  3346. if self.enc_algo is not None:
  3347. options['enc_algo'] = self.enc_algo
  3348. if self.enc_key is not None:
  3349. options['enc_key'] = self.enc_key
  3350. if self.enc_serializer is not None:
  3351. options['enc_serializer'] = self.enc_serializer
  3352. if self.caller is not None:
  3353. options['caller'] = self.caller
  3354. if self.caller_authid is not None:
  3355. options['caller_authid'] = self.caller_authid
  3356. if self.caller_authrole is not None:
  3357. options['caller_authrole'] = self.caller_authrole
  3358. if self.forward_for is not None:
  3359. options['forward_for'] = self.forward_for
  3360. return options
  3361. def marshal(self):
  3362. """
  3363. Marshal this object into a raw message for subsequent serialization to bytes.
  3364. :returns: The serialized raw message.
  3365. :rtype: list
  3366. """
  3367. options = self.marshal_options()
  3368. if self.payload:
  3369. return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.payload]
  3370. else:
  3371. if self.kwargs:
  3372. return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.args, self.kwargs]
  3373. elif self.args:
  3374. return [Call.MESSAGE_TYPE, self.request, options, self.procedure, self.args]
  3375. else:
  3376. return [Call.MESSAGE_TYPE, self.request, options, self.procedure]
  3377. class Cancel(Message):
  3378. """
  3379. A WAMP ``CANCEL`` message.
  3380. Format: ``[CANCEL, CALL.Request|id, Options|dict]``
  3381. See: https://wamp-proto.org/static/rfc/draft-oberstet-hybi-crossbar-wamp.html#rfc.section.14.3.4
  3382. """
  3383. MESSAGE_TYPE = 49
  3384. """
  3385. The WAMP message code for this type of message.
  3386. """
  3387. SKIP = 'skip'
  3388. KILL = 'kill'
  3389. KILLNOWAIT = 'killnowait'
  3390. __slots__ = (
  3391. 'request',
  3392. 'mode',
  3393. 'forward_for',
  3394. )
  3395. def __init__(self, request, mode=None, forward_for=None):
  3396. """
  3397. :param request: The WAMP request ID of the original `CALL` to cancel.
  3398. :type request: int
  3399. :param mode: Specifies how to cancel the call (``"skip"``, ``"killnowait"`` or ``"kill"``).
  3400. :type mode: str or None
  3401. :param forward_for: When this Cancel is forwarded for a client (or from an intermediary router).
  3402. :type forward_for: list[dict]
  3403. """
  3404. assert(type(request) == int)
  3405. assert(mode is None or type(mode) == str)
  3406. assert(mode in [None, self.SKIP, self.KILLNOWAIT, self.KILL])
  3407. assert(forward_for is None or type(forward_for) == list)
  3408. if forward_for:
  3409. for ff in forward_for:
  3410. assert type(ff) == dict
  3411. assert 'session' in ff and type(ff['session']) == int
  3412. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  3413. assert 'authrole' in ff and type(ff['authrole']) == str
  3414. Message.__init__(self)
  3415. self.request = request
  3416. self.mode = mode
  3417. # message forwarding
  3418. self.forward_for = forward_for
  3419. @staticmethod
  3420. def parse(wmsg):
  3421. """
  3422. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3423. :param wmsg: The unserialized raw message.
  3424. :type wmsg: list
  3425. :returns: An instance of this class.
  3426. """
  3427. # this should already be verified by WampSerializer.unserialize
  3428. assert(len(wmsg) > 0 and wmsg[0] == Cancel.MESSAGE_TYPE)
  3429. if len(wmsg) != 3:
  3430. raise ProtocolError("invalid message length {0} for CANCEL".format(len(wmsg)))
  3431. request = check_or_raise_id(wmsg[1], "'request' in CANCEL")
  3432. options = check_or_raise_extra(wmsg[2], "'options' in CANCEL")
  3433. # options
  3434. #
  3435. mode = None
  3436. forward_for = None
  3437. if 'mode' in options:
  3438. option_mode = options['mode']
  3439. if type(option_mode) != str:
  3440. raise ProtocolError("invalid type {0} for 'mode' option in CANCEL".format(type(option_mode)))
  3441. if option_mode not in [Cancel.SKIP, Cancel.KILLNOWAIT, Cancel.KILL]:
  3442. raise ProtocolError("invalid value '{0}' for 'mode' option in CANCEL".format(option_mode))
  3443. mode = option_mode
  3444. if 'forward_for' in options:
  3445. forward_for = options['forward_for']
  3446. valid = False
  3447. if type(forward_for) == list:
  3448. for ff in forward_for:
  3449. if type(ff) != dict:
  3450. break
  3451. if 'session' not in ff or type(ff['session']) != int:
  3452. break
  3453. if 'authid' not in ff or type(ff['authid']) != str:
  3454. break
  3455. if 'authrole' not in ff or type(ff['authrole']) != str:
  3456. break
  3457. valid = True
  3458. if not valid:
  3459. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in CANCEL")
  3460. obj = Cancel(request, mode=mode, forward_for=forward_for)
  3461. return obj
  3462. def marshal(self):
  3463. """
  3464. Marshal this object into a raw message for subsequent serialization to bytes.
  3465. :returns: The serialized raw message.
  3466. :rtype: list
  3467. """
  3468. options = {}
  3469. if self.mode is not None:
  3470. options['mode'] = self.mode
  3471. if self.forward_for is not None:
  3472. options['forward_for'] = self.forward_for
  3473. return [Cancel.MESSAGE_TYPE, self.request, options]
  3474. class Result(Message):
  3475. """
  3476. A WAMP ``RESULT`` message.
  3477. Formats:
  3478. * ``[RESULT, CALL.Request|id, Details|dict]``
  3479. * ``[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list]``
  3480. * ``[RESULT, CALL.Request|id, Details|dict, YIELD.Arguments|list, YIELD.ArgumentsKw|dict]``
  3481. * ``[RESULT, CALL.Request|id, Details|dict, Payload|binary]``
  3482. """
  3483. MESSAGE_TYPE = 50
  3484. """
  3485. The WAMP message code for this type of message.
  3486. """
  3487. __slots__ = (
  3488. 'request',
  3489. 'args',
  3490. 'kwargs',
  3491. 'payload',
  3492. 'progress',
  3493. 'enc_algo',
  3494. 'enc_key',
  3495. 'enc_serializer',
  3496. 'callee',
  3497. 'callee_authid',
  3498. 'callee_authrole',
  3499. 'forward_for',
  3500. )
  3501. def __init__(self,
  3502. request,
  3503. args=None,
  3504. kwargs=None,
  3505. payload=None,
  3506. progress=None,
  3507. enc_algo=None,
  3508. enc_key=None,
  3509. enc_serializer=None,
  3510. callee=None,
  3511. callee_authid=None,
  3512. callee_authrole=None,
  3513. forward_for=None):
  3514. """
  3515. :param request: The request ID of the original `CALL` request.
  3516. :type request: int
  3517. :param args: Positional values for application-defined event payload.
  3518. Must be serializable using any serializers in use.
  3519. :type args: list or tuple or None
  3520. :param kwargs: Keyword values for application-defined event payload.
  3521. Must be serializable using any serializers in use.
  3522. :type kwargs: dict or None
  3523. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  3524. :type payload: bytes or None
  3525. :param progress: If ``True``, this result is a progressive call result, and subsequent
  3526. results (or a final error) will follow.
  3527. :type progress: bool or None
  3528. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  3529. :type enc_algo: str or None
  3530. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  3531. :type enc_key: str or None
  3532. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  3533. :type enc_serializer: str or None
  3534. :param callee: The WAMP session ID of the effective callee that responded with the result. Only filled if callee is disclosed.
  3535. :type callee: None or int
  3536. :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed.
  3537. :type callee_authid: None or unicode
  3538. :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed.
  3539. :type callee_authrole: None or unicode
  3540. :param forward_for: When this Result is forwarded for a client/callee (or from an intermediary router).
  3541. :type forward_for: list[dict]
  3542. """
  3543. assert(type(request) == int)
  3544. assert(args is None or type(args) in [list, tuple])
  3545. assert(kwargs is None or type(kwargs) == dict)
  3546. assert(payload is None or type(payload) == bytes)
  3547. assert(payload is None or (payload is not None and args is None and kwargs is None))
  3548. assert(progress is None or type(progress) == bool)
  3549. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  3550. assert(enc_key is None or type(enc_key) == str)
  3551. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  3552. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  3553. assert(callee is None or type(callee) == int)
  3554. assert(callee_authid is None or type(callee_authid) == str)
  3555. assert(callee_authrole is None or type(callee_authrole) == str)
  3556. assert(forward_for is None or type(forward_for) == list)
  3557. if forward_for:
  3558. for ff in forward_for:
  3559. assert type(ff) == dict
  3560. assert 'session' in ff and type(ff['session']) == int
  3561. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  3562. assert 'authrole' in ff and type(ff['authrole']) == str
  3563. Message.__init__(self)
  3564. self.request = request
  3565. self.args = args
  3566. self.kwargs = _validate_kwargs(kwargs)
  3567. self.payload = payload
  3568. self.progress = progress
  3569. # payload transparency related knobs
  3570. self.enc_algo = enc_algo
  3571. self.enc_key = enc_key
  3572. self.enc_serializer = enc_serializer
  3573. # effective callee that responded with the result
  3574. self.callee = callee
  3575. self.callee_authid = callee_authid
  3576. self.callee_authrole = callee_authrole
  3577. # message forwarding
  3578. self.forward_for = forward_for
  3579. @staticmethod
  3580. def parse(wmsg):
  3581. """
  3582. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3583. :param wmsg: The unserialized raw message.
  3584. :type wmsg: list
  3585. :returns: An instance of this class.
  3586. """
  3587. # this should already be verified by WampSerializer.unserialize
  3588. assert(len(wmsg) > 0 and wmsg[0] == Result.MESSAGE_TYPE)
  3589. if len(wmsg) not in (3, 4, 5):
  3590. raise ProtocolError("invalid message length {0} for RESULT".format(len(wmsg)))
  3591. request = check_or_raise_id(wmsg[1], "'request' in RESULT")
  3592. details = check_or_raise_extra(wmsg[2], "'details' in RESULT")
  3593. args = None
  3594. kwargs = None
  3595. payload = None
  3596. progress = None
  3597. enc_algo = None
  3598. enc_key = None
  3599. enc_serializer = None
  3600. callee = None
  3601. callee_authid = None
  3602. callee_authrole = None
  3603. forward_for = None
  3604. if len(wmsg) == 4 and type(wmsg[3]) in [str, bytes]:
  3605. payload = wmsg[3]
  3606. enc_algo = details.get('enc_algo', None)
  3607. if enc_algo and not is_valid_enc_algo(enc_algo):
  3608. raise ProtocolError("invalid value {0} for 'enc_algo' detail in RESULT".format(enc_algo))
  3609. enc_key = details.get('enc_key', None)
  3610. if enc_key and type(enc_key) != str:
  3611. raise ProtocolError("invalid type {0} for 'enc_key' detail in RESULT".format(type(enc_key)))
  3612. enc_serializer = details.get('enc_serializer', None)
  3613. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  3614. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in RESULT".format(enc_serializer))
  3615. else:
  3616. if len(wmsg) > 3:
  3617. args = wmsg[3]
  3618. if args is not None and type(args) != list:
  3619. raise ProtocolError("invalid type {0} for 'args' in RESULT".format(type(args)))
  3620. if len(wmsg) > 4:
  3621. kwargs = wmsg[4]
  3622. if type(kwargs) != dict:
  3623. raise ProtocolError("invalid type {0} for 'kwargs' in RESULT".format(type(kwargs)))
  3624. if 'progress' in details:
  3625. detail_progress = details['progress']
  3626. if type(detail_progress) != bool:
  3627. raise ProtocolError("invalid type {0} for 'progress' option in RESULT".format(type(detail_progress)))
  3628. progress = detail_progress
  3629. if 'callee' in details:
  3630. detail_callee = details['callee']
  3631. if type(detail_callee) != int:
  3632. raise ProtocolError("invalid type {0} for 'callee' detail in RESULT".format(type(detail_callee)))
  3633. callee = detail_callee
  3634. if 'callee_authid' in details:
  3635. detail_callee_authid = details['callee_authid']
  3636. if type(detail_callee_authid) != str:
  3637. raise ProtocolError("invalid type {0} for 'callee_authid' detail in RESULT".format(type(detail_callee_authid)))
  3638. callee_authid = detail_callee_authid
  3639. if 'callee_authrole' in details:
  3640. detail_callee_authrole = details['callee_authrole']
  3641. if type(detail_callee_authrole) != str:
  3642. raise ProtocolError("invalid type {0} for 'callee_authrole' detail in RESULT".format(type(detail_callee_authrole)))
  3643. callee_authrole = detail_callee_authrole
  3644. if 'forward_for' in details:
  3645. forward_for = details['forward_for']
  3646. valid = False
  3647. if type(forward_for) == list:
  3648. for ff in forward_for:
  3649. if type(ff) != dict:
  3650. break
  3651. if 'session' not in ff or type(ff['session']) != int:
  3652. break
  3653. if 'authid' not in ff or type(ff['authid']) != str:
  3654. break
  3655. if 'authrole' not in ff or type(ff['authrole']) != str:
  3656. break
  3657. valid = True
  3658. if not valid:
  3659. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in RESULT")
  3660. obj = Result(request,
  3661. args=args,
  3662. kwargs=kwargs,
  3663. payload=payload,
  3664. progress=progress,
  3665. enc_algo=enc_algo,
  3666. enc_key=enc_key,
  3667. enc_serializer=enc_serializer,
  3668. callee=callee,
  3669. callee_authid=callee_authid,
  3670. callee_authrole=callee_authrole,
  3671. forward_for=forward_for)
  3672. return obj
  3673. def marshal(self):
  3674. """
  3675. Marshal this object into a raw message for subsequent serialization to bytes.
  3676. :returns: The serialized raw message.
  3677. :rtype: list
  3678. """
  3679. details = {}
  3680. if self.progress is not None:
  3681. details['progress'] = self.progress
  3682. if self.callee is not None:
  3683. details['callee'] = self.callee
  3684. if self.callee_authid is not None:
  3685. details['callee_authid'] = self.callee_authid
  3686. if self.callee_authrole is not None:
  3687. details['callee_authrole'] = self.callee_authrole
  3688. if self.forward_for is not None:
  3689. details['forward_for'] = self.forward_for
  3690. if self.payload:
  3691. if self.enc_algo is not None:
  3692. details['enc_algo'] = self.enc_algo
  3693. if self.enc_key is not None:
  3694. details['enc_key'] = self.enc_key
  3695. if self.enc_serializer is not None:
  3696. details['enc_serializer'] = self.enc_serializer
  3697. return [Result.MESSAGE_TYPE, self.request, details, self.payload]
  3698. else:
  3699. if self.kwargs:
  3700. return [Result.MESSAGE_TYPE, self.request, details, self.args, self.kwargs]
  3701. elif self.args:
  3702. return [Result.MESSAGE_TYPE, self.request, details, self.args]
  3703. else:
  3704. return [Result.MESSAGE_TYPE, self.request, details]
  3705. class Register(Message):
  3706. """
  3707. A WAMP ``REGISTER`` message.
  3708. Format: ``[REGISTER, Request|id, Options|dict, Procedure|uri]``
  3709. """
  3710. MESSAGE_TYPE = 64
  3711. """
  3712. The WAMP message code for this type of message.
  3713. """
  3714. MATCH_EXACT = 'exact'
  3715. MATCH_PREFIX = 'prefix'
  3716. MATCH_WILDCARD = 'wildcard'
  3717. INVOKE_SINGLE = 'single'
  3718. INVOKE_FIRST = 'first'
  3719. INVOKE_LAST = 'last'
  3720. INVOKE_ROUNDROBIN = 'roundrobin'
  3721. INVOKE_RANDOM = 'random'
  3722. INVOKE_ALL = 'all'
  3723. __slots__ = (
  3724. 'request',
  3725. 'procedure',
  3726. 'match',
  3727. 'invoke',
  3728. 'concurrency',
  3729. 'force_reregister',
  3730. 'forward_for',
  3731. )
  3732. def __init__(self,
  3733. request,
  3734. procedure,
  3735. match=None,
  3736. invoke=None,
  3737. concurrency=None,
  3738. force_reregister=None,
  3739. forward_for=None):
  3740. """
  3741. :param request: The WAMP request ID of this request.
  3742. :type request: int
  3743. :param procedure: The WAMP or application URI of the RPC endpoint provided.
  3744. :type procedure: str
  3745. :param match: The procedure matching policy to be used for the registration.
  3746. :type match: str
  3747. :param invoke: The procedure invocation policy to be used for the registration.
  3748. :type invoke: str
  3749. :param concurrency: The (maximum) concurrency to be used for the registration.
  3750. :type concurrency: int
  3751. :param forward_for: When this Register is forwarded over a router-to-router link,
  3752. or via an intermediary router.
  3753. :type forward_for: list[dict]
  3754. """
  3755. assert(type(request) == int)
  3756. assert(type(procedure) == str)
  3757. assert(match is None or type(match) == str)
  3758. assert(match is None or match in [Register.MATCH_EXACT, Register.MATCH_PREFIX, Register.MATCH_WILDCARD])
  3759. assert(invoke is None or type(invoke) == str)
  3760. assert(invoke is None or invoke in [Register.INVOKE_SINGLE, Register.INVOKE_FIRST, Register.INVOKE_LAST, Register.INVOKE_ROUNDROBIN, Register.INVOKE_RANDOM])
  3761. assert(concurrency is None or (type(concurrency) == int and concurrency > 0))
  3762. assert force_reregister in [None, True, False]
  3763. assert(forward_for is None or type(forward_for) == list)
  3764. if forward_for:
  3765. for ff in forward_for:
  3766. assert type(ff) == dict
  3767. assert 'session' in ff and type(ff['session']) == int
  3768. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  3769. assert 'authrole' in ff and type(ff['authrole']) == str
  3770. Message.__init__(self)
  3771. self.request = request
  3772. self.procedure = procedure
  3773. self.match = match or Register.MATCH_EXACT
  3774. self.invoke = invoke or Register.INVOKE_SINGLE
  3775. self.concurrency = concurrency
  3776. self.force_reregister = force_reregister
  3777. self.forward_for = forward_for
  3778. @staticmethod
  3779. def parse(wmsg):
  3780. """
  3781. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3782. :param wmsg: The unserialized raw message.
  3783. :type wmsg: list
  3784. :returns: An instance of this class.
  3785. """
  3786. # this should already be verified by WampSerializer.unserialize
  3787. assert(len(wmsg) > 0 and wmsg[0] == Register.MESSAGE_TYPE)
  3788. if len(wmsg) != 4:
  3789. raise ProtocolError("invalid message length {0} for REGISTER".format(len(wmsg)))
  3790. request = check_or_raise_id(wmsg[1], "'request' in REGISTER")
  3791. options = check_or_raise_extra(wmsg[2], "'options' in REGISTER")
  3792. match = Register.MATCH_EXACT
  3793. invoke = Register.INVOKE_SINGLE
  3794. concurrency = None
  3795. force_reregister = None
  3796. forward_for = None
  3797. if 'match' in options:
  3798. option_match = options['match']
  3799. if type(option_match) != str:
  3800. raise ProtocolError("invalid type {0} for 'match' option in REGISTER".format(type(option_match)))
  3801. if option_match not in [Register.MATCH_EXACT, Register.MATCH_PREFIX, Register.MATCH_WILDCARD]:
  3802. raise ProtocolError("invalid value {0} for 'match' option in REGISTER".format(option_match))
  3803. match = option_match
  3804. if match == Register.MATCH_EXACT:
  3805. allow_empty_components = False
  3806. allow_last_empty = False
  3807. elif match == Register.MATCH_PREFIX:
  3808. allow_empty_components = False
  3809. allow_last_empty = True
  3810. elif match == Register.MATCH_WILDCARD:
  3811. allow_empty_components = True
  3812. allow_last_empty = False
  3813. else:
  3814. raise Exception("logic error")
  3815. procedure = check_or_raise_uri(wmsg[3], "'procedure' in REGISTER", allow_empty_components=allow_empty_components, allow_last_empty=allow_last_empty)
  3816. if 'invoke' in options:
  3817. option_invoke = options['invoke']
  3818. if type(option_invoke) != str:
  3819. raise ProtocolError("invalid type {0} for 'invoke' option in REGISTER".format(type(option_invoke)))
  3820. if option_invoke not in [Register.INVOKE_SINGLE, Register.INVOKE_FIRST, Register.INVOKE_LAST, Register.INVOKE_ROUNDROBIN, Register.INVOKE_RANDOM]:
  3821. raise ProtocolError("invalid value {0} for 'invoke' option in REGISTER".format(option_invoke))
  3822. invoke = option_invoke
  3823. if 'concurrency' in options:
  3824. options_concurrency = options['concurrency']
  3825. if type(options_concurrency) != int:
  3826. raise ProtocolError("invalid type {0} for 'concurrency' option in REGISTER".format(type(options_concurrency)))
  3827. if options_concurrency < 1:
  3828. raise ProtocolError("invalid value {0} for 'concurrency' option in REGISTER".format(options_concurrency))
  3829. concurrency = options_concurrency
  3830. options_reregister = options.get('force_reregister', None)
  3831. if options_reregister not in [True, False, None]:
  3832. raise ProtocolError(
  3833. "invalid type {0} for 'force_reregister option in REGISTER".format(
  3834. type(options_reregister)
  3835. )
  3836. )
  3837. if options_reregister is not None:
  3838. force_reregister = options_reregister
  3839. if 'forward_for' in options:
  3840. forward_for = options['forward_for']
  3841. valid = False
  3842. if type(forward_for) == list:
  3843. for ff in forward_for:
  3844. if type(ff) != dict:
  3845. break
  3846. if 'session' not in ff or type(ff['session']) != int:
  3847. break
  3848. if 'authid' not in ff or type(ff['authid']) != str:
  3849. break
  3850. if 'authrole' not in ff or type(ff['authrole']) != str:
  3851. break
  3852. valid = True
  3853. if not valid:
  3854. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in REGISTER")
  3855. obj = Register(request, procedure, match=match, invoke=invoke, concurrency=concurrency,
  3856. force_reregister=force_reregister, forward_for=forward_for)
  3857. return obj
  3858. def marshal_options(self):
  3859. options = {}
  3860. if self.match and self.match != Register.MATCH_EXACT:
  3861. options['match'] = self.match
  3862. if self.invoke and self.invoke != Register.INVOKE_SINGLE:
  3863. options['invoke'] = self.invoke
  3864. if self.concurrency:
  3865. options['concurrency'] = self.concurrency
  3866. if self.force_reregister is not None:
  3867. options['force_reregister'] = self.force_reregister
  3868. if self.forward_for is not None:
  3869. options['forward_for'] = self.forward_for
  3870. return options
  3871. def marshal(self):
  3872. """
  3873. Marshal this object into a raw message for subsequent serialization to bytes.
  3874. :returns: The serialized raw message.
  3875. :rtype: list
  3876. """
  3877. return [Register.MESSAGE_TYPE, self.request, self.marshal_options(), self.procedure]
  3878. class Registered(Message):
  3879. """
  3880. A WAMP ``REGISTERED`` message.
  3881. Format: ``[REGISTERED, REGISTER.Request|id, Registration|id]``
  3882. """
  3883. MESSAGE_TYPE = 65
  3884. """
  3885. The WAMP message code for this type of message.
  3886. """
  3887. __slots__ = (
  3888. 'request',
  3889. 'registration',
  3890. )
  3891. def __init__(self, request, registration):
  3892. """
  3893. :param request: The request ID of the original ``REGISTER`` request.
  3894. :type request: int
  3895. :param registration: The registration ID for the registered procedure (or procedure pattern).
  3896. :type registration: int
  3897. """
  3898. assert(type(request) == int)
  3899. assert(type(registration) == int)
  3900. Message.__init__(self)
  3901. self.request = request
  3902. self.registration = registration
  3903. @staticmethod
  3904. def parse(wmsg):
  3905. """
  3906. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3907. :param wmsg: The unserialized raw message.
  3908. :type wmsg: list
  3909. :returns: An instance of this class.
  3910. """
  3911. # this should already be verified by WampSerializer.unserialize
  3912. assert(len(wmsg) > 0 and wmsg[0] == Registered.MESSAGE_TYPE)
  3913. if len(wmsg) != 3:
  3914. raise ProtocolError("invalid message length {0} for REGISTERED".format(len(wmsg)))
  3915. request = check_or_raise_id(wmsg[1], "'request' in REGISTERED")
  3916. registration = check_or_raise_id(wmsg[2], "'registration' in REGISTERED")
  3917. obj = Registered(request, registration)
  3918. return obj
  3919. def marshal(self):
  3920. """
  3921. Marshal this object into a raw message for subsequent serialization to bytes.
  3922. :returns: The serialized raw message.
  3923. :rtype: list
  3924. """
  3925. return [Registered.MESSAGE_TYPE, self.request, self.registration]
  3926. class Unregister(Message):
  3927. """
  3928. A WAMP `UNREGISTER` message.
  3929. Formats:
  3930. * ``[UNREGISTER, Request|id, REGISTERED.Registration|id]``
  3931. * ``[UNREGISTER, Request|id, REGISTERED.Registration|id, Options|dict]``
  3932. """
  3933. MESSAGE_TYPE = 66
  3934. """
  3935. The WAMP message code for this type of message.
  3936. """
  3937. __slots__ = (
  3938. 'request',
  3939. 'registration',
  3940. 'forward_for',
  3941. )
  3942. def __init__(self, request, registration, forward_for=None):
  3943. """
  3944. :param request: The WAMP request ID of this request.
  3945. :type request: int
  3946. :param registration: The registration ID for the registration to unregister.
  3947. :type registration: int
  3948. :param forward_for: When this Unregister is forwarded over a router-to-router link,
  3949. or via an intermediary router.
  3950. :type forward_for: list[dict]
  3951. """
  3952. assert(type(request) == int)
  3953. assert(type(registration) == int)
  3954. Message.__init__(self)
  3955. self.request = request
  3956. self.registration = registration
  3957. self.forward_for = forward_for
  3958. @staticmethod
  3959. def parse(wmsg):
  3960. """
  3961. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  3962. :param wmsg: The unserialized raw message.
  3963. :type wmsg: list
  3964. :returns: An instance of this class.
  3965. """
  3966. # this should already be verified by WampSerializer.unserialize
  3967. assert(len(wmsg) > 0 and wmsg[0] == Unregister.MESSAGE_TYPE)
  3968. if len(wmsg) not in [3, 4]:
  3969. raise ProtocolError("invalid message length {0} for WAMP UNREGISTER".format(len(wmsg)))
  3970. request = check_or_raise_id(wmsg[1], "'request' in UNREGISTER")
  3971. registration = check_or_raise_id(wmsg[2], "'registration' in UNREGISTER")
  3972. options = None
  3973. if len(wmsg) > 3:
  3974. options = check_or_raise_extra(wmsg[3], "'options' in UNREGISTER")
  3975. forward_for = None
  3976. if options and 'forward_for' in options:
  3977. forward_for = options['forward_for']
  3978. valid = False
  3979. if type(forward_for) == list:
  3980. for ff in forward_for:
  3981. if type(ff) != dict:
  3982. break
  3983. if 'session' not in ff or type(ff['session']) != int:
  3984. break
  3985. if 'authid' not in ff or type(ff['authid']) != str:
  3986. break
  3987. if 'authrole' not in ff or type(ff['authrole']) != str:
  3988. break
  3989. valid = True
  3990. if not valid:
  3991. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in UNREGISTER")
  3992. obj = Unregister(request, registration, forward_for=forward_for)
  3993. return obj
  3994. def marshal(self):
  3995. """
  3996. Marshal this object into a raw message for subsequent serialization to bytes.
  3997. :returns: The serialized raw message.
  3998. :rtype: list
  3999. """
  4000. if self.forward_for:
  4001. options = {
  4002. 'forward_for': self.forward_for,
  4003. }
  4004. return [Unregister.MESSAGE_TYPE, self.request, self.registration, options]
  4005. else:
  4006. return [Unregister.MESSAGE_TYPE, self.request, self.registration]
  4007. class Unregistered(Message):
  4008. """
  4009. A WAMP ``UNREGISTERED`` message.
  4010. Formats:
  4011. * ``[UNREGISTERED, UNREGISTER.Request|id]``
  4012. * ``[UNREGISTERED, UNREGISTER.Request|id, Details|dict]``
  4013. """
  4014. MESSAGE_TYPE = 67
  4015. """
  4016. The WAMP message code for this type of message.
  4017. """
  4018. __slots__ = (
  4019. 'request',
  4020. 'registration',
  4021. 'reason',
  4022. )
  4023. def __init__(self, request, registration=None, reason=None):
  4024. """
  4025. :param request: The request ID of the original ``UNREGISTER`` request.
  4026. :type request: int
  4027. :param registration: If unregister was actively triggered by router, the ID
  4028. of the registration revoked.
  4029. :type registration: int or None
  4030. :param reason: The reason (an URI) for revocation.
  4031. :type reason: str or None.
  4032. """
  4033. assert(type(request) == int)
  4034. assert(registration is None or type(registration) == int)
  4035. assert(reason is None or type(reason) == str)
  4036. assert((request != 0 and registration is None) or (request == 0 and registration != 0))
  4037. Message.__init__(self)
  4038. self.request = request
  4039. self.registration = registration
  4040. self.reason = reason
  4041. @staticmethod
  4042. def parse(wmsg):
  4043. """
  4044. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  4045. :param wmsg: The unserialized raw message.
  4046. :type wmsg: list
  4047. :returns: An instance of this class.
  4048. """
  4049. # this should already be verified by WampSerializer.unserialize
  4050. assert(len(wmsg) > 0 and wmsg[0] == Unregistered.MESSAGE_TYPE)
  4051. if len(wmsg) not in [2, 3]:
  4052. raise ProtocolError("invalid message length {0} for UNREGISTERED".format(len(wmsg)))
  4053. request = check_or_raise_id(wmsg[1], "'request' in UNREGISTERED")
  4054. registration = None
  4055. reason = None
  4056. if len(wmsg) > 2:
  4057. details = check_or_raise_extra(wmsg[2], "'details' in UNREGISTERED")
  4058. if "registration" in details:
  4059. details_registration = details["registration"]
  4060. if type(details_registration) != int:
  4061. raise ProtocolError("invalid type {0} for 'registration' detail in UNREGISTERED".format(type(details_registration)))
  4062. registration = details_registration
  4063. if "reason" in details:
  4064. reason = check_or_raise_uri(details["reason"], "'reason' in UNREGISTERED")
  4065. obj = Unregistered(request, registration, reason)
  4066. return obj
  4067. def marshal(self):
  4068. """
  4069. Marshal this object into a raw message for subsequent serialization to bytes.
  4070. :returns: The serialized raw message.
  4071. :rtype: list
  4072. """
  4073. if self.reason is not None or self.registration is not None:
  4074. details = {}
  4075. if self.reason is not None:
  4076. details["reason"] = self.reason
  4077. if self.registration is not None:
  4078. details["registration"] = self.registration
  4079. return [Unregistered.MESSAGE_TYPE, self.request, details]
  4080. else:
  4081. return [Unregistered.MESSAGE_TYPE, self.request]
  4082. class Invocation(Message):
  4083. """
  4084. A WAMP ``INVOCATION`` message.
  4085. Formats:
  4086. * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict]``
  4087. * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list]``
  4088. * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, CALL.Arguments|list, CALL.ArgumentsKw|dict]``
  4089. * ``[INVOCATION, Request|id, REGISTERED.Registration|id, Details|dict, Payload|binary]``
  4090. """
  4091. MESSAGE_TYPE = 68
  4092. """
  4093. The WAMP message code for this type of message.
  4094. """
  4095. __slots__ = (
  4096. 'request',
  4097. 'registration',
  4098. 'args',
  4099. 'kwargs',
  4100. 'payload',
  4101. 'timeout',
  4102. 'receive_progress',
  4103. 'caller',
  4104. 'caller_authid',
  4105. 'caller_authrole',
  4106. 'procedure',
  4107. 'transaction_hash',
  4108. 'enc_algo',
  4109. 'enc_key',
  4110. 'enc_serializer',
  4111. 'forward_for',
  4112. )
  4113. def __init__(self,
  4114. request,
  4115. registration,
  4116. args=None,
  4117. kwargs=None,
  4118. payload=None,
  4119. timeout=None,
  4120. receive_progress=None,
  4121. caller=None,
  4122. caller_authid=None,
  4123. caller_authrole=None,
  4124. procedure=None,
  4125. transaction_hash=None,
  4126. enc_algo=None,
  4127. enc_key=None,
  4128. enc_serializer=None,
  4129. forward_for=None):
  4130. """
  4131. :param request: The WAMP request ID of this request.
  4132. :type request: int
  4133. :param registration: The registration ID of the endpoint to be invoked.
  4134. :type registration: int
  4135. :param args: Positional values for application-defined event payload.
  4136. Must be serializable using any serializers in use.
  4137. :type args: list or tuple or None
  4138. :param kwargs: Keyword values for application-defined event payload.
  4139. Must be serializable using any serializers in use.
  4140. :type kwargs: dict or None
  4141. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  4142. :type payload: bytes or None
  4143. :param timeout: If present, let the callee automatically cancels
  4144. the invocation after this ms.
  4145. :type timeout: int or None
  4146. :param receive_progress: Indicates if the callee should produce progressive results.
  4147. :type receive_progress: bool or None
  4148. :param caller: The WAMP session ID of the caller. Only filled if caller is disclosed.
  4149. :type caller: None or int
  4150. :param caller_authid: The WAMP authid of the caller. Only filled if caller is disclosed.
  4151. :type caller_authid: None or unicode
  4152. :param caller_authrole: The WAMP authrole of the caller. Only filled if caller is disclosed.
  4153. :type caller_authrole: None or unicode
  4154. :param procedure: For pattern-based registrations, the invocation MUST include the actual procedure being called.
  4155. :type procedure: str or None
  4156. :param transaction_hash: An application provided transaction hash for the originating call, which may
  4157. be used in the router to throttle or deduplicate the calls on the procedure. See the discussion
  4158. `here <https://github.com/wamp-proto/wamp-proto/issues/391#issuecomment-998577967>`_.
  4159. :type transaction_hash: str
  4160. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  4161. :type enc_algo: str or None
  4162. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  4163. :type enc_key: str or None
  4164. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  4165. :type enc_serializer: str or None
  4166. :param forward_for: When this Call is forwarded for a client (or from an intermediary router).
  4167. :type forward_for: list[dict]
  4168. """
  4169. assert(type(request) == int)
  4170. assert(type(registration) == int)
  4171. assert(args is None or type(args) in [list, tuple])
  4172. assert(kwargs is None or type(kwargs) == dict)
  4173. assert(payload is None or type(payload) == bytes)
  4174. assert(payload is None or (payload is not None and args is None and kwargs is None))
  4175. assert(timeout is None or type(timeout) == int)
  4176. assert(receive_progress is None or type(receive_progress) == bool)
  4177. assert(caller is None or type(caller) == int)
  4178. assert(caller_authid is None or type(caller_authid) == str)
  4179. assert(caller_authrole is None or type(caller_authrole) == str)
  4180. assert(procedure is None or type(procedure) == str)
  4181. assert(transaction_hash is None or type(transaction_hash) == str)
  4182. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  4183. assert(enc_key is None or type(enc_key) == str)
  4184. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  4185. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  4186. assert(forward_for is None or type(forward_for) == list)
  4187. if forward_for:
  4188. for ff in forward_for:
  4189. assert type(ff) == dict
  4190. assert 'session' in ff and type(ff['session']) == int
  4191. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  4192. assert 'authrole' in ff and type(ff['authrole']) == str
  4193. Message.__init__(self)
  4194. self.request = request
  4195. self.registration = registration
  4196. self.args = args
  4197. self.kwargs = _validate_kwargs(kwargs)
  4198. self.payload = payload
  4199. self.timeout = timeout
  4200. self.receive_progress = receive_progress
  4201. self.caller = caller
  4202. self.caller_authid = caller_authid
  4203. self.caller_authrole = caller_authrole
  4204. self.procedure = procedure
  4205. self.transaction_hash = transaction_hash
  4206. self.enc_algo = enc_algo
  4207. self.enc_key = enc_key
  4208. self.enc_serializer = enc_serializer
  4209. # message forwarding
  4210. self.forward_for = forward_for
  4211. @staticmethod
  4212. def parse(wmsg):
  4213. """
  4214. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  4215. :param wmsg: The unserialized raw message.
  4216. :type wmsg: list
  4217. :returns: An instance of this class.
  4218. """
  4219. # this should already be verified by WampSerializer.unserialize
  4220. assert(len(wmsg) > 0 and wmsg[0] == Invocation.MESSAGE_TYPE)
  4221. if len(wmsg) not in (4, 5, 6):
  4222. raise ProtocolError("invalid message length {0} for INVOCATION".format(len(wmsg)))
  4223. request = check_or_raise_id(wmsg[1], "'request' in INVOCATION")
  4224. registration = check_or_raise_id(wmsg[2], "'registration' in INVOCATION")
  4225. details = check_or_raise_extra(wmsg[3], "'details' in INVOCATION")
  4226. args = None
  4227. kwargs = None
  4228. payload = None
  4229. enc_algo = None
  4230. enc_key = None
  4231. enc_serializer = None
  4232. if len(wmsg) == 5 and type(wmsg[4]) == bytes:
  4233. payload = wmsg[4]
  4234. enc_algo = details.get('enc_algo', None)
  4235. if enc_algo and not is_valid_enc_algo(enc_algo):
  4236. raise ProtocolError("invalid value {0} for 'enc_algo' detail in INVOCATION".format(enc_algo))
  4237. enc_key = details.get('enc_key', None)
  4238. if enc_key and type(enc_key) != str:
  4239. raise ProtocolError("invalid type {0} for 'enc_key' detail in INVOCATION".format(type(enc_key)))
  4240. enc_serializer = details.get('enc_serializer', None)
  4241. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  4242. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in INVOCATION".format(enc_serializer))
  4243. else:
  4244. if len(wmsg) > 4:
  4245. args = wmsg[4]
  4246. if args is not None and type(args) != list:
  4247. raise ProtocolError("invalid type {0} for 'args' in INVOCATION".format(type(args)))
  4248. if len(wmsg) > 5:
  4249. kwargs = wmsg[5]
  4250. if type(kwargs) != dict:
  4251. raise ProtocolError("invalid type {0} for 'kwargs' in INVOCATION".format(type(kwargs)))
  4252. timeout = None
  4253. receive_progress = None
  4254. caller = None
  4255. caller_authid = None
  4256. caller_authrole = None
  4257. procedure = None
  4258. transaction_hash = None
  4259. forward_for = None
  4260. if 'timeout' in details:
  4261. detail_timeout = details['timeout']
  4262. if type(detail_timeout) != int:
  4263. raise ProtocolError("invalid type {0} for 'timeout' detail in INVOCATION".format(type(detail_timeout)))
  4264. if detail_timeout < 0:
  4265. raise ProtocolError("invalid value {0} for 'timeout' detail in INVOCATION".format(detail_timeout))
  4266. timeout = detail_timeout
  4267. if 'receive_progress' in details:
  4268. detail_receive_progress = details['receive_progress']
  4269. if type(detail_receive_progress) != bool:
  4270. raise ProtocolError("invalid type {0} for 'receive_progress' detail in INVOCATION".format(type(detail_receive_progress)))
  4271. receive_progress = detail_receive_progress
  4272. if 'caller' in details:
  4273. detail_caller = details['caller']
  4274. if type(detail_caller) != int:
  4275. raise ProtocolError("invalid type {0} for 'caller' detail in INVOCATION".format(type(detail_caller)))
  4276. caller = detail_caller
  4277. if 'caller_authid' in details:
  4278. detail_caller_authid = details['caller_authid']
  4279. if type(detail_caller_authid) != str:
  4280. raise ProtocolError("invalid type {0} for 'caller_authid' detail in INVOCATION".format(type(detail_caller_authid)))
  4281. caller_authid = detail_caller_authid
  4282. if 'caller_authrole' in details:
  4283. detail_caller_authrole = details['caller_authrole']
  4284. if type(detail_caller_authrole) != str:
  4285. raise ProtocolError("invalid type {0} for 'caller_authrole' detail in INVOCATION".format(type(detail_caller_authrole)))
  4286. caller_authrole = detail_caller_authrole
  4287. if 'procedure' in details:
  4288. detail_procedure = details['procedure']
  4289. if type(detail_procedure) != str:
  4290. raise ProtocolError("invalid type {0} for 'procedure' detail in INVOCATION".format(type(detail_procedure)))
  4291. procedure = detail_procedure
  4292. if 'transaction_hash' in details:
  4293. detail_transaction_hash = details['transaction_hash']
  4294. if type(detail_transaction_hash) != str:
  4295. raise ProtocolError("invalid type {0} for 'transaction_hash' detail in EVENT".format(type(detail_transaction_hash)))
  4296. transaction_hash = detail_transaction_hash
  4297. if 'forward_for' in details:
  4298. forward_for = details['forward_for']
  4299. valid = False
  4300. if type(forward_for) == list:
  4301. for ff in forward_for:
  4302. if type(ff) != dict:
  4303. break
  4304. if 'session' not in ff or type(ff['session']) != int:
  4305. break
  4306. if 'authid' not in ff or type(ff['authid']) != str:
  4307. break
  4308. if 'authrole' not in ff or type(ff['authrole']) != str:
  4309. break
  4310. valid = True
  4311. if not valid:
  4312. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in INVOCATION")
  4313. obj = Invocation(request,
  4314. registration,
  4315. args=args,
  4316. kwargs=kwargs,
  4317. payload=payload,
  4318. timeout=timeout,
  4319. receive_progress=receive_progress,
  4320. caller=caller,
  4321. caller_authid=caller_authid,
  4322. caller_authrole=caller_authrole,
  4323. procedure=procedure,
  4324. transaction_hash=transaction_hash,
  4325. enc_algo=enc_algo,
  4326. enc_key=enc_key,
  4327. enc_serializer=enc_serializer,
  4328. forward_for=forward_for)
  4329. return obj
  4330. def marshal(self):
  4331. """
  4332. Marshal this object into a raw message for subsequent serialization to bytes.
  4333. :returns: The serialized raw message.
  4334. :rtype: list
  4335. """
  4336. options = {}
  4337. if self.timeout is not None:
  4338. options['timeout'] = self.timeout
  4339. if self.receive_progress is not None:
  4340. options['receive_progress'] = self.receive_progress
  4341. if self.caller is not None:
  4342. options['caller'] = self.caller
  4343. if self.caller_authid is not None:
  4344. options['caller_authid'] = self.caller_authid
  4345. if self.caller_authrole is not None:
  4346. options['caller_authrole'] = self.caller_authrole
  4347. if self.procedure is not None:
  4348. options['procedure'] = self.procedure
  4349. if self.transaction_hash is not None:
  4350. options['transaction_hash'] = self.transaction_hash
  4351. if self.forward_for is not None:
  4352. options['forward_for'] = self.forward_for
  4353. if self.payload:
  4354. if self.enc_algo is not None:
  4355. options['enc_algo'] = self.enc_algo
  4356. if self.enc_key is not None:
  4357. options['enc_key'] = self.enc_key
  4358. if self.enc_serializer is not None:
  4359. options['enc_serializer'] = self.enc_serializer
  4360. return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.payload]
  4361. else:
  4362. if self.kwargs:
  4363. return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.args, self.kwargs]
  4364. elif self.args:
  4365. return [Invocation.MESSAGE_TYPE, self.request, self.registration, options, self.args]
  4366. else:
  4367. return [Invocation.MESSAGE_TYPE, self.request, self.registration, options]
  4368. class Interrupt(Message):
  4369. """
  4370. A WAMP ``INTERRUPT`` message.
  4371. Format: ``[INTERRUPT, INVOCATION.Request|id, Options|dict]``
  4372. See: https://wamp-proto.org/static/rfc/draft-oberstet-hybi-crossbar-wamp.html#rfc.section.14.3.4
  4373. """
  4374. MESSAGE_TYPE = 69
  4375. """
  4376. The WAMP message code for this type of message.
  4377. """
  4378. KILL = 'kill'
  4379. KILLNOWAIT = 'killnowait'
  4380. __slots__ = (
  4381. 'request',
  4382. 'mode',
  4383. 'reason',
  4384. 'forward_for',
  4385. )
  4386. def __init__(self, request, mode=None, reason=None, forward_for=None):
  4387. """
  4388. :param request: The WAMP request ID of the original ``INVOCATION`` to interrupt.
  4389. :type request: int
  4390. :param mode: Specifies how to interrupt the invocation (``"killnowait"`` or ``"kill"``).
  4391. With ``"kill"``, the router will wait for the callee to return an ERROR before
  4392. proceeding (sending back an ERROR to the original caller). With ``"killnowait"`` the
  4393. router will immediately proceed (on the caller side returning an ERROR) - but still
  4394. expects the callee to send an ERROR to conclude the message exchange for the inflight
  4395. call.
  4396. :type mode: str or None
  4397. :param reason: The reason (an URI) for the invocation interrupt, eg actively
  4398. triggered by the caller (``"wamp.error.canceled"`` - ApplicationError.CANCELED) or
  4399. passively because of timeout (``"wamp.error.timeout"`` - ApplicationError.TIMEOUT).
  4400. :type reason: str or None.
  4401. :param forward_for: When this Call is forwarded for a client (or from an intermediary router).
  4402. :type forward_for: list[dict]
  4403. """
  4404. assert(type(request) == int)
  4405. assert(mode is None or type(mode) == str)
  4406. assert(mode is None or mode in [self.KILL, self.KILLNOWAIT])
  4407. assert(reason is None or type(reason) == str)
  4408. assert(forward_for is None or type(forward_for) == list)
  4409. if forward_for:
  4410. for ff in forward_for:
  4411. assert type(ff) == dict
  4412. assert 'session' in ff and type(ff['session']) == int
  4413. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  4414. assert 'authrole' in ff and type(ff['authrole']) == str
  4415. Message.__init__(self)
  4416. self.request = request
  4417. self.mode = mode
  4418. self.reason = reason
  4419. # message forwarding
  4420. self.forward_for = forward_for
  4421. @staticmethod
  4422. def parse(wmsg):
  4423. """
  4424. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  4425. :param wmsg: The unserialized raw message.
  4426. :type wmsg: list
  4427. :returns: An instance of this class.
  4428. """
  4429. # this should already be verified by WampSerializer.unserialize
  4430. assert(len(wmsg) > 0 and wmsg[0] == Interrupt.MESSAGE_TYPE)
  4431. if len(wmsg) != 3:
  4432. raise ProtocolError("invalid message length {0} for INTERRUPT".format(len(wmsg)))
  4433. request = check_or_raise_id(wmsg[1], "'request' in INTERRUPT")
  4434. options = check_or_raise_extra(wmsg[2], "'options' in INTERRUPT")
  4435. # options
  4436. #
  4437. mode = None
  4438. reason = None
  4439. forward_for = None
  4440. if 'mode' in options:
  4441. option_mode = options['mode']
  4442. if type(option_mode) != str:
  4443. raise ProtocolError("invalid type {0} for 'mode' option in INTERRUPT".format(type(option_mode)))
  4444. if option_mode not in [Interrupt.KILL, Interrupt.KILLNOWAIT]:
  4445. raise ProtocolError("invalid value '{0}' for 'mode' option in INTERRUPT".format(option_mode))
  4446. mode = option_mode
  4447. if 'reason' in options:
  4448. reason = check_or_raise_uri(options['reason'], '"reason" in INTERRUPT')
  4449. if 'forward_for' in options:
  4450. forward_for = options['forward_for']
  4451. valid = False
  4452. if type(forward_for) == list:
  4453. for ff in forward_for:
  4454. if type(ff) != dict:
  4455. break
  4456. if 'session' not in ff or type(ff['session']) != int:
  4457. break
  4458. if 'authid' not in ff or type(ff['authid']) != str:
  4459. break
  4460. if 'authrole' not in ff or type(ff['authrole']) != str:
  4461. break
  4462. valid = True
  4463. if not valid:
  4464. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in INTERRUPT")
  4465. obj = Interrupt(request, mode=mode, reason=reason, forward_for=forward_for)
  4466. return obj
  4467. def marshal(self):
  4468. """
  4469. Marshal this object into a raw message for subsequent serialization to bytes.
  4470. :returns: The serialized raw message.
  4471. :rtype: list
  4472. """
  4473. options = {}
  4474. if self.mode is not None:
  4475. options['mode'] = self.mode
  4476. if self.reason is not None:
  4477. options['reason'] = self.reason
  4478. if self.forward_for is not None:
  4479. options['forward_for'] = self.forward_for
  4480. return [Interrupt.MESSAGE_TYPE, self.request, options]
  4481. class Yield(Message):
  4482. """
  4483. A WAMP ``YIELD`` message.
  4484. Formats:
  4485. * ``[YIELD, INVOCATION.Request|id, Options|dict]``
  4486. * ``[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list]``
  4487. * ``[YIELD, INVOCATION.Request|id, Options|dict, Arguments|list, ArgumentsKw|dict]``
  4488. * ``[YIELD, INVOCATION.Request|id, Options|dict, Payload|binary]``
  4489. """
  4490. MESSAGE_TYPE = 70
  4491. """
  4492. The WAMP message code for this type of message.
  4493. """
  4494. __slots__ = (
  4495. 'request',
  4496. 'args',
  4497. 'kwargs',
  4498. 'payload',
  4499. 'progress',
  4500. 'enc_algo',
  4501. 'enc_key',
  4502. 'enc_serializer',
  4503. 'callee',
  4504. 'callee_authid',
  4505. 'callee_authrole',
  4506. 'forward_for',
  4507. )
  4508. def __init__(self,
  4509. request,
  4510. args=None,
  4511. kwargs=None,
  4512. payload=None,
  4513. progress=None,
  4514. enc_algo=None,
  4515. enc_key=None,
  4516. enc_serializer=None,
  4517. callee=None,
  4518. callee_authid=None,
  4519. callee_authrole=None,
  4520. forward_for=None):
  4521. """
  4522. :param request: The WAMP request ID of the original call.
  4523. :type request: int
  4524. :param args: Positional values for application-defined event payload.
  4525. Must be serializable using any serializers in use.
  4526. :type args: list or tuple or None
  4527. :param kwargs: Keyword values for application-defined event payload.
  4528. Must be serializable using any serializers in use.
  4529. :type kwargs: dict or None
  4530. :param payload: Alternative, transparent payload. If given, ``args`` and ``kwargs`` must be left unset.
  4531. :type payload: bytes or None
  4532. :param progress: If ``True``, this result is a progressive invocation result, and subsequent
  4533. results (or a final error) will follow.
  4534. :type progress: bool or None
  4535. :param enc_algo: If using payload transparency, the encoding algorithm that was used to encode the payload.
  4536. :type enc_algo: str or None
  4537. :param enc_key: If using payload transparency with an encryption algorithm, the payload encryption key.
  4538. :type enc_key: str or None
  4539. :param enc_serializer: If using payload transparency, the payload object serializer that was used encoding the payload.
  4540. :type enc_serializer: str or None
  4541. :param callee: The WAMP session ID of the effective callee that responded with the error. Only filled if callee is disclosed.
  4542. :type callee: None or int
  4543. :param callee_authid: The WAMP authid of the responding callee. Only filled if callee is disclosed.
  4544. :type callee_authid: None or unicode
  4545. :param callee_authrole: The WAMP authrole of the responding callee. Only filled if callee is disclosed.
  4546. :type callee_authrole: None or unicode
  4547. :param forward_for: When this Call is forwarded for a client (or from an intermediary router).
  4548. :type forward_for: list[dict]
  4549. """
  4550. assert(type(request) == int)
  4551. assert(args is None or type(args) in [list, tuple])
  4552. assert(kwargs is None or type(kwargs) == dict)
  4553. assert(payload is None or type(payload) == bytes)
  4554. assert(payload is None or (payload is not None and args is None and kwargs is None))
  4555. assert(progress is None or type(progress) == bool)
  4556. assert(enc_algo is None or is_valid_enc_algo(enc_algo))
  4557. assert((enc_algo is None and enc_key is None and enc_serializer is None) or (payload is not None and enc_algo is not None))
  4558. assert(enc_key is None or type(enc_key) == str)
  4559. assert(enc_serializer is None or is_valid_enc_serializer(enc_serializer))
  4560. assert(callee is None or type(callee) == int)
  4561. assert(callee_authid is None or type(callee_authid) == str)
  4562. assert(callee_authrole is None or type(callee_authrole) == str)
  4563. assert(forward_for is None or type(forward_for) == list)
  4564. if forward_for:
  4565. for ff in forward_for:
  4566. assert type(ff) == dict
  4567. assert 'session' in ff and type(ff['session']) == int
  4568. assert 'authid' in ff and (ff['authid'] is None or type(ff['authid']) == str)
  4569. assert 'authrole' in ff and type(ff['authrole']) == str
  4570. Message.__init__(self)
  4571. self.request = request
  4572. self.args = args
  4573. self.kwargs = _validate_kwargs(kwargs)
  4574. self.payload = payload
  4575. self.progress = progress
  4576. self.enc_algo = enc_algo
  4577. self.enc_key = enc_key
  4578. self.enc_serializer = enc_serializer
  4579. # effective callee that responded with the result
  4580. self.callee = callee
  4581. self.callee_authid = callee_authid
  4582. self.callee_authrole = callee_authrole
  4583. # message forwarding
  4584. self.forward_for = forward_for
  4585. @staticmethod
  4586. def parse(wmsg):
  4587. """
  4588. Verifies and parses an unserialized raw message into an actual WAMP message instance.
  4589. :param wmsg: The unserialized raw message.
  4590. :type wmsg: list
  4591. :returns: An instance of this class.
  4592. """
  4593. # this should already be verified by WampSerializer.unserialize
  4594. assert(len(wmsg) > 0 and wmsg[0] == Yield.MESSAGE_TYPE)
  4595. if len(wmsg) not in (3, 4, 5):
  4596. raise ProtocolError("invalid message length {0} for YIELD".format(len(wmsg)))
  4597. request = check_or_raise_id(wmsg[1], "'request' in YIELD")
  4598. options = check_or_raise_extra(wmsg[2], "'options' in YIELD")
  4599. args = None
  4600. kwargs = None
  4601. payload = None
  4602. enc_algo = None
  4603. enc_key = None
  4604. enc_serializer = None
  4605. if len(wmsg) == 4 and type(wmsg[3]) == bytes:
  4606. payload = wmsg[3]
  4607. enc_algo = options.get('enc_algo', None)
  4608. if enc_algo and not is_valid_enc_algo(enc_algo):
  4609. raise ProtocolError("invalid value {0} for 'enc_algo' detail in YIELD".format(enc_algo))
  4610. enc_key = options.get('enc_key', None)
  4611. if enc_key and type(enc_key) != str:
  4612. raise ProtocolError("invalid type {0} for 'enc_key' detail in YIELD".format(type(enc_key)))
  4613. enc_serializer = options.get('enc_serializer', None)
  4614. if enc_serializer and not is_valid_enc_serializer(enc_serializer):
  4615. raise ProtocolError("invalid value {0} for 'enc_serializer' detail in YIELD".format(enc_serializer))
  4616. else:
  4617. if len(wmsg) > 3:
  4618. args = wmsg[3]
  4619. if args is not None and type(args) != list:
  4620. raise ProtocolError("invalid type {0} for 'args' in YIELD".format(type(args)))
  4621. if len(wmsg) > 4:
  4622. kwargs = wmsg[4]
  4623. if type(kwargs) != dict:
  4624. raise ProtocolError("invalid type {0} for 'kwargs' in YIELD".format(type(kwargs)))
  4625. progress = None
  4626. callee = None
  4627. callee_authid = None
  4628. callee_authrole = None
  4629. forward_for = None
  4630. if 'progress' in options:
  4631. option_progress = options['progress']
  4632. if type(option_progress) != bool:
  4633. raise ProtocolError("invalid type {0} for 'progress' option in YIELD".format(type(option_progress)))
  4634. progress = option_progress
  4635. if 'callee' in options:
  4636. option_callee = options['callee']
  4637. if type(option_callee) != int:
  4638. raise ProtocolError("invalid type {0} for 'callee' detail in YIELD".format(type(option_callee)))
  4639. callee = option_callee
  4640. if 'callee_authid' in options:
  4641. option_callee_authid = options['callee_authid']
  4642. if type(option_callee_authid) != str:
  4643. raise ProtocolError("invalid type {0} for 'callee_authid' detail in YIELD".format(type(option_callee_authid)))
  4644. callee_authid = option_callee_authid
  4645. if 'callee_authrole' in options:
  4646. option_callee_authrole = options['callee_authrole']
  4647. if type(option_callee_authrole) != str:
  4648. raise ProtocolError("invalid type {0} for 'callee_authrole' detail in YIELD".format(type(option_callee_authrole)))
  4649. callee_authrole = option_callee_authrole
  4650. if 'forward_for' in options:
  4651. forward_for = options['forward_for']
  4652. valid = False
  4653. if type(forward_for) == list:
  4654. for ff in forward_for:
  4655. if type(ff) != dict:
  4656. break
  4657. if 'session' not in ff or type(ff['session']) != int:
  4658. break
  4659. if 'authid' not in ff or type(ff['authid']) != str:
  4660. break
  4661. if 'authrole' not in ff or type(ff['authrole']) != str:
  4662. break
  4663. valid = True
  4664. if not valid:
  4665. raise ProtocolError("invalid type/value {0} for/within 'forward_for' option in YIELD")
  4666. obj = Yield(request,
  4667. args=args,
  4668. kwargs=kwargs,
  4669. payload=payload,
  4670. progress=progress,
  4671. enc_algo=enc_algo,
  4672. enc_key=enc_key,
  4673. enc_serializer=enc_serializer,
  4674. callee=callee,
  4675. callee_authid=callee_authid,
  4676. callee_authrole=callee_authrole,
  4677. forward_for=forward_for)
  4678. return obj
  4679. def marshal(self):
  4680. """
  4681. Marshal this object into a raw message for subsequent serialization to bytes.
  4682. :returns: The serialized raw message.
  4683. :rtype: list
  4684. """
  4685. options = {}
  4686. if self.progress is not None:
  4687. options['progress'] = self.progress
  4688. if self.callee is not None:
  4689. options['callee'] = self.callee
  4690. if self.callee_authid is not None:
  4691. options['callee_authid'] = self.callee_authid
  4692. if self.callee_authrole is not None:
  4693. options['callee_authrole'] = self.callee_authrole
  4694. if self.forward_for is not None:
  4695. options['forward_for'] = self.forward_for
  4696. if self.payload:
  4697. if self.enc_algo is not None:
  4698. options['enc_algo'] = self.enc_algo
  4699. if self.enc_key is not None:
  4700. options['enc_key'] = self.enc_key
  4701. if self.enc_serializer is not None:
  4702. options['enc_serializer'] = self.enc_serializer
  4703. return [Yield.MESSAGE_TYPE, self.request, options, self.payload]
  4704. else:
  4705. if self.kwargs:
  4706. return [Yield.MESSAGE_TYPE, self.request, options, self.args, self.kwargs]
  4707. elif self.args:
  4708. return [Yield.MESSAGE_TYPE, self.request, options, self.args]
  4709. else:
  4710. return [Yield.MESSAGE_TYPE, self.request, options]