PNG  IHDRQgAMA a cHRMz&u0`:pQ<bKGDgmIDATxwUﹻ& ^CX(J I@ "% (** BX +*i"]j(IH{~R)[~>h{}gy)I$Ij .I$I$ʊy@}x.: $I$Ii}VZPC)I$IF ^0ʐJ$I$Q^}{"r=OzI$gRZeC.IOvH eKX $IMpxsk.쒷/&r[޳<v| .I~)@$updYRa$I |M.e JaֶpSYR6j>h%IRز if&uJ)M$I vLi=H;7UJ,],X$I1AҒJ$ XY XzI@GNҥRT)E@;]K*Mw;#5_wOn~\ DC&$(A5 RRFkvIR}l!RytRl;~^ǷJj اy뷦BZJr&ӥ8Pjw~vnv X^(I;4R=P[3]J,]ȏ~:3?[ a&e)`e*P[4]T=Cq6R[ ~ޤrXR Հg(t_HZ-Hg M$ãmL5R uk*`%C-E6/%[t X.{8P9Z.vkXŐKjgKZHg(aK9ڦmKjѺm_ \#$5,)-  61eJ,5m| r'= &ڡd%-]J on Xm|{ RҞe $eڧY XYrԮ-a7RK6h>n$5AVڴi*ֆK)mѦtmr1p| q:흺,)Oi*ֺK)ܬ֦K-5r3>0ԔHjJئEZj,%re~/z%jVMڸmrt)3]J,T K֦OvԒgii*bKiNO~%PW0=dii2tJ9Jݕ{7"I P9JKTbu,%r"6RKU}Ij2HKZXJ,妝 XYrP ެ24c%i^IK|.H,%rb:XRl1X4Pe/`x&P8Pj28Mzsx2r\zRPz4J}yP[g=L) .Q[6RjWgp FIH*-`IMRaK9TXcq*I y[jE>cw%gLRԕiFCj-ďa`#e~I j,%r,)?[gp FI˨mnWX#>mʔ XA DZf9,nKҲzIZXJ,L#kiPz4JZF,I,`61%2s $,VOϚ2/UFJfy7K> X+6 STXIeJILzMfKm LRaK9%|4p9LwJI!`NsiazĔ)%- XMq>pk$-$Q2x#N ؎-QR}ᶦHZډ)J,l#i@yn3LN`;nڔ XuX5pF)m|^0(>BHF9(cզEerJI rg7 4I@z0\JIi䵙RR0s;$s6eJ,`n 䂦0a)S)A 1eJ,堌#635RIgpNHuTH_SԕqVe ` &S)>p;S$魁eKIuX`I4춒o}`m$1":PI<[v9^\pTJjriRŭ P{#{R2,`)e-`mgj~1ϣLKam7&U\j/3mJ,`F;M'䱀 .KR#)yhTq;pcK9(q!w?uRR,n.yw*UXj#\]ɱ(qv2=RqfB#iJmmL<]Y͙#$5 uTU7ӦXR+q,`I}qL'`6Kͷ6r,]0S$- [RKR3oiRE|nӦXR.(i:LDLTJjY%o:)6rxzҒqTJjh㞦I.$YR.ʼnGZ\ֿf:%55 I˼!6dKxm4E"mG_ s? .e*?LRfK9%q#uh$)i3ULRfK9yxm܌bj84$i1U^@Wbm4uJ,ҪA>_Ij?1v32[gLRD96oTaR׿N7%L2 NT,`)7&ƝL*꽙yp_$M2#AS,`)7$rkTA29_Iye"|/0t)$n XT2`YJ;6Jx".e<`$) PI$5V4]29SRI>~=@j]lp2`K9Jaai^" Ԋ29ORI%:XV5]JmN9]H;1UC39NI%Xe78t)a;Oi Ҙ>Xt"~G>_mn:%|~ޅ_+]$o)@ǀ{hgN;IK6G&rp)T2i୦KJuv*T=TOSV>(~D>dm,I*Ɛ:R#ۙNI%D>G.n$o;+#RR!.eU˽TRI28t)1LWϚ>IJa3oFbu&:tJ*(F7y0ZR ^p'Ii L24x| XRI%ۄ>S1]Jy[zL$adB7.eh4%%누>WETf+3IR:I3Xה)3אOۦSRO'ٺ)S}"qOr[B7ϙ.edG)^ETR"RtRݜh0}LFVӦDB^k_JDj\=LS(Iv─aTeZ%eUAM-0;~˃@i|l @S4y72>sX-vA}ϛBI!ݎߨWl*)3{'Y|iSlEڻ(5KtSI$Uv02,~ԩ~x;P4ցCrO%tyn425:KMlD ^4JRxSهF_}شJTS6uj+ﷸk$eZO%G*^V2u3EMj3k%)okI]dT)URKDS 7~m@TJR~荪fT"֛L \sM -0T KfJz+nإKr L&j()[E&I ߴ>e FW_kJR|!O:5/2跌3T-'|zX ryp0JS ~^F>-2< `*%ZFP)bSn"L :)+pʷf(pO3TMW$~>@~ū:TAIsV1}S2<%ޟM?@iT ,Eūoz%i~g|`wS(]oȤ8)$ ntu`өe`6yPl IzMI{ʣzʨ )IZ2= ld:5+請M$-ї;U>_gsY$ÁN5WzWfIZ)-yuXIfp~S*IZdt;t>KūKR|$#LcԀ+2\;kJ`]YǔM1B)UbG"IRߊ<xܾӔJ0Z='Y嵤 Leveg)$znV-º^3Ւof#0Tfk^Zs[*I꯳3{)ˬW4Ւ4 OdpbZRS|*I 55#"&-IvT&/윚Ye:i$ 9{LkuRe[I~_\ؠ%>GL$iY8 9ܕ"S`kS.IlC;Ҏ4x&>u_0JLr<J2(^$5L s=MgV ~,Iju> 7r2)^=G$1:3G< `J3~&IR% 6Tx/rIj3O< ʔ&#f_yXJiގNSz; Tx(i8%#4 ~AS+IjerIUrIj362v885+IjAhK__5X%nV%Iͳ-y|7XV2v4fzo_68"S/I-qbf; LkF)KSM$ Ms>K WNV}^`-큧32ŒVؙGdu,^^m%6~Nn&͓3ŒVZMsRpfEW%IwdǀLm[7W&bIRL@Q|)* i ImsIMmKmyV`i$G+R 0tV'!V)֏28vU7͒vHꦼtxꗞT ;S}7Mf+fIRHNZUkUx5SAJㄌ9MqμAIRi|j5)o*^'<$TwI1hEU^c_j?Е$%d`z cyf,XO IJnTgA UXRD }{H}^S,P5V2\Xx`pZ|Yk:$e ~ @nWL.j+ϝYb퇪bZ BVu)u/IJ_ 1[p.p60bC >|X91P:N\!5qUB}5a5ja `ubcVxYt1N0Zzl4]7­gKj]?4ϻ *[bg$)+À*x쳀ogO$~,5 زUS9 lq3+5mgw@np1sso Ӻ=|N6 /g(Wv7U;zωM=wk,0uTg_`_P`uz?2yI!b`kĸSo+Qx%!\οe|އԁKS-s6pu_(ֿ$i++T8=eY; צP+phxWQv*|p1. ά. XRkIQYP,drZ | B%wP|S5`~́@i޾ E;Չaw{o'Q?%iL{u D?N1BD!owPHReFZ* k_-~{E9b-~P`fE{AܶBJAFO wx6Rox5 K5=WwehS8 (JClJ~ p+Fi;ŗo+:bD#g(C"wA^ r.F8L;dzdIHUX݆ϞXg )IFqem%I4dj&ppT{'{HOx( Rk6^C٫O.)3:s(۳(Z?~ٻ89zmT"PLtw䥈5&b<8GZ-Y&K?e8,`I6e(֍xb83 `rzXj)F=l($Ij 2*(F?h(/9ik:I`m#p3MgLaKjc/U#n5S# m(^)=y=đx8ŬI[U]~SцA4p$-F i(R,7Cx;X=cI>{Km\ o(Tv2vx2qiiDJN,Ҏ!1f 5quBj1!8 rDFd(!WQl,gSkL1Bxg''՞^ǘ;pQ P(c_ IRujg(Wz bs#P­rz> k c&nB=q+ؔXn#r5)co*Ũ+G?7< |PQӣ'G`uOd>%Mctz# Ԫڞ&7CaQ~N'-P.W`Oedp03C!IZcIAMPUۀ5J<\u~+{9(FbbyAeBhOSܳ1 bÈT#ŠyDžs,`5}DC-`̞%r&ڙa87QWWp6e7 Rϫ/oY ꇅ Nܶըtc!LA T7V4Jsū I-0Pxz7QNF_iZgúWkG83 0eWr9 X]㾮݁#Jˢ C}0=3ݱtBi]_ &{{[/o[~ \q鯜00٩|cD3=4B_b RYb$óBRsf&lLX#M*C_L܄:gx)WΘsGSbuL rF$9';\4Ɍq'n[%p.Q`u hNb`eCQyQ|l_C>Lb꟟3hSb #xNxSs^ 88|Mz)}:](vbۢamŖ࿥ 0)Q7@0=?^k(*J}3ibkFn HjB׻NO z x}7p 0tfDX.lwgȔhԾŲ }6g E |LkLZteu+=q\Iv0쮑)QٵpH8/2?Σo>Jvppho~f>%bMM}\//":PTc(v9v!gոQ )UfVG+! 35{=x\2+ki,y$~A1iC6#)vC5^>+gǵ@1Hy٪7u;p psϰu/S <aʸGu'tD1ԝI<pg|6j'p:tպhX{o(7v],*}6a_ wXRk,O]Lܳ~Vo45rp"N5k;m{rZbΦ${#)`(Ŵg,;j%6j.pyYT?}-kBDc3qA`NWQū20/^AZW%NQ MI.X#P#,^Ebc&?XR tAV|Y.1!؅⨉ccww>ivl(JT~ u`ٵDm q)+Ri x/x8cyFO!/*!/&,7<.N,YDŽ&ܑQF1Bz)FPʛ?5d 6`kQձ λc؎%582Y&nD_$Je4>a?! ͨ|ȎWZSsv8 j(I&yj Jb5m?HWp=g}G3#|I,5v珿] H~R3@B[☉9Ox~oMy=J;xUVoj bUsl_35t-(ՃɼRB7U!qc+x4H_Qo֮$[GO<4`&č\GOc[.[*Af%mG/ ňM/r W/Nw~B1U3J?P&Y )`ѓZ1p]^l“W#)lWZilUQu`-m|xĐ,_ƪ|9i:_{*(3Gѧ}UoD+>m_?VPۅ15&}2|/pIOʵ> GZ9cmíتmnz)yߐbD >e}:) r|@R5qVSA10C%E_'^8cR7O;6[eKePGϦX7jb}OTGO^jn*媓7nGMC t,k31Rb (vyܴʭ!iTh8~ZYZp(qsRL ?b}cŨʊGO^!rPJO15MJ[c&~Z`"ѓޔH1C&^|Ш|rʼ,AwĴ?b5)tLU)F| &g٣O]oqSUjy(x<Ϳ3 .FSkoYg2 \_#wj{u'rQ>o;%n|F*O_L"e9umDds?.fuuQbIWz |4\0 sb;OvxOSs; G%T4gFRurj(֍ڑb uԖKDu1MK{1^ q; C=6\8FR艇!%\YÔU| 88m)֓NcLve C6z;o&X x59:q61Z(T7>C?gcļxѐ Z oo-08jہ x,`' ҔOcRlf~`jj".Nv+sM_]Zk g( UOPyεx%pUh2(@il0ݽQXxppx-NS( WO+轾 nFߢ3M<;z)FBZjciu/QoF 7R¥ ZFLF~#ȣߨ^<쩡ݛкvџ))ME>ώx4m#!-m!L;vv#~Y[đKmx9.[,UFS CVkZ +ߟrY٧IZd/ioi$%͝ب_ֶX3ܫhNU ZZgk=]=bbJS[wjU()*I =ώ:}-蹞lUj:1}MWm=̛ _ ¾,8{__m{_PVK^n3esw5ӫh#$-q=A̟> ,^I}P^J$qY~Q[ Xq9{#&T.^GVj__RKpn,b=`żY@^՝;z{paVKkQXj/)y TIc&F;FBG7wg ZZDG!x r_tƢ!}i/V=M/#nB8 XxЫ ^@CR<{䤭YCN)eKOSƟa $&g[i3.C6xrOc8TI;o hH6P&L{@q6[ Gzp^71j(l`J}]e6X☉#͕ ׈$AB1Vjh㭦IRsqFBjwQ_7Xk>y"N=MB0 ,C #o6MRc0|$)ف"1!ixY<B9mx `,tA>)5ػQ?jQ?cn>YZe Tisvh# GMމȇp:ԴVuږ8ɼH]C.5C!UV;F`mbBk LTMvPʍϤj?ԯ/Qr1NB`9s"s TYsz &9S%U԰> {<ؿSMxB|H\3@!U| k']$U+> |HHMLޢ?V9iD!-@x TIî%6Z*9X@HMW#?nN ,oe6?tQwڱ.]-y':mW0#!J82qFjH -`ѓ&M0u Uγmxϵ^-_\])@0Rt.8/?ٰCY]x}=sD3ojަЫNuS%U}ԤwHH>ڗjܷ_3gN q7[q2la*ArǓԖ+p8/RGM ]jacd(JhWko6ڎbj]i5Bj3+3!\j1UZLsLTv8HHmup<>gKMJj0@H%,W΃7R) ">c, xixј^ aܖ>H[i.UIHc U1=yW\=S*GR~)AF=`&2h`DzT󑓶J+?W+}C%P:|0H܆}-<;OC[~o.$~i}~HQ TvXΈr=b}$vizL4:ȰT|4~*!oXQR6Lk+#t/g lԁߖ[Jڶ_N$k*". xsxX7jRVbAAʯKҎU3)zSNN _'s?f)6X!%ssAkʱ>qƷb hg %n ~p1REGMHH=BJiy[<5 ǁJҖgKR*倳e~HUy)Ag,K)`Vw6bRR:qL#\rclK/$sh*$ 6덤 KԖc 3Z9=Ɣ=o>X Ώ"1 )a`SJJ6k(<c e{%kϊP+SL'TcMJWRm ŏ"w)qc ef꒵i?b7b('"2r%~HUS1\<(`1Wx9=8HY9m:X18bgD1u ~|H;K-Uep,, C1 RV.MR5άh,tWO8WC$ XRVsQS]3GJ|12 [vM :k#~tH30Rf-HYݺ-`I9%lIDTm\ S{]9gOڒMNCV\G*2JRŨ;Rҏ^ڽ̱mq1Eu?To3I)y^#jJw^Ńj^vvlB_⋌P4x>0$c>K†Aļ9s_VjTt0l#m>E-,,x,-W)سo&96RE XR.6bXw+)GAEvL)͞K4$p=Ũi_ѱOjb HY/+@θH9޼]Nԥ%n{ &zjT? Ty) s^ULlb,PiTf^<À] 62R^V7)S!nllS6~͝V}-=%* ʻ>G DnK<y&>LPy7'r=Hj 9V`[c"*^8HpcO8bnU`4JȪAƋ#1_\ XϘHPRgik(~G~0DAA_2p|J묭a2\NCr]M_0 ^T%e#vD^%xy-n}-E\3aS%yN!r_{ )sAw ڼp1pEAk~v<:`'ӭ^5 ArXOI驻T (dk)_\ PuA*BY]yB"l\ey hH*tbK)3 IKZ򹞋XjN n *n>k]X_d!ryBH ]*R 0(#'7 %es9??ښFC,ՁQPjARJ\Ρw K#jahgw;2$l*) %Xq5!U᢯6Re] |0[__64ch&_}iL8KEgҎ7 M/\`|.p,~`a=BR?xܐrQ8K XR2M8f ?`sgWS%" Ԉ 7R%$ N}?QL1|-эټwIZ%pvL3Hk>,ImgW7{E xPHx73RA @RS CC !\ȟ5IXR^ZxHл$Q[ŝ40 (>+ _C >BRt<,TrT {O/H+˟Pl6 I B)/VC<6a2~(XwV4gnXR ϱ5ǀHٻ?tw똤Eyxp{#WK qG%5],(0ӈH HZ])ג=K1j&G(FbM@)%I` XRg ʔ KZG(vP,<`[ Kn^ SJRsAʠ5xՅF`0&RbV tx:EaUE/{fi2;.IAwW8/tTxAGOoN?G}l L(n`Zv?pB8K_gI+ܗ #i?ޙ.) p$utc ~DžfՈEo3l/)I-U?aԅ^jxArA ΧX}DmZ@QLےbTXGd.^|xKHR{|ΕW_h] IJ`[G9{).y) 0X YA1]qp?p_k+J*Y@HI>^?gt.06Rn ,` ?);p pSF9ZXLBJPWjgQ|&)7! HjQt<| ؅W5 x W HIzYoVMGP Hjn`+\(dNW)F+IrS[|/a`K|ͻ0Hj{R,Q=\ (F}\WR)AgSG`IsnAR=|8$}G(vC$)s FBJ?]_u XRvύ6z ŨG[36-T9HzpW̞ú Xg큽=7CufzI$)ki^qk-) 0H*N` QZkk]/tnnsI^Gu't=7$ Z;{8^jB% IItRQS7[ϭ3 $_OQJ`7!]W"W,)Iy W AJA;KWG`IY{8k$I$^%9.^(`N|LJ%@$I}ֽp=FB*xN=gI?Q{٥4B)mw $Igc~dZ@G9K X?7)aK%݅K$IZ-`IpC U6$I\0>!9k} Xa IIS0H$I H ?1R.Чj:4~Rw@p$IrA*u}WjWFPJ$I➓/6#! LӾ+ X36x8J |+L;v$Io4301R20M I$-E}@,pS^ޟR[/s¹'0H$IKyfŸfVOπFT*a$I>He~VY/3R/)>d$I>28`Cjw,n@FU*9ttf$I~<;=/4RD~@ X-ѕzἱI$: ԍR a@b X{+Qxuq$IЛzo /~3\8ڒ4BN7$IҀj V]n18H$IYFBj3̵̚ja pp $Is/3R Ӻ-Yj+L;.0ŔI$Av? #!5"aʄj}UKmɽH$IjCYs?h$IDl843.v}m7UiI=&=0Lg0$I4: embe` eQbm0u? $IT!Sƍ'-sv)s#C0:XB2a w I$zbww{."pPzO =Ɔ\[ o($Iaw]`E).Kvi:L*#gР7[$IyGPI=@R 4yR~̮´cg I$I/<tPͽ hDgo 94Z^k盇΄8I56^W$I^0̜N?4*H`237}g+hxoq)SJ@p|` $I%>-hO0eO>\ԣNߌZD6R=K ~n($I$y3D>o4b#px2$yڪtzW~a $I~?x'BwwpH$IZݑnC㧄Pc_9sO gwJ=l1:mKB>Ab<4Lp$Ib o1ZQ@85b̍ S'F,Fe,^I$IjEdù{l4 8Ys_s Z8.x m"+{~?q,Z D!I$ϻ'|XhB)=…']M>5 rgotԎ 獽PH$IjIPhh)n#cÔqA'ug5qwU&rF|1E%I$%]!'3AFD/;Ck_`9 v!ٴtPV;x`'*bQa w I$Ix5 FC3D_~A_#O݆DvV?<qw+I$I{=Z8".#RIYyjǪ=fDl9%M,a8$I$Ywi[7ݍFe$s1ՋBVA?`]#!oz4zjLJo8$I$%@3jAa4(o ;p,,dya=F9ً[LSPH$IJYЉ+3> 5"39aZ<ñh!{TpBGkj}Sp $IlvF.F$I z< '\K*qq.f<2Y!S"-\I$IYwčjF$ w9 \ߪB.1v!Ʊ?+r:^!I$BϹB H"B;L'G[ 4U#5>੐)|#o0aڱ$I>}k&1`U#V?YsV x>{t1[I~D&(I$I/{H0fw"q"y%4 IXyE~M3 8XψL}qE$I[> nD?~sf ]o΁ cT6"?'_Ἣ $I>~.f|'!N?⟩0G KkXZE]ޡ;/&?k OۘH$IRۀwXӨ<7@PnS04aӶp.:@\IWQJ6sS%I$e5ڑv`3:x';wq_vpgHyXZ 3gЂ7{{EuԹn±}$I$8t;b|591nءQ"P6O5i }iR̈́%Q̄p!I䮢]O{H$IRϻ9s֧ a=`- aB\X0"+5"C1Hb?߮3x3&gşggl_hZ^,`5?ߎvĸ%̀M!OZC2#0x LJ0 Gw$I$I}<{Eb+y;iI,`ܚF:5ܛA8-O-|8K7s|#Z8a&><a&/VtbtLʌI$I$I$I$I$I$IRjDD%tEXtdate:create2022-05-31T04:40:26+00:00!Î%tEXtdate:modify2022-05-31T04:40:26+00:00|{2IENDB`Mini Shell

HOME


Mini Shell 1.0
DIR:/proc/self/root/opt/cpanel/ea-ruby27/root/usr/share/passenger/phusion_passenger/
Upload File :
Current File : //proc/self/root/opt/cpanel/ea-ruby27/root/usr/share/passenger/phusion_passenger/request_handler.rb
# encoding: binary
#  Phusion Passenger - https://www.phusionpassenger.com/
#  Copyright (c) 2010-2018 Phusion Holding B.V.
#
#  "Passenger", "Phusion Passenger" and "Union Station" are registered
#  trademarks of Phusion Holding B.V.
#
#  Permission is hereby granted, free of charge, to any person obtaining a copy
#  of this software and associated documentation files (the "Software"), to deal
#  in the Software without restriction, including without limitation the rights
#  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#  copies of the Software, and to permit persons to whom the Software is
#  furnished to do so, subject to the following conditions:
#
#  The above copyright notice and this permission notice shall be included in
#  all copies or substantial portions of the Software.
#
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#  THE SOFTWARE.

require 'socket'
require 'fcntl'
PhusionPassenger.require_passenger_lib 'constants'
PhusionPassenger.require_passenger_lib 'public_api'
PhusionPassenger.require_passenger_lib 'message_client'
PhusionPassenger.require_passenger_lib 'debug_logging'
PhusionPassenger.require_passenger_lib 'native_support'
PhusionPassenger.require_passenger_lib 'utils'
PhusionPassenger.require_passenger_lib 'ruby_core_enhancements'
PhusionPassenger.require_passenger_lib 'ruby_core_io_enhancements'
PhusionPassenger.require_passenger_lib 'request_handler/thread_handler'

module PhusionPassenger

  class RequestHandler
    include DebugLogging
    include Utils

    # Signal which will cause the application to exit immediately.
    HARD_TERMINATION_SIGNAL = "SIGTERM"
    BACKLOG_SIZE    = 500

    # String constants which exist to relieve Ruby's garbage collector.
    IGNORE              = 'IGNORE'              # :nodoc:
    DEFAULT             = 'DEFAULT'             # :nodoc:

    # A hash containing all server sockets that this request handler listens on.
    # The hash is in the form of:
    #
    #   {
    #      name1 => [socket_address1, socket_type1, socket1],
    #      name2 => [socket_address2, socket_type2, socket2],
    #      ...
    #   }
    #
    # +name+ is a Symbol. +socket_addressx+ is the address of the socket,
    # +socket_typex+ is the socket's type (either 'unix' or 'tcp') and
    # +socketx+ is the actual socket IO objec.
    # There's guaranteed to be at least one server socket, namely one with the
    # name +:main+.
    attr_reader :server_sockets

    attr_reader :concurrency

    # A password with which clients must authenticate. Default is unauthenticated.
    attr_accessor :connect_password

    # Create a new RequestHandler with the given owner pipe.
    # +owner_pipe+ must be the readable part of a pipe IO object.
    #
    # Additionally, the following options may be given:
    # - connect_password
    def initialize(owner_pipe, options = {})
      require_option(options, "app_group_name")
      install_options_as_ivars(self, options,
        "app",
        "app_group_name",
        "connect_password"
      )

      @keepalive = options.fetch("keepalive", true).to_s == "true"
      @force_http_session = ENV["_PASSENGER_FORCE_HTTP_SESSION"] == "true"
      if @force_http_session
        @connect_password = nil
      end
      @thread_handler = options["thread_handler"] || ThreadHandler
      @concurrency = 1

      #############
      #############

      @server_sockets = {}

      if should_use_unix_sockets?
        @main_socket_address, @main_socket = create_unix_socket_on_filesystem(options)
      else
        @main_socket_address, @main_socket = create_tcp_socket(options)
      end
      @server_sockets[:main] = {
        :address     => @main_socket_address,
        :socket      => @main_socket,
        :protocol    => @force_http_session ? :http : :session,
        :concurrency => @concurrency,
        :accept_http_requests => true
      }

      @http_socket_address, @http_socket = create_tcp_socket(options)
      @server_sockets[:http] = {
        :address     => @http_socket_address,
        :socket      => @http_socket,
        :protocol    => :http,
        :concurrency => 1
      }

      @owner_pipe = owner_pipe
      @options = options
      @previous_signal_handlers = {}
      @main_loop_generation  = 0
      @main_loop_thread_lock = Mutex.new
      @main_loop_thread_cond = ConditionVariable.new
      @threads = []
      @threads_mutex = Mutex.new
      @main_loop_running  = false

      #############
    end

    # Clean up temporary stuff created by the request handler.
    #
    # If the main loop was started by #main_loop, then this method may only
    # be called after the main loop has exited.
    #
    # If the main loop was started by #start_main_loop_thread, then this method
    # may be called at any time, and it will stop the main loop thread.
    def cleanup
      if @main_loop_thread
        @main_loop_thread_lock.synchronize do
          @graceful_termination_pipe[1].close rescue nil
        end
        @main_loop_thread.join
      end
      @server_sockets.each_value do |info|
        socket = info[:socket]
        type = get_socket_address_type(info[:address])

        begin
          socket.close if !socket.closed?
        rescue Exception => e
          # Ignore "stream closed" error, which occurs in some unit tests.
          # We catch Exception here instead of IOError because of a Ruby 1.8.7 bug.
          if e.to_s !~ /stream closed/ && e.message.to_s !~ /stream closed/
            raise e
          end
        end
        if type == :unix
          filename = info[:address].sub(/^unix:/, '')
          File.unlink(filename) rescue nil
        end
      end
      @owner_pipe.close rescue nil
    end

    # Check whether the main loop's currently running.
    def main_loop_running?
      @main_loop_thread_lock.synchronize do
        return @main_loop_running
      end
    end

    # Enter the request handler's main loop.
    def main_loop
      debug("Entering request handler main loop")
      reset_signal_handlers
      begin
        @graceful_termination_pipe = IO.pipe
        @graceful_termination_pipe[0].close_on_exec!
        @graceful_termination_pipe[1].close_on_exec!

        @main_loop_thread_lock.synchronize do
          @main_loop_generation += 1
          @main_loop_running = true
          @main_loop_thread_cond.broadcast

          @select_timeout = nil

          @selectable_sockets = []
          @server_sockets.each_value do |value|
            socket = value[2]
            @selectable_sockets << socket if socket
          end
          @selectable_sockets << @owner_pipe
          @selectable_sockets << @graceful_termination_pipe[0]
        end

        install_useful_signal_handlers
        start_threads
        wait_until_termination_requested
        wait_until_all_threads_are_idle
        terminate_threads
        debug("Request handler main loop exited normally")

      rescue EOFError
        # Exit main loop.
        trace(2, "Request handler main loop interrupted by EOFError exception")
      rescue Interrupt
        # Exit main loop.
        trace(2, "Request handler main loop interrupted by Interrupt exception")
      rescue SignalException => signal
        trace(2, "Request handler main loop interrupted by SignalException")
        if signal.message != HARD_TERMINATION_SIGNAL
          raise
        end
      rescue Exception => e
        trace(2, "Request handler main loop interrupted by #{e.class} exception")
        raise
      ensure
        debug("Exiting request handler main loop")
        revert_signal_handlers
        @main_loop_thread_lock.synchronize do
          @graceful_termination_pipe[1].close rescue nil
          @graceful_termination_pipe[0].close rescue nil
          @selectable_sockets = []
          @main_loop_generation += 1
          @main_loop_running = false
          @main_loop_thread_cond.broadcast
        end
      end
    end

    # Start the main loop in a new thread. This thread will be stopped by #cleanup.
    def start_main_loop_thread
      current_generation = @main_loop_generation
      @main_loop_thread = create_thread_and_abort_on_exception do
        main_loop
      end
      @main_loop_thread_lock.synchronize do
        while @main_loop_generation == current_generation
          @main_loop_thread_cond.wait(@main_loop_thread_lock)
        end
      end
    end

  private
    def should_use_unix_sockets?
      # Historical note:
      # There seems to be a bug in MacOS X Leopard w.r.t. Unix server
      # sockets file descriptors that are passed to another process.
      # Usually Unix server sockets work fine, but when they're passed
      # to another process, then clients that connect to the socket
      # can incorrectly determine that the client socket is closed,
      # even though that's not actually the case. More specifically:
      # recv()/read() calls on these client sockets can return 0 even
      # when we know EOF is not reached.
      #
      # The ApplicationPool infrastructure used to connect to a backend
      # process's Unix socket in the Passenger core process, and then
      # pass the connection file descriptor to the web server, which
      # triggers this kernel bug. We used to work around this by using
      # TCP sockets instead of Unix sockets; TCP sockets can still fail
      # with this fake-EOF bug once in a while, but not nearly as often
      # as with Unix sockets.
      #
      # This problem no longer applies today. The web server now passes
      # all I/O through the Passenger core, and the bug is no longer
      # triggered. Nevertheless, we keep this function intact so that
      # if something like this ever happens again, we know why, and we
      # can easily reactivate the workaround. Or maybe if we just need
      # TCP sockets for some other reason.

      #return RUBY_PLATFORM !~ /darwin/

      ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
      # Unix domain socket implementation on JRuby
      # is still bugged as of version 1.7.0. They can
      # cause unexplicable freezes when used in combination
      # with threading.
      # It's also bugged in Windows Subsystem for Linux
      # as of Windows 10, version 1803 (Fall Creators Update)
      # You will get "Address In Use" errors even if the port is unused.
      return !@force_http_session && ruby_engine != "jruby" && !PlatformInfo.windows_subsystem?
    end

    def create_unix_socket_on_filesystem(options)
      if defined?(NativeSupport)
        unix_path_max = NativeSupport::UNIX_PATH_MAX
      else
        unix_path_max = options.fetch('UNIX_PATH_MAX', 100).to_i
      end
      if options['socket_dir']
        socket_dir = options['socket_dir']
        socket_prefix = "ruby"
      else
        socket_dir = Dir.tmpdir
        socket_prefix = "PsgRubyApp"
      end

      retry_at_most(128, Errno::EADDRINUSE) do
        socket_address = "#{socket_dir}/#{socket_prefix}.#{generate_random_id(:base64)}"
        socket_address = socket_address.slice(0, unix_path_max - 10)
        socket = UNIXServer.new(socket_address)
        socket.listen(BACKLOG_SIZE)
        socket.binmode
        socket.sync = true
        socket.close_on_exec!
        File.chmod(0600, socket_address)
        ["unix:#{socket_address}", socket]
      end
    end

    def create_tcp_socket(options)
      # We default to "127.0.0.1" as address in order to force
      # TCPv4 instead of TCPv6.
      bind_address = options.fetch('bind_address', '127.0.0.1')
      socket = TCPServer.new(bind_address, 0)
      socket.listen(BACKLOG_SIZE)
      socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
      socket.binmode
      socket.sync = true
      socket.close_on_exec!
      socket_address = "tcp://#{bind_address}:#{socket.addr[1]}"
      return [socket_address, socket]
    end

    # Reset signal handlers to their default handler, and install some
    # special handlers for a few signals. The previous signal handlers
    # will be put back by calling revert_signal_handlers.
    def reset_signal_handlers
      Signal.list_trappable.each_key do |signal|
        begin
          prev_handler = trap(signal, DEFAULT)
          if prev_handler != DEFAULT
            @previous_signal_handlers[signal] = prev_handler
          end
        rescue ArgumentError
          # Signal cannot be trapped; ignore it.
        end
      end
      trap('HUP', IGNORE)
      PhusionPassenger.call_event(:after_installing_signal_handlers)
    end

    def install_useful_signal_handlers
      trappable_signals = Signal.list_trappable

      trap('ABRT') do
        print_status_report
        abort
      end if trappable_signals.has_key?('ABRT')
      trap('QUIT') do
        print_status_report
      end if trappable_signals.has_key?('QUIT')
    end

    def revert_signal_handlers
      @previous_signal_handlers.each_pair do |signal, handler|
        trap(signal, handler)
      end
    end

    def print_status_report
      warn(Utils.global_backtrace_report)
      warn("Threads: #{@threads.inspect}")
    end

    def start_threads
      common_options = {
        :app              => @app,
        :app_group_name   => @app_group_name,
        :connect_password => @connect_password,
        :keepalive_enabled  => @keepalive
      }
      main_socket_options = common_options.merge(
        :server_socket => @main_socket,
        :socket_name => "main socket",
        :protocol => @server_sockets[:main][:protocol] == :session ?
          :session :
          :http
      )
      http_socket_options = common_options.merge(
        :server_socket => @http_socket,
        :socket_name => "HTTP socket",
        :protocol => :http
      )

      # Used for marking threads that have finished initializing,
      # or failed during initialization. Threads that are not yet done
      # are not in `initialization_state`. Threads that have succeeded
      # set their own state to true. Threads that have failed set their
      # own state to false.
      initialization_state_mutex = Mutex.new
      initialization_state_cond = ConditionVariable.new
      initialization_state = {}
      set_initialization_state = lambda do |value|
        initialization_state_mutex.synchronize do
          initialization_state[Thread.current] = value
          initialization_state_cond.signal
        end
      end
      set_initialization_state_to_true = lambda do
        set_initialization_state.call(true)
      end

      # Actually start all the threads.
      thread_handler = @thread_handler
      expected_nthreads = 0

      @threads_mutex.synchronize do
        @concurrency.times do |i|
          thread = create_thread_and_abort_on_exception(i) do |number|
            begin
              Thread.current[:name] = "Worker #{number + 1}"
              handler = thread_handler.new(self, main_socket_options)
              handler.install
              handler.main_loop(set_initialization_state_to_true)
            ensure
              set_initialization_state.call(false)
              unregister_current_thread
            end
          end
          @threads << thread
          expected_nthreads += 1
        end

        thread = create_thread_and_abort_on_exception do
          begin
            Thread.current[:name] = "HTTP helper worker"
            handler = thread_handler.new(self, http_socket_options)
            handler.install
            handler.main_loop(set_initialization_state_to_true)
          ensure
            set_initialization_state.call(false)
            unregister_current_thread
          end
        end
        @threads << thread
        expected_nthreads += 1
      end

      # Wait until all threads have finished starting.
      initialization_state_mutex.synchronize do
        while initialization_state.size != expected_nthreads
          initialization_state_cond.wait(initialization_state_mutex)
        end
      end
    end

    def unregister_current_thread
      @threads_mutex.synchronize do
        @threads.delete(Thread.current)
      end
    end

    def wait_until_termination_requested
      ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
      if ruby_engine == "jruby"
        # On JRuby, selecting on an input TTY always returns, so
        # we use threads to do the job.
        owner_pipe_watcher = IO.pipe
        owner_pipe_watcher_thread = create_thread_and_abort_on_exception do
          Thread.current[:name] = "Owner pipe waiter"
          begin
            @owner_pipe.read(1)
          ensure
            owner_pipe_watcher[1].write('x')
          end
        end
        begin
          ios = select([owner_pipe_watcher[0], @graceful_termination_pipe[0]])[0]
          if ios.include?(owner_pipe_watcher[0])
            trace(2, "Owner pipe closed")
          else
            trace(2, "Graceful termination pipe closed")
          end
        ensure
          owner_pipe_watcher_thread.kill
          owner_pipe_watcher_thread.join
          owner_pipe_watcher[0].close if !owner_pipe_watcher[0].closed?
          owner_pipe_watcher[1].close if !owner_pipe_watcher[1].closed?
        end
      else
        ios = select([@owner_pipe, @graceful_termination_pipe[0]])[0]
        if ios.include?(@owner_pipe)
          trace(2, "Owner pipe closed")
        else
          trace(2, "Graceful termination pipe closed")
        end
      end
    end

    def wakeup_all_threads
      threads = []
      if get_socket_address_type(@server_sockets[:main][:address]) == :unix &&
         !File.exist?(@server_sockets[:main][:address].sub(/^unix:/, ''))
        # It looks like someone deleted the Unix domain socket we listen on.
        # This makes it impossible to wake up the worker threads gracefully,
        # so we hard kill them.
        warn("Unix domain socket gone; force aborting all threads")
        @threads_mutex.synchronize do
          @threads.each do |thread|
            thread.raise(RuntimeError.new("Force abort"))
          end
        end
      else
        @concurrency.times do
          threads << create_thread_and_abort_on_exception(@server_sockets[:main][:address]) do |address|
            begin
              debug("Shutting down worker thread by connecting to #{address}")
              connect_to_server(address).close
            rescue Errno::ECONNREFUSED
              debug("Worker thread listening on #{address} already exited")
            rescue SystemCallError, IOError => e
              debug("Error shutting down worker thread (#{address}): #{e} (#{e.class})")
            end
          end
        end
      end
      threads << create_thread_and_abort_on_exception(@server_sockets[:http][:address]) do |address|
        begin
          debug("Shutting down HTTP thread by connecting to #{address}")
          connect_to_server(address).close
        rescue Errno::ECONNREFUSED
          debug("Worker thread listening on #{address} already exited")
        rescue SystemCallError, IOError => e
          debug("Error shutting down HTTP thread (#{address}): #{e} (#{e.class})")
        end
      end
      return threads
    end

    def terminate_threads
      debug("Stopping all threads")
      threads = @threads_mutex.synchronize do
        @threads.dup
      end
      threads.each do |thr|
        thr.raise(ThreadHandler::Interrupted.new)
      end
      threads.each do |thr|
        thr.join
      end
      debug("All threads stopped")
    end

    def wait_until_all_threads_are_idle
      debug("Waiting until all threads have become idle...")

      # We wait until 100 ms have passed since all handlers have become
      # interruptable and remained in the same iterations.

      done = false

      while !done
        handlers = @threads_mutex.synchronize do
          @threads.map do |thr|
            thr[:passenger_thread_handler]
          end
        end
        debug("There are currently #{handlers.size} threads")
        if handlers.empty?
          # There are no threads, so we're done.
          done = true
          break
        end

        # Record initial state.
        handlers.each { |h| h.stats_mutex.lock }
        iterations = handlers.map { |h| h.iteration }
        handlers.each { |h| h.stats_mutex.unlock }

        start_time = Time.now
        sleep 0.01

        while true
          if handlers.size != @threads_mutex.synchronize { @threads.size }
            debug("The number of threads changed. Restarting waiting algorithm")
            break
          end

          # Record current state.
          handlers.each { |h| h.stats_mutex.lock }
          all_interruptable = handlers.all? { |h| h.interruptable }
          new_iterations    = handlers.map  { |h| h.iteration }

          # Are all threads interruptable and has there been no activity
          # since last time we checked?
          if all_interruptable && new_iterations == iterations
            # Yes. If enough time has passed then we're done.
            handlers.each { |h| h.stats_mutex.unlock }
            if Time.now >= start_time + 0.1
              done = true
              break
            end
          else
            # No. We reset the timer and check again later.
            handlers.each { |h| h.stats_mutex.unlock }
            iterations = new_iterations
            start_time = Time.now
            sleep 0.01
          end
        end
      end

      debug("All threads are now idle")
    end
  end

end # module PhusionPassenger