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: /opt/cpanel/ea-ruby24/root/usr/share/gems/gems/rack-1.6.10/test
Viewing File: /opt/cpanel/ea-ruby24/root/usr/share/gems/gems/rack-1.6.10/test/spec_request.rb
require 'stringio' require 'cgi' require 'rack/request' require 'rack/mock' require 'securerandom' describe Rack::Request do should "wrap the rack variables" do req = Rack::Request.new(Rack::MockRequest.env_for("http://example.com:8080/")) req.body.should.respond_to? :gets req.scheme.should.equal "http" req.request_method.should.equal "GET" req.should.be.get req.should.not.be.post req.should.not.be.put req.should.not.be.delete req.should.not.be.head req.should.not.be.patch req.script_name.should.equal "" req.path_info.should.equal "/" req.query_string.should.equal "" req.host.should.equal "example.com" req.port.should.equal 8080 req.content_length.should.equal "0" req.content_type.should.be.nil end should "figure out the correct host" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") req.host.should.equal "www2.example.org" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292") req.host.should.equal "example.org" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.host.should.equal "example.org" env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292") env.delete("SERVER_NAME") req = Rack::Request.new(env) req.host.should.equal "192.168.1.1" env = Rack::MockRequest.env_for("/") env.delete("SERVER_NAME") req = Rack::Request.new(env) req.host.should.equal "" end should "figure out the correct port" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") req.port.should.equal 80 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81") req.port.should.equal 81 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292") req.port.should.equal 9292 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.port.should.equal 9292 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org") req.port.should.equal 80 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on") req.port.should.equal 443 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https") req.port.should.equal 443 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393") req.port.should.equal 9393 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80") req.port.should.equal 9393 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393") req.port.should.equal 80 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80") req.port.should.equal 443 req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https,https", "SERVER_PORT" => "80") req.port.should.equal 443 end should "figure out the correct host with port" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org") req.host_with_port.should.equal "www2.example.org" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81") req.host_with_port.should.equal "localhost:81" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292") req.host_with_port.should.equal "example.org:9292" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292") req.host_with_port.should.equal "example.org:9292" req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393") req.host_with_port.should.equal "example.org" end should "parse the query string" do req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla")) req.query_string.should.equal "foo=bar&quux=bla" req.GET.should.equal "foo" => "bar", "quux" => "bla" req.POST.should.be.empty req.params.should.equal "foo" => "bar", "quux" => "bla" end should "not truncate query strings containing semi-colons #543 only in POST" do mr = Rack::MockRequest.env_for("/", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=b;la") req = Rack::Request.new mr req.query_string.should.equal "" req.GET.should.be.empty req.POST.should.equal "foo" => "bar", "quux" => "b;la" req.params.should.equal req.GET.merge(req.POST) end should "use semi-colons as separators for query strings in GET" do req = Rack::Request.new(Rack::MockRequest.env_for("/?foo=bar&quux=b;la;wun=duh")) req.query_string.should.equal "foo=bar&quux=b;la;wun=duh" req.GET.should.equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh" req.POST.should.be.empty req.params.should.equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh" end should "limit the keys from the GET query string" do env = Rack::MockRequest.env_for("/?foo=bar") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = Rack::Request.new(env) lambda { req.GET }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "limit the key size per nested params hash" do nested_query = Rack::MockRequest.env_for("/?foo%5Bbar%5D%5Bbaz%5D%5Bqux%5D=1") plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3 begin lambda { Rack::Request.new(nested_query).GET }.should.not.raise(RangeError) lambda { Rack::Request.new(plain_query).GET }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "not unify GET and POST when calling params" do mr = Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=bla" ) req = Rack::Request.new mr req.params req.GET.should.equal "foo" => "quux" req.POST.should.equal "foo" => "bar", "quux" => "bla" req.params.should.equal req.GET.merge(req.POST) end should "raise if input params has invalid %-encoding" do mr = Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', :input => "a%=1" ) req = Rack::Request.new mr lambda { req.POST }. should.raise(Rack::Utils::InvalidParameterError). message.should.equal "invalid %-encoding (a%)" end should "raise if rack.input is missing" do req = Rack::Request.new({}) lambda { req.POST }.should.raise(RuntimeError) end should "parse POST data when method is POST and no Content-Type given" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=bla") req.content_type.should.be.nil req.media_type.should.be.nil req.query_string.should.equal "foo=quux" req.GET.should.equal "foo" => "quux" req.POST.should.equal "foo" => "bar", "quux" => "bla" req.params.should.equal "foo" => "bar", "quux" => "bla" end should "limit the keys from the POST form data" do env = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=bar&quux=bla") old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 begin req = Rack::Request.new(env) lambda { req.POST }.should.raise(RangeError) ensure Rack::Utils.key_space_limit = old end end should "parse POST data with explicit content type regardless of method" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar', :input => "foo=bar&quux=bla") req.content_type.should.equal 'application/x-www-form-urlencoded;foo=bar' req.media_type.should.equal 'application/x-www-form-urlencoded' req.media_type_params['foo'].should.equal 'bar' req.POST.should.equal "foo" => "bar", "quux" => "bla" req.params.should.equal "foo" => "bar", "quux" => "bla" end should "not parse POST data when media type is not form-data" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'POST', "CONTENT_TYPE" => 'text/plain;charset=utf-8', :input => "foo=bar&quux=bla") req.content_type.should.equal 'text/plain;charset=utf-8' req.media_type.should.equal 'text/plain' req.media_type_params['charset'].should.equal 'utf-8' req.POST.should.be.empty req.params.should.equal "foo" => "quux" req.body.read.should.equal "foo=bar&quux=bla" end should "parse POST data on PUT when media type is form-data" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "REQUEST_METHOD" => 'PUT', "CONTENT_TYPE" => 'application/x-www-form-urlencoded', :input => "foo=bar&quux=bla") req.POST.should.equal "foo" => "bar", "quux" => "bla" req.body.read.should.equal "foo=bar&quux=bla" end should "rewind input after parsing POST data" do input = StringIO.new("foo=bar&quux=bla") req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar', :input => input) req.params.should.equal "foo" => "bar", "quux" => "bla" input.read.should.equal "foo=bar&quux=bla" end should "clean up Safari's ajax POST body" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", 'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0") req.POST.should.equal "foo" => "bar", "quux" => "bla" end should "get value by key from params with #[]" do req = Rack::Request.new \ Rack::MockRequest.env_for("?foo=quux") req['foo'].should.equal 'quux' req[:foo].should.equal 'quux' end should "set value to key on params with #[]=" do req = Rack::Request.new \ Rack::MockRequest.env_for("?foo=duh") req['foo'].should.equal 'duh' req[:foo].should.equal 'duh' req.params.should.equal 'foo' => 'duh' req['foo'] = 'bar' req.params.should.equal 'foo' => 'bar' req['foo'].should.equal 'bar' req[:foo].should.equal 'bar' req[:foo] = 'jaz' req.params.should.equal 'foo' => 'jaz' req['foo'].should.equal 'jaz' req[:foo].should.equal 'jaz' end should "return values for the keys in the order given from values_at" do req = Rack::Request.new \ Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful") req.values_at('foo').should.equal ['baz'] req.values_at('foo', 'wun').should.equal ['baz', 'der'] req.values_at('bar', 'foo', 'wun').should.equal ['ful', 'baz', 'der'] end should "extract referrer correctly" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path") req.referer.should.equal "/some/path" req = Rack::Request.new \ Rack::MockRequest.env_for("/") req.referer.should.equal nil end should "extract user agent correctly" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)") req.user_agent.should.equal "Mozilla/4.0 (compatible)" req = Rack::Request.new \ Rack::MockRequest.env_for("/") req.user_agent.should.equal nil end should "treat missing content type as nil" do req = Rack::Request.new \ Rack::MockRequest.env_for("/") req.content_type.should.equal nil end should "treat empty content type as nil" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "") req.content_type.should.equal nil end should "return nil media type for empty content type" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "") req.media_type.should.equal nil end should "cache, but invalidates the cache" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", "CONTENT_TYPE" => "application/x-www-form-urlencoded", :input => "foo=bar&quux=bla") req.GET.should.equal "foo" => "quux" req.GET.should.equal "foo" => "quux" req.env["QUERY_STRING"] = "bla=foo" req.GET.should.equal "bla" => "foo" req.GET.should.equal "bla" => "foo" req.POST.should.equal "foo" => "bar", "quux" => "bla" req.POST.should.equal "foo" => "bar", "quux" => "bla" req.env["rack.input"] = StringIO.new("foo=bla&quux=bar") req.POST.should.equal "foo" => "bla", "quux" => "bar" req.POST.should.equal "foo" => "bla", "quux" => "bar" end should "figure out if called via XHR" do req = Rack::Request.new(Rack::MockRequest.env_for("")) req.should.not.be.xhr req = Rack::Request.new \ Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest") req.should.be.xhr end should "ssl detection" do request = Rack::Request.new(Rack::MockRequest.env_for("/")) request.scheme.should.equal "http" request.should.not.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTPS' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080')) request.scheme.should.equal "http" request.should.not.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https')) request.scheme.should.equal "https" request.should.be.ssl? request = Rack::Request.new(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http')) request.scheme.should.equal "https" request.should.be.ssl? end should "parse cookies" do req = Rack::Request.new \ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") req.cookies.should.equal "foo" => "bar", "quux" => "h&m" req.cookies.should.equal "foo" => "bar", "quux" => "h&m" req.env.delete("HTTP_COOKIE") req.cookies.should.equal({}) end should "always return the same hash object" do req = Rack::Request.new \ Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m") hash = req.cookies req.env.delete("HTTP_COOKIE") req.cookies.should.equal(hash) req.env["HTTP_COOKIE"] = "zoo=m" req.cookies.should.equal(hash) end should "modify the cookies hash in place" do req = Rack::Request.new(Rack::MockRequest.env_for("")) req.cookies.should.equal({}) req.cookies['foo'] = 'bar' req.cookies.should.equal 'foo' => 'bar' end should "not modify the params hash in place" do e = Rack::MockRequest.env_for("") req1 = Rack::Request.new(e) req1.params.should.equal({}) req1.params['foo'] = 'bar' req1.params.should.equal 'foo' => 'bar' req2 = Rack::Request.new(e) req2.params.should.equal({}) end should "modify params hash if param is in GET" do e = Rack::MockRequest.env_for("?foo=duh") req1 = Rack::Request.new(e) req1.params.should.equal 'foo' => 'duh' req1.update_param 'foo', 'bar' req1.params.should.equal 'foo' => 'bar' req2 = Rack::Request.new(e) req2.params.should.equal 'foo' => 'bar' end should "modify params hash if param is in POST" do e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=duh') req1 = Rack::Request.new(e) req1.params.should.equal 'foo' => 'duh' req1.update_param 'foo', 'bar' req1.params.should.equal 'foo' => 'bar' req2 = Rack::Request.new(e) req2.params.should.equal 'foo' => 'bar' end should "modify params hash, even if param didn't exist before" do e = Rack::MockRequest.env_for("") req1 = Rack::Request.new(e) req1.params.should.equal({}) req1.update_param 'foo', 'bar' req1.params.should.equal 'foo' => 'bar' req2 = Rack::Request.new(e) req2.params.should.equal 'foo' => 'bar' end should "modify params hash by changing only GET" do e = Rack::MockRequest.env_for("?foo=duhget") req = Rack::Request.new(e) req.GET.should.equal 'foo' => 'duhget' req.POST.should.equal({}) req.update_param 'foo', 'bar' req.GET.should.equal 'foo' => 'bar' req.POST.should.equal({}) end should "modify params hash by changing only POST" do e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost") req = Rack::Request.new(e) req.GET.should.equal({}) req.POST.should.equal 'foo' => 'duhpost' req.update_param 'foo', 'bar' req.GET.should.equal({}) req.POST.should.equal 'foo' => 'bar' end should "modify params hash, even if param is defined in both POST and GET" do e = Rack::MockRequest.env_for("?foo=duhget", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost") req1 = Rack::Request.new(e) req1.GET.should.equal 'foo' => 'duhget' req1.POST.should.equal 'foo' => 'duhpost' req1.params.should.equal 'foo' => 'duhpost' req1.update_param 'foo', 'bar' req1.GET.should.equal 'foo' => 'bar' req1.POST.should.equal 'foo' => 'bar' req1.params.should.equal 'foo' => 'bar' req2 = Rack::Request.new(e) req2.GET.should.equal 'foo' => 'bar' req2.POST.should.equal 'foo' => 'bar' req2.params.should.equal 'foo' => 'bar' req2.params.should.equal 'foo' => 'bar' end should "allow deleting from params hash if param is in GET" do e = Rack::MockRequest.env_for("?foo=bar") req1 = Rack::Request.new(e) req1.params.should.equal 'foo' => 'bar' req1.delete_param('foo').should.equal 'bar' req1.params.should.equal({}) req2 = Rack::Request.new(e) req2.params.should.equal({}) end should "allow deleting from params hash if param is in POST" do e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=bar') req1 = Rack::Request.new(e) req1.params.should.equal 'foo' => 'bar' req1.delete_param('foo').should.equal 'bar' req1.params.should.equal({}) req2 = Rack::Request.new(e) req2.params.should.equal({}) end should "pass through non-uri escaped cookies as-is" do req = Rack::Request.new Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%") req.cookies["foo"].should == "%" end should "parse cookies according to RFC 2109" do req = Rack::Request.new \ Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car') req.cookies.should.equal 'foo' => 'bar' end should 'parse cookies with quotes' do req = Rack::Request.new Rack::MockRequest.env_for('', { 'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"' }) req.cookies.should.equal({ '$Version' => '"1"', 'Customer' => '"WILE_E_COYOTE"', '$Path' => '"/acme"', 'Part_Number' => '"Rocket_Launcher_0001"', }) end should "provide setters" do req = Rack::Request.new(e=Rack::MockRequest.env_for("")) req.script_name.should.equal "" req.script_name = "/foo" req.script_name.should.equal "/foo" e["SCRIPT_NAME"].should.equal "/foo" req.path_info.should.equal "/" req.path_info = "/foo" req.path_info.should.equal "/foo" e["PATH_INFO"].should.equal "/foo" end should "provide the original env" do req = Rack::Request.new(e = Rack::MockRequest.env_for("")) req.env.should == e end should "restore the base URL" do Rack::Request.new(Rack::MockRequest.env_for("")).base_url. should.equal "http://example.org" Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url. should.equal "http://example.org" end should "restore the URL" do Rack::Request.new(Rack::MockRequest.env_for("")).url. should.equal "http://example.org/" Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url. should.equal "http://example.org/foo/" Rack::Request.new(Rack::MockRequest.env_for("/foo")).url. should.equal "http://example.org/foo" Rack::Request.new(Rack::MockRequest.env_for("?foo")).url. should.equal "http://example.org/?foo" Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).url. should.equal "http://example.org:8080/" Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).url. should.equal "https://example.org/" Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org/")).url. should.equal "coffee://example.org/" Rack::Request.new(Rack::MockRequest.env_for("coffee://example.org:443/")).url. should.equal "coffee://example.org:443/" Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url. should.equal "https://example.com:8080/foo?foo" end should "restore the full path" do Rack::Request.new(Rack::MockRequest.env_for("")).fullpath. should.equal "/" Rack::Request.new(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath. should.equal "/foo/" Rack::Request.new(Rack::MockRequest.env_for("/foo")).fullpath. should.equal "/foo" Rack::Request.new(Rack::MockRequest.env_for("?foo")).fullpath. should.equal "/?foo" Rack::Request.new(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath. should.equal "/" Rack::Request.new(Rack::MockRequest.env_for("https://example.org/")).fullpath. should.equal "/" Rack::Request.new(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath. should.equal "/foo?foo" end should "handle multiple media type parameters" do req = Rack::Request.new \ Rack::MockRequest.env_for("/", "CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"') req.should.not.be.form_data req.media_type_params.should.include 'foo' req.media_type_params['foo'].should.equal 'BAR' req.media_type_params.should.include 'baz' req.media_type_params['baz'].should.equal 'bizzle dizzle' req.media_type_params.should.not.include 'BLING' req.media_type_params.should.include 'bling' req.media_type_params['bling'].should.equal 'bam' req.media_type_params['blong'].should.equal 'boo' req.media_type_params['zump'].should.equal 'zoo\"o' req.media_type_params['weird'].should.equal 'lol"' end should "parse with junk before boundry" do # Adapted from RFC 1867. input = <<EOF blah blah\r \r --AaB03x\r content-disposition: form-data; name="reply"\r \r yes\r --AaB03x\r content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r Content-Type: image/jpeg\r Content-Transfer-Encoding: base64\r \r /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req.POST.should.include "fileupload" req.POST.should.include "reply" req.should.be.form_data req.content_length.should.equal input.size req.media_type.should.equal 'multipart/form-data' req.media_type_params.should.include 'boundary' req.media_type_params['boundary'].should.equal 'AaB03x' req.POST["reply"].should.equal "yes" f = req.POST["fileupload"] f.should.be.kind_of Hash f[:type].should.equal "image/jpeg" f[:filename].should.equal "dj.jpg" f.should.include :tempfile f[:tempfile].size.should.equal 76 end should "not infinite loop with a malformed HTTP request" do # Adapted from RFC 1867. input = <<EOF --AaB03x content-disposition: form-data; name="reply" yes --AaB03x content-disposition: form-data; name="fileupload"; filename="dj.jpg" Content-Type: image/jpeg Content-Transfer-Encoding: base64 /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg --AaB03x-- EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda{req.POST}.should.raise(EOFError) end should "parse multipart form data" do # Adapted from RFC 1867. input = <<EOF --AaB03x\r content-disposition: form-data; name="reply"\r \r yes\r --AaB03x\r content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r Content-Type: image/jpeg\r Content-Transfer-Encoding: base64\r \r /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req.POST.should.include "fileupload" req.POST.should.include "reply" req.should.be.form_data req.content_length.should.equal input.size req.media_type.should.equal 'multipart/form-data' req.media_type_params.should.include 'boundary' req.media_type_params['boundary'].should.equal 'AaB03x' req.POST["reply"].should.equal "yes" f = req.POST["fileupload"] f.should.be.kind_of Hash f[:type].should.equal "image/jpeg" f[:filename].should.equal "dj.jpg" f.should.include :tempfile f[:tempfile].size.should.equal 76 end should "MultipartPartLimitError when request has too many multipart parts if limit set" do begin data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n") data += "--AaB03x--\r" options = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", "CONTENT_LENGTH" => data.length.to_s, :input => StringIO.new(data) } request = Rack::Request.new Rack::MockRequest.env_for("/", options) lambda { request.POST }.should.raise(Rack::Multipart::MultipartPartLimitError) end end should "parse big multipart form data" do input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r \r #{"x"*32768}\r --AaB03x\r content-disposition: form-data; name="mean"; filename="mean"\r \r --AaB03xha\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req.POST["huge"][:tempfile].size.should.equal 32768 req.POST["mean"][:tempfile].size.should.equal 10 req.POST["mean"][:tempfile].read.should.equal "--AaB03xha" end should "record tempfiles from multipart form data in env[rack.tempfiles]" do input = <<EOF --AaB03x\r content-disposition: form-data; name="fileupload"; filename="foo.jpg"\r Content-Type: image/jpeg\r Content-Transfer-Encoding: base64\r \r /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r --AaB03x\r content-disposition: form-data; name="fileupload"; filename="bar.jpg"\r Content-Type: image/jpeg\r Content-Transfer-Encoding: base64\r \r /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r --AaB03x--\r EOF env = Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req = Rack::Request.new(env) req.params env['rack.tempfiles'].size.should.equal(2) end should "detect invalid multipart form data" do input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r \r foo\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r \r foo\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) end should "consistently raise EOFError on bad multipart form data" do input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda { req.POST }.should.raise(EOFError) lambda { req.POST }.should.raise(EOFError) end should "correctly parse the part name from Content-Id header" do input = <<EOF --AaB03x\r Content-Type: text/xml; charset=utf-8\r Content-Id: <soap-start>\r Content-Transfer-Encoding: 7bit\r \r foo\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/related, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) req.params.keys.should.equal ["<soap-start>"] end should "not try to interpret binary as utf8" do if /regexp/.respond_to?(:kcode) # < 1.9 begin original_kcode = $KCODE $KCODE='UTF8' input = <<EOF --AaB03x\r content-disposition: form-data; name="fileupload"; filename="junk.a"\r content-type: application/octet-stream\r \r #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda{req.POST}.should.not.raise(EOFError) req.POST["fileupload"][:tempfile].size.should.equal 4 ensure $KCODE = original_kcode end else # >= 1.9 input = <<EOF --AaB03x\r content-disposition: form-data; name="fileupload"; filename="junk.a"\r content-type: application/octet-stream\r \r #{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r --AaB03x--\r EOF req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => input) lambda{req.POST}.should.not.raise(EOFError) req.POST["fileupload"][:tempfile].size.should.equal 4 end end should "work around buggy 1.8.* Tempfile equality" do input = <<EOF --AaB03x\r content-disposition: form-data; name="huge"; filename="huge"\r \r foo\r --AaB03x-- EOF rack_input = Tempfile.new("rackspec") rack_input.write(input) rack_input.rewind req = Rack::Request.new Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size, :input => rack_input) lambda{ req.POST }.should.not.raise lambda{ req.POST }.should.not.raise("input re-processed!") end should "use form_hash when form_input is a Tempfile" do input = "{foo: 'bar'}" rack_input = Tempfile.new("rackspec") rack_input.write(input) rack_input.rewind req = Rack::Request.new Rack::MockRequest.env_for("/", "rack.request.form_hash" => {'foo' => 'bar'}, "rack.request.form_input" => rack_input, :input => rack_input) req.POST.should.equal(req.env['rack.request.form_hash']) end should "conform to the Rack spec" do app = lambda { |env| content = Rack::Request.new(env).POST["file"].inspect size = content.respond_to?(:bytesize) ? content.bytesize : content.size [200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]] } input = <<EOF --AaB03x\r content-disposition: form-data; name="reply"\r \r yes\r --AaB03x\r content-disposition: form-data; name="fileupload"; filename="dj.jpg"\r Content-Type: image/jpeg\r Content-Transfer-Encoding: base64\r \r /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r --AaB03x--\r EOF input.force_encoding("ASCII-8BIT") if input.respond_to? :force_encoding res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/", "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x", "CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input) res.should.be.ok end should "parse Accept-Encoding correctly" do parser = lambda do |x| Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding end parser.call(nil).should.equal([]) parser.call("compress, gzip").should.equal([["compress", 1.0], ["gzip", 1.0]]) parser.call("").should.equal([]) parser.call("*").should.equal([["*", 1.0]]) parser.call("compress;q=0.5, gzip;q=1.0").should.equal([["compress", 0.5], ["gzip", 1.0]]) parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").should.equal([["gzip", 1.0], ["identity", 0.5], ["*", 0] ]) parser.call("gzip ; q=0.9").should.equal([["gzip", 0.9]]) parser.call("gzip ; deflate").should.equal([["gzip", 1.0]]) end should "parse Accept-Language correctly" do parser = lambda do |x| Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language end parser.call(nil).should.equal([]) parser.call("fr, en").should.equal([["fr", 1.0], ["en", 1.0]]) parser.call("").should.equal([]) parser.call("*").should.equal([["*", 1.0]]) parser.call("fr;q=0.5, en;q=1.0").should.equal([["fr", 0.5], ["en", 1.0]]) parser.call("fr;q=1.0, en; q=0.5, *;q=0").should.equal([["fr", 1.0], ["en", 0.5], ["*", 0] ]) parser.call("fr ; q=0.9").should.equal([["fr", 0.9]]) parser.call("fr").should.equal([["fr", 1.0]]) end ip_app = lambda { |env| request = Rack::Request.new(env) response = Rack::Response.new response.write request.ip response.finish } should 'provide ip information' do mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4' res.body.should.equal '1.2.3.4' res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329' res.body.should.equal 'fe80::202:b3ff:fe1e:8329' res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6' res.body.should.equal '1.2.3.4' end should 'deals with proxies' do mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '1.2.3.4' res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4', 'HTTP_X_FORWARDED_FOR' => 'unknown' res.body.should.equal '1.2.3.4' res = mock.get '/', 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1' res.body.should.equal 'unknown' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4' res.body.should.equal '3.4.5.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000' res.body.should.equal '2620:0:1c00:0:812c:9583:754b:ca11' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1', 'HTTP_CLIENT_IP' => '1.1.1.1' res.body.should.equal '1.1.1.1' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9' res.body.should.equal '9.9.9.9' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329' res.body.should.equal 'fe80::202:b3ff:fe1e:8329' # Unix Sockets res = mock.get '/', 'REMOTE_ADDR' => 'unix', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '3.4.5.6' res = mock.get '/', 'REMOTE_ADDR' => 'unix:/tmp/foo', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' res.body.should.equal '3.4.5.6' end should "not allow IP spoofing via Client-IP and X-Forwarded-For headers" do mock = Rack::MockRequest.new(Rack::Lint.new(ip_app)) # IP Spoofing attempt: # Client sends X-Forwarded-For: 6.6.6.6 # Client-IP: 6.6.6.6 # Load balancer adds X-Forwarded-For: 2.2.2.3, 192.168.0.7 # App receives: X-Forwarded-For: 6.6.6.6 # X-Forwarded-For: 2.2.2.3, 192.168.0.7 # Client-IP: 6.6.6.6 # Rack env: HTTP_X_FORWARDED_FOR: '6.6.6.6, 2.2.2.3, 192.168.0.7' # HTTP_CLIENT_IP: '6.6.6.6' res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '6.6.6.6, 2.2.2.3, 192.168.0.7', 'HTTP_CLIENT_IP' => '6.6.6.6' res.body.should.equal '2.2.2.3' end should "regard local addresses as proxies" do req = Rack::Request.new(Rack::MockRequest.env_for("/")) req.trusted_proxy?('127.0.0.1').should.equal 0 req.trusted_proxy?('10.0.0.1').should.equal 0 req.trusted_proxy?('172.16.0.1').should.equal 0 req.trusted_proxy?('172.20.0.1').should.equal 0 req.trusted_proxy?('172.30.0.1').should.equal 0 req.trusted_proxy?('172.31.0.1').should.equal 0 req.trusted_proxy?('192.168.0.1').should.equal 0 req.trusted_proxy?('::1').should.equal 0 req.trusted_proxy?('fd00::').should.equal 0 req.trusted_proxy?('localhost').should.equal 0 req.trusted_proxy?('unix').should.equal 0 req.trusted_proxy?('unix:/tmp/sock').should.equal 0 req.trusted_proxy?("unix.example.org").should.equal nil req.trusted_proxy?("example.org\n127.0.0.1").should.equal nil req.trusted_proxy?("127.0.0.1\nexample.org").should.equal nil req.trusted_proxy?("11.0.0.1").should.equal nil req.trusted_proxy?("172.15.0.1").should.equal nil req.trusted_proxy?("172.32.0.1").should.equal nil req.trusted_proxy?("2001:470:1f0b:18f8::1").should.equal nil end class MyRequest < Rack::Request def params {:foo => "bar"} end end should "allow subclass request to be instantiated after parent request" do env = Rack::MockRequest.env_for("/?foo=bar") req1 = Rack::Request.new(env) req1.GET.should.equal "foo" => "bar" req1.params.should.equal "foo" => "bar" req2 = MyRequest.new(env) req2.GET.should.equal "foo" => "bar" req2.params.should.equal :foo => "bar" end should "allow parent request to be instantiated after subclass request" do env = Rack::MockRequest.env_for("/?foo=bar") req1 = MyRequest.new(env) req1.GET.should.equal "foo" => "bar" req1.params.should.equal :foo => "bar" req2 = Rack::Request.new(env) req2.GET.should.equal "foo" => "bar" req2.params.should.equal "foo" => "bar" end should "raise TypeError every time if request parameters are broken" do broken_query = Rack::MockRequest.env_for("/?foo%5B%5D=0&foo%5Bbar%5D=1") req = Rack::Request.new(broken_query) lambda{req.GET}.should.raise(TypeError) lambda{req.params}.should.raise(TypeError) end (0x20...0x7E).collect { |a| b = a.chr c = CGI.escape(b) should "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do url = "/?foo=#{c}bar#{c}" env = Rack::MockRequest.env_for(url) req2 = Rack::Request.new(env) req2.GET.should.equal "foo" => "#{b}bar#{b}" req2.params.should.equal "foo" => "#{b}bar#{b}" end } end