PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /usr/local/nagios/venv/lib/python3.13/site-packages/supervisor/tests
Viewing File: /usr/local/nagios/venv/lib/python3.13/site-packages/supervisor/tests/test_http.py
import base64 import os import stat import socket import tempfile import unittest from supervisor.compat import as_bytes from supervisor.compat import as_string from supervisor.compat import sha1 from supervisor.tests.base import DummySupervisor from supervisor.tests.base import PopulatedDummySupervisor from supervisor.tests.base import DummyRPCInterfaceFactory from supervisor.tests.base import DummyPConfig from supervisor.tests.base import DummyOptions from supervisor.tests.base import DummyRequest from supervisor.tests.base import DummyLogger from supervisor.http import NOT_DONE_YET class HandlerTests: def _makeOne(self, supervisord): return self._getTargetClass()(supervisord) def test_match(self): class FakeRequest: def __init__(self, uri): self.uri = uri supervisor = DummySupervisor() handler = self._makeOne(supervisor) self.assertEqual(handler.match(FakeRequest(handler.path)), True) class LogtailHandlerTests(HandlerTests, unittest.TestCase): def _getTargetClass(self): from supervisor.http import logtail_handler return logtail_handler def test_handle_request_stdout_logfile_none(self): options = DummyOptions() pconfig = DummyPConfig(options, 'process1', '/bin/process1', priority=1, stdout_logfile='/tmp/process1.log') supervisord = PopulatedDummySupervisor(options, 'process1', pconfig) handler = self._makeOne(supervisord) request = DummyRequest('/logtail/process1', None, None, None) handler.handle_request(request) self.assertEqual(request._error, 404) def test_handle_request_stdout_logfile_missing(self): options = DummyOptions() pconfig = DummyPConfig(options, 'foo', 'foo', 'it/is/missing') supervisord = PopulatedDummySupervisor(options, 'foo', pconfig) handler = self._makeOne(supervisord) request = DummyRequest('/logtail/foo', None, None, None) handler.handle_request(request) self.assertEqual(request._error, 404) def test_handle_request(self): with tempfile.NamedTemporaryFile() as f: t = f.name options = DummyOptions() pconfig = DummyPConfig(options, 'foo', 'foo', stdout_logfile=t) supervisord = PopulatedDummySupervisor(options, 'foo', pconfig) handler = self._makeOne(supervisord) request = DummyRequest('/logtail/foo', None, None, None) handler.handle_request(request) self.assertEqual(request._error, None) from supervisor.medusa import http_date self.assertEqual(request.headers['Last-Modified'], http_date.build_http_date(os.stat(t)[stat.ST_MTIME])) self.assertEqual(request.headers['Content-Type'], 'text/plain;charset=utf-8') self.assertEqual(request.headers['X-Accel-Buffering'], 'no') self.assertEqual(len(request.producers), 1) self.assertEqual(request._done, True) class MainLogTailHandlerTests(HandlerTests, unittest.TestCase): def _getTargetClass(self): from supervisor.http import mainlogtail_handler return mainlogtail_handler def test_handle_request_stdout_logfile_none(self): supervisor = DummySupervisor() handler = self._makeOne(supervisor) request = DummyRequest('/mainlogtail', None, None, None) handler.handle_request(request) self.assertEqual(request._error, 404) def test_handle_request_stdout_logfile_missing(self): supervisor = DummySupervisor() supervisor.options.logfile = '/not/there' request = DummyRequest('/mainlogtail', None, None, None) handler = self._makeOne(supervisor) handler.handle_request(request) self.assertEqual(request._error, 404) def test_handle_request(self): supervisor = DummySupervisor() with tempfile.NamedTemporaryFile() as f: t = f.name supervisor.options.logfile = t handler = self._makeOne(supervisor) request = DummyRequest('/mainlogtail', None, None, None) handler.handle_request(request) self.assertEqual(request._error, None) from supervisor.medusa import http_date self.assertEqual(request.headers['Last-Modified'], http_date.build_http_date(os.stat(t)[stat.ST_MTIME])) self.assertEqual(request.headers['Content-Type'], 'text/plain;charset=utf-8') self.assertEqual(len(request.producers), 1) self.assertEqual(request._done, True) class TailFProducerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import tail_f_producer return tail_f_producer def _makeOne(self, request, filename, head): return self._getTargetClass()(request, filename, head) def test_handle_more(self): request = DummyRequest('/logtail/foo', None, None, None) from supervisor import http f = tempfile.NamedTemporaryFile() f.write(b'a' * 80) f.flush() producer = self._makeOne(request, f.name, 80) result = producer.more() self.assertEqual(result, b'a' * 80) f.write(as_bytes(b'w' * 100)) f.flush() result = producer.more() self.assertEqual(result, b'w' * 100) result = producer.more() self.assertEqual(result, http.NOT_DONE_YET) f.truncate(0) f.flush() result = producer.more() self.assertEqual(result, '==> File truncated <==\n') def test_handle_more_fd_closed(self): request = DummyRequest('/logtail/foo', None, None, None) with tempfile.NamedTemporaryFile() as f: f.write(as_bytes('a' * 80)) f.flush() producer = self._makeOne(request, f.name, 80) producer.file.close() result = producer.more() self.assertEqual(result, producer.more()) def test_handle_more_follow_file_recreated(self): request = DummyRequest('/logtail/foo', None, None, None) f = tempfile.NamedTemporaryFile() f.write(as_bytes('a' * 80)) f.flush() producer = self._makeOne(request, f.name, 80) result = producer.more() self.assertEqual(result, b'a' * 80) f.close() f2 = open(f.name, 'wb') try: f2.write(as_bytes(b'b' * 80)) f2.close() result = producer.more() finally: os.unlink(f2.name) self.assertEqual(result, b'b' * 80) def test_handle_more_follow_file_gone(self): request = DummyRequest('/logtail/foo', None, None, None) with tempfile.NamedTemporaryFile(delete=False) as f: filename = f.name f.write(b'a' * 80) try: producer = self._makeOne(request, f.name, 80) finally: os.unlink(f.name) result = producer.more() self.assertEqual(result, b'a' * 80) with open(filename, 'wb') as f: f.write(as_bytes(b'b' * 80)) try: result = producer.more() # should open in new file self.assertEqual(result, b'b' * 80) finally: os.unlink(f.name) class DeferringChunkedProducerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_chunked_producer return deferring_chunked_producer def _makeOne(self, producer, footers=None): return self._getTargetClass()(producer, footers) def test_more_not_done_yet(self): wrapped = DummyProducer(NOT_DONE_YET) producer = self._makeOne(wrapped) self.assertEqual(producer.more(), NOT_DONE_YET) def test_more_string(self): wrapped = DummyProducer(b'hello') producer = self._makeOne(wrapped) self.assertEqual(producer.more(), b'5\r\nhello\r\n') def test_more_nodata(self): wrapped = DummyProducer() producer = self._makeOne(wrapped, footers=[b'a', b'b']) self.assertEqual(producer.more(), b'0\r\na\r\nb\r\n\r\n') def test_more_nodata_footers(self): wrapped = DummyProducer(b'') producer = self._makeOne(wrapped, footers=[b'a', b'b']) self.assertEqual(producer.more(), b'0\r\na\r\nb\r\n\r\n') def test_more_nodata_nofooters(self): wrapped = DummyProducer(b'') producer = self._makeOne(wrapped) self.assertEqual(producer.more(), b'0\r\n\r\n') def test_more_noproducer(self): producer = self._makeOne(None) self.assertEqual(producer.more(), b'') class DeferringCompositeProducerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_composite_producer return deferring_composite_producer def _makeOne(self, producers): return self._getTargetClass()(producers) def test_more_not_done_yet(self): wrapped = DummyProducer(NOT_DONE_YET) producer = self._makeOne([wrapped]) self.assertEqual(producer.more(), NOT_DONE_YET) def test_more_string(self): wrapped1 = DummyProducer('hello') wrapped2 = DummyProducer('goodbye') producer = self._makeOne([wrapped1, wrapped2]) self.assertEqual(producer.more(), 'hello') self.assertEqual(producer.more(), 'goodbye') self.assertEqual(producer.more(), b'') def test_more_nodata(self): wrapped = DummyProducer() producer = self._makeOne([wrapped]) self.assertEqual(producer.more(), b'') class DeferringGlobbingProducerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_globbing_producer return deferring_globbing_producer def _makeOne(self, producer, buffer_size=1<<16): return self._getTargetClass()(producer, buffer_size) def test_more_not_done_yet(self): wrapped = DummyProducer(NOT_DONE_YET) producer = self._makeOne(wrapped) self.assertEqual(producer.more(), NOT_DONE_YET) def test_more_string(self): wrapped = DummyProducer('hello', 'there', 'guy') producer = self._makeOne(wrapped, buffer_size=1) self.assertEqual(producer.more(), b'hello') wrapped = DummyProducer('hello', 'there', 'guy') producer = self._makeOne(wrapped, buffer_size=50) self.assertEqual(producer.more(), b'hellothereguy') def test_more_nodata(self): wrapped = DummyProducer() producer = self._makeOne(wrapped) self.assertEqual(producer.more(), b'') class DeferringHookedProducerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_hooked_producer return deferring_hooked_producer def _makeOne(self, producer, function): return self._getTargetClass()(producer, function) def test_more_not_done_yet(self): wrapped = DummyProducer(NOT_DONE_YET) producer = self._makeOne(wrapped, None) self.assertEqual(producer.more(), NOT_DONE_YET) def test_more_string(self): wrapped = DummyProducer('hello') L = [] def callback(bytes): L.append(bytes) producer = self._makeOne(wrapped, callback) self.assertEqual(producer.more(), 'hello') self.assertEqual(L, []) producer.more() self.assertEqual(L, [5]) def test_more_nodata(self): wrapped = DummyProducer() L = [] def callback(bytes): L.append(bytes) producer = self._makeOne(wrapped, callback) self.assertEqual(producer.more(), b'') self.assertEqual(L, [0]) def test_more_noproducer(self): producer = self._makeOne(None, None) self.assertEqual(producer.more(), b'') class DeferringHttpRequestTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_http_request return deferring_http_request def _makeOne( self, channel=None, req='GET / HTTP/1.0', command='GET', uri='/', version='1.0', header=(), ): return self._getTargetClass()( channel, req, command, uri, version, header ) def _makeChannel(self): class Channel: closed = False def close_when_done(self): self.closed = True def push_with_producer(self, producer): self.producer = producer return Channel() def test_done_http_10_nokeepalive(self): channel = self._makeChannel() inst = self._makeOne(channel=channel, version='1.0') inst.done() self.assertTrue(channel.closed) def test_done_http_10_keepalive_no_content_length(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.0', header=['Connection: Keep-Alive'], ) inst.done() self.assertTrue(channel.closed) def test_done_http_10_keepalive_and_content_length(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.0', header=['Connection: Keep-Alive'], ) inst.reply_headers['Content-Length'] = 1 inst.done() self.assertEqual(inst['Connection'], 'Keep-Alive') self.assertFalse(channel.closed) def test_done_http_11_connection_close(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.1', header=['Connection: close'] ) inst.done() self.assertTrue(channel.closed) def test_done_http_11_unknown_transfer_encoding(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.1', ) inst.reply_headers['Transfer-Encoding'] = 'notchunked' inst.done() self.assertTrue(channel.closed) def test_done_http_11_chunked_transfer_encoding(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.1', ) inst.reply_headers['Transfer-Encoding'] = 'chunked' inst.done() self.assertFalse(channel.closed) def test_done_http_11_use_chunked(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.1', ) inst.use_chunked = True inst.done() self.assertTrue('Transfer-Encoding' in inst) self.assertFalse(channel.closed) def test_done_http_11_wo_content_length_no_te_no_use_chunked_close(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version='1.1', ) inst.use_chunked = False inst.done() self.assertTrue(channel.closed) def test_done_http_09(self): channel = self._makeChannel() inst = self._makeOne( channel=channel, version=None, ) inst.done() self.assertTrue(channel.closed) class DeferringHttpChannelTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import deferring_http_channel return deferring_http_channel def _makeOne(self): return self._getTargetClass()( server=None, conn=None, addr=None ) def test_defaults_delay_and_last_writable_check_time(self): channel = self._makeOne() self.assertEqual(channel.delay, 0) self.assertEqual(channel.last_writable_check, 0) def test_writable_with_delay_is_False_if_elapsed_lt_delay(self): channel = self._makeOne() channel.delay = 2 channel.last_writable_check = _NOW later = _NOW + 1 self.assertFalse(channel.writable(now=later)) self.assertEqual(channel.last_writable_check, _NOW) def test_writable_with_delay_is_False_if_elapsed_eq_delay(self): channel = self._makeOne() channel.delay = 2 channel.last_writable_check = _NOW later = _NOW + channel.delay self.assertFalse(channel.writable(now=later)) self.assertEqual(channel.last_writable_check, _NOW) def test_writable_with_delay_is_True_if_elapsed_gt_delay(self): channel = self._makeOne() channel.delay = 2 channel.last_writable_check = _NOW later = _NOW + channel.delay + 0.1 self.assertTrue(channel.writable(now=later)) self.assertEqual(channel.last_writable_check, later) def test_writable_with_delay_is_True_if_system_time_goes_backwards(self): channel = self._makeOne() channel.delay = 2 channel.last_writable_check = _NOW later = _NOW - 3600 # last check was in the future self.assertTrue(channel.writable(now=later)) self.assertEqual(channel.last_writable_check, later) _NOW = 1470085990 class EncryptedDictionaryAuthorizedTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import encrypted_dictionary_authorizer return encrypted_dictionary_authorizer def _makeOne(self, dict): return self._getTargetClass()(dict) def test_authorize_baduser(self): authorizer = self._makeOne({}) self.assertFalse(authorizer.authorize(('foo', 'bar'))) def test_authorize_gooduser_badpassword(self): authorizer = self._makeOne({'foo':'password'}) self.assertFalse(authorizer.authorize(('foo', 'bar'))) def test_authorize_gooduser_goodpassword(self): authorizer = self._makeOne({'foo':'password'}) self.assertTrue(authorizer.authorize(('foo', 'password'))) def test_authorize_gooduser_goodpassword_with_colon(self): authorizer = self._makeOne({'foo':'pass:word'}) self.assertTrue(authorizer.authorize(('foo', 'pass:word'))) def test_authorize_gooduser_badpassword_sha(self): password = '{SHA}' + sha1(as_bytes('password')).hexdigest() authorizer = self._makeOne({'foo':password}) self.assertFalse(authorizer.authorize(('foo', 'bar'))) def test_authorize_gooduser_goodpassword_sha(self): password = '{SHA}' + sha1(as_bytes('password')).hexdigest() authorizer = self._makeOne({'foo':password}) self.assertTrue(authorizer.authorize(('foo', 'password'))) class SupervisorAuthHandlerTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import supervisor_auth_handler return supervisor_auth_handler def _makeOne(self, dict, handler): return self._getTargetClass()(dict, handler) def test_ctor(self): handler = self._makeOne({'a':1}, None) from supervisor.http import encrypted_dictionary_authorizer self.assertEqual(handler.authorizer.__class__, encrypted_dictionary_authorizer) def test_handle_request_authorizes_good_credentials(self): request = DummyRequest('/logtail/process1', None, None, None) encoded = base64.b64encode(as_bytes("user:password")) request.header = ["Authorization: Basic %s" % as_string(encoded)] handler = DummyHandler() auth_handler = self._makeOne({'user':'password'}, handler) auth_handler.handle_request(request) self.assertTrue(handler.handled_request) def test_handle_request_authorizes_good_password_with_colon(self): request = DummyRequest('/logtail/process1', None, None, None) # password contains colon encoded = base64.b64encode(as_bytes("user:pass:word")) request.header = ["Authorization: Basic %s" % as_string(encoded)] handler = DummyHandler() auth_handler = self._makeOne({'user':'pass:word'}, handler) auth_handler.handle_request(request) self.assertTrue(handler.handled_request) def test_handle_request_does_not_authorize_bad_credentials(self): request = DummyRequest('/logtail/process1', None, None, None) encoded = base64.b64encode(as_bytes("wrong:wrong")) request.header = ["Authorization: Basic %s" % as_string(encoded)] handler = DummyHandler() auth_handler = self._makeOne({'user':'password'}, handler) auth_handler.handle_request(request) self.assertFalse(handler.handled_request) class LogWrapperTests(unittest.TestCase): def _getTargetClass(self): from supervisor.http import LogWrapper return LogWrapper def _makeOne(self, logger): return self._getTargetClass()(logger) def test_strips_trailing_newlines_from_msgs(self): logger = DummyLogger() log_wrapper = self._makeOne(logger) log_wrapper.log("foo\n") logdata = logger.data self.assertEqual(len(logdata), 1) self.assertEqual(logdata[0], "foo") def test_logs_msgs_with_error_at_error_level(self): logger = DummyLogger() log_wrapper = self._makeOne(logger) errors = [] logger.error = errors.append log_wrapper.log("Server Error") self.assertEqual(len(errors), 1) self.assertEqual(errors[0], "Server Error") def test_logs_other_messages_at_trace_level(self): logger = DummyLogger() log_wrapper = self._makeOne(logger) traces = [] logger.trace = traces.append log_wrapper.log("GET /") self.assertEqual(len(traces), 1) self.assertEqual(traces[0], "GET /") class TopLevelFunctionTests(unittest.TestCase): def _make_http_servers(self, sconfigs): options = DummyOptions() options.server_configs = sconfigs options.rpcinterface_factories = [('dummy',DummyRPCInterfaceFactory,{})] supervisord = DummySupervisor() from supervisor.http import make_http_servers servers = make_http_servers(options, supervisord) try: for config, s in servers: s.close() socketfile = config.get('file') if socketfile is not None: os.unlink(socketfile) finally: from supervisor.medusa.asyncore_25 import socket_map socket_map.clear() return servers def test_make_http_servers_socket_type_error(self): config = {'family':999, 'host':'localhost', 'port':17735, 'username':None, 'password':None, 'section':'inet_http_server'} try: self._make_http_servers([config]) self.fail('nothing raised') except ValueError as exc: self.assertEqual(exc.args[0], 'Cannot determine socket type 999') def test_make_http_servers_noauth(self): with tempfile.NamedTemporaryFile(delete=True) as f: socketfile = f.name self.assertFalse(os.path.exists(socketfile)) inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17735, 'username':None, 'password':None, 'section':'inet_http_server'} unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':0o700, 'chown':(-1, -1), 'username':None, 'password':None, 'section':'unix_http_server'} servers = self._make_http_servers([inet, unix]) self.assertEqual(len(servers), 2) inetdata = servers[0] self.assertEqual(inetdata[0], inet) server = inetdata[1] idents = [ 'Supervisor XML-RPC Handler', 'Logtail HTTP Request Handler', 'Main Logtail HTTP Request Handler', 'Supervisor Web UI HTTP Request Handler', 'Default HTTP Request Handler' ] self.assertEqual([x.IDENT for x in server.handlers], idents) unixdata = servers[1] self.assertEqual(unixdata[0], unix) server = unixdata[1] self.assertEqual([x.IDENT for x in server.handlers], idents) def test_make_http_servers_withauth(self): with tempfile.NamedTemporaryFile(delete=True) as f: socketfile = f.name self.assertFalse(os.path.exists(socketfile)) inet = {'family':socket.AF_INET, 'host':'localhost', 'port':17736, 'username':'username', 'password':'password', 'section':'inet_http_server'} unix = {'family':socket.AF_UNIX, 'file':socketfile, 'chmod':0o700, 'chown':(-1, -1), 'username':'username', 'password':'password', 'section':'unix_http_server'} servers = self._make_http_servers([inet, unix]) self.assertEqual(len(servers), 2) from supervisor.http import supervisor_auth_handler for config, server in servers: for handler in server.handlers: self.assertTrue(isinstance(handler, supervisor_auth_handler), handler) class DummyHandler: def __init__(self): self.handled_request = False def handle_request(self, request): self.handled_request = True class DummyProducer: def __init__(self, *data): self.data = list(data) def more(self): if self.data: return self.data.pop(0) else: return b''