From 4bb1a9d8d8fd12556a10e063151f75a746e5b86b Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Fri, 8 Jul 2016 17:13:57 -0700 Subject: [PATCH 01/30] added lb diagram and some more detail --- doc/images/load_balancing_design.png | Bin 0 -> 38180 bytes doc/load-balancing.md | 73 ++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 14 deletions(-) create mode 100644 doc/images/load_balancing_design.png diff --git a/doc/images/load_balancing_design.png b/doc/images/load_balancing_design.png new file mode 100644 index 0000000000000000000000000000000000000000..716378c0db20da9875b0cc1892e0e1aa217c1540 GIT binary patch literal 38180 zcmbq)g;!P4_BD-kNjFG$OG`_G(%s!H-G~UNl(ckrcXvs5mvnc1oA-Y2FZc#BaGZPZ zx##S?)?9PVHBY#bf)olO0U`th1d8-W@lOyCQ0fp6kni8Xfg^5?Gx6XLoRO@QIK<2A z&!3jO7;pr^;iINA1OyV+>n|ikQVJe82=5{-F9E*>j{w1l^65QS5d;JogtWMbipRoX z>K7f=?dzA-T~=ZkNJl8D9!RRZwZf2`@I-Q}phU4OdA(%DC$9O+=~7qQa_zbX#=LW@ z!~!aka%8#B%~qbO!`;{|<44mZkZZb&16{X4VZ_hrc zoOMc{<>O73Q)?Ey}6QvVMW4|nr?WjFS3n*xgS2&Ebzusl=_9AKcrR>9`GJd^-Z?p`E<-idM_?h%cHpn z$9M`QYtxdm(u?|za-K$-@NXV5P^l6Ly3ek`>$5_F(@W{RRsLvO;|y=EI(62Jk52mh zHQ=Ebh&)g|)RT&y@m?ksk@aeeMTjSbH#hN{ZSXC>e2G-oasAhRcu=O8odlEVfX`8# zm%_A@6aDM%GG|o&n3{y6J8WF6`6$h^RTt^pblDcNM3fug*5KBR?TFNATPw`?(PX-qCIpVO( z(Z61%S+DV;h$h>_5-%rSuh!tj(EHO(``!6Q8r-0wGz3cTJ;JT1g8t!~wiI(IQ4->X zM%Hy4{YE#Js>Nv6#FnrNN4E*K@;dlBl&g+;Y4<(*O9#?;f=OxK&o0UjOMcD%SK5`l zu_l>f+PI;?wI&uX@p0GAhbrXC$sBhFzqRacYxI!!cQv;idu&E180uodH!_SYHqs<) z(uFL2`OJV^dN0#AUXw+b73#658`ccPVa=UJQ(Q(979%HaKS04b7vv>*KO@aQ1Nk!J z3olcL8|>LdU%Uc)4ABbZ7Mm=r{Ls$}!6?~)l0F-(DQ39YohIlWcK5(j)H$*^IdLn< zn>e+k<9UD2PQm))1526A5b<#T+Rc^;W>uBr%-+A9O)--M=4#vVPP>M~ObRtpq2Z2; z-Lsq1wW#A|37u(pLB6K5{kML4Pg^RHOF_c6!zP0%;t+v+P0AV;hGz@T!*}!{V|vFn zSg@<&Iap1U$tN@B=q*$csJ6Nvd9{c07)HxY6x4;Ze3JyjF3zr9D7J0bNSTG@mmUo~ zUDzQUXa~BFI8bApYJazi9KXfUx^?NnzlUq6I$u7nzHx1+Cgc{mix3F)|#`eAp4=L#{XJ_8z^s(lnMNP*=+sjMUlkKd;#3fv82`fDk zK_8z1*2G(z8#_LJbZd&zegD1_tG=ZAVWdX*3&df8t7`-h$&~joB-{+M&zrEw$xc6R z+5~OQ9i@4-<@(%u4)y25u3MQ8`F<$guA(`N4M`<>M6eXSOcR6TpigvF>@Xp>hriyeb?$$vy=>1s|N;b}C>cZdkxcUY_aWVs)Cl6O~x2mbh`G5S<7l(eMMf%i;E4S3&eYJw;_ zU4r(Fi@JMeE4g#Hvuu5#>Qd0LU5**5_#DzKRFfsYr3Jfe&H@X9rBB6VC%l2?_f-#F z>9JLLQ`Bn7*jgkFKf7l+8lCw!n|Kxx^duCjBvr)NgA?pEwLfUAA0F=Nm(HWPSskZ~ zJ?#5X)rf_Lw^mxd*#0F;x$8sJIzgY zm?<)U7!(Wh;AV~rdAL5CiR6J|rR0uBjtSp3?nR%;fQBGFS?B>9Oz7j)lb4O|^XNfl zWgrO(n(howdR0u6qW=E(4%M;CFVd<}BywZ)>nr^;%~vk|sSoAqxbC7s+71*Y4A5#Ay6IEIJ z_q?VU-G>qH{`8a8V@U_xAlp@tX*e%gNMLJhhelFyK9n&Zd)Gg5Qsu-C#&;{g!Chak zHC^GmU9`PWL7USMyGz|K)Sj%D)*&!JfKYqA#jvI{MfH@5#pAWV@p8#?X{E%=cti`I z(sTmt4KYRS`ifj!sX@F_7$OBIRdqQPykvO zTAyZ_`E#0JttrjT@d(nap0zfrflf3D#5*%{=t0~1-9ydeqv;{)Vu&d%f0CVb2d%f)N=gUNw@ z`t6cDXpeuj+-^hhM^)7mLM2AdHyz%``1463PXd}vbTO@1*KB5A(RlQCd)9u$yTxZ7 zA$+++aVpztb#a@(5c)%vUi!W(<6VrdcWj&&r8b9;9_DM7JZiCk44vsVb-HlXt39$+ z?9Z~6;t8{4*Ye`8I{47KT-|nML$1guVv+U^+E__sU*95gPh<^6-e5V*2`iok&iBPbfNBZFIq*_531uyE>vnkuy(nkDo# z9rP1B-m0zv3ZKVAVp35M&P;a1OnD|YHy7E@2gC$!o~6N6J1;7c@)r@cKgdoNF0CZG z)C^DXG_l)D6TGJE5b48zP#!pk_mkM?F6YkpXr*eqt0>m;&DV z&@fueot+O{SJ{XpY!7w2qz zcMa$BL#YUR^MN4JS;37w4I3($znz_TggDfic?g^-Sopg#8v^Q?48Z2oIbke><4-sr z?6FA~+m1|=)v!UXCGs=*H%vmkMrLm+Z}E~tT28Nvz|s|WfT~6aMD;f*ebQa}kiSRk zLdD@jTTkXAkA1%ttnJP1@UsI^7w-zMdXdiz_mXv!CuUu7zZ4^iwxFZw92wlh{jtYB z5LYE{EPcDq(%lpC9R;z{*_unD*eI`j%JHj$uOpjY(nqadKiZSe&XAAie)=srKlKsVazw-Zf7A)F z7b=f$9U+8>*#a>F0=Nz1w8$6lr?qtArc*W2bj_2-9y;%2j=cxxYKx3W9lUBr?ll|&{r;iY-CM{_wQ+JJi4nLc{< z*VeY)Cm*d!Shy$Uj!tKGwhIR-E>^9(x-NzlJMqiaIO-Pm@7LP<5S^?szt~0XftpVH z4%GH2+iBdsEw+;RW`;1!lN5uFVrFjZC3soIsv8H$n7--)x9$}H-7e?OrlXH<0T_|s z*^*LT-#FTSIev<1J{4hq@w}#QI`RWGJ1uQWkEBQb>$0QY*-^5vD(`B~bYfB2L2rGY zeaCFu@Uo!wqm*jt=CP%Lfw;b%*CF-N(vdD~o?^;oAKGk{-=gw^xqlNw*9nPu_g2Th zRI6*0dgCX(8K&FRssVhr$wjzZcHiEQrLCnlTPOR9zNU+fFK6RBvxlP(d(k9hwAIgr zABmQ}5Vh#(mpz~dpR1_qmxn|kwZcKWUmXriJUt|~nWKHazpB!#G<$yL56Q`qLqt{c znUD@i%--4A$jQ;6Wn?m)A@!THG;D;UOZe`)|H&s4xSC6Y$*TYaOB_w^_9 zPU@37q)&dg*W7@t;F}HeOM}8Iu$&5VRthksb!e^E(hB;Pgp{DdnmUg37NY)G-ah>E zQ^?MHIt;8jN_ZL?jrUDMR6Xdt{Cl^jAqwBU_tC3_geAJk?D?u)U-S319}E@n_V36@ ztM!3m{Jjhd8nkA?RF7v!;T>kVoYi2M&igMv;v^TMS{hh}pEw1Y1qFMbfD;p&2;~ zJ{>=6THOa-Q=SsCx*vjVqy}TTK`DZ+X^BXAa|opFiqKuuI_7l>j>-srw)}%940X=T zyg_}dO?})a|I%W-uMhno@EliBE{Wz~Tl5qaB$+E#?TwcMBU2Rk`hu!Xn%B;rZ`VDI z+EyhWMs4w4UMS8^CUE>?;6LAOCkx*`hHML*_v&5}PE4-f>$RrAgt+xCPnN7KaI&qx zB^AD4x@mDD1ZqLO0NCHtQWC-6xFn-Pv$}ob!*VO%qj$r^!d|Nz|HQ<{k)h3#r~CO| zenJybTN0emr`f{pqdCM9~xnfIxK4>(057t=7uX2&-@4-WWYco6q|-Ljb%{ zl(Xmy#%LvyRZHw2%T9R3`f)Mue_OlzA6ResT;f_Te+Z{O4#krS&LtDCUYiXpI3Egr zf@rUnKB}jwyn@{^l58BJ?bUY&UqYTqVdAzBQlCYv1)4@n5*X|tXPbWV0 zJ){)adX4nT0lL?{I2o>g$Q@S0BS?{eFiwnQv?K0Z1`3pD#os@E@wExQuJu z(JMLe36s%UVDzZB*Vk@ur1Z2t=10TDZ6)UVR?`<%!ONRi-7H{-fNUaHXUAQH)gB26 z$wT+wI z&6t)eIb2b`XK18kyw>`6n=sn-acVs+x#4CUwz_XAZnd2_@_A{jQguu^gL7K`;jbbK zU3{?7<3S8Gl&&fVWAqyI6B#8#5}F29PIjuqvR@89*XR>xFj>d46jBeV*0d$1v}IYV zkboT+vh1mBeRm93Y~MYP(yRz)-FJ)Q0Hpz249lG7dP>{y$^+{^@WHnF=+u9KGH%dy?fctFxSWZJ6wLhlHGes}jHTq)X*PB8FWd9;+QSseK|^6qL5KTwo^ zlDoZbaQoQf+zkBg(C?BidQIRsn_{cayf5C!#Dof5h)Gs$XIXs~c}e!VXt#Es)5$Cn zy2EZ$D3MkiqPURbCJ|!l2d6VTqdrdL4HYpKop0JhZ}ToP`ds_i9Kj@fLP4AD>=Vvl zt=|Cuk3JfezjWMp1;Pzln3E?W-r)z=;)d-X&TdTIg77JUAXI#_;ob7S%8?(37IMUrX^ z{=*#efcc<-SSj^UEtUka;Bn`!<5Tn z{1@xhR7*F5wl$blHHQq_B@K}^Wjdv;xS3BXZ?ysev|CgPNFFWWuFJ3GtRo}O_(FW- z_ucDAe~EaA0GGMM;rx9BHqU*6<9JVdu5R0b3In!$e^X!M>Wl@o4tK7J@}|XGJ7+GZ zdDy%4DeviH*Y_zoOe_Uu9Tm~;E%I87&=6P&k6Rs2Za?cncqvDfgig>PXV>QkKj^iN zfd0`g>1}W6+>?!7OAOoRPTLszZC7-aDIFL zfc|zN@jz17xK2-gl0oN9Q9eBr*X&-8jNC`KHJ1rq1)6y;tK}tKfVYnK-Mq;fM}KQ< zbDW*rRHJ7zA1U(n9aUS)zDsC|5jyKPVEUX86&|lU8Eo!iU|cZaL;x|pJMQlmtw?0C zc2J=#j$=TA6w#?CrAI%1=RV?hC@tt!DA%3cO6n~aCo+pdr=hE+ioDc={oJN5kB5#s z#^*jO0dFl8#Qj>tf?0LBqH(sGlcmr}no+R!ff3fz(HT?5<^WRUV9iwiW(JP#!TPWS!(>!NoJ~vZFF$5b&JQ(TS4^ zljJXdcBCi`?)_d6FItrG8KPJUwo)p?s+1SH45DntwXGUXo?iy`N9s@QEL#xB=W7S< z=OFAD@7qYfIIh3tch|H38M z=$&-o&+pFaE*`7dVqTjYJ7X_V678EU?l@T+pD#<%i1)2xRhOKXSaM?6`NRmfR((%3 z+{Cr%}N%^{k7ih zGb-)Oxf!a{re&tjn1wx8)D@S?88OhlCo11l7^>G z_;6?YMW9Id?&_D!rA|QW#^Tp|8C_@8ZN3(Ki?Z-h6m>0))R8e7UYfiKMZL2eb#m#^ z0rD;rt4dtXru?s4DMU?4U%&M9fGNoj-6__4)0irq5$#{S3nPhgc?+&B!$XkY%j0*|u$uQ^_NP227KB&hMX167fVGD4#mDfWW z3$9l3N*3ndB4lpQx~@=z=8HCK`|ONX@?q1?m@UmtEZP7H3aP0nvSn8^^lQdSF|j0ZQfV`jf?{ z=+yOlZ82+sLWllT=(+O?X0>K}lYRae8^}E{Su+?hCw%k9YK-`pXLdiOM6IT3U^k~s zM^DJ|D?Hax-73+{CA8{=y_N;7aigw|(PGC?{5*R(j|9%)@(?_~d!_dJVhp^ww2jN$+V=fdPckai_|L+fF%m|+jdpbw*VeWL*CU;mm8&DW~LE;aqc7qFT#(qLT+#i81BL-iwjK82yg?&W0HA=i* z@+uT(3LgjjH4w7VgVcjgAsQA|Qs0m{$u{3##Jcn7{Unr93j5=SY-e-(AiOG>gLkh1Ea=}> zXTsb^=SSGOlbz$isXChTD{F>3P{-%WZ5Y5NfGTI(lM63-X}aN@WkvPeD~Q!m811)C ziX}`=Tg?I6Kpl0=8=mKmMB;H2rIl{~Fk0g1>`nzLu&;J)+{;amm}{D-Xg*e_{?`a3 ze8RUbnwWY*OLZ)IrKWeI6X)j|?uU4awsZb5dWvAQ;v_+^2i-bwdXGy7wAp-R$&34q zv$G@mxXoAbCQM&=G^t*0MeFxI>&4?B%5DQ?Gj<(Hi$nWSTYMT2D`<9N(p)UG)$|=5 zEM)B(RwBsJNDW5S>5z}~5VT|Xr3-P>Ep8kr?KddRh8i9%L-QwxWG+dLqKpSZG%K493 zz@iQCmUuW@+)nFY`Eo7OPe~v0WYd&9dDEx5TenrVVhBBJHHNtu_1*iV2sxsnrQ<9; z-#WlWB2-{`gFbP$oI1mmbjHo`hV%4N3axzPR(LpPhaf%KVIKc}xg&`DIV~+gO6O`% z?1Sus`;3^+J?niR=^Yq;V5n1+mNrjY9p%aic~&@yOaXz9O}i?5HAYreBg^l9UI5T- zKLu(z?8=uAHLi0X53kSK&j0>49E^5qbZKA@gjeHPhIR^+WK3T~Z)GC=NLB028bLw;}wBuPa^MztMi zlKmv1qpOMBPF<+@?ViCE56uabS>G2&_#pci?Ho=-K*%RRsPae1%NNt}EZNzs6iNT^ za8=7}EMNPVdI@&xg#NRH%3Gmr*?kpPWG>#?)3ezb3(`N8qwa|DM!TX{peyYe>*_LG znkwZm|EegxF7C$7q@(k1z7YGTZqtHh8_`c)m2Q^saG)`fb(v87T}&qAb9+~q&?6tuX6aEN0q}SsS@=^G{CTVc_-ecZKmLB}#*^k8D&?h`~^QVJl>r z{?l+?S&4zUWAZqUY9SwoC z9%b|V%45C3UX$5}D?HcRr8@>x%-4Aj_sM8Q(d6vFSlle>LBI1!y2&dd-AP?xGu*@0 z7=R$yVUpe~kUl!xsQ0H|j-V{N*_c*TaHJiWnldUQz@lDLn?gS|Xovbo&sat9CclVy zA1rpO6F=CG(^EqC^}Xdlz>JM>>~}81onPy?4z`HJhwPngM{eQt_0*y7uwaVEVPt%F z_fOdy1&+__iz5YpGMML}xgBriRTv+^$V!M@e{|uTmymF|P{vsV%_FP*gIgNm`@d?? z4n9T{q=!ob9p8~#m#X0X(9kL)zGxot-Gk>VMPtKE(lvX`)sF2}(flZhcK;2TWjQDWw+n-yX}!ZqC=gmvlWE zY&7n(36$+tg(fqT#c64*SFgMu_FxGI0|ht8b)bNM_d0%q_PELh)PhJbU;^r3g;Up; zG`!U)UPYH~Ft7(B!zH$?V;O}j6f46;zaEPI&%L##t~%6UU+yf8&1#N2z&lL<^KfpJ zS=X1H#N%#{02);A)!94F`hAm-b-xQJg$ldRID*I6j;^r05Xex^q783KpoZ@G;XJZS z-DbGk6DkxVU(1?@<)s&3tv|j{6FL{X5?ALAe@Eb0LEAjryd@Y+>yu*Vu!Y(Wu^uYU zw2mg;+|81@^J?>LCc-gGeqM35wE zKhpK__Sp~g=LTCsLdCSLxN_FTiX0FO`YLau#ignYJ7M@m@X~*E{!-4e*j#a_3Jtz{ zxG*?b36H?UjNS%}-km)F?-6z`)KHdvji zGt1v(`3a9)EQq)M;(emI)9Id_=_r9x=t=G3t87jhv# z9k9Hvx3Yv^wk&ctf3^XBwuFiIQBhw%xg4}9r)!~6#CPj|$wB=)+-NnsJh8`;10uKF z-fZ+$k}O-LEUvG#+&eCoXg^eZBW={R3hPosKTkfp(a|kz+>5S`Amo;Pdj9dIG>hIJ{Pj#RbfYEQBlwwshd~dW=7c_`HVK9?O10@_W5N_ z9?eRtAG{8-fhr1wE)y{9kAso!CM)9;UitP={)zC$z)K%@M*+zI470CEn8|$gncM7! zsO_Xst6Japq*30*Ed_xY)$0WFh)Rd8Q+YsCR;eKjIX^ozEnn{FDzK~#EvPqG<^V|) zdSi3F>xhv{!i>X_MzQi8d4R6@=;`EKg-3loEAUwW1>fH(F?DcY-NKAIS+U?;jmoT# z^oWF+xjA36z4$Ie!Nj~uSTI$%+LZxwYH&qq{lqZ6=*yU<>cu6W-@~nlc}tq2ee1kg z2t6ZH&&WV{To~#+llh2Z&A~8!)85lUtu6a(jr7-|OF1eW`rP-d*agY9z&`_qKYfL* z?KZN1_dZ?%_e4R}rhrQ(x<5&3k=uZ(3);OWudYLrPtX!Dy^O|I*a8>8uwWUH>2~Oa z9R6aw$kX9avPb;^uWRndGz;}8?sgunRWIncVRQpBdQ=9Pq>mQeFwhsuJ$*Nj97D^K zRpl(KKB%SzsR2%LJhGw-HAS3Sr@j6VODQR@o`e}9-cCVvefjYDVg8pVmLW8va;m{g zC)XVHWNzH0zO*Xy#41+?fOa;Mz@MUqs8r|k6~7RTumWyDy+)|LKqs!q4J76P7FN-6 z6t=q0%LW-r;O5;R&9Qt{{`FbuQ#o3 zLw7>*xYnTHb^++o+3DeCGyf(%3Epz!L?cL()648@0nUf_eV-8-!i#{bA@xyr8{1*~ z77n|(760Mh4xTe0jzRt%=f=&zSUZJE(oYAK?SLZ4gGDZq`lh9@-k5g8Kladk(UBl6 zmWX#wNS&zAjQ*sPrbC8n?zfzc*)D37yR+1}9rVuqA@i)ZlK3zb4>}rFkNHaaI%iNx znr2ILz|7&LejN@hT~=~58i)@8TwEz5x1$IVk9yiHvGAO8ex9cMH!SMv+KkgLx_*La zH+z$Zd}1*_!e{2Pv#UiPSOU}bH+qlqQ+Rz|&Y;qKX~HA% zpMNHTxrD1#B$ibVNB%j_ZZ)o)#7prTh72}iSL?QtC(oa(jwiukC#@5d)5WDcMIT?w(?YF9hg4UX^XEJTZfkWX1%;Z$JK#)e-Y_CTLs$=3 zY1x~tEs-b6+1Ay0Ea4ipa)+%WX}?YPHM+f7b*?EZ{Bept!mGh3Z&kXo0ilj& z>#X-NR*QJ3faAIbw(fXf8(7{FuiY?7jy=N=#EIEVZM5OSYTwa~tc1)0XO?m3?d$LZ z%bcX9h{gh-`8z;pxbfeQZm{6qaAGQ^IhpRkM|ObJpaHZ|7v27^cs zhQhS(i*nDoP`+}-*~ncN1sBU$Y3&-AK;RrM_B{%EojN$K1NqW+Ci8K_sA=H~DElu< zwIvHj+Pd!M7Z*plWrChlIAyBZ#b0WK<|?}EnyjNqwhbiVKxj!W;xOS&*Dw(N`gLej z3J6?cl_}B?8mEGygSQM~RhN8-c}GQMqo6@ltCa&fj^wcNxT z`%{D=C;-@`h(?C1x8wMJrs~oi7x5MMT~CFgAiq3&foihWJkWhz%?WpZH!lg%>icvB z-n#{OQ|n4of}N1i;YV3YP^?}HP_9BML3+K6yeX(XWUOyZQ*9SEtUBN5zb+>THaCf6 zP>d;oL8C|+DLuW(FEYugt|3Zr>)714y+BYwwxhqKVU3OY{)@I zu$9&jR;2!Jp(?EbDQ?}+&P;`0=%73d4;8T7elbBLij@cRFHo>#je|*E6+h+DKr%ukso-faCef*#{{yy8({PGX|VJx+4tShL_zWJx4py!;ES&i*@ zV0v$q>D77{@ZUNCfYgS9W-!%nYjm~Fy*bf04loxSJs;ApThEs-o5GchY1jJfaJJyW z?`Vom`ASO%=L*BV_?BOC+S&1uv=tGzG{%NUllpzeJ}e?!ho9RXFQfwtn|P;m#Ut8H zb|QlyRf2r9I-!bT2&qG5h2EVSagwpsJttsE2AS)hG8l_+R@FLh7XlL%V1jLN6-@m61J=!U7~s`rO%TeF@X`a`1DY5L9s8#9ra z#Ep|{NwvH`@Ltl_S-%$Z@Qw~SWYkipM73K`z3<{%JE+=QKKeB!Ok9(s#FS{Qbn>mSEHU4!tcpPana>$}s0VJIoE%)oG zs6#=W_NOx9E|9fg(g&&npbqOzb&b4?=Sv!|CeA>vU5z`tI(n?%h*#&>ZE_D!*r%zS z;eWg5BMT!e<@?Y>E45JQ>*U^`Yp=g7w@<*ikchSMd$4w|PF5+;)?$h4Ow$36&bR3J zbZWt(?7?%ksDFQFLgbMG=j%Ufw#vvbUH-FKix<&D-Ao|B>iZ?{eQ87kAUKx)nSJFO z?a;K))Ejyc3)CgF?_!$88#P`pDctrb*UTdRFe);@^zAtP3uHbc_w0-UPgc z&(8vLP3{!s%D{B4Ly|%lQKg0IO)gIXto6a&s$r01DYlBK@0g5KajaW!l>~I53jBoR*vUZ>y<%5pBDb8XyvVw%^i6A zRptlbtM7h4ZN0rH|9ufWoou`YZgv6gKLpehK5`}|ol`xGeN^0t?K{zyXIFoGN5?T> za!@F<()3dtL#thKUu|Phu+KjrCS@~{DbQQ(lrz+8A|shSil=SVK2dbb6KpQGo6hy$Y^jai*~2);-nywPiN00X9_e$|M~U*+7X7wl0B7zh2aQoPMt==^ywOs9N22V&a-?G;6v3o z{EV?#=HVLB#w{M<>dSIz(i0Ng+q=3bHOgl7i7_x7{}j~9GR-y_v|$Z_H6&WK=%h#W zs7kvYPti*v&#$D5Q3}@>`iv|NNJJChYq8PDHR>lI*aj*whs=LmU~C-0l5pzuJt5jS+KC{MjjY=;ZD#i(N0K+xHS1Y z5Mjz{?dn{Hs>r{Gm~N;|bAln@GM&D~^<~^c^#mwk1)D`CCu@K2F`_G8-(dOY z(wNMt%q^)^wlg*7RAcGR%qSO!gS8(g-$dw*-wL^aL=DnE0~R6SfhBulZ=zgo0blB930zTk z^2m;@XNyq#!in$m-7a_Cisu2ZsOIE!hy8BHV z-;szmoG;P8DLfw*$8Pm4PH}LO>=Bcb$Z5;}s%nvA1tk|8_t!?V&QABYLY`N*3A?;# z+>U78B(@e~d?8FcEba8c)n`VWp`-1qz?hiNFy$&*HFn)`T*qCCNwW3dI}6Es2}h(N zG3Ox#SQ7Sr@%lS0kBBuuGKA8-gNRU$FBHNKp2U-X&m^T;LH`M6DfD|bRuUU(;4BZc zenUfp1h~Bz|BmA#@w_SxMjT>R777ZZ^l^(6eZyxw40SFs8t^Cvml6Fb@8GuyTvcJE zdKWtgUuYfTVZ!iARA%v{zRI%06)Z~Rm$Sqggf%^e!0d~DJ_|KC6pHGlq%M1BC z2=WjZoI}2}S#=JE4aU+rAC@u89bL}S~iWUXlZxG?N=AxrDrv?2>T3wvEEqQq=+fV6@3hp=!aEAQGX z0&#x_!Ec!%@9Jiy>h^@ z5>Ef|*K@QxI@*X0-4E3C8Y_*_@XBh%LKBaopN+Qqe@gkkubOWb;NiImBt=j9D+4WO zRyA)vS8mtP}>{D&Ed+MgVH9s57B*odZNZK91&K zLUm{A_(=IzU@vfLV2P+(v-q`dr{! zC!W)oI|~)w2UapuW&SEtdn*d>uQVumD__#VJ!Br~7W>XNo%*}5XrxE{0WGJ8k+E?q z5)zUWSZOI&zU7bY^ETXd8K}j(;0`&~$%NBYH6S$@q0( zwS$b3X=>vsHRA4Q z>tH+z5Ka!&ozh0P`X0~wq^Dx@r^W~pC&DxxWPpl}PIG;&=6F_CR(gd7;|16TNO$)e zk?oRCH9Z!b3-`JvCfS!wTHYB*fl9qcN#*n+R0VGhT^+xR<$SXEceD#HfYcmXs89PQ z0XAzK4^?i4J&zE=g-%vroUdI33zfY!)Xhy3BKpqH(GJrjAA2K%_aU!-Ef;!4S zMT8e}DJv_JQ^#0_)Gq6!D%`B~|D;s^Z`v>{y#Q(kJX- z6D}imep8OLouo;b@F^b+gvmp`3;CbGL0m|EDM?AaU->))k)M_CHjuTNr#zVCgI=FS zE2VYz?^u97A-9au!V0KVsS)WZjLdV)7MMdp7N;U;fz~K#$xf3BTn%a5bw0XLhYK?7 z%8CD3(Fi$ShzGWR3}DOmh#Ftj4oQ{aX~_ofK(eXO^$!)-92!YkGtHSbsj#e>#eP>E zjJBjz`<4%5vHa0Tw6ZB=Q<8^x-z7Iyvc})%wccQ3VNIGZH@bR7N@jt5*(uhpv&+;8 z2n_FT|0-umN?Vq~1!o(fC-F-fyrUPQ#dL(# zMRr!cvo}iWzq|eOO;q+U&VjAdJSrZ_(3H4ox7e;dTB#xcA_5t%t6EmN=9L@;H||KR zF{ykSQX(>8!hsQ?rLjW}2?@z!!WqhHPyYGyXTrz?F%}k@mOOuT=>i0O$pW-ZyZ7rvyb+8O0-E7BgDj zh3w-;A>QAJ#laDb%Pn>!T{==*sq8I(?Wr2b2oIN1y2~FlS9qB*s$e7$^t{BDW%dqP z@)x6H;0MeS(7yLu(ken(aw^Q!6+$Xw5wtf29Q6NQju|`{Ygzs=T>f~0xg{Ok=f#UF zT^BM4TB435WOBYYVRH5~XwYo4N}QaXa&@?eh0$Bfp~fi@dp9;MK?Zpw>P!JMSLhSD z0{hYOYvGx==56tO_UZ?0+16i1)fsQ??6MGwlWVR$pgleZdu~$l@p)GF7=4jxidQoh zV?V_%zm<8D^6%fjo7dg(Z1Mfkt;(g>?L@^%Q&BF8rIsF1UKnp+5O>XwWttlP%dEl? zHLi9OdrejT5Z^x`+mZkm#89?E9$`4W-6M24Ej#=9w_XD+MjX<1B`qyalo6iRSJusm zDJ0r6d?*QPiZqS&r)Jv2#3hANMro#MBwqyMQ$!`k#@5zlNRJV(l9E!n%i&x_BFg&j z*N}RAeDsS3i&>{b4yZhPj53wHlfTde5f=rr>5=uvDfc_VjBpHW>}_*Sf&~1?Uj1yY z;Gyu?HjE^`W#w&Jh&0jRLoOVDW>2p(&Z1C&tLgBjRYj2%+N1Vt|r z1)6?z5K9B~BlqTDe^-mI16->JVt_slfO-^ANa8wmLQx3gizfG|H5ucJBqIr?4JOP$ z4!ucLs4J?jc9F2LDK;0g(*{Ur$4OvrNk<>7B>(}FQE<*|6}*OF@QqW7Douo{+Z!3+ zf0jbxH-a~;2-ug;wVcw+SlHOunUi}YY>9F))yjTxm|jstypILiMzb=4@ohy z@{bQ*)K*i;O4QkE8Bh<-0q~XrmUJ5g;|b0P^ju;@^i5A64HBp?H@CTq1wm02ZPGs} z*I*nHTUE2$YLvtkCXlP@@-B!LnNfhtg274Xl4JX)=M6^G?pzbEoCyB?_4fq zHb9o#{Y?qbmz9w@ZoQh(?g^v=fK7nyT%Dl7e)@wmEYdOmUHG5Chdi>_s0HyV6kwkBb5JVUv^tlMMsRls@@BtTop@8${($oW&NRMJ1+~W zp~Pq(RR!lpg!37~UU5xAMK34xF*B_U;oHL3qTD2V8VFXaS!)4#4;O0}7Z>-49?YWD zgK6wt>wuL=_PND@0n2*o;?ayHe>Rj$rS$2vk?o!%WD^zFR0{lskj8gJsxfwN3iFRj zLh!D){^tc)2r_dD*(QB8`f8X$ZLA~XjQRojKd2oE_dVwpGDmV+TRN*ic=#aBln{2b{5+f%yFPV z`s7XSrGcI#14Tn4!)K~BLlCz_n4%aa19EeBE$5EoRHor2C;%!}d$d0Xd;JPhA@kea zXvyj6o6^(s%2@u|%_MZjG5RTs(q0)D2R;6=F3u&W=>V+<#VOy}&|IDR`9BCo5!Y4U zL)G6m^@>YKplH-s#YJa*(ei{{A9PfEphYPgkcv{f?=|8i$T9F_La+Oyl#p#mTK6`} zX;x6*Zo$NWX|S#BjjHdqJTo(i1-U7kYFS*e9-H1Kc5IWqRLCZtmcn;fsA!%Ep-03) zRk%T7FJY8Ps(;1dp?e?VU2ys37M^h7y+i1eIs7HJsHfQTR;{^{NvHQ*e3q7%(>q0k zSfnb{A|q9c)b2QIKRh^*w%b;ln^#_AG@@;rvP$z5^LI1RPT>YAs}=3iMi@IuHXP$v zzLbr(&e^wpJrWN37B?uKqnj_2A{gN$7Ug>S17XVduWz)SiS1!l&6&;7qyF@0RYtE? zOTJ+L?8lq@Unfd-`^&MWllJAL9RP|)0ort0aF#l_xd~ZTXb1sh^if)ekU!rPA3SwB9J??``5E64f5;S5@B+1sRyNB5KwEoKCB_u=&FDv6 z)PaL)=b;J|*J{slu{Hb}jj6A>b@6WSmlH#rv%KIpf(D+frE17_w-V=Blk7B_a?uB< zhk8VYuhUOkc`eyizl_#{Yvsoz-<7Im>ew(SQ$L437i%hLeh*8ZSy|7A$)hMetY}0a zMAq85bOgVr=SE6HDXrCPCJJpTprUfd3{-oAE{!en^psL5C`ylorzG;Stx*be)Klc- zNlHlwhsCEgCZe$EXq`gW2nH#5J5 z^W<;-!WdGsiZXASk`rOyjOWB3a83)Tqr`*y5&{`><@xlEsUuJIkqI7$CwF)ciaZ~q z;HMTTqErYDvh%49T>ERck*WjQgU&cZoI!c@pJ<%x=YM9x8Q8H+ryG^v&Ia;5N_13) z+kX}cRnkS|(2p(WuiePc<@xtdk#O2iIQ&1dz5*!9@BNqVk_IVJx)BLMI+U&jq?<)b zIwX}&kp}6I?(Rmqq(PAG?!0Gzzx$ti@7(XqcZOLv&Yts}Pd(>-*KwZ|sty0kD1SUj zB4p}R1IoiL(eSgR8_|FaEAR*~_;gMQC8(h^fYshFWg>+px)l%f)feNU zF85O+yT!o02a?&msyWz|6mr;XrMHABh;Vs{`(-8#SZd( z?@eD9 z3No0Q=mo`mHsc0-a>VpZX(}qQ2E>#u&73HD#=Z>QTsuQ*e`X##Ar(4*aLwwd3Zm4f zjkl2@A?Um*!#?m^SSa6o)F}UH5&%i_PJw<>CN_ZA(~Ofe52O(Cry}U!EG>N>{|f3s zE?4m8KjOV_19pEXi3qsR?>fPW1T$vlyzz{Sh-ZJeZO5QIw#YcKv9UsBQh416}Dq7Qt$rbz~Yf}{HGSZlDX_4a6 z*3p=eLsKt44E_|JGp;DM#+V!5BL}vJEN;4I(zs&Z5h#2?SoBjJUgBl_peT(wP0{a2 z$Fu&$F@U~UgO9)n`Lps}3tHUPdtH1pEGcBoQDS?uKHJ!)MMEMD7+8RXeg@2=*a}Wq z&uZ~S?o3VtEEqr;#=g$48Et54Qi@q|-f;zAK5vLZy|Z6(CDYvLuZ%Js(~k{eyia26 z--~*b6@az9vpkapzVTC74=smu1W?R};H_DdHAD!G6YJrNOta~Wd1cjr{UPUjmM@t@ zm%pfLLU~3&d0K(tp@};haVi%-VA*AD@#Fl4 zXU#XBTH2_b3H_+OF#M?$8;=rP$_eZX#L4M=dv(w)YY5#?Bnx$M@ zPV$I<(%JU$@kg@xFI07up=hi~2+H;^6}WImU$;Bv8?OHHy|bjE^W075S4YpN&-3`I zP1#fis-dVzqvW(8NUs?iYx1y*UO=lG$|Id9pVww=Y<&AZp@5*+y)^K@RgrK^`YlwX zF(pqRd56G|jS~r7pQKQ1FyoQ1Y9o$u=K+j zrd?ir<5^OXr&s@opP7_xrbulA7#aC51&COh5&bSEh=e2&Bbu3gI}8)exs!OK$lvUO zo2?){(-e2?BGAfP`bG_B1sX|tp&$f$7Sr!Uy#iE>XvC$}`_cxq6_x`D3kee&D>kA4 zwEvv`XHAMCXRCYeRuJ-2%~gG+v0EJ-cy9ugq)D5Owf~bIEshP82nC0Fg`@n1`b5OZxs%CVV#clm-+Rwu9sk0Z_J}QFEFc>yn}?4Yq4N?u!>dm=Sl9~u&Jg4DY$HsiVrR%5$&HdttAjE6sHE&L2uOsX9O7N|CRiuIamLxDrW zL7QOUUW<@k%fTNT0mJ?JS;-06T1rh$yxo;@seC>}1549Izjp!fd9b z&R+=HtZ`Y4)GVQ^!~noN_O?9WSAI|(oy|yCKdC^I!cYv03Jc2u{QZB%rZEvZOJ?f1 z;i23!SMwL4$;NY^M;U9yS#xu9KO%IhLk{X)4(6FBXmQp&D73}4??&cptcrl$jmctE zJ9kvzB9DMK)YRifq{92h4Ywhl}L>b zhXGvBk12QOCtl0Jk&%}GRqw`Di`e1BIyyRzd7c&$ z5kUzOk!r+uX(|v%+seuczol(v{R=Klaif1Bc~XelyXS|-E)TX)vFh6W9E-4jc8r$Y zj?~=TAk1G1m(ErYc-J((h|koeuKdH-9Lh2&F$iEM=TP5`RHg%Vpxl`Fk~;F^ag$$m2X^%5gy%B~cke6B1|PMsVkg58 z^`Rjwk5HDWn4AwtT%}0=9>?)*rZ9LCwC%M|$iBs2VkbLwi@thZK5N9A5V4G32B}UZbsCbDhf3Sn%aKdMhw&Ryd!iB( z6S+dOu%_;47u6LFSpCMOz5#xh2BjQED78aHN_t&X9jvTsLH_k~+qEKJajmM*5`v5` z!v#sD@;Nkw>Q0Qx?;tcQ7PXKuw30f}XRS9Pjy6;I)v=^&8B6Gd0c1+LurN|0Y!r4g zveT7!qR2lkPzz`l?Rs~-tbbC;#et85MuGpMhZ#Krr^wE;4$P|>JmuwwNUP?oWAmON ziT!@=vl95YYNdu%S|@b%AwkBiycbfUUGoRnFg?)SSz;Jgv4$e+PNn0bj)m4?_8`Ud z|Cuo5j+m@M2|%Ew&FXAf4UDHV6`gDhVPBnDfMa8~rssTbYjCo9zB4Iw`unAsI>b4~ zfk!I+_wV<7)j9F>?Pa&u3x4r&EQGlFl3|%%D#_w_R1||3WIODk-dF~8)~~m{`13X~ zrGjCT1qOW2meL1bSXOqFFd;7WylPJOuPHYh07ilD2g32F#5-$B;HaCvb`kU?i>7N3 zQZ?q4xDSutaGUQ8V`C*UiXqBz`eA_-ItVyzGX~A_?hfEQ#dy1(LqPwx112yXefjGCO z)!a0J#%6-#ye;4C8;MbGtu`bLKaX zt=fA0+fIuBudiVIMZz&;UUdRM=QW`eS%#gq5fAwtG7{2#TpxQ|7Q_zEA3r2G7{j$o z$Se1sX7y@`577yFx4Ks#tA`Bk6Y`=Mht%ZT1(^?pg(Il({{H^!nxFFN>c$)Jsf1t? z;0GxzEt!l##gu)i>GSzk>~dES(P%(;#Tk z^caTK7)^;mrp%y4b25Ne8mlcQ&7a;;xe#i@Z^un^!sy5{gIzmb&z)w*&_wKaWWT9) zWc<0I2_NS&(CAp$*a~sP%xQ_k<`UA(EdLa52OO5lwN;R?ixk^~MB!*S4zjqvsKurh7S?Kj25J@~W=_0rh})b8 z2v;G{)ea3Q6=@`7>^f&6yH+mwWhj1Js6lgd+xN z97RlxuP$Pd5i<(z+7me1;#}@KnoA8#tKD?#U=}5kl|r8C%F9u-NyxAT8KrzJBF2GQG)>taM$VzA-B*ov=+fsM#c})~l(_+4aJU#4YDw-ZhS%mq^@El0hosFPE5P!{9a&(zdq4wSi6s@pI7Ig0|YsmNDD z*2=qzIw2fB*WoHYBxrQ-V9*n2F{_P=+#cBZSEr&#B8vxLH(MQIlM)vPp<;`#U~FrP z&z=(SLJW>+VhFaUBQ$HBwD5oKR&Ps#sLfnkoJfcO-X=XfJe*TXrIO`W8C-m@q2Uf# zw2f@5nNl)!2-`0E%BhD>2|_o5ns3y1a49+%3K};CFdT@N-odzq%(unWblORjT;+#| z;3Xvh4Wpa}2L^VF0KmXOAn+~-VG+_M*IvqV#Gzcb4?cCIx}TWwibbk~#V}2vIL(3e z=rEB;>)-bH9l<8)VO^p*@_lt;dX=w=?XVa5F-n(fVIc}r=vP}&4i^UpZQ6mITmk^? ztnhD5)8QA+`?VZ-LgM*+BOHPPf(Rmlg2%~L2y5X{TXW}N`e`A>cBuijew3v4H z4b1Gt%6X)8c7^ok&&R)wO|@*q!{8TRxt{(F2?-&Ki;k}7vgS?TfETe~SqUN@Sb;A_ zY+h%_#I>PiQJ~poouHO(8ehkz;*nm_spckus~QWfmA&t#ER_T;%hJ%S`VEaEHqa}4 zqg`ue|B9->#%1(BISIjCV zGL9h+*ZRGQ4Xm$_Jb5N01GNO>p)2{o%fI&1Gy+<}f`cm(3d&{ppRn&>w`$zcO+;8$39IWRDg z{3lN#<)O}-;nk~kJxitzY0%`>nX5D-DJYy*S>^!^R&PQe+~>oOM5|X1TbQ)7eoIk? zI5%SwT7Fd>rsdIGENe6}iKb_&EW&YyKn}iba$g`RL!9GDRmHtwkSi!4P=e4*DD?r^ z0~s0lK}(hdA18yzM>vFjh9-ntJky0g(`h0ig-fyQooH9#voQiTExq5UFt#+Va6-5R zYEx)Irmd*3Q42Cfms|Wn8-Zi@^|ss2i@GHNdvLnV3*mnt%dXVXh^A zN|8wRy$pdYi8Rl{qTpzD{BsCmSgxOSouY^^sI)>NVo^8_8TEU zst@D3QHbA(SOn=~n3Ppw;Q?8I#pb0Nbx;#{aJ8h%q}jH27lg~sV!ZQX2bxnR$IO8a zN5RQSAh~t|M|LKMhcHtA{ zCMvB6E{!fus1MZrJuq-PF*oP#V{UHlmcnbZ4$rYj0DQqi4$&bR3r$hi%^5A931v?` zJ|M0Q5TO@Q*P~pY!l$~DCJ%KX$QERn$;*fT%3@seJq0f-Te@`z@8Xgj@(bgua&=l# z>i1b?yi^vk-!V$?MEriWKVAvZOqc5E^q6ulaVE+0fxvJnN9r)d-a~{+Gx>Q3K!B)_ zkQ-ih_EJBm5OE*#`5!(=|02Ji^3kPhKrQ<;zyfwU499)y0#AKDWbPOlw*uUf9x#Oo zS&TloFp@uHe7*gp$G9SVB8H|lhFt}^FIiMGPsWx~9?cU%gT^c5`}!8kTK+|97u*os zqb50aaeaOLFJRKuLB!B&%8e|WKtSdD!T=@%6XJ@*`|?op5oX3P7BR*B@SctT7*-Os zGDO51dWiaRDyqY4S4!RV5>Jm@-Y?crQ8Xao3uzR)Td5LID z`LkibCPk_PjD{G=&&yw2iA>egX@74<)s}T+q!Z#?Fy(%OdYjRJu|Kd=SjdhTA8Wkf zB*wVoBt;;?9z}0)mJ5d83)$(?5FGT)6QyuXk9tb8ttT7P=xJ0FcQyub>^q?Qfx6_N zh?`vV0|x`_bGOma1H|OzNTs&DS0*8qWGV_{ehcL6d#+faMckDMvPfqPwNnRiPD=^Wu4k{AgzJA>tDB(CIy||-8GuDk0Mae{t`<2zt zP8j$C#Q$<4G9eNwgnSvHg@tWz1q6oM?AN5-np_SfKkA#01t?luTca9Q6c*Zws%mIx zkm#9eFwVPW1sHjL{i8tCUC(UKgOIoLbQqv9SXRz>83>ZOY8k2xb8`zbvy#|^!qM;vShixB(fQc;lj;$tE4o;1%fu0X7yifJ&(o1l->lHzX+XE0qP7^DlYsOC*N=}d-}9P0LeEbhe18RSV{8rw zbWUt6tb4u2$Yaj2CEvmEBJ%vgTO)wn78BfbC=0LnsMt_kvUx(lfKNJu^S+MZ)>mmMGvM|qWVX;Mxh=}MDH;q?WA>-rX z`saz=B}78J1CJ?iwWkl-sP;khsA;whS6gMv_*-G&9g)-r;P}A%X~bk?(6nV_WXyqJ zNh%J6)GtD_Jbxr5H7r{54kBs%R%FJ*z?#A?%xuTu*?FFc^a8Q%8ZUSgr5a(Wp$eZz zx;o=R{}!BGmg)s3g$dRQ7(+Dy&qodwfJ|frr8RcNDYJfMb+xkHxS|?DXqH?LA0Tv% zKN}dIdk{tc_EuWOgB?@cvK^5hF~2=uaB&7x!A1|cG%F;Mm<*uW1CalpxRV~gFR(5K z#<%l7*oiTs*Td64+}x|FG>iUEVnAnyl-X@KhPt$b)hbH!arw#z>VPR_fBXoJN3ijw z3}?yFt@rV_a74xA;mMLp)F&0cMC(K}41W|C_*VPXnSmqg`E9G!1=)ne3vTWSHjvgT z!z{s~VrCAh=J@#dBhVpMW~wx3oh9#wAmbnqhtfhHud7_k%Z3xlapm1eN$fyKm08f@ z3Y_`&{=PL*>-Pm_LRTRAUKp>`4YmM;(}0c2!6XY-7CE)77Pa zKZq7^?j5;qjz0+`1SsDx7F60Ehto|ziFBxAjm$vV&XnryWdL%ZVFd2H-53HP0q*=~ ztPV4w1(@XLpp7C`Sh$-zQ@T$?Ncf8OySR}Gi5mtRMR&XcV?YLw0Ed1rptOz!x8Xe= zDPOkxB^Yx-Z{whhQC10@fyiT&b=f$7(|*6QV(5;CMuz~Fg<9cw9Bm({zOYzre2k=J zco80(&S_EBV|30C)NT4dRY(R%yvV4ieS6Lg=32`&faWj;+ zVc{n%=h5?PEi5!26<)+10&zQwgoK3J1}_F3{C^eb5Mn2sIwV!C)_TrWs!i5S^e6Jy zar_iv5CGYXd~+sf%};j~>S?b1MXH&baN1a53TGbevs9n~HO`=Fgx@@&35Olil!Z*i z-}-QXG)!J47Fl&u!VS)Pg8Ddxs=FUHL;o@Phe>T9t$Pl@N7eS*v2R5d9i7co`c27r zHRe+!@xFdmuigi-XnICf))|Z6cYfwj_S!|#fLbAIQ61*PM;a^^UqFPmp;^tDj02t| zBneQMd8DO}DTos8;M%=lZ36iLWpw;ADsC#MZ6|iS#RLVYlDc0E*YFMReh=;; z7G*y^oh({BHL>w8_4gOZDl3m!H?WCYin}6uN9Z%lx+3DleDGKH=cJjy0gp;v$lNKF3F)SKdU6As)%_>LfwM>f5d^W63i)=- zPgf91B!KGr`uP|C2Ld6da1a8VLRmEb-FRLR@FW<;baEEMozE8ZfT%ED=VdItI9M4X zskwr}-7-Z*#o_L9{7|xz`m_0}ugbc8z|{f`?4#Fa;-1ua*I{D>%(~70c5` z!yk^D9b5G*^usI~c{Hn7Vd>wagGk5QKBI=lwh^uuCJRpb!F! zpezp!DXsgKVE$^;e2A}zcK9LM z%3v1vVK>+ZuFrKG=W1osjf&wn&F`$y_@pGCqMEdZC*mgWKtArz6l$zPRl8l4OvinP zC;Phb=kH3;V^|(GuS$Pq1K#r$565qHuL)tbbge6CIe&a0NmQy1;61i#(ObO=M_GTI zA7tDN9mtMBdy+AQil*Zxo6OA2E~ctdl_5li4CS*>o-Wz#<#&M zB48e_XP6DfKa7JGG!k)`5nZfkaXV)wioi@CDwMhhVN^_{GeKOD`fpi z3j(Fg64N==_GKC$XBS{u3gT>=Yk*(LXT*Sh%?eBX`7=IvL4vXO+EHyXLMNOx#qXj& zl}bI`lqY7BNS0qDs{y@8xm;BsE1j+vQt`=PVK5V3+w83J_DMui2eRv{F_9rOFAE{5 z{+fzx=4;#h5dzWM0zHCR5Ol~Cc+tngwf^p^9~k*$Ne^7YtJ5+it1d*g!DF*-FUDR7 z*NT+av|(5L>H`U%9EoSXodNv$+K83ZunNSP5045T-$6B5mr=}GYS86$bnWcrIzddn z#cywG9&*%BIbA%5=DWRJ0(Zn@TVsv?jehQK!@$d1i(M`cfz$!DiwWgo{7M7$3@1Jo zCUleLD;7QT_s@2R_4oX4E`JUp7KLS0gMGuMp8^1LxtTnp?cv@Z1Ah$*Pp-dz9C zWfEfJ`@>&_g{5kcDFv0-HvpGYXoeZlaOyLW8%E*({;4at{j#TH`cEpnh&FtfE`ReZ zH5uxm3EJ$sf>npR&sWyh*Gb69nOi?+RvSslF^opOZm35xF*X(gH$o#L#nvK{B6|kf zP|R!6cn+L+*W1(XLZ1<#{`Batg6$!%&D^!66Ou!m zkGCw%F2j>_T6ywh`fmS_^}TP*JiWa3;JYrO=4mkMzkX&ZfRcZfsIt+)o)0Ypv3O8C zDFU8VjdoL=)y&~no)nt(8}Cr0!p)P0P-2q%H)D#)a^Xc;-6bEt!U0%C z2&puCBNezeQ?~s6!-t!>;o*ZxO#?6I7@ky`^#SW@R{AD6PMDJir#MlorDJGsI6+$S z+LBqU{@{xbhPKT@QRZrMbP@FgUjO+`?~0TwHuLHwo!qt=1Tlw2vg_ zCtS%%+5OnzvC4p|A$d7Doz2}{<`Vi|NHlaZGL9J{d+@iVX@RAz`D<0xYdDRfi>#QK zNl(B!`M^uzOVFXY_VNG;hqLT`XG27zL2dOMmU7owPHqH%9eN$*)jPNV-Z7C%^Fmn{|;{vvj}OZdsUJw>~3H#7B5zdpz4Bl3K)@ z>-Fn1G9JBWFBnMbzj=gSMKAlsNtM5A%!-r7cM^WOKWqCmRrrR6g@wfm9PKzU@I2|c z1XF#*(W9WMerId{izlk(WxEqkv`V_;*2o)_*=Bb~xgFvw;Y0JR8{LhldnPuYM&gd3 zro{xEM=UaK$%n_gqbYFW_`?^%rB)q$ES4S3TWSLXgBoxl0BEGAiR7%f78&OS1IJTQcc*fD zGmYN&Wy~~=7M=q?$zm+Evr#0{y|u}6NI4xxsZzk+II)}m`q=e1;W^#9DN1cnGmVK7 zqw$)eq$EhMK@;roC1BJ4{`w9$$^4%`K=brJe}MM#|NF=P_e|hW{-2kKKkHLgXMeS` zZN&alBw^2D1O-uPx*$l$%r)phvrKn$tixaTifL;%RFl$fprLWoedN5F=dp<`?PU;t zf3@~Wp-z-Vx?R$mP)}TpW|_n3E@Zd9L8V5wf!1C@v($j$q!U?)MZLXB9GT1yHa1et zfSxS7Jc1f#a73Vaj5!$HV5s7`NDvPFSeez|9-YB@Qum9Rv~W^EWQ(rh^kmDLN~gaw ztjQ0^U^F8YN^KoaSw~GT!ybks_&pPozFZzl4aPeaYL>m%Ra*_f^e@rr8~c?We;fAS z55xV+R7J9;pPSrO8;iLWqE+iV{M;nzyhAx}Oa5H^e>ZK z^SO!lD_-WWf8?01uX#5w_)t%mZBF1C$fX+oS#z#j4LSZ4cK?alzlA|avY|0YNxj^= z|F4hP7_) zuiOg9Ffxm8e!Fj|SdMShIP##zZDU4^4nIwPnzSSinWTB2AE%A3~t<3@ai3qjGh|b=OT0b-=BnWkb%H4m-ViX^o zauifdH`gAFhch)bKb3akX&OoEDbs!f%F}T3a7o?CK3atVtYxshbDi~#KxD8PCa4Vt z6Z+xqIYXMsPiE`whly*`8=qcL&enxZGCVrXx||=|NwVsQ%AN`|UXV!!ghu_nT&&Hw z0DI#EU_{-(A((t*-^Rbf4qH_fNB8dZw#$kHxVXfqs0*5fyy-&yDHRpXcW0x;&;?>& zE}dHC8tad%jZT@yN!%_H%ud7Pv^dn}ooAzKx4&Nw$-Ot6nX3x-*d#;Y{e&dy;PF;3 zD=Vi11`1rIhw=GOtX*JnXA?iTko>IbXiw~a&DB>j=$t=5)X?v;uy6f6pdkfJTJLDNCvp@D&Q!%ksv%iXUSMAT-t%^LuN z*~Z+9X57LV_WooWty!R9?-{wm&to-DGCai!rbl!ou&c~Lb0Sl8B1Zu{i>*IB_LcSb zyFrTI_cc0Kn43j^|2cHRqWQkjA)q^!&?QaypnN$qjKsZXkfra098yG&&HmqY=2+k8 zEIQ8DDP5?tz!X07luP@PN%Zb*?dtGI8D6XHYkg;j-8b)+E8h^bCOR%yxvMT;NlsLF zwk4#+_dYHOLf4mB4W3Q>0Jkg+aHvBnSr81S%1Db<^|4@WHd(nWcYc2ky^j=Uk(y z!t45`s*68NaCa7SnswHH7?BT{z!+j}F>3A=1ZGYtM!UWXxNfTZMgG%UPWVTyNc)a0 zA{wzV#2DcREop*E5>Z_00tL8rsjQF2lY`P#Lt39RwDB63ak zUABy=%^q=n&v{oqahCJE^8eQIr7By8@9|-P1*b?KG8osjvL3s~HJX##pHBeDM3yI@ z(c!}S46c)8XtVvV9@uZ)tzK7=Y?rL9U&m^;bgY9vb^%69e7aD-#r^VBT2?Ul+>2Du z6HbjG+l{Q>Y+!3oje(nfUr3>zf6XY|8nLr@ zpn!T4BegtY_~l-y;c!jA=B+{sDjxo=(m*|_qo?<_A;L1pec+N{?dRKdd%#eu2WxWM zR_`!>zWWKd!(pc_BqDTWtOF6iHFV47ndp`G5i!5NC>|B@O~eZe3&3_IGDRym*L+q+ z%V|G6Bgc=a=HJ!Rg?K|V;`PtsZe81L)LO}tUC!)~q>d0a;grK@eEeLEwb?#3b z19}#t^HDa#1IE*>Ui*SRRP{T;6tnryEct9XJieyPbmv4oI!b|V@c8xGZzIL@>&bsn0=AR{k8gtE%j&i~0@rpo7Rk~lN|wU9 zrAvDI>bM?Ip2FHMQw7qT!YjH^3EiAM0Bp)riiF0VZMiil$jrO$1iE?|7-!RNPajIH z&d3Z#5QXy8ayeO{^ZEi-E7lT9@Ya(EMl*mer$tA1fRBDJKbvB*NclFg9VQ%ZCnf

oXQe~N~ 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 Vn`JgvQafaeb~dMumLGP%{U5zNV6gxI literal 0 HcmV?d00001 diff --git a/doc/load-balancing.md b/doc/load-balancing.md index 681be02a72f..05d555a3f13 100644 --- a/doc/load-balancing.md +++ b/doc/load-balancing.md @@ -4,7 +4,7 @@ Load Balancing in gRPC # Objective To design a load balancing API between a gRPC client and a Load Balancer to -instruct the client how to send load to multiple backend servers. +instruct the client how to send load to multiple backend servers. # Background @@ -19,7 +19,7 @@ have temporary copies of the RPC request and response. This model also increases latency to the RPCs. The proxy model was deemed inefficient when considering request heavy services -like storage. +like storage. ### Balancing-aware Client @@ -28,7 +28,7 @@ example, the client could contain many load balancing policies (Round Robin, Random, etc) used to select servers from a list. In this model, a list of servers would be either statically configured in the client, provided by the name resolution system, an external load balancer, etc. In any case, the client -is responsible for choosing the preferred server from the list. +is responsible for choosing the preferred server from the list. One of the drawbacks of this approach is writing and maintaining the load balancing policies in multiple languages and/or versions of the clients. These @@ -53,14 +53,67 @@ unavailability or health issues. The load balancer will make any necessary complex decisions and inform the client. The load balancer may communicate with the backend servers to collect load and health information. + +## Requirements + +#### Simple API and client + +The gRPC client load balancing code must be simple and portable. The client +should only contain simple algorithms (ie Round Robin) for server selection. For +complex algorithms, the client should rely on a load balancer to provide load +balancing configuration and the list of servers to which the client should send +requests. The balancer will update the server list as needed to balance the load +as well as handle server unavailability or health issues. The load balancer will +make any necessary complex decisions and inform the client. The load balancer +may communicate with the backend servers to collect load and health information. + +#### Security + +The load balancer may be separate from the actual server backends and a +compromise of the load balancer should only lead to a compromise of the +loadbalancing functionality. In other words, a compromised load balancer should +not be able to cause a client to trust a (potentially malicious) backend server +any more than in a comparable situation without loadbalancing. + # Proposed Architecture -The gRPC load balancing approach follows the third approach, by having an -external load balancer which provides simple clients with a list of servers. +The gRPC load balancing implements the external load balancing server approach: +an external load balancer provides simple clients with an up-to-date list of +servers. + +![image](images/load_balancing_design.png) + +1. On startup, the gRPC client issues a name resolution request for the service. + The name will resolve to one or more IP addresses to gRPC servers, a hint on + whether the IP address(es) point to a load balancer or not, and also return a + client config. +2. The gRPC client connects to a gRPC Server. + 1. If the name resolution has hinted that the endpoint is a load balancer, + the client will attempt to open a stream to the load balancer service. The + server may respond in only one of the following ways. + 1. `status::UNIMPLEMENTED`. There is no loadbalancing in use. The client + proceeds by sending all RPCs to this gRPC server. + 1. "I am a Load Balancer and here is the server list." (Goto Step 4.) + 1. "Please contact Load Balancer X" (See Step 3.) The client will close + this connection and cancel the stream. + 1. If the server fails to respond, the client will wait for some timeout + and then re-resolve the name (process to Step 1 above). + 1. If the name resolution has not hinted that the endpoint is a load + balancer, the client connects directly to the service it wants to talk to. +3. The gRPC client opens a separate connection to the Load Balancer. If this + fails, it will go back to step 1 and try another address. + 1. During channel initialization to the Load Balancer, the client will + attempt to open a stream to the Load Balancer service. + 1. The load balancer will return a server list to the gRPC client. + Optional: The load balancer will also open channels to the gRPC servers if + load reporting is needed. +4. The gRPC client will send RPCs to the gRPC servers contained in the server + list from the load balancer. +5. Optional: The gRPC servers may periodically report load to the Load Balancer. ## Client -When establishing a gRPC stream to the balancer, the client will send an initial +When establishing a gRPC _stream_ to the balancer, the client will send an initial request to the load balancer (via a regular gRPC message). The load balancer will respond with client config (including, for example, settings for flow control, RPC deadlines, etc.) or a redirect to another load balancer. If the @@ -87,11 +140,3 @@ balancer in order to compute the next list of servers. The gRPC Server is responsible for answering RPC requests and providing responses to the client. The server will also report load to the load balancer if a reporting stream was opened for this purpose. - -### Security - -The load balancer may be separate from the actual server backends and a -compromise of the load balancer should only lead to a compromise of the -loadbalancing functionality. In other words, a compromised load balancer should -not be able to cause a client to trust a (potentially malicious) backend server -any more than in a comparable situation without loadbalancing. From 52664ccee592f884435a668b9d4da25f62b4eca3 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sat, 9 Jul 2016 11:24:30 +0800 Subject: [PATCH 02/30] Fix orig files for conflicting generated --- .gitignore | 3 ++ tools/run_tests/sanity/check_orig_mk.sh | 39 ++++++++++++++++++++++++ tools/run_tests/sanity/sanity_tests.yaml | 1 + 3 files changed, 43 insertions(+) create mode 100755 tools/run_tests/sanity/check_orig_mk.sh diff --git a/.gitignore b/.gitignore index f37989176ea..09223fa1ead 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,6 @@ Pods/ # Artifacts directory artifacts/ + +# Git generated files for conflicting +*.orig diff --git a/tools/run_tests/sanity/check_orig_mk.sh b/tools/run_tests/sanity/check_orig_mk.sh new file mode 100755 index 00000000000..142ef009ace --- /dev/null +++ b/tools/run_tests/sanity/check_orig_mk.sh @@ -0,0 +1,39 @@ +#!/bin/sh + +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +set -e + +if [ -f *.orig ] ; then + echo "Please don't commit *.orig file" + exit 1 +fi + diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml index e699c5194df..dde05131cd2 100644 --- a/tools/run_tests/sanity/sanity_tests.yaml +++ b/tools/run_tests/sanity/sanity_tests.yaml @@ -1,5 +1,6 @@ # a set of tests that are run in parallel for sanity tests - script: tools/run_tests/sanity/check_cache_mk.sh +- script: tools/run_tests/sanity/check_orig_mk.sh - script: tools/run_tests/sanity/check_sources_and_headers.py - script: tools/run_tests/sanity/check_submodules.sh - script: tools/buildgen/generate_projects.sh -j 3 From 7deb5fd79562388a928aaa3da37bbc8921ebc162 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 13 Jul 2016 11:40:56 -0700 Subject: [PATCH 03/30] Establish Objective C end-to-end core test with Cronet --- gRPC-Cronet.podspec | 88 ++++ .../End2EndTest.xcodeproj/project.pbxproj | 405 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../End2EndTest/End2EndTest/AppDelegate.h | 17 + .../End2EndTest/End2EndTest/AppDelegate.m | 45 ++ .../AppIcon.appiconset/Contents.json | 38 ++ .../Base.lproj/LaunchScreen.storyboard | 27 ++ .../End2EndTest/Base.lproj/Main.storyboard | 25 ++ .../tests/End2EndTest/End2EndTest/Info.plist | 40 ++ .../End2EndTest/End2EndTest/ViewController.h | 15 + .../End2EndTest/End2EndTest/ViewController.m | 27 ++ .../tests/End2EndTest/End2EndTest/h2_ssl.m | 199 +++++++++ .../tests/End2EndTest/End2EndTest/main.m | 16 + src/objective-c/tests/End2EndTest/Podfile | 83 ++++ 14 files changed, 1032 insertions(+) create mode 100644 gRPC-Cronet.podspec create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Info.plist create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m create mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/main.m create mode 100644 src/objective-c/tests/End2EndTest/Podfile diff --git a/gRPC-Cronet.podspec b/gRPC-Cronet.podspec new file mode 100644 index 00000000000..8db5490e0e4 --- /dev/null +++ b/gRPC-Cronet.podspec @@ -0,0 +1,88 @@ +# GRPC CocoaPods podspec +# This file has been automatically generated from a template file. +# Please look at the templates directory instead. +# This file can be regenerated from the template by running +# tools/buildgen/generate_projects.sh + +# Copyright 2015, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Pod::Spec.new do |s| + s.name = 'gRPC-Cronet' + version = '0.14.0' + s.version = version + s.summary = 'Integration of CroNet framework into gRPC' + s.homepage = 'http://www.grpc.io' + s.license = 'New BSD' + s.authors = { 'The gRPC contributors' => 'grpc-packages@google.com' } + + s.source = { + :git => 'https://github.com/grpc/grpc.git', + :tag => "release-#{version.gsub(/\./, '_')}-objectivec-#{version}", + } + + s.ios.deployment_target = '7.1' + s.osx.deployment_target = '10.9' + s.requires_arc = false + + name = 'grpc' + + s.module_name = name + + s.header_mappings_dir = '.' + + src_root = '$(PODS_ROOT)/gRPC-Cronet' + s.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the + # build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + + s.subspec 'Interface' do |ss| + ss.header_mappings_dir = 'include/grpc' + ss.source_files = 'include/grpc/grpc_cronet.h' + end + + s.subspec 'Implementation' do |ss| + ss.header_mappings_dir = '.' + + ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c', + 'src/core/ext/transport/cronet/transport/cronet_transport.c', + 'test/core/end2end/**/*.{c,h}', + 'test/core/util' + + ss.dependency 'gRPC-Core', version + end +end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..bc5006e9366 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj @@ -0,0 +1,405 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5E201A771D3452D500A81F3A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A761D3452D500A81F3A /* AppDelegate.m */; }; + 5E201A7A1D3452D500A81F3A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A791D3452D500A81F3A /* ViewController.m */; }; + 5E201A7D1D3452D500A81F3A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7B1D3452D500A81F3A /* Main.storyboard */; }; + 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7E1D3452D600A81F3A /* Assets.xcassets */; }; + 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */; }; + 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC76B961D36B2EE00A74FED /* h2_ssl.m */; }; + C124E05DC4ED9F5CA3EFD20D /* libPods-End2EndTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 931C29940B821E295AA90634 /* libPods-End2EndTest.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.debug.xcconfig"; sourceTree = ""; }; + 5E201A6F1D3452D500A81F3A /* End2EndTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = End2EndTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E201A751D3452D500A81F3A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 5E201A761D3452D500A81F3A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 5E201A781D3452D500A81F3A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 5E201A791D3452D500A81F3A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 5E201A7C1D3452D500A81F3A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 5E201A7E1D3452D600A81F3A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 5E201A811D3452D600A81F3A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 5E201A831D3452D600A81F3A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5EC76B961D36B2EE00A74FED /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; + 931C29940B821E295AA90634 /* libPods-End2EndTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-End2EndTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5E201A6C1D3452D500A81F3A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C124E05DC4ED9F5CA3EFD20D /* libPods-End2EndTest.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 52027776D515577217FE123B /* Pods */ = { + isa = PBXGroup; + children = ( + 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */, + E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + 5E201A661D3452D500A81F3A = { + isa = PBXGroup; + children = ( + 5E201A711D3452D500A81F3A /* End2EndTest */, + 5E201A701D3452D500A81F3A /* Products */, + 52027776D515577217FE123B /* Pods */, + D564C37197511893E4E62D12 /* Frameworks */, + ); + sourceTree = ""; + }; + 5E201A701D3452D500A81F3A /* Products */ = { + isa = PBXGroup; + children = ( + 5E201A6F1D3452D500A81F3A /* End2EndTest.app */, + ); + name = Products; + sourceTree = ""; + }; + 5E201A711D3452D500A81F3A /* End2EndTest */ = { + isa = PBXGroup; + children = ( + 5E201A751D3452D500A81F3A /* AppDelegate.h */, + 5E201A761D3452D500A81F3A /* AppDelegate.m */, + 5E201A781D3452D500A81F3A /* ViewController.h */, + 5E201A791D3452D500A81F3A /* ViewController.m */, + 5E201A7B1D3452D500A81F3A /* Main.storyboard */, + 5E201A7E1D3452D600A81F3A /* Assets.xcassets */, + 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */, + 5E201A831D3452D600A81F3A /* Info.plist */, + 5E201A721D3452D500A81F3A /* Supporting Files */, + ); + path = End2EndTest; + sourceTree = ""; + }; + 5E201A721D3452D500A81F3A /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5EC76B961D36B2EE00A74FED /* h2_ssl.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + D564C37197511893E4E62D12 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 931C29940B821E295AA90634 /* libPods-End2EndTest.a */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 5E201A6E1D3452D500A81F3A /* End2EndTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E201A861D3452D600A81F3A /* Build configuration list for PBXNativeTarget "End2EndTest" */; + buildPhases = ( + B6E7E45E860B5453E3A5F4A0 /* [CP] Check Pods Manifest.lock */, + 5E201A6B1D3452D500A81F3A /* Sources */, + 5E201A6C1D3452D500A81F3A /* Frameworks */, + 5E201A6D1D3452D500A81F3A /* Resources */, + AE303423292913AB8D447419 /* [CP] Embed Pods Frameworks */, + A4C61E4A2A73F6E1E1143310 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = End2EndTest; + productName = End2EndTest; + productReference = 5E201A6F1D3452D500A81F3A /* End2EndTest.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5E201A671D3452D500A81F3A /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0730; + ORGANIZATIONNAME = Google; + TargetAttributes = { + 5E201A6E1D3452D500A81F3A = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 5E201A6A1D3452D500A81F3A /* Build configuration list for PBXProject "End2EndTest" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5E201A661D3452D500A81F3A; + productRefGroup = 5E201A701D3452D500A81F3A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5E201A6E1D3452D500A81F3A /* End2EndTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5E201A6D1D3452D500A81F3A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */, + 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */, + 5E201A7D1D3452D500A81F3A /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + A4C61E4A2A73F6E1E1143310 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + AE303423292913AB8D447419 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + B6E7E45E860B5453E3A5F4A0 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5E201A6B1D3452D500A81F3A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E201A7A1D3452D500A81F3A /* ViewController.m in Sources */, + 5E201A771D3452D500A81F3A /* AppDelegate.m in Sources */, + 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 5E201A7B1D3452D500A81F3A /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5E201A7C1D3452D500A81F3A /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 5E201A811D3452D600A81F3A /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 5E201A841D3452D600A81F3A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 5E201A851D3452D600A81F3A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 5E201A871D3452D600A81F3A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/Headers/Public\"", + "\"${PODS_ROOT}/Headers/Public/BoringSSL\"", + "\"${PODS_ROOT}/Headers/Public/CronetFramework\"", + "\"${PODS_ROOT}/Headers/Public/gRPC-Core\"", + "\"${PODS_ROOT}/Headers/Public/gRPC-Cronet\"", + "\"${PODS_ROOT}/../../../../../include\"", + ); + INFOPLIST_FILE = End2EndTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.End2EndTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../../.."; + }; + name = Debug; + }; + 5E201A881D3452D600A81F3A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/Headers/Public\"", + "\"${PODS_ROOT}/Headers/Public/BoringSSL\"", + "\"${PODS_ROOT}/Headers/Public/CronetFramework\"", + "\"${PODS_ROOT}/Headers/Public/gRPC-Core\"", + "\"${PODS_ROOT}/Headers/Public/gRPC-Cronet\"", + "\"${PODS_ROOT}/../../../../../include\"", + ); + INFOPLIST_FILE = End2EndTest/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.google.End2EndTest; + PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../../.."; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5E201A6A1D3452D500A81F3A /* Build configuration list for PBXProject "End2EndTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E201A841D3452D600A81F3A /* Debug */, + 5E201A851D3452D600A81F3A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5E201A861D3452D600A81F3A /* Build configuration list for PBXNativeTarget "End2EndTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E201A871D3452D600A81F3A /* Debug */, + 5E201A881D3452D600A81F3A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5E201A671D3452D500A81F3A /* Project object */; +} diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..220e863b896 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h new file mode 100644 index 00000000000..dbc592fcc87 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h @@ -0,0 +1,17 @@ +// +// AppDelegate.h +// End2EndTest +// +// Created by mxyan on 7/11/16. +// Copyright © 2016 Google. All rights reserved. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + + +@end + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m new file mode 100644 index 00000000000..a4beef9954d --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m @@ -0,0 +1,45 @@ +// +// AppDelegate.m +// End2EndTest +// +// Created by mxyan on 7/11/16. +// Copyright © 2016 Google. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. +} + +- (void)applicationDidEnterBackground:(UIApplication *)application { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. +} + +- (void)applicationWillEnterForeground:(UIApplication *)application { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. +} + +- (void)applicationWillTerminate:(UIApplication *)application { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. +} + +@end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000000..118c98f7461 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000000..2e721e1833f --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard new file mode 100644 index 00000000000..f56d2f3bb56 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Info.plist b/src/objective-c/tests/End2EndTest/End2EndTest/Info.plist new file mode 100644 index 00000000000..6905cc67bbf --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h new file mode 100644 index 00000000000..941477a48ca --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h @@ -0,0 +1,15 @@ +// +// ViewController.h +// End2EndTest +// +// Created by mxyan on 7/11/16. +// Copyright © 2016 Google. All rights reserved. +// + +#import + +@interface ViewController : UIViewController + + +@end + diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m new file mode 100644 index 00000000000..535e40bcb39 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m @@ -0,0 +1,27 @@ +// +// ViewController.m +// End2EndTest +// +// Created by mxyan on 7/11/16. +// Copyright © 2016 Google. All rights reserved. +// + +#import "ViewController.h" + +@interface ViewController () + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + // Do any additional setup after loading the view, typically from a nib. +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; + // Dispose of any resources that can be recreated. +} + +@end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m b/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m new file mode 100644 index 00000000000..2649546fcad --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m @@ -0,0 +1,199 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "test/core/end2end/end2end_tests.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/tmpfile.h" +#include "test/core/end2end/data/ssl_test_data.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include +#import + +#import +#import "AppDelegate.h" + +typedef struct fullstack_secure_fixture_data { + char *localaddr; +} fullstack_secure_fixture_data; + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( + grpc_channel_args *client_args, grpc_channel_args *server_args) { + grpc_end2end_test_fixture f; + int port = grpc_pick_unused_port_or_die(); + fullstack_secure_fixture_data *ffd = + gpr_malloc(sizeof(fullstack_secure_fixture_data)); + memset(&f, 0, sizeof(f)); + + gpr_join_host_port(&ffd->localaddr, "localhost", port); + + f.fixture_data = ffd; + f.cq = grpc_completion_queue_create(NULL); + + return f; +} + +static void process_auth_failure(void *state, grpc_auth_context *ctx, + const grpc_metadata *md, size_t md_count, + grpc_process_auth_metadata_done_cb cb, + void *user_data) { + GPR_ASSERT(state == NULL); + cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); +} + +static void chttp2_init_client_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args, + cronet_engine *cronetEngine) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + f->client = + grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL); + GPR_ASSERT(f->client != NULL); +} + +static void chttp2_init_server_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args, + grpc_server_credentials *server_creds) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + if (f->server) { + grpc_server_destroy(f->server); + } + f->server = grpc_server_create(server_args, NULL); + grpc_server_register_completion_queue(f->server, f->cq, NULL); + GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, + server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(f->server); +} + +void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + gpr_free(ffd->localaddr); + gpr_free(ffd); +} + +static void chttp2_init_client_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { + grpc_arg ssl_name_override = {GRPC_ARG_STRING, + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + {"foo.test.google.fr"}}; + + grpc_channel_args *new_client_args = + grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); + [Cronet setHttp2Enabled:YES]; + [Cronet start]; + cronet_engine *cronetEngine = [Cronet getGlobalEngine]; + + chttp2_init_client_secure_fullstack(f, new_client_args, cronetEngine); + grpc_channel_args_destroy(new_client_args); +} + +static int fail_server_auth_check(grpc_channel_args *server_args) { + size_t i; + if (server_args == NULL) return 0; + for (i = 0; i < server_args->num_args; i++) { + if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) == + 0) { + return 1; + } + } + return 0; +} + +static void chttp2_init_server_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args) { + grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, + test_server1_cert}; + grpc_server_credentials *ssl_creds = + grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL); + if (fail_server_auth_check(server_args)) { + grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL}; + grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); + } + chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); +} + +/* All test configurations */ + +static grpc_end2end_test_config configs[] = { + {"chttp2/simple_ssl_fullstack", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, + chttp2_create_fixture_secure_fullstack, + chttp2_init_client_simple_ssl_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, +}; + +int main(int argc, char **argv) { + size_t i; + FILE *roots_file; + size_t roots_size = strlen(test_root_cert); + char *roots_filename; + + grpc_test_init(argc, argv); + grpc_end2end_tests_pre_init(); + + /* Set the SSL roots env var. */ + roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); + GPR_ASSERT(roots_filename != NULL); + GPR_ASSERT(roots_file != NULL); + GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); + fclose(roots_file); + gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); + + grpc_init(); + + for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { + grpc_end2end_tests(argc, argv, configs[i]); + } + + grpc_shutdown(); + + /* Cleanup. */ + remove(roots_filename); + gpr_free(roots_filename); + + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); +} diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/main.m b/src/objective-c/tests/End2EndTest/End2EndTest/main.m new file mode 100644 index 00000000000..a1894cdb413 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/End2EndTest/main.m @@ -0,0 +1,16 @@ +// +// main.m +// End2EndTest +// +// Created by mxyan on 7/11/16. +// Copyright © 2016 Google. All rights reserved. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/src/objective-c/tests/End2EndTest/Podfile b/src/objective-c/tests/End2EndTest/Podfile new file mode 100644 index 00000000000..59b64badfb9 --- /dev/null +++ b/src/objective-c/tests/End2EndTest/Podfile @@ -0,0 +1,83 @@ +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' + +install! 'cocoapods', :deterministic_uuids => false + +# Location of gRPC's repo root relative to this file. +GRPC_LOCAL_SRC = '../../../..' + +# Install the dependencies in the main target plus all test targets. +%w( + End2EndTest +).each do |target_name| + target target_name do + pod 'BoringSSL', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true + pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" + pod 'gRPC-Core', :path => GRPC_LOCAL_SRC + pod 'gRPC-Cronet', :path => GRPC_LOCAL_SRC + end +end + +# gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's +# pre_install hook lets us do that. The block passed to it runs after the podspecs are downloaded +# and before they are installed in the user project. +# +# This podspec searches for the gRPC core library headers under "$(PODS_ROOT)/gRPC-Core", where +# Cocoapods normally places the downloaded sources. When doing local development of the libraries, +# though, Cocoapods just takes the sources from whatever directory was specified using `:path`, and +# doesn't copy them under $(PODS_ROOT). When using static libraries, one can sometimes rely on the +# symbolic links to the pods headers that Cocoapods creates under "$(PODS_ROOT)/Headers". But those +# aren't created when using dynamic frameworks. So our solution is to modify the podspec on the fly +# to point at the local directory where the sources are. +# +# TODO(jcanizales): Send a PR to Cocoapods to get rid of this need. +pre_install do |installer| + # This is the gRPC-Core podspec object, as initialized by its podspec file. + grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec + + # Copied from gRPC-Core.podspec, except for the adjusted src_root: + src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" + grpc_core_spec.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the + # build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + +# This is the gRPC-Core podspec object, as initialized by its podspec file. + grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Cronet'}.root_spec + + # Copied from gRPC-Core.podspec, except for the adjusted src_root: + src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" + grpc_core_spec.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the + # build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + target.build_configurations.each do |config| + config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES' + end + if target.name == 'gRPC-Core' + target.build_configurations.each do |config| + # TODO(zyc) Remove this setting after the issue is resolved + # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void + # function" warning + config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO' + end + end + end +end From 4a7aca0e4bf357aa9755ac2a61ef23bf9035b3c1 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Wed, 13 Jul 2016 15:32:13 -0700 Subject: [PATCH 04/30] Establish Objective C end-to-end core test with Cronet --- gRPC-Cronet.podspec | 37 ++++++++++++-- .../End2EndTest.xcodeproj/project.pbxproj | 8 +-- .../End2EndTest/End2EndTest/AppDelegate.h | 39 ++++++++++++--- .../End2EndTest/End2EndTest/AppDelegate.m | 39 ++++++++++++--- .../tests/End2EndTest/End2EndTest/h2_ssl.m | 15 ++++-- .../tests/End2EndTest/End2EndTest/main.m | 16 ------ src/objective-c/tests/End2EndTest/Podfile | 49 +++++++------------ 7 files changed, 130 insertions(+), 73 deletions(-) delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/main.m diff --git a/gRPC-Cronet.podspec b/gRPC-Cronet.podspec index 8db5490e0e4..b353bf9a437 100644 --- a/gRPC-Cronet.podspec +++ b/gRPC-Cronet.podspec @@ -52,12 +52,34 @@ Pod::Spec.new do |s| s.osx.deployment_target = '10.9' s.requires_arc = false - name = 'grpc' + name = 'GRPCCronet' s.module_name = name - s.header_mappings_dir = '.' + # When creating a dynamic framework, copy the headers under `include/grpc/` into the root of + # the `Headers/` directory of the framework (i.e., not under `Headers/include/grpc`). + s.header_mappings_dir = 'include/grpc' + # The above has an undesired effect when creating a static library: It forces users to write + # includes like `#include `. `s.header_dir` adds a path prefix to that, and + # because Cocoapods lets omit the pod name when including headers of static libraries, the + # following lets users write `#include `. + s.header_dir = 'grpc' + + # To compile the library, we need the user headers search path (quoted includes) to point to the + # root of the repo, and the system headers search path (angled includes) to point to `include/`. + # Cocoapods effectively clones the repo under `/Pods/gRPC-Cronet/`, and sets a build + # variable called `$(PODS_ROOT)` to `/Pods/`, so we use that. + # + # Relying on the file structure under $(PODS_ROOT) isn't officially supported in Cocoapods, as it + # is taken as an implementation detail. We've asked for an alternative, and have been told that + # what we're doing should keep working: https://github.com/CocoaPods/CocoaPods/issues/4386 + # + # The `src_root` value of `$(PODS_ROOT)/gRPC-Cronet` assumes Cocoapods is installing this pod from + # its remote repo. For local development of this library, enabled by using `:path` in the Podfile, + # that assumption is wrong. In such case, the following settings need to be reset with the + # appropriate value of `src_root`. This can be accomplished in the `pre_install` hook of the + # Podfile; see `src/objective-c/tests/Podfile` for an example. src_root = '$(PODS_ROOT)/gRPC-Cronet' s.pod_target_xcconfig = { 'GRPC_SRC_ROOT' => src_root, @@ -80,9 +102,16 @@ Pod::Spec.new do |s| ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c', 'src/core/ext/transport/cronet/transport/cronet_transport.c', - 'test/core/end2end/**/*.{c,h}', - 'test/core/util' + 'test/core/end2end/cq_verifier.{c,h}', + 'test/core/end2end/end2end_tests.{c,h}', + 'test/core/end2end/tests/*.{c,h}', + 'test/core/end2end/data/*.{c,h}', + 'test/core/util/test_config.{c,h}', + 'test/core/util/port.h', + 'test/core/util/port_posix.c', + 'test/core/util/port_server_client.{c,h}' ss.dependency 'gRPC-Core', version + ss.dependency 'CronetFramework' end end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj index bc5006e9366..a878db9c2e3 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj @@ -13,11 +13,12 @@ 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7E1D3452D600A81F3A /* Assets.xcassets */; }; 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */; }; 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC76B961D36B2EE00A74FED /* h2_ssl.m */; }; - C124E05DC4ED9F5CA3EFD20D /* libPods-End2EndTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 931C29940B821E295AA90634 /* libPods-End2EndTest.a */; }; + 809A4524D003F25B67A9E20C /* libPods-End2EndTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.debug.xcconfig"; sourceTree = ""; }; + 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-End2EndTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 5E201A6F1D3452D500A81F3A /* End2EndTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = End2EndTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5E201A751D3452D500A81F3A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 5E201A761D3452D500A81F3A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -28,7 +29,6 @@ 5E201A811D3452D600A81F3A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 5E201A831D3452D600A81F3A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 5EC76B961D36B2EE00A74FED /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; - 931C29940B821E295AA90634 /* libPods-End2EndTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-End2EndTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -37,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C124E05DC4ED9F5CA3EFD20D /* libPods-End2EndTest.a in Frameworks */, + 809A4524D003F25B67A9E20C /* libPods-End2EndTest.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -98,7 +98,7 @@ D564C37197511893E4E62D12 /* Frameworks */ = { isa = PBXGroup; children = ( - 931C29940B821E295AA90634 /* libPods-End2EndTest.a */, + 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */, ); name = Frameworks; sourceTree = ""; diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h index dbc592fcc87..867e62842ae 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h +++ b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h @@ -1,10 +1,35 @@ -// -// AppDelegate.h -// End2EndTest -// -// Created by mxyan on 7/11/16. -// Copyright © 2016 Google. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ #import diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m index a4beef9954d..66fceffd85c 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m +++ b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m @@ -1,10 +1,35 @@ -// -// AppDelegate.m -// End2EndTest -// -// Created by mxyan on 7/11/16. -// Copyright © 2016 Google. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ #import "AppDelegate.h" diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m b/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m index 2649546fcad..800dd56649a 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m +++ b/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m @@ -31,6 +31,13 @@ * */ +/* + * This fixture creates a server full stack using chttp2 and a client + * full stack using Cronet. End-to-end tests are run against this fixture + * setting. + * + */ + #include "test/core/end2end/end2end_tests.h" #include @@ -83,7 +90,7 @@ static void process_auth_failure(void *state, grpc_auth_context *ctx, cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); } -static void chttp2_init_client_secure_fullstack( +static void cronet_init_client_secure_fullstack( grpc_end2end_test_fixture *f, grpc_channel_args *client_args, cronet_engine *cronetEngine) { fullstack_secure_fixture_data *ffd = f->fixture_data; @@ -113,7 +120,7 @@ void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { gpr_free(ffd); } -static void chttp2_init_client_simple_ssl_secure_fullstack( +static void cronet_init_client_simple_ssl_secure_fullstack( grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { grpc_arg ssl_name_override = {GRPC_ARG_STRING, GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, @@ -125,7 +132,7 @@ static void chttp2_init_client_simple_ssl_secure_fullstack( [Cronet start]; cronet_engine *cronetEngine = [Cronet getGlobalEngine]; - chttp2_init_client_secure_fullstack(f, new_client_args, cronetEngine); + cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine); grpc_channel_args_destroy(new_client_args); } @@ -161,7 +168,7 @@ static grpc_end2end_test_config configs[] = { FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, chttp2_create_fixture_secure_fullstack, - chttp2_init_client_simple_ssl_secure_fullstack, + cronet_init_client_simple_ssl_secure_fullstack, chttp2_init_server_simple_ssl_secure_fullstack, chttp2_tear_down_secure_fullstack}, }; diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/main.m b/src/objective-c/tests/End2EndTest/End2EndTest/main.m deleted file mode 100644 index a1894cdb413..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/main.m +++ /dev/null @@ -1,16 +0,0 @@ -// -// main.m -// End2EndTest -// -// Created by mxyan on 7/11/16. -// Copyright © 2016 Google. All rights reserved. -// - -#import -#import "AppDelegate.h" - -int main(int argc, char * argv[]) { - @autoreleasepool { - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/src/objective-c/tests/End2EndTest/Podfile b/src/objective-c/tests/End2EndTest/Podfile index 59b64badfb9..a3fd4d5e955 100644 --- a/src/objective-c/tests/End2EndTest/Podfile +++ b/src/objective-c/tests/End2EndTest/Podfile @@ -32,38 +32,25 @@ end # # TODO(jcanizales): Send a PR to Cocoapods to get rid of this need. pre_install do |installer| - # This is the gRPC-Core podspec object, as initialized by its podspec file. - grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec - - # Copied from gRPC-Core.podspec, except for the adjusted src_root: - src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" - grpc_core_spec.pod_target_xcconfig = { - 'GRPC_SRC_ROOT' => src_root, - 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', - 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', - # If we don't set these two settings, `include/grpc/support/time.h` and - # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the - # build. - 'USE_HEADERMAP' => 'NO', - 'ALWAYS_SEARCH_USER_PATHS' => 'NO', - } - -# This is the gRPC-Core podspec object, as initialized by its podspec file. - grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Cronet'}.root_spec - - # Copied from gRPC-Core.podspec, except for the adjusted src_root: - src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" - grpc_core_spec.pod_target_xcconfig = { - 'GRPC_SRC_ROOT' => src_root, - 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', - 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', - # If we don't set these two settings, `include/grpc/support/time.h` and - # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the - # build. - 'USE_HEADERMAP' => 'NO', - 'ALWAYS_SEARCH_USER_PATHS' => 'NO', - } + %w( + gRPC-Core + gRPC-Cronet + ).each do |target_name| + grpc_core_spec = installer.pod_targets.find{|t| t.name == target_name}.root_spec + # Copied from gRPC-Core.podspec, except for the adjusted src_root: + src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" + grpc_core_spec.pod_target_xcconfig = { + 'GRPC_SRC_ROOT' => src_root, + 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', + 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', + # If we don't set these two settings, `include/grpc/support/time.h` and + # `src/core/lib/support/string.h` shadow the system `` and ``, breaking + # the build. + 'USE_HEADERMAP' => 'NO', + 'ALWAYS_SEARCH_USER_PATHS' => 'NO', + } + end end post_install do |installer| From 54698a9184467c8db0d5844da086c1d5dcc606e4 Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Thu, 14 Jul 2016 11:32:15 -0700 Subject: [PATCH 05/30] Updated with PR comments. --- doc/load-balancing.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/load-balancing.md b/doc/load-balancing.md index 05d555a3f13..6da2479d24a 100644 --- a/doc/load-balancing.md +++ b/doc/load-balancing.md @@ -92,7 +92,7 @@ servers. the client will attempt to open a stream to the load balancer service. The server may respond in only one of the following ways. 1. `status::UNIMPLEMENTED`. There is no loadbalancing in use. The client - proceeds by sending all RPCs to this gRPC server. + call will fail. 1. "I am a Load Balancer and here is the server list." (Goto Step 4.) 1. "Please contact Load Balancer X" (See Step 3.) The client will close this connection and cancel the stream. From 142efc905f835f39010cf3efe6ec5cfe04a251ff Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Thu, 14 Jul 2016 13:33:35 -0700 Subject: [PATCH 06/30] Establish Objective C end-to-end core test with Cronet --- .../End2EndTest.xcodeproj/project.pbxproj | 8 ++-- .../End2EndTest/End2EndTest/ViewController.h | 39 +++++++++++++++---- .../End2EndTest/End2EndTest/ViewController.m | 39 +++++++++++++++---- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj index a878db9c2e3..bf412187f43 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj @@ -7,18 +7,18 @@ objects = { /* Begin PBXBuildFile section */ + 1518576073757E501D232EDA /* Pods_End2EndTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */; }; 5E201A771D3452D500A81F3A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A761D3452D500A81F3A /* AppDelegate.m */; }; 5E201A7A1D3452D500A81F3A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A791D3452D500A81F3A /* ViewController.m */; }; 5E201A7D1D3452D500A81F3A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7B1D3452D500A81F3A /* Main.storyboard */; }; 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7E1D3452D600A81F3A /* Assets.xcassets */; }; 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */; }; 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC76B961D36B2EE00A74FED /* h2_ssl.m */; }; - 809A4524D003F25B67A9E20C /* libPods-End2EndTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.debug.xcconfig"; sourceTree = ""; }; - 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-End2EndTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_End2EndTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 5E201A6F1D3452D500A81F3A /* End2EndTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = End2EndTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; 5E201A751D3452D500A81F3A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 5E201A761D3452D500A81F3A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -37,7 +37,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 809A4524D003F25B67A9E20C /* libPods-End2EndTest.a in Frameworks */, + 1518576073757E501D232EDA /* Pods_End2EndTest.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -98,7 +98,7 @@ D564C37197511893E4E62D12 /* Frameworks */ = { isa = PBXGroup; children = ( - 45442114339F6871ABA5F464 /* libPods-End2EndTest.a */, + 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */, ); name = Frameworks; sourceTree = ""; diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h index 941477a48ca..38cd7f92b66 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h +++ b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h @@ -1,10 +1,35 @@ -// -// ViewController.h -// End2EndTest -// -// Created by mxyan on 7/11/16. -// Copyright © 2016 Google. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ #import diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m index 535e40bcb39..70b5d458110 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m +++ b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m @@ -1,10 +1,35 @@ -// -// ViewController.m -// End2EndTest -// -// Created by mxyan on 7/11/16. -// Copyright © 2016 Google. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ #import "ViewController.h" From 37480eb60a3beebddb6a582c5f95de660abe4d8b Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Thu, 14 Jul 2016 16:22:03 -0700 Subject: [PATCH 07/30] Establish Objective C end-to-end core test with Cronet --- gRPC-Core.podspec | 23 +- gRPC-Cronet.podspec | 117 ---------- .../CoreCronetEnd2EndTests.m | 39 ++++ .../tests/CoreCronetEnd2EndTests/h2_ssl.m | 206 ++++++++++++++++++ src/objective-c/tests/Podfile | 9 +- .../tests/Tests.xcodeproj/project.pbxproj | 170 +++++++++++++++ templates/gRPC-Core.podspec.template | 24 ++ 7 files changed, 469 insertions(+), 119 deletions(-) delete mode 100644 gRPC-Cronet.podspec create mode 100644 src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m create mode 100644 src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index e10e05387b1..4811abf698b 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -193,7 +193,6 @@ Pod::Spec.new do |s| ss.dependency "#{s.name}/Interface", version ss.dependency 'BoringSSL', '~> 4.0' - # To save you from scrolling, this is the last part of the podspec. ss.source_files = 'src/core/lib/profiling/timers.h', 'src/core/lib/support/backoff.h', 'src/core/lib/support/block_annotate.h', @@ -759,4 +758,26 @@ Pod::Spec.new do |s| 'src/core/ext/census/mlog.h', 'src/core/ext/census/rpc_metric_id.h' end + + s.subspec 'Cronet-Interface' do |ss| + ss.header_mappings_dir = 'include/grpc' + ss.source_files = 'include/grpc/grpc_cronet.h' + end + + s.subspec 'Cronet-Tests' do |ss| + ss.header_mappings_dir = '.' + + ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c', + 'src/core/ext/transport/cronet/transport/cronet_transport.c', + 'test/core/end2end/cq_verifier.{c,h}', + 'test/core/end2end/end2end_tests.{c,h}', + 'test/core/end2end/tests/*.{c,h}', + 'test/core/end2end/data/*.{c,h}', + 'test/core/util/test_config.{c,h}', + 'test/core/util/port.h', + 'test/core/util/port_posix.c', + 'test/core/util/port_server_client.{c,h}' + + ss.dependency 'CronetFramework' + end end diff --git a/gRPC-Cronet.podspec b/gRPC-Cronet.podspec deleted file mode 100644 index b353bf9a437..00000000000 --- a/gRPC-Cronet.podspec +++ /dev/null @@ -1,117 +0,0 @@ -# GRPC CocoaPods podspec -# This file has been automatically generated from a template file. -# Please look at the templates directory instead. -# This file can be regenerated from the template by running -# tools/buildgen/generate_projects.sh - -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -Pod::Spec.new do |s| - s.name = 'gRPC-Cronet' - version = '0.14.0' - s.version = version - s.summary = 'Integration of CroNet framework into gRPC' - s.homepage = 'http://www.grpc.io' - s.license = 'New BSD' - s.authors = { 'The gRPC contributors' => 'grpc-packages@google.com' } - - s.source = { - :git => 'https://github.com/grpc/grpc.git', - :tag => "release-#{version.gsub(/\./, '_')}-objectivec-#{version}", - } - - s.ios.deployment_target = '7.1' - s.osx.deployment_target = '10.9' - s.requires_arc = false - - name = 'GRPCCronet' - - s.module_name = name - - # When creating a dynamic framework, copy the headers under `include/grpc/` into the root of - # the `Headers/` directory of the framework (i.e., not under `Headers/include/grpc`). - s.header_mappings_dir = 'include/grpc' - - # The above has an undesired effect when creating a static library: It forces users to write - # includes like `#include `. `s.header_dir` adds a path prefix to that, and - # because Cocoapods lets omit the pod name when including headers of static libraries, the - # following lets users write `#include `. - s.header_dir = 'grpc' - - # To compile the library, we need the user headers search path (quoted includes) to point to the - # root of the repo, and the system headers search path (angled includes) to point to `include/`. - # Cocoapods effectively clones the repo under `/Pods/gRPC-Cronet/`, and sets a build - # variable called `$(PODS_ROOT)` to `/Pods/`, so we use that. - # - # Relying on the file structure under $(PODS_ROOT) isn't officially supported in Cocoapods, as it - # is taken as an implementation detail. We've asked for an alternative, and have been told that - # what we're doing should keep working: https://github.com/CocoaPods/CocoaPods/issues/4386 - # - # The `src_root` value of `$(PODS_ROOT)/gRPC-Cronet` assumes Cocoapods is installing this pod from - # its remote repo. For local development of this library, enabled by using `:path` in the Podfile, - # that assumption is wrong. In such case, the following settings need to be reset with the - # appropriate value of `src_root`. This can be accomplished in the `pre_install` hook of the - # Podfile; see `src/objective-c/tests/Podfile` for an example. - src_root = '$(PODS_ROOT)/gRPC-Cronet' - s.pod_target_xcconfig = { - 'GRPC_SRC_ROOT' => src_root, - 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', - 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', - # If we don't set these two settings, `include/grpc/support/time.h` and - # `src/core/lib/support/string.h` shadow the system `` and ``, breaking the - # build. - 'USE_HEADERMAP' => 'NO', - 'ALWAYS_SEARCH_USER_PATHS' => 'NO', - } - - s.subspec 'Interface' do |ss| - ss.header_mappings_dir = 'include/grpc' - ss.source_files = 'include/grpc/grpc_cronet.h' - end - - s.subspec 'Implementation' do |ss| - ss.header_mappings_dir = '.' - - ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c', - 'src/core/ext/transport/cronet/transport/cronet_transport.c', - 'test/core/end2end/cq_verifier.{c,h}', - 'test/core/end2end/end2end_tests.{c,h}', - 'test/core/end2end/tests/*.{c,h}', - 'test/core/end2end/data/*.{c,h}', - 'test/core/util/test_config.{c,h}', - 'test/core/util/port.h', - 'test/core/util/port_posix.c', - 'test/core/util/port_server_client.{c,h}' - - ss.dependency 'gRPC-Core', version - ss.dependency 'CronetFramework' - end -end diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m new file mode 100644 index 00000000000..0f07b6682e6 --- /dev/null +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -0,0 +1,39 @@ +// +// CoreCronetEnd2EndTests.m +// CoreCronetEnd2EndTests +// +// Created by Muxi Yan on 7/14/16. +// Copyright © 2016 gRPC. All rights reserved. +// + +#import + +@interface CoreCronetEnd2EndTests : XCTestCase + +@end + +@implementation CoreCronetEnd2EndTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m new file mode 100644 index 00000000000..800dd56649a --- /dev/null +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m @@ -0,0 +1,206 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * This fixture creates a server full stack using chttp2 and a client + * full stack using Cronet. End-to-end tests are run against this fixture + * setting. + * + */ + +#include "test/core/end2end/end2end_tests.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/tmpfile.h" +#include "test/core/end2end/data/ssl_test_data.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include +#import + +#import +#import "AppDelegate.h" + +typedef struct fullstack_secure_fixture_data { + char *localaddr; +} fullstack_secure_fixture_data; + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( + grpc_channel_args *client_args, grpc_channel_args *server_args) { + grpc_end2end_test_fixture f; + int port = grpc_pick_unused_port_or_die(); + fullstack_secure_fixture_data *ffd = + gpr_malloc(sizeof(fullstack_secure_fixture_data)); + memset(&f, 0, sizeof(f)); + + gpr_join_host_port(&ffd->localaddr, "localhost", port); + + f.fixture_data = ffd; + f.cq = grpc_completion_queue_create(NULL); + + return f; +} + +static void process_auth_failure(void *state, grpc_auth_context *ctx, + const grpc_metadata *md, size_t md_count, + grpc_process_auth_metadata_done_cb cb, + void *user_data) { + GPR_ASSERT(state == NULL); + cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); +} + +static void cronet_init_client_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args, + cronet_engine *cronetEngine) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + f->client = + grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL); + GPR_ASSERT(f->client != NULL); +} + +static void chttp2_init_server_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args, + grpc_server_credentials *server_creds) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + if (f->server) { + grpc_server_destroy(f->server); + } + f->server = grpc_server_create(server_args, NULL); + grpc_server_register_completion_queue(f->server, f->cq, NULL); + GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, + server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(f->server); +} + +void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + gpr_free(ffd->localaddr); + gpr_free(ffd); +} + +static void cronet_init_client_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { + grpc_arg ssl_name_override = {GRPC_ARG_STRING, + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + {"foo.test.google.fr"}}; + + grpc_channel_args *new_client_args = + grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); + [Cronet setHttp2Enabled:YES]; + [Cronet start]; + cronet_engine *cronetEngine = [Cronet getGlobalEngine]; + + cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine); + grpc_channel_args_destroy(new_client_args); +} + +static int fail_server_auth_check(grpc_channel_args *server_args) { + size_t i; + if (server_args == NULL) return 0; + for (i = 0; i < server_args->num_args; i++) { + if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) == + 0) { + return 1; + } + } + return 0; +} + +static void chttp2_init_server_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args) { + grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, + test_server1_cert}; + grpc_server_credentials *ssl_creds = + grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL); + if (fail_server_auth_check(server_args)) { + grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL}; + grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); + } + chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); +} + +/* All test configurations */ + +static grpc_end2end_test_config configs[] = { + {"chttp2/simple_ssl_fullstack", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, + chttp2_create_fixture_secure_fullstack, + cronet_init_client_simple_ssl_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, +}; + +int main(int argc, char **argv) { + size_t i; + FILE *roots_file; + size_t roots_size = strlen(test_root_cert); + char *roots_filename; + + grpc_test_init(argc, argv); + grpc_end2end_tests_pre_init(); + + /* Set the SSL roots env var. */ + roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); + GPR_ASSERT(roots_filename != NULL); + GPR_ASSERT(roots_file != NULL); + GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); + fclose(roots_file); + gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); + + grpc_init(); + + for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { + grpc_end2end_tests(argc, argv, configs[i]); + } + + grpc_shutdown(); + + /* Cleanup. */ + remove(roots_filename); + gpr_free(roots_filename); + + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); +} diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 30a34260d40..62ffd59f079 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -18,7 +18,6 @@ GRPC_LOCAL_SRC = '../../..' target target_name do pod 'Protobuf', :path => "#{GRPC_LOCAL_SRC}/third_party/protobuf", :inhibit_warnings => true pod 'BoringSSL', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true - pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" pod 'gRPC', :path => GRPC_LOCAL_SRC pod 'gRPC-Core', :path => GRPC_LOCAL_SRC pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC @@ -27,6 +26,14 @@ GRPC_LOCAL_SRC = '../../..' end end +target 'CoreCronetEnd2EndTests' do + pod 'BoringSSL', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true + pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" + pod 'gRPC-Core', :path => GRPC_LOCAL_SRC + pod 'gRPC-Core/Cronet-Interface', :path => GRPC_LOCAL_SRC + pod 'gRPC-Core/Cronet-Tests', :path => GRPC_LOCAL_SRC +end + # gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's # pre_install hook lets us do that. The block passed to it runs after the podspecs are downloaded # and before they are installed in the user project. diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index f9389a4977f..6c41563a350 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -12,6 +12,10 @@ 20DFDF829DD993A4A00D5662 /* libPods-RxLibraryUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */; }; 333E8FC01C8285B7C547D799 /* libPods-InteropTestsLocalCleartext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */; }; 3D7C85F6AA68C4A205E3BA16 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */; }; + 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */; }; + 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; + 5E8A5DB01D3849F1000F8BC4 /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */; }; + 60D2A57ED559F34428C2EEC5 /* libPods-CoreCronetEnd2EndTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */; }; 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; }; 63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; 635697CD1B14FC11007A7283 /* Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 635697CC1B14FC11007A7283 /* Tests.m */; }; @@ -38,6 +42,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 5E8A5DAA1D3840B4000F8BC4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 635697BF1B14FC11007A7283 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 635697C61B14FC11007A7283; + remoteInfo = Tests; + }; 63423F4B1B150A5F006CF63C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 635697BF1B14FC11007A7283 /* Project object */; @@ -91,12 +102,17 @@ 060EF32D7EC0DF67ED617507 /* Pods-Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.debug.xcconfig"; sourceTree = ""; }; 07D10A965323BEA7FE59A74B /* Pods-RxLibraryUnitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxLibraryUnitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests.debug.xcconfig"; sourceTree = ""; }; 0A4F89D9C90E9C30990218F0 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + 0D2284C3DF7E57F0ED504E39 /* Pods-CoreCronetEnd2EndTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.debug.xcconfig"; sourceTree = ""; }; 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 35F2B6BF3BAE8F0DC4AFD76E /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3B0861FC805389C52DB260D4 /* Pods-RxLibraryUnitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxLibraryUnitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests.release.xcconfig"; sourceTree = ""; }; + 4AD97096D13D7416DC91A72A /* Pods-CoreCronetEnd2EndTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CoreCronetEnd2EndTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests.release.xcconfig"; sourceTree = ""; }; 51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = ""; }; 553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = ""; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = ""; }; + 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; + 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; 63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = ""; }; @@ -123,11 +139,21 @@ E1486220285AF123EB124008 /* Pods-InteropTestsLocalCleartext.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.debug.xcconfig"; sourceTree = ""; }; E4275A759BDBDF143B9B438F /* Pods-InteropTestsRemote.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsRemote.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsRemote/Pods-InteropTestsRemote.release.xcconfig"; sourceTree = ""; }; E6733B838B28453434B556E2 /* Pods-Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Tests/Pods-Tests.release.xcconfig"; sourceTree = ""; }; + FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CoreCronetEnd2EndTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-InteropTestsLocalCleartext.a"; sourceTree = BUILT_PRODUCTS_DIR; }; FF7B5489BCFE40111D768DD0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 5E8A5DA11D3840B4000F8BC4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */, + 60D2A57ED559F34428C2EEC5 /* libPods-CoreCronetEnd2EndTests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 63423F411B150A5F006CF63C /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -194,6 +220,7 @@ DBE059B4AC7A51919467EEC0 /* libPods-InteropTestsRemote.a */, A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */, 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */, + FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */, ); name = Frameworks; sourceTree = ""; @@ -215,15 +242,27 @@ 3B0861FC805389C52DB260D4 /* Pods-RxLibraryUnitTests.release.xcconfig */, 060EF32D7EC0DF67ED617507 /* Pods-Tests.debug.xcconfig */, E6733B838B28453434B556E2 /* Pods-Tests.release.xcconfig */, + 0D2284C3DF7E57F0ED504E39 /* Pods-CoreCronetEnd2EndTests.debug.xcconfig */, + 4AD97096D13D7416DC91A72A /* Pods-CoreCronetEnd2EndTests.release.xcconfig */, ); name = Pods; sourceTree = ""; }; + 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { + isa = PBXGroup; + children = ( + 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */, + 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, + ); + path = CoreCronetEnd2EndTests; + sourceTree = ""; + }; 635697BE1B14FC11007A7283 = { isa = PBXGroup; children = ( 635697C91B14FC11007A7283 /* Tests */, 63E240CF1B6C63DC005F3B0E /* TestCertificates.bundle */, + 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */, 635697C81B14FC11007A7283 /* Products */, 51E4650F34F854F41FF053B3 /* Pods */, 136D535E19727099B941D7B1 /* Frameworks */, @@ -239,6 +278,7 @@ 63DC84231BE15267000708E8 /* InteropTestsRemote.xctest */, 63DC84341BE15294000708E8 /* InteropTestsLocalSSL.xctest */, 63DC84431BE152B5000708E8 /* InteropTestsLocalCleartext.xctest */, + 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */, ); name = Products; sourceTree = ""; @@ -270,6 +310,27 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 5E8A5DA31D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5E8A5DAE1D3840B4000F8BC4 /* Build configuration list for PBXNativeTarget "CoreCronetEnd2EndTests" */; + buildPhases = ( + F58F17E425446B15028B9F74 /* [CP] Check Pods Manifest.lock */, + 5E8A5DA01D3840B4000F8BC4 /* Sources */, + 5E8A5DA11D3840B4000F8BC4 /* Frameworks */, + 5E8A5DA21D3840B4000F8BC4 /* Resources */, + E63468C760D0724F18861822 /* [CP] Embed Pods Frameworks */, + 6DFE9E77CAB5760196D79E0F /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5E8A5DAB1D3840B4000F8BC4 /* PBXTargetDependency */, + ); + name = CoreCronetEnd2EndTests; + productName = CoreCronetEnd2EndTests; + productReference = 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; 63423F431B150A5F006CF63C /* AllTests */ = { isa = PBXNativeTarget; buildConfigurationList = 63423F4D1B150A5F006CF63C /* Build configuration list for PBXNativeTarget "AllTests" */; @@ -403,6 +464,9 @@ LastUpgradeCheck = 0630; ORGANIZATIONNAME = gRPC; TargetAttributes = { + 5E8A5DA31D3840B4000F8BC4 = { + CreatedOnToolsVersion = 7.3.1; + }; 63423F431B150A5F006CF63C = { CreatedOnToolsVersion = 6.3.1; }; @@ -441,11 +505,19 @@ 63DC84221BE15267000708E8 /* InteropTestsRemote */, 63DC84331BE15294000708E8 /* InteropTestsLocalSSL */, 63DC84421BE152B5000708E8 /* InteropTestsLocalCleartext */, + 5E8A5DA31D3840B4000F8BC4 /* CoreCronetEnd2EndTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 5E8A5DA21D3840B4000F8BC4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 63423F421B150A5F006CF63C /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -561,6 +633,21 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL-resources.sh\"\n"; showEnvVarsInLog = 0; }; + 6DFE9E77CAB5760196D79E0F /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 7418AC7B3844B29E48D24FC7 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -741,9 +828,48 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RxLibraryUnitTests/Pods-RxLibraryUnitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; + E63468C760D0724F18861822 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CoreCronetEnd2EndTests/Pods-CoreCronetEnd2EndTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F58F17E425446B15028B9F74 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 5E8A5DA01D3840B4000F8BC4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5E8A5DB01D3849F1000F8BC4 /* h2_ssl.m in Sources */, + 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 63423F401B150A5F006CF63C /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -804,6 +930,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 5E8A5DAB1D3840B4000F8BC4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 635697C61B14FC11007A7283 /* Tests */; + targetProxy = 5E8A5DAA1D3840B4000F8BC4 /* PBXContainerItemProxy */; + }; 63423F4C1B150A5F006CF63C /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 635697C61B14FC11007A7283 /* Tests */; @@ -832,6 +963,36 @@ /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + 5E8A5DAC1D3840B4000F8BC4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0D2284C3DF7E57F0ED504E39 /* Pods-CoreCronetEnd2EndTests.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + INFOPLIST_FILE = CoreCronetEnd2EndTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5E8A5DAD1D3840B4000F8BC4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4AD97096D13D7416DC91A72A /* Pods-CoreCronetEnd2EndTests.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + INFOPLIST_FILE = CoreCronetEnd2EndTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; 63423F4E1B150A5F006CF63C /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = B94C27C06733CF98CE1B2757 /* Pods-AllTests.debug.xcconfig */; @@ -1071,6 +1232,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 5E8A5DAE1D3840B4000F8BC4 /* Build configuration list for PBXNativeTarget "CoreCronetEnd2EndTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5E8A5DAC1D3840B4000F8BC4 /* Debug */, + 5E8A5DAD1D3840B4000F8BC4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 63423F4D1B150A5F006CF63C /* Build configuration list for PBXNativeTarget "AllTests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template index aefe6e965cc..9eeee222713 100644 --- a/templates/gRPC-Core.podspec.template +++ b/templates/gRPC-Core.podspec.template @@ -128,6 +128,8 @@ 'ALWAYS_SEARCH_USER_PATHS' => 'NO', } + s.default_subspecs = 'Interface', 'Implementation' + # Like many other C libraries, gRPC-Core has its public headers under `include//` and its # sources and private headers in other directories outside `include/`. Cocoapods' linter doesn't # allow any header to be listed outside the `header_mappings_dir` (even though doing so works in @@ -154,4 +156,26 @@ ss.private_header_files = ${ruby_multiline_list(grpc_private_headers(libs), 30)} end + + s.subspec 'Cronet-Interface' do |ss| + ss.header_mappings_dir = 'include/grpc' + ss.source_files = 'include/grpc/grpc_cronet.h' + end + + s.subspec 'Cronet-Tests' do |ss| + ss.header_mappings_dir = '.' + + ss.source_files = 'src/core/ext/transport/cronet/client/secure/cronet_channel_create.c', + 'src/core/ext/transport/cronet/transport/cronet_transport.c', + 'test/core/end2end/cq_verifier.{c,h}', + 'test/core/end2end/end2end_tests.{c,h}', + 'test/core/end2end/tests/*.{c,h}', + 'test/core/end2end/data/*.{c,h}', + 'test/core/util/test_config.{c,h}', + 'test/core/util/port.h', + 'test/core/util/port_posix.c', + 'test/core/util/port_server_client.{c,h}' + + ss.dependency 'CronetFramework' + end end From 26b60607297ff5caf01c72ba31f70b2641fd9198 Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Fri, 15 Jul 2016 07:35:13 -0700 Subject: [PATCH 08/30] Small tweaks in text and diagram --- doc/images/load_balancing_design.png | Bin 38180 -> 40354 bytes doc/load-balancing.md | 26 ++++++++++++++------------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/doc/images/load_balancing_design.png b/doc/images/load_balancing_design.png index 716378c0db20da9875b0cc1892e0e1aa217c1540..86183966fb88e24a5a86dbd4b62ae2a308d0a542 100644 GIT binary patch literal 40354 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

oXQe~N~ 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 Vn`JgvQafaeb~dMumLGP%{U5zNV6gxI diff --git a/doc/load-balancing.md b/doc/load-balancing.md index 6da2479d24a..dfaa7a7f33b 100644 --- a/doc/load-balancing.md +++ b/doc/load-balancing.md @@ -89,26 +89,28 @@ servers. client config. 2. The gRPC client connects to a gRPC Server. 1. If the name resolution has hinted that the endpoint is a load balancer, - the client will attempt to open a stream to the load balancer service. The - server may respond in only one of the following ways. + the client's gRPC LB policy will attempt to open a stream to the load + balancer service. The server may respond in only one of the following + ways. 1. `status::UNIMPLEMENTED`. There is no loadbalancing in use. The client call will fail. - 1. "I am a Load Balancer and here is the server list." (Goto Step 4.) - 1. "Please contact Load Balancer X" (See Step 3.) The client will close + 2. "I am a Load Balancer and here is the server list." (Goto Step 4.) + 3. "Please contact Load Balancer X" (See Step 3.) The client will close this connection and cancel the stream. - 1. If the server fails to respond, the client will wait for some timeout + 4. If the server fails to respond, the client will wait for some timeout and then re-resolve the name (process to Step 1 above). - 1. If the name resolution has not hinted that the endpoint is a load + 2. If the name resolution has not hinted that the endpoint is a load balancer, the client connects directly to the service it wants to talk to. -3. The gRPC client opens a separate connection to the Load Balancer. If this - fails, it will go back to step 1 and try another address. +3. The gRPC client's gRPC LB policy opens a separate connection to the Load + Balancer. If this fails, it will go back to step 1 and try another address. 1. During channel initialization to the Load Balancer, the client will attempt to open a stream to the Load Balancer service. - 1. The load balancer will return a server list to the gRPC client. - Optional: The load balancer will also open channels to the gRPC servers if - load reporting is needed. + 2. The Load Balancer will return a server list to the gRPC client. If the + server list is empty, the call will wait until a non-empty one is + received. Optional: The Load Balancer will also open channels to the gRPC + servers if load reporting is needed. 4. The gRPC client will send RPCs to the gRPC servers contained in the server - list from the load balancer. + list from the Load Balancer. 5. Optional: The gRPC servers may periodically report load to the Load Balancer. ## Client From 4e9753d28c09d9a2828c0f38a161ec7b0d3ec85c Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 15 Jul 2016 10:07:57 -0700 Subject: [PATCH 09/30] Establish Objective C end-to-end core test with Cronet --- .../CoreCronetEnd2EndTests.m | 52 ++- .../Info.plist | 20 +- .../tests/CoreCronetEnd2EndTests/h2_ssl.m | 5 +- .../End2EndTest.xcodeproj/project.pbxproj | 405 ------------------ .../contents.xcworkspacedata | 7 - .../End2EndTest/End2EndTest/AppDelegate.h | 42 -- .../End2EndTest/End2EndTest/AppDelegate.m | 70 --- .../AppIcon.appiconset/Contents.json | 38 -- .../Base.lproj/LaunchScreen.storyboard | 27 -- .../End2EndTest/Base.lproj/Main.storyboard | 25 -- .../End2EndTest/End2EndTest/ViewController.h | 40 -- .../End2EndTest/End2EndTest/ViewController.m | 52 --- .../tests/End2EndTest/End2EndTest/h2_ssl.m | 206 --------- src/objective-c/tests/End2EndTest/Podfile | 70 --- src/objective-c/tests/Podfile | 2 - .../tests/Tests.xcodeproj/project.pbxproj | 2 + 16 files changed, 42 insertions(+), 1021 deletions(-) rename src/objective-c/tests/{End2EndTest/End2EndTest => CoreCronetEnd2EndTests}/Info.plist (55%) delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m delete mode 100644 src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m delete mode 100644 src/objective-c/tests/End2EndTest/Podfile diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index 0f07b6682e6..1703b5265e5 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -1,10 +1,35 @@ -// -// CoreCronetEnd2EndTests.m -// CoreCronetEnd2EndTests -// -// Created by Muxi Yan on 7/14/16. -// Copyright © 2016 gRPC. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ #import @@ -12,6 +37,8 @@ @end +int main(int argc, char **argv); + @implementation CoreCronetEnd2EndTests - (void)setUp { @@ -24,16 +51,11 @@ [super tearDown]; } -- (void)testExample { +- (void)testCoreCronetEnd2End { // This is an example of a functional test case. // Use XCTAssert and related functions to verify your tests produce the correct results. -} - -- (void)testPerformanceExample { - // This is an example of a performance test case. - [self measureBlock:^{ - // Put the code you want to measure the time of here. - }]; + char *argv[] = {"h2_ssl"}; + main(1, argv); } @end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Info.plist b/src/objective-c/tests/CoreCronetEnd2EndTests/Info.plist similarity index 55% rename from src/objective-c/tests/End2EndTest/End2EndTest/Info.plist rename to src/objective-c/tests/CoreCronetEnd2EndTests/Info.plist index 6905cc67bbf..fbeeb96ba6c 100644 --- a/src/objective-c/tests/End2EndTest/End2EndTest/Info.plist +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/Info.plist @@ -7,34 +7,18 @@ CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) + gRPC.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName $(PRODUCT_NAME) CFBundlePackageType - APPL + BNDL CFBundleShortVersionString 1.0 CFBundleSignature ???? CFBundleVersion 1 - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m index 800dd56649a..68010f341b3 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m @@ -59,9 +59,6 @@ #include #import -#import -#import "AppDelegate.h" - typedef struct fullstack_secure_fixture_data { char *localaddr; } fullstack_secure_fixture_data; @@ -202,5 +199,5 @@ int main(int argc, char **argv) { remove(roots_filename); gpr_free(roots_filename); - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + return 0; } diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj deleted file mode 100644 index bf412187f43..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.pbxproj +++ /dev/null @@ -1,405 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1518576073757E501D232EDA /* Pods_End2EndTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */; }; - 5E201A771D3452D500A81F3A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A761D3452D500A81F3A /* AppDelegate.m */; }; - 5E201A7A1D3452D500A81F3A /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E201A791D3452D500A81F3A /* ViewController.m */; }; - 5E201A7D1D3452D500A81F3A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7B1D3452D500A81F3A /* Main.storyboard */; }; - 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A7E1D3452D600A81F3A /* Assets.xcassets */; }; - 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */; }; - 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EC76B961D36B2EE00A74FED /* h2_ssl.m */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.debug.xcconfig"; sourceTree = ""; }; - 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_End2EndTest.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E201A6F1D3452D500A81F3A /* End2EndTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = End2EndTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 5E201A751D3452D500A81F3A /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; - 5E201A761D3452D500A81F3A /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 5E201A781D3452D500A81F3A /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; - 5E201A791D3452D500A81F3A /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; - 5E201A7C1D3452D500A81F3A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 5E201A7E1D3452D600A81F3A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 5E201A811D3452D600A81F3A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 5E201A831D3452D600A81F3A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 5EC76B961D36B2EE00A74FED /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; - E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-End2EndTest.release.xcconfig"; path = "Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 5E201A6C1D3452D500A81F3A /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 1518576073757E501D232EDA /* Pods_End2EndTest.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 52027776D515577217FE123B /* Pods */ = { - isa = PBXGroup; - children = ( - 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */, - E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */, - ); - name = Pods; - sourceTree = ""; - }; - 5E201A661D3452D500A81F3A = { - isa = PBXGroup; - children = ( - 5E201A711D3452D500A81F3A /* End2EndTest */, - 5E201A701D3452D500A81F3A /* Products */, - 52027776D515577217FE123B /* Pods */, - D564C37197511893E4E62D12 /* Frameworks */, - ); - sourceTree = ""; - }; - 5E201A701D3452D500A81F3A /* Products */ = { - isa = PBXGroup; - children = ( - 5E201A6F1D3452D500A81F3A /* End2EndTest.app */, - ); - name = Products; - sourceTree = ""; - }; - 5E201A711D3452D500A81F3A /* End2EndTest */ = { - isa = PBXGroup; - children = ( - 5E201A751D3452D500A81F3A /* AppDelegate.h */, - 5E201A761D3452D500A81F3A /* AppDelegate.m */, - 5E201A781D3452D500A81F3A /* ViewController.h */, - 5E201A791D3452D500A81F3A /* ViewController.m */, - 5E201A7B1D3452D500A81F3A /* Main.storyboard */, - 5E201A7E1D3452D600A81F3A /* Assets.xcassets */, - 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */, - 5E201A831D3452D600A81F3A /* Info.plist */, - 5E201A721D3452D500A81F3A /* Supporting Files */, - ); - path = End2EndTest; - sourceTree = ""; - }; - 5E201A721D3452D500A81F3A /* Supporting Files */ = { - isa = PBXGroup; - children = ( - 5EC76B961D36B2EE00A74FED /* h2_ssl.m */, - ); - name = "Supporting Files"; - sourceTree = ""; - }; - D564C37197511893E4E62D12 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 4035DBC43CDC1A29CB0EB05D /* Pods_End2EndTest.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 5E201A6E1D3452D500A81F3A /* End2EndTest */ = { - isa = PBXNativeTarget; - buildConfigurationList = 5E201A861D3452D600A81F3A /* Build configuration list for PBXNativeTarget "End2EndTest" */; - buildPhases = ( - B6E7E45E860B5453E3A5F4A0 /* [CP] Check Pods Manifest.lock */, - 5E201A6B1D3452D500A81F3A /* Sources */, - 5E201A6C1D3452D500A81F3A /* Frameworks */, - 5E201A6D1D3452D500A81F3A /* Resources */, - AE303423292913AB8D447419 /* [CP] Embed Pods Frameworks */, - A4C61E4A2A73F6E1E1143310 /* [CP] Copy Pods Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = End2EndTest; - productName = End2EndTest; - productReference = 5E201A6F1D3452D500A81F3A /* End2EndTest.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 5E201A671D3452D500A81F3A /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 0730; - ORGANIZATIONNAME = Google; - TargetAttributes = { - 5E201A6E1D3452D500A81F3A = { - CreatedOnToolsVersion = 7.3.1; - }; - }; - }; - buildConfigurationList = 5E201A6A1D3452D500A81F3A /* Build configuration list for PBXProject "End2EndTest" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 5E201A661D3452D500A81F3A; - productRefGroup = 5E201A701D3452D500A81F3A /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 5E201A6E1D3452D500A81F3A /* End2EndTest */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 5E201A6D1D3452D500A81F3A /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 5E201A821D3452D600A81F3A /* LaunchScreen.storyboard in Resources */, - 5E201A7F1D3452D600A81F3A /* Assets.xcassets in Resources */, - 5E201A7D1D3452D500A81F3A /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - A4C61E4A2A73F6E1E1143310 /* [CP] Copy Pods Resources */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Copy Pods Resources"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest-resources.sh\"\n"; - showEnvVarsInLog = 0; - }; - AE303423292913AB8D447419 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Embed Pods Frameworks"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-End2EndTest/Pods-End2EndTest-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; - B6E7E45E860B5453E3A5F4A0 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "[CP] Check Pods Manifest.lock"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 5E201A6B1D3452D500A81F3A /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 5E201A7A1D3452D500A81F3A /* ViewController.m in Sources */, - 5E201A771D3452D500A81F3A /* AppDelegate.m in Sources */, - 5EC76B971D36B2EE00A74FED /* h2_ssl.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 5E201A7B1D3452D500A81F3A /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 5E201A7C1D3452D500A81F3A /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 5E201A801D3452D600A81F3A /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 5E201A811D3452D600A81F3A /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 5E201A841D3452D600A81F3A /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - }; - name = Debug; - }; - 5E201A851D3452D600A81F3A /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.3; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 5E201A871D3452D600A81F3A /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 18957F402A222D334CEBE57B /* Pods-End2EndTest.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "\"${PODS_ROOT}/Headers/Public\"", - "\"${PODS_ROOT}/Headers/Public/BoringSSL\"", - "\"${PODS_ROOT}/Headers/Public/CronetFramework\"", - "\"${PODS_ROOT}/Headers/Public/gRPC-Core\"", - "\"${PODS_ROOT}/Headers/Public/gRPC-Cronet\"", - "\"${PODS_ROOT}/../../../../../include\"", - ); - INFOPLIST_FILE = End2EndTest/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.End2EndTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../../.."; - }; - name = Debug; - }; - 5E201A881D3452D600A81F3A /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = E7725B916D22B5AC6ECF8964 /* Pods-End2EndTest.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - HEADER_SEARCH_PATHS = ( - "$(inherited)", - "\"${PODS_ROOT}/Headers/Public\"", - "\"${PODS_ROOT}/Headers/Public/BoringSSL\"", - "\"${PODS_ROOT}/Headers/Public/CronetFramework\"", - "\"${PODS_ROOT}/Headers/Public/gRPC-Core\"", - "\"${PODS_ROOT}/Headers/Public/gRPC-Cronet\"", - "\"${PODS_ROOT}/../../../../../include\"", - ); - INFOPLIST_FILE = End2EndTest/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.google.End2EndTest; - PRODUCT_NAME = "$(TARGET_NAME)"; - USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../../.."; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 5E201A6A1D3452D500A81F3A /* Build configuration list for PBXProject "End2EndTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5E201A841D3452D600A81F3A /* Debug */, - 5E201A851D3452D600A81F3A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 5E201A861D3452D600A81F3A /* Build configuration list for PBXNativeTarget "End2EndTest" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 5E201A871D3452D600A81F3A /* Debug */, - 5E201A881D3452D600A81F3A /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 5E201A671D3452D500A81F3A /* Project object */; -} diff --git a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 220e863b896..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h deleted file mode 100644 index 867e62842ae..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import - -@interface AppDelegate : UIResponder - -@property (strong, nonatomic) UIWindow *window; - - -@end - diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m b/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m deleted file mode 100644 index 66fceffd85c..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/AppDelegate.m +++ /dev/null @@ -1,70 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import "AppDelegate.h" - -@interface AppDelegate () - -@end - -@implementation AppDelegate - - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - // Override point for customization after application launch. - return YES; -} - -- (void)applicationWillResignActive:(UIApplication *)application { - // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. - // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. -} - -- (void)applicationDidEnterBackground:(UIApplication *)application { - // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. - // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. -} - -- (void)applicationWillEnterForeground:(UIApplication *)application { - // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. -} - -- (void)applicationDidBecomeActive:(UIApplication *)application { - // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. -} - -- (void)applicationWillTerminate:(UIApplication *)application { - // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. -} - -@end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json b/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 118c98f7461..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "3x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index 2e721e1833f..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard b/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard deleted file mode 100644 index f56d2f3bb56..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/Base.lproj/Main.storyboard +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h deleted file mode 100644 index 38cd7f92b66..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import - -@interface ViewController : UIViewController - - -@end - diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m b/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m deleted file mode 100644 index 70b5d458110..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/ViewController.m +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -#import "ViewController.h" - -@interface ViewController () - -@end - -@implementation ViewController - -- (void)viewDidLoad { - [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. -} - -- (void)didReceiveMemoryWarning { - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. -} - -@end diff --git a/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m b/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m deleted file mode 100644 index 800dd56649a..00000000000 --- a/src/objective-c/tests/End2EndTest/End2EndTest/h2_ssl.m +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This fixture creates a server full stack using chttp2 and a client - * full stack using Cronet. End-to-end tests are run against this fixture - * setting. - * - */ - -#include "test/core/end2end/end2end_tests.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/support/tmpfile.h" -#include "test/core/end2end/data/ssl_test_data.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -#include -#import - -#import -#import "AppDelegate.h" - -typedef struct fullstack_secure_fixture_data { - char *localaddr; -} fullstack_secure_fixture_data; - -static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( - grpc_channel_args *client_args, grpc_channel_args *server_args) { - grpc_end2end_test_fixture f; - int port = grpc_pick_unused_port_or_die(); - fullstack_secure_fixture_data *ffd = - gpr_malloc(sizeof(fullstack_secure_fixture_data)); - memset(&f, 0, sizeof(f)); - - gpr_join_host_port(&ffd->localaddr, "localhost", port); - - f.fixture_data = ffd; - f.cq = grpc_completion_queue_create(NULL); - - return f; -} - -static void process_auth_failure(void *state, grpc_auth_context *ctx, - const grpc_metadata *md, size_t md_count, - grpc_process_auth_metadata_done_cb cb, - void *user_data) { - GPR_ASSERT(state == NULL); - cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); -} - -static void cronet_init_client_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *client_args, - cronet_engine *cronetEngine) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - f->client = - grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL); - GPR_ASSERT(f->client != NULL); -} - -static void chttp2_init_server_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *server_args, - grpc_server_credentials *server_creds) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - if (f->server) { - grpc_server_destroy(f->server); - } - f->server = grpc_server_create(server_args, NULL); - grpc_server_register_completion_queue(f->server, f->cq, NULL); - GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, - server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(f->server); -} - -void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - gpr_free(ffd->localaddr); - gpr_free(ffd); -} - -static void cronet_init_client_simple_ssl_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { - grpc_arg ssl_name_override = {GRPC_ARG_STRING, - GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, - {"foo.test.google.fr"}}; - - grpc_channel_args *new_client_args = - grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); - [Cronet setHttp2Enabled:YES]; - [Cronet start]; - cronet_engine *cronetEngine = [Cronet getGlobalEngine]; - - cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine); - grpc_channel_args_destroy(new_client_args); -} - -static int fail_server_auth_check(grpc_channel_args *server_args) { - size_t i; - if (server_args == NULL) return 0; - for (i = 0; i < server_args->num_args; i++) { - if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) == - 0) { - return 1; - } - } - return 0; -} - -static void chttp2_init_server_simple_ssl_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *server_args) { - grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, - test_server1_cert}; - grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL); - if (fail_server_auth_check(server_args)) { - grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL}; - grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); - } - chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); -} - -/* All test configurations */ - -static grpc_end2end_test_config configs[] = { - {"chttp2/simple_ssl_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - chttp2_create_fixture_secure_fullstack, - cronet_init_client_simple_ssl_secure_fullstack, - chttp2_init_server_simple_ssl_secure_fullstack, - chttp2_tear_down_secure_fullstack}, -}; - -int main(int argc, char **argv) { - size_t i; - FILE *roots_file; - size_t roots_size = strlen(test_root_cert); - char *roots_filename; - - grpc_test_init(argc, argv); - grpc_end2end_tests_pre_init(); - - /* Set the SSL roots env var. */ - roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); - GPR_ASSERT(roots_filename != NULL); - GPR_ASSERT(roots_file != NULL); - GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); - fclose(roots_file); - gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - /* Cleanup. */ - remove(roots_filename); - gpr_free(roots_filename); - - return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); -} diff --git a/src/objective-c/tests/End2EndTest/Podfile b/src/objective-c/tests/End2EndTest/Podfile deleted file mode 100644 index a3fd4d5e955..00000000000 --- a/src/objective-c/tests/End2EndTest/Podfile +++ /dev/null @@ -1,70 +0,0 @@ -source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '8.0' - -install! 'cocoapods', :deterministic_uuids => false - -# Location of gRPC's repo root relative to this file. -GRPC_LOCAL_SRC = '../../../..' - -# Install the dependencies in the main target plus all test targets. -%w( - End2EndTest -).each do |target_name| - target target_name do - pod 'BoringSSL', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c", :inhibit_warnings => true - pod 'CronetFramework', :podspec => "#{GRPC_LOCAL_SRC}/src/objective-c" - pod 'gRPC-Core', :path => GRPC_LOCAL_SRC - pod 'gRPC-Cronet', :path => GRPC_LOCAL_SRC - end -end - -# gRPC-Core.podspec needs to be modified to be successfully used for local development. A Podfile's -# pre_install hook lets us do that. The block passed to it runs after the podspecs are downloaded -# and before they are installed in the user project. -# -# This podspec searches for the gRPC core library headers under "$(PODS_ROOT)/gRPC-Core", where -# Cocoapods normally places the downloaded sources. When doing local development of the libraries, -# though, Cocoapods just takes the sources from whatever directory was specified using `:path`, and -# doesn't copy them under $(PODS_ROOT). When using static libraries, one can sometimes rely on the -# symbolic links to the pods headers that Cocoapods creates under "$(PODS_ROOT)/Headers". But those -# aren't created when using dynamic frameworks. So our solution is to modify the podspec on the fly -# to point at the local directory where the sources are. -# -# TODO(jcanizales): Send a PR to Cocoapods to get rid of this need. -pre_install do |installer| - %w( - gRPC-Core - gRPC-Cronet - ).each do |target_name| - grpc_core_spec = installer.pod_targets.find{|t| t.name == target_name}.root_spec - - # Copied from gRPC-Core.podspec, except for the adjusted src_root: - src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}" - grpc_core_spec.pod_target_xcconfig = { - 'GRPC_SRC_ROOT' => src_root, - 'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"', - 'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"', - # If we don't set these two settings, `include/grpc/support/time.h` and - # `src/core/lib/support/string.h` shadow the system `` and ``, breaking - # the build. - 'USE_HEADERMAP' => 'NO', - 'ALWAYS_SEARCH_USER_PATHS' => 'NO', - } - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - target.build_configurations.each do |config| - config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES' - end - if target.name == 'gRPC-Core' - target.build_configurations.each do |config| - # TODO(zyc) Remove this setting after the issue is resolved - # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void - # function" warning - config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO' - end - end - end -end diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index e5265630a2c..546017c9f78 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -27,8 +27,6 @@ GRPC_LOCAL_SRC = '../../..' pod 'gRPC-Core', :path => GRPC_LOCAL_SRC pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC pod 'gRPC-ProtoRPC', :path => GRPC_LOCAL_SRC - - pod 'RemoteTest', :path => "RemoteTestClient" end end diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 6c41563a350..4aa5b1bdbe4 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -976,6 +976,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests; PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../..\""; }; name = Debug; }; @@ -990,6 +991,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CoreCronetEnd2EndTests; PRODUCT_NAME = "$(TARGET_NAME)"; + USER_HEADER_SEARCH_PATHS = "$(inherited) \"${PODS_ROOT}/../../../..\""; }; name = Release; }; From 65515a76dd783296769dee23712804b2036c5d80 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 15 Jul 2016 15:12:33 -0700 Subject: [PATCH 10/30] Establish Objective C end-to-end core test with Cronet --- .../tests/CoreCronetEnd2EndTests/h2_ssl.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h new file mode 100644 index 00000000000..a7f2bd724a3 --- /dev/null +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h @@ -0,0 +1,13 @@ +// +// h2_ssl.h +// Tests +// +// Created by Muxi Yan on 7/15/16. +// Copyright © 2016 gRPC. All rights reserved. +// + +#ifndef h2_ssl_h +#define h2_ssl_h + + +#endif /* h2_ssl_h */ From e04089e7c043e34e79bd077ca9a8c0b98d6c9ec7 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Fri, 15 Jul 2016 16:35:20 -0700 Subject: [PATCH 11/30] Establish Objective C end-to-end core test with Cronet --- gRPC-Core.podspec | 2 + .../CoreCronetEnd2EndTests.m | 15 +----- .../tests/CoreCronetEnd2EndTests/h2_ssl.h | 46 +++++++++++++++---- src/objective-c/tests/Podfile | 3 +- .../tests/Tests.xcodeproj/project.pbxproj | 2 + 5 files changed, 43 insertions(+), 25 deletions(-) diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 4811abf698b..0678f3b3ae7 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -101,6 +101,8 @@ Pod::Spec.new do |s| 'ALWAYS_SEARCH_USER_PATHS' => 'NO', } + s.default_subspecs = 'Interface', 'Implementation' + # Like many other C libraries, gRPC-Core has its public headers under `include//` and its # sources and private headers in other directories outside `include/`. Cocoapods' linter doesn't # allow any header to be listed outside the `header_mappings_dir` (even though doing so works in diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index 1703b5265e5..5d501602d90 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -32,28 +32,15 @@ */ #import +#include "./h2_ssl.h" @interface CoreCronetEnd2EndTests : XCTestCase @end -int main(int argc, char **argv); - @implementation CoreCronetEnd2EndTests -- (void)setUp { - [super setUp]; - // Put setup code here. This method is called before the invocation of each test method in the class. -} - -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the class. - [super tearDown]; -} - - (void)testCoreCronetEnd2End { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. char *argv[] = {"h2_ssl"}; main(1, argv); } diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h index a7f2bd724a3..3aa0a05ddad 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h @@ -1,13 +1,39 @@ -// -// h2_ssl.h -// Tests -// -// Created by Muxi Yan on 7/15/16. -// Copyright © 2016 gRPC. All rights reserved. -// +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ -#ifndef h2_ssl_h -#define h2_ssl_h +#ifndef GRPC_H2_SSL_H +#define GRPC_H2_SSL_H +int main(int argc, char **argv); -#endif /* h2_ssl_h */ +#endif /* GRPC_H2_SSL_H */ \ No newline at end of file diff --git a/src/objective-c/tests/Podfile b/src/objective-c/tests/Podfile index 546017c9f78..db11208a1bb 100644 --- a/src/objective-c/tests/Podfile +++ b/src/objective-c/tests/Podfile @@ -27,6 +27,7 @@ GRPC_LOCAL_SRC = '../../..' pod 'gRPC-Core', :path => GRPC_LOCAL_SRC pod 'gRPC-RxLibrary', :path => GRPC_LOCAL_SRC pod 'gRPC-ProtoRPC', :path => GRPC_LOCAL_SRC + pod 'RemoteTest', :path => "RemoteTestClient" end end @@ -74,7 +75,7 @@ post_install do |installer| target.build_configurations.each do |config| config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES' end - if target.name == 'gRPC-Core' + if target.name == 'gRPC-Core' or target.name == 'gRPC-Core.default-Cronet-Interface-Cronet-Tests' target.build_configurations.each do |config| # TODO(zyc): Remove this setting after the issue is resolved # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 4aa5b1bdbe4..4161dcc43cf 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -113,6 +113,7 @@ 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; + 5ED3173E1D397C7500C613BD /* h2_ssl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_ssl.h; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; 63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = ""; }; @@ -252,6 +253,7 @@ isa = PBXGroup; children = ( 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */, + 5ED3173E1D397C7500C613BD /* h2_ssl.h */, 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, ); path = CoreCronetEnd2EndTests; From 3a9ba6bd53cdf5b4ae471f17c366b1010a16e549 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Sat, 16 Jul 2016 10:14:56 +0800 Subject: [PATCH 12/30] delete some commits which not need --- tools/run_tests/sanity/check_orig_mk.sh | 39 ------------------------ tools/run_tests/sanity/sanity_tests.yaml | 1 - 2 files changed, 40 deletions(-) delete mode 100755 tools/run_tests/sanity/check_orig_mk.sh diff --git a/tools/run_tests/sanity/check_orig_mk.sh b/tools/run_tests/sanity/check_orig_mk.sh deleted file mode 100755 index 142ef009ace..00000000000 --- a/tools/run_tests/sanity/check_orig_mk.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh - -# Copyright 2015, Google Inc. -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above -# copyright notice, this list of conditions and the following disclaimer -# in the documentation and/or other materials provided with the -# distribution. -# * Neither the name of Google Inc. nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -set -e - -if [ -f *.orig ] ; then - echo "Please don't commit *.orig file" - exit 1 -fi - diff --git a/tools/run_tests/sanity/sanity_tests.yaml b/tools/run_tests/sanity/sanity_tests.yaml index dde05131cd2..e699c5194df 100644 --- a/tools/run_tests/sanity/sanity_tests.yaml +++ b/tools/run_tests/sanity/sanity_tests.yaml @@ -1,6 +1,5 @@ # a set of tests that are run in parallel for sanity tests - script: tools/run_tests/sanity/check_cache_mk.sh -- script: tools/run_tests/sanity/check_orig_mk.sh - script: tools/run_tests/sanity/check_sources_and_headers.py - script: tools/run_tests/sanity/check_submodules.sh - script: tools/buildgen/generate_projects.sh -j 3 From 9f6e6dcc30f61061b26304cd30dab2d287c80c61 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 18 Jul 2016 11:50:10 -0700 Subject: [PATCH 13/30] Rename h2_ssl.* to h2_ssl_cronet.* and add static specifier to chttp2_tear_down_secure_fullstack in h2_ssl_cronet.m --- .../CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m | 2 +- .../{h2_ssl.h => h2_ssl_cronet.h} | 0 .../{h2_ssl.m => h2_ssl_cronet.m} | 2 +- .../tests/Tests.xcodeproj/project.pbxproj | 12 ++++++------ 4 files changed, 8 insertions(+), 8 deletions(-) rename src/objective-c/tests/CoreCronetEnd2EndTests/{h2_ssl.h => h2_ssl_cronet.h} (100%) rename src/objective-c/tests/CoreCronetEnd2EndTests/{h2_ssl.m => h2_ssl_cronet.m} (98%) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index 5d501602d90..097e3686749 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -32,7 +32,7 @@ */ #import -#include "./h2_ssl.h" +#include "./h2_ssl_cronet.h" @interface CoreCronetEnd2EndTests : XCTestCase diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.h similarity index 100% rename from src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.h rename to src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.h diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m similarity index 98% rename from src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m rename to src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m index 68010f341b3..dfa27abf8d0 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m @@ -111,7 +111,7 @@ static void chttp2_init_server_secure_fullstack( grpc_server_start(f->server); } -void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { +static void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { fullstack_secure_fixture_data *ffd = f->fixture_data; gpr_free(ffd->localaddr); gpr_free(ffd); diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 4161dcc43cf..9b7d23728ca 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -12,9 +12,9 @@ 20DFDF829DD993A4A00D5662 /* libPods-RxLibraryUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */; }; 333E8FC01C8285B7C547D799 /* libPods-InteropTestsLocalCleartext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */; }; 3D7C85F6AA68C4A205E3BA16 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */; }; + 5E1BD3B21D3D583900A47325 /* h2_ssl_cronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */; }; 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */; }; 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; - 5E8A5DB01D3849F1000F8BC4 /* h2_ssl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */; }; 60D2A57ED559F34428C2EEC5 /* libPods-CoreCronetEnd2EndTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */; }; 6312AE4E1B1BF49B00341DEE /* GRPCClientTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */; }; 63423F4A1B150A5F006CF63C /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; @@ -110,10 +110,10 @@ 51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = ""; }; 553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = ""; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = ""; }; + 5E1BD3B01D3D583900A47325 /* h2_ssl_cronet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_ssl_cronet.h; sourceTree = ""; }; + 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl_cronet.m; sourceTree = ""; }; 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; - 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl.m; sourceTree = ""; }; - 5ED3173E1D397C7500C613BD /* h2_ssl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = h2_ssl.h; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; 63423F441B150A5F006CF63C /* AllTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AllTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 63423F501B151B77006CF63C /* RxLibraryUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RxLibraryUnitTests.m; sourceTree = ""; }; @@ -252,8 +252,8 @@ 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXGroup; children = ( - 5E8A5DAF1D3849F1000F8BC4 /* h2_ssl.m */, - 5ED3173E1D397C7500C613BD /* h2_ssl.h */, + 5E1BD3B01D3D583900A47325 /* h2_ssl_cronet.h */, + 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */, 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, ); path = CoreCronetEnd2EndTests; @@ -867,8 +867,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5E8A5DB01D3849F1000F8BC4 /* h2_ssl.m in Sources */, 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */, + 5E1BD3B21D3D583900A47325 /* h2_ssl_cronet.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From f8f70ebb2be4ea72ac77fc4b35c75e58adb77198 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 18 Jul 2016 13:32:48 -0700 Subject: [PATCH 14/30] Update cronet test comments and structure --- .../CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m | 11 ++++++++++- .../{h2_ssl_cronet.h => fixture.h} | 7 +------ .../tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m | 3 +++ src/objective-c/tests/Tests.xcodeproj/project.pbxproj | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) rename src/objective-c/tests/CoreCronetEnd2EndTests/{h2_ssl_cronet.h => fixture.h} (93%) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index 097e3686749..b0e58fe2ed7 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -32,7 +32,7 @@ */ #import -#include "./h2_ssl_cronet.h" +#import "src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h" @interface CoreCronetEnd2EndTests : XCTestCase @@ -42,6 +42,15 @@ - (void)testCoreCronetEnd2End { char *argv[] = {"h2_ssl"}; + + // This main() function is not the entry point of this test case; it + // refers to that in h2_ssl_cronet.m. We can use it because XCode + // builder does not use main() as the entry point for a test. + // Since h2_ssl_cronet.m is derived from h2_ssl.c in the core end2end + // test fixture, we preserves the fixture structure (in particular + // the main() function here) so that another fixture can easily + // replace the fixture h2_ssl_cronet, in case we need more tests in + // the future main(1, argv); } diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.h b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h similarity index 93% rename from src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.h rename to src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h index 3aa0a05ddad..31e36a71f2a 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.h +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h @@ -31,9 +31,4 @@ * */ -#ifndef GRPC_H2_SSL_H -#define GRPC_H2_SSL_H - -int main(int argc, char **argv); - -#endif /* GRPC_H2_SSL_H */ \ No newline at end of file +int main(int argc, char **argv); \ No newline at end of file diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m index dfa27abf8d0..37d98b793c8 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m @@ -32,6 +32,9 @@ */ /* + * This fixture derives from h2_ssl.c fixture in core end2end test + * (test/core/end2end/fixture/h2_ssl.c) + * * This fixture creates a server full stack using chttp2 and a client * full stack using Cronet. End-to-end tests are run against this fixture * setting. diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 9b7d23728ca..c1fd8afa23c 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -110,8 +110,8 @@ 51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = ""; }; 553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = ""; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = ""; }; - 5E1BD3B01D3D583900A47325 /* h2_ssl_cronet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = h2_ssl_cronet.h; sourceTree = ""; }; 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl_cronet.m; sourceTree = ""; }; + 5E1BD3B31D3D736C00A47325 /* fixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixture.h; sourceTree = ""; }; 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; @@ -252,7 +252,7 @@ 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXGroup; children = ( - 5E1BD3B01D3D583900A47325 /* h2_ssl_cronet.h */, + 5E1BD3B31D3D736C00A47325 /* fixture.h */, 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */, 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, ); From 9afdd01e6901fe1f6be68c4242e68332e58efed0 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 18 Jul 2016 13:35:30 -0700 Subject: [PATCH 15/30] Rename h2_ssl_cronet.m to fixture_h2_ssl_cronet.m --- .../{h2_ssl_cronet.m => fixture_h2_ssl_cronet.m} | 0 src/objective-c/tests/Tests.xcodeproj/project.pbxproj | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/objective-c/tests/CoreCronetEnd2EndTests/{h2_ssl_cronet.m => fixture_h2_ssl_cronet.m} (100%) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m similarity index 100% rename from src/objective-c/tests/CoreCronetEnd2EndTests/h2_ssl_cronet.m rename to src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index c1fd8afa23c..8de0fdcb8e5 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -12,7 +12,7 @@ 20DFDF829DD993A4A00D5662 /* libPods-RxLibraryUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */; }; 333E8FC01C8285B7C547D799 /* libPods-InteropTestsLocalCleartext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */; }; 3D7C85F6AA68C4A205E3BA16 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */; }; - 5E1BD3B21D3D583900A47325 /* h2_ssl_cronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */; }; + 5E1BD3B51D3D74EC00A47325 /* fixture_h2_ssl_cronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */; }; 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */; }; 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; 60D2A57ED559F34428C2EEC5 /* libPods-CoreCronetEnd2EndTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */; }; @@ -110,8 +110,8 @@ 51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = ""; }; 553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = ""; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = ""; }; - 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = h2_ssl_cronet.m; sourceTree = ""; }; 5E1BD3B31D3D736C00A47325 /* fixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixture.h; sourceTree = ""; }; + 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fixture_h2_ssl_cronet.m; sourceTree = ""; }; 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; @@ -252,8 +252,8 @@ 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXGroup; children = ( + 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */, 5E1BD3B31D3D736C00A47325 /* fixture.h */, - 5E1BD3B11D3D583900A47325 /* h2_ssl_cronet.m */, 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, ); path = CoreCronetEnd2EndTests; @@ -868,7 +868,7 @@ buildActionMask = 2147483647; files = ( 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */, - 5E1BD3B21D3D583900A47325 /* h2_ssl_cronet.m in Sources */, + 5E1BD3B51D3D74EC00A47325 /* fixture_h2_ssl_cronet.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 7c32959390881e3236dd5c61d28660271bd4289a Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Mon, 18 Jul 2016 14:22:01 -0700 Subject: [PATCH 16/30] Updated outdated comment about uchannels --- src/core/ext/client_config/channel_connectivity.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/ext/client_config/channel_connectivity.c b/src/core/ext/client_config/channel_connectivity.c index c1220e3a8c3..03db06c2f74 100644 --- a/src/core/ext/client_config/channel_connectivity.c +++ b/src/core/ext/client_config/channel_connectivity.c @@ -59,8 +59,7 @@ grpc_connectivity_state grpc_channel_check_connectivity_state( } gpr_log(GPR_ERROR, "grpc_channel_check_connectivity_state called on something that is " - "not a (u)client channel, but '%s'", - client_channel_elem->filter->name); + "not a client channel, but '%s'", client_channel_elem->filter->name); grpc_exec_ctx_finish(&exec_ctx); return GRPC_CHANNEL_SHUTDOWN; } From 791c4d7b7c89d68a3eda7e66a35ace852030caf8 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Mon, 18 Jul 2016 15:12:19 -0700 Subject: [PATCH 17/30] Add new line to the end of fixture.h --- src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h index 31e36a71f2a..45fbfc4bc82 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h @@ -31,4 +31,5 @@ * */ -int main(int argc, char **argv); \ No newline at end of file +int main(int argc, char **argv); + From c23d33b29ba6971713da5128687cba4984e0468a Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Tue, 19 Jul 2016 11:19:12 -0700 Subject: [PATCH 18/30] Revert "Generate html report for perf tests" --- tools/run_tests/perf_html_report.template | 21 ------------- tools/run_tests/report_utils.py | 37 ----------------------- tools/run_tests/run_performance_tests.py | 11 +------ 3 files changed, 1 insertion(+), 68 deletions(-) delete mode 100644 tools/run_tests/perf_html_report.template diff --git a/tools/run_tests/perf_html_report.template b/tools/run_tests/perf_html_report.template deleted file mode 100644 index c219fa888a8..00000000000 --- a/tools/run_tests/perf_html_report.template +++ /dev/null @@ -1,21 +0,0 @@ - - -Performance Test Result - -

Performance Test Result

- - <% sorted_test_cases = sorted(resultset.keys()) %> - % for test_case in sorted_test_cases: - - <% result = resultset[test_case] %> - - - % endfor -
${test_case} - % for k, v in result.iteritems(): - ${k}: ${v}
- % endfor -
- - - diff --git a/tools/run_tests/report_utils.py b/tools/run_tests/report_utils.py index 7188d3dcd70..5648a694cd0 100644 --- a/tools/run_tests/report_utils.py +++ b/tools/run_tests/report_utils.py @@ -37,8 +37,6 @@ try: from mako import exceptions except (ImportError): pass # Mako not installed but it is ok. -import glob -import json import os import string import xml.etree.cElementTree as ET @@ -122,38 +120,3 @@ def render_interop_html_report( print(exceptions.text_error_template().render()) raise - -def render_perf_html_report(report_dir): - """Generate a simple HTML report for the perf tests.""" - template_file = 'tools/run_tests/perf_html_report.template' - try: - mytemplate = Template(filename=template_file, format_exceptions=True) - except NameError: - print('Mako template is not installed. Skipping HTML report generation.') - return - except IOError as e: - print('Failed to find the template %s: %s' % (template_file, e)) - return - - resultset = {} - for result_file in glob.glob(os.path.join(report_dir, '*.json')): - with open(result_file, 'r') as f: - scenario_result = json.loads(f.read()) - test_case = scenario_result['scenario']['name'] - if 'ping_pong' in test_case: - latency50 = round(scenario_result['summary']['latency50'], 2) - latency99 = round(scenario_result['summary']['latency99'], 2) - summary = {'latency50': latency50, 'latency99': latency99} - else: - summary = {'qps': round(scenario_result['summary']['qps'], 2)} - resultset[test_case] = summary - - args = {'resultset': resultset} - - html_file_path = os.path.join(report_dir, 'index.html') - try: - with open(html_file_path, 'w') as output_file: - mytemplate.render_context(Context(output_file, **args)) - except: - print(exceptions.text_error_template().render()) - raise diff --git a/tools/run_tests/run_performance_tests.py b/tools/run_tests/run_performance_tests.py index 5ff96968089..5fdf7a407d9 100755 --- a/tools/run_tests/run_performance_tests.py +++ b/tools/run_tests/run_performance_tests.py @@ -40,7 +40,6 @@ import multiprocessing import os import pipes import re -import report_utils import subprocess import sys import tempfile @@ -55,7 +54,6 @@ os.chdir(_ROOT) _REMOTE_HOST_USERNAME = 'jenkins' -_REPORT_DIR = 'perf_reports' class QpsWorkerJob: @@ -105,11 +103,7 @@ def create_scenario_jobspec(scenario_json, workers, remote_host=None, cmd += 'BQ_RESULT_TABLE="%s" ' % bq_result_table cmd += 'tools/run_tests/performance/run_qps_driver.sh ' cmd += '--scenarios_json=%s ' % pipes.quote(json.dumps({'scenarios': [scenario_json]})) - if not os.path.isdir(_REPORT_DIR): - os.makedirs(_REPORT_DIR) - report_path = os.path.join(_REPORT_DIR, - '%s-scenario_result.json' % scenario_json['name']) - cmd += '--scenario_result_file=%s' % report_path + cmd += '--scenario_result_file=scenario_result.json' if remote_host: user_at_host = '%s@%s' % (_REMOTE_HOST_USERNAME, remote_host) cmd = 'ssh %s "cd ~/performance_workspace/grpc/ && "%s' % (user_at_host, pipes.quote(cmd)) @@ -442,9 +436,6 @@ try: jobset.message('START', 'Running scenarios.', do_newline=True) num_failures, _ = jobset.run( scenarios, newline_on_success=True, maxjobs=1) - - report_utils.render_perf_html_report(_REPORT_DIR) - if num_failures == 0: jobset.message('SUCCESS', 'All scenarios finished successfully.', From bff5cbf6b588feb7fd218d4cee5657c8effe885d Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Tue, 19 Jul 2016 11:22:20 -0700 Subject: [PATCH 19/30] Collapse fixture into CoreCronetEnd2EndTests.m so that individual test cases can be run, instead of only allow running the entire fixture --- .../CoreCronetEnd2EndTests.m | 349 +++++++++++++++++- .../tests/CoreCronetEnd2EndTests/fixture.h | 35 -- .../fixture_h2_ssl_cronet.m | 206 ----------- .../tests/Tests.xcodeproj/project.pbxproj | 6 - 4 files changed, 336 insertions(+), 260 deletions(-) delete mode 100644 src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h delete mode 100644 src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index b0e58fe2ed7..ec1d222fbd0 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -32,7 +32,140 @@ */ #import -#import "src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h" +#include "test/core/end2end/end2end_tests.h" + +#include +#include + +#include +#include +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/security/credentials/credentials.h" +#include "src/core/lib/support/env.h" +#include "src/core/lib/support/string.h" +#include "src/core/lib/support/tmpfile.h" +#include "test/core/end2end/data/ssl_test_data.h" +#include "test/core/util/port.h" +#include "test/core/util/test_config.h" + +#include +#import + +typedef struct fullstack_secure_fixture_data { + char *localaddr; +} fullstack_secure_fixture_data; + +static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( + grpc_channel_args *client_args, grpc_channel_args *server_args) { + grpc_end2end_test_fixture f; + int port = grpc_pick_unused_port_or_die(); + fullstack_secure_fixture_data *ffd = + gpr_malloc(sizeof(fullstack_secure_fixture_data)); + memset(&f, 0, sizeof(f)); + + gpr_join_host_port(&ffd->localaddr, "localhost", port); + + f.fixture_data = ffd; + f.cq = grpc_completion_queue_create(NULL); + + return f; +} + +static void process_auth_failure(void *state, grpc_auth_context *ctx, + const grpc_metadata *md, size_t md_count, + grpc_process_auth_metadata_done_cb cb, + void *user_data) { + GPR_ASSERT(state == NULL); + cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); +} + +static void cronet_init_client_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args, + cronet_engine *cronetEngine) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + f->client = + grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL); + GPR_ASSERT(f->client != NULL); +} + +static void chttp2_init_server_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args, + grpc_server_credentials *server_creds) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + if (f->server) { + grpc_server_destroy(f->server); + } + f->server = grpc_server_create(server_args, NULL); + grpc_server_register_completion_queue(f->server, f->cq, NULL); + GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, + server_creds)); + grpc_server_credentials_release(server_creds); + grpc_server_start(f->server); +} + +static void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { + fullstack_secure_fixture_data *ffd = f->fixture_data; + gpr_free(ffd->localaddr); + gpr_free(ffd); +} + +static void cronet_init_client_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { + grpc_arg ssl_name_override = {GRPC_ARG_STRING, + GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, + {"foo.test.google.fr"}}; + + grpc_channel_args *new_client_args = + grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); + [Cronet setHttp2Enabled:YES]; + [Cronet start]; + cronet_engine *cronetEngine = [Cronet getGlobalEngine]; + + cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine); + grpc_channel_args_destroy(new_client_args); +} + +static int fail_server_auth_check(grpc_channel_args *server_args) { + size_t i; + if (server_args == NULL) return 0; + for (i = 0; i < server_args->num_args; i++) { + if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) == + 0) { + return 1; + } + } + return 0; +} + +static void chttp2_init_server_simple_ssl_secure_fullstack( + grpc_end2end_test_fixture *f, grpc_channel_args *server_args) { + grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, + test_server1_cert}; + grpc_server_credentials *ssl_creds = + grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL); + if (fail_server_auth_check(server_args)) { + grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL}; + grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); + } + chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); +} + +/* All test configurations */ + +static grpc_end2end_test_config configs[] = { + {"chttp2/simple_ssl_fullstack", + FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | + FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, + chttp2_create_fixture_secure_fullstack, + cronet_init_client_simple_ssl_secure_fullstack, + chttp2_init_server_simple_ssl_secure_fullstack, + chttp2_tear_down_secure_fullstack}, +}; + + + @interface CoreCronetEnd2EndTests : XCTestCase @@ -40,18 +173,208 @@ @implementation CoreCronetEnd2EndTests -- (void)testCoreCronetEnd2End { - char *argv[] = {"h2_ssl"}; - - // This main() function is not the entry point of this test case; it - // refers to that in h2_ssl_cronet.m. We can use it because XCode - // builder does not use main() as the entry point for a test. - // Since h2_ssl_cronet.m is derived from h2_ssl.c in the core end2end - // test fixture, we preserves the fixture structure (in particular - // the main() function here) so that another fixture can easily - // replace the fixture h2_ssl_cronet, in case we need more tests in - // the future - main(1, argv); +char *roots_filename; + +// The setUp() function is run before the test cases run and only run once ++ (void)setUp { + [super setUp]; + + FILE *roots_file; + size_t roots_size = strlen(test_root_cert); + + char *argv[] = {"CoreCronetEnd2EndTests"}; + grpc_test_init(1, argv); + grpc_end2end_tests_pre_init(); + + /* Set the SSL roots env var. */ + roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); + GPR_ASSERT(roots_filename != NULL); + GPR_ASSERT(roots_file != NULL); + GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); + fclose(roots_file); + gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); + + grpc_init(); + +} + +// The tearDown() function is run after all test cases finish running ++ (void)tearDown { + grpc_shutdown(); + + /* Cleanup. */ + remove(roots_filename); + gpr_free(roots_filename); + + [super tearDown]; +} + +- (void)testIndividualCase:(char*)test_case { + char *argv[] = {"h2_ssl", test_case}; + + for (int i = 0; i < sizeof(configs) / sizeof(*configs); i++) { + grpc_end2end_tests(sizeof(argv) / sizeof(argv[0]), argv, configs[i]); + } +} + +- (void)testBadHostname { + [self testIndividualCase:"bad_hostname"]; +} + +- (void)testBinaryMetadata { + [self testIndividualCase:"binary_metadata"]; +} + +- (void)testCallCreds { + [self testIndividualCase:"call_creds"]; +} + +- (void)testCancelAfterAccept { + [self testIndividualCase:"cancel_after_accept"]; +} + +- (void)testCancelAfterClientDone { + [self testIndividualCase:"cancel_after_client_done"]; +} + +- (void)testCancelAfterInvoke { + [self testIndividualCase:"cancel_after_invoke"]; +} + +- (void)testCancelBeforeInvoke { + [self testIndividualCase:"cancel_before_invoke"]; +} + +- (void)testCancelInAVacuum { + [self testIndividualCase:"cancel_in_a_vacuum"]; +} + +- (void)testCancelWithStatus { + [self testIndividualCase:"cancel_with_status"]; +} + +- (void)testCompressedPayload { + [self testIndividualCase:"compressed_payload"]; +} + +- (void)testConnectivity { + [self testIndividualCase:"connectivity"]; +} + +- (void)testDefaultHost { + [self testIndividualCase:"default_host"]; +} + +- (void)testDisappearingServer { + [self testIndividualCase:"disappearing_server"]; +} + +- (void)testEmptyBatch { + [self testIndividualCase:"empty_batch"]; +} + +- (void)testFilterCausesClose { + [self testIndividualCase:"filter_causes_close"]; +} + +- (void)testGracefulServerShutdown { + [self testIndividualCase:"graceful_server_shutdown"]; +} + +- (void)testHighInitialSeqno { + [self testIndividualCase:"high_initial_seqno"]; +} + +- (void)testHpackSize { + [self testIndividualCase:"hpack_size"]; +} + +- (void)testIdempotentRequest { + [self testIndividualCase:"idempotent_request"]; +} + +- (void)testInvokeLargeRequest { + [self testIndividualCase:"invoke_large_request"]; +} + +- (void)testLargeMetadata { + [self testIndividualCase:"large_metadata"]; +} + +- (void)testMaxConcurrentStreams { + [self testIndividualCase:"max_concurrent_streams"]; +} + +- (void)testMaxMessageLength { + [self testIndividualCase:"max_message_length"]; +} + +- (void)testNegativeDeadline { + [self testIndividualCase:"negative_deadline"]; +} + +- (void)testNetworkStatusChange { + [self testIndividualCase:"network_status_change"]; +} + +- (void)testNoOp { + [self testIndividualCase:"no_op"]; +} + +- (void)testPayload { + [self testIndividualCase:"payload"]; +} + +- (void)testPing { + [self testIndividualCase:"ping"]; +} + +- (void)testPingPongStreaming { + [self testIndividualCase:"ping_pong_streaming"]; +} + +- (void)testRegisteredCall { + [self testIndividualCase:"registered_call"]; +} + +- (void)testRequestWithFlags { + [self testIndividualCase:"request_with_flags"]; +} + +- (void)testRequestWithPayload { + [self testIndividualCase:"request_with_payload"]; +} + +- (void)testServerFinishesRequest { + [self testIndividualCase:"server_finishes_request"]; +} + +- (void)testShutdownFinishesCalls { + [self testIndividualCase:"shutdown_finishes_calls"]; +} + +- (void)testShutdownFinishesTags { + [self testIndividualCase:"shutdown_finishes_tags"]; +} + +- (void)testSimpleDelayedRequest { + [self testIndividualCase:"simple_delayed_request"]; +} + +- (void)testSimpleMetadata { + [self testIndividualCase:"simple_metadata"]; +} + +- (void)testSimpleRequest { + [self testIndividualCase:"simple_request"]; +} + +- (void)testStreamingErrorResponse { + [self testIndividualCase:"streaming_error_response"]; +} + +- (void)testTrailingMetadata { + [self testIndividualCase:"trailing_metadata"]; } @end diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h deleted file mode 100644 index 45fbfc4bc82..00000000000 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -int main(int argc, char **argv); - diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m b/src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m deleted file mode 100644 index 37d98b793c8..00000000000 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/fixture_h2_ssl_cronet.m +++ /dev/null @@ -1,206 +0,0 @@ -/* - * - * Copyright 2015, Google Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - */ - -/* - * This fixture derives from h2_ssl.c fixture in core end2end test - * (test/core/end2end/fixture/h2_ssl.c) - * - * This fixture creates a server full stack using chttp2 and a client - * full stack using Cronet. End-to-end tests are run against this fixture - * setting. - * - */ - -#include "test/core/end2end/end2end_tests.h" - -#include -#include - -#include -#include -#include - -#include "src/core/lib/channel/channel_args.h" -#include "src/core/lib/security/credentials/credentials.h" -#include "src/core/lib/support/env.h" -#include "src/core/lib/support/string.h" -#include "src/core/lib/support/tmpfile.h" -#include "test/core/end2end/data/ssl_test_data.h" -#include "test/core/util/port.h" -#include "test/core/util/test_config.h" - -#include -#import - -typedef struct fullstack_secure_fixture_data { - char *localaddr; -} fullstack_secure_fixture_data; - -static grpc_end2end_test_fixture chttp2_create_fixture_secure_fullstack( - grpc_channel_args *client_args, grpc_channel_args *server_args) { - grpc_end2end_test_fixture f; - int port = grpc_pick_unused_port_or_die(); - fullstack_secure_fixture_data *ffd = - gpr_malloc(sizeof(fullstack_secure_fixture_data)); - memset(&f, 0, sizeof(f)); - - gpr_join_host_port(&ffd->localaddr, "localhost", port); - - f.fixture_data = ffd; - f.cq = grpc_completion_queue_create(NULL); - - return f; -} - -static void process_auth_failure(void *state, grpc_auth_context *ctx, - const grpc_metadata *md, size_t md_count, - grpc_process_auth_metadata_done_cb cb, - void *user_data) { - GPR_ASSERT(state == NULL); - cb(user_data, NULL, 0, NULL, 0, GRPC_STATUS_UNAUTHENTICATED, NULL); -} - -static void cronet_init_client_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *client_args, - cronet_engine *cronetEngine) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - f->client = - grpc_cronet_secure_channel_create(cronetEngine, ffd->localaddr, client_args, NULL); - GPR_ASSERT(f->client != NULL); -} - -static void chttp2_init_server_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *server_args, - grpc_server_credentials *server_creds) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - if (f->server) { - grpc_server_destroy(f->server); - } - f->server = grpc_server_create(server_args, NULL); - grpc_server_register_completion_queue(f->server, f->cq, NULL); - GPR_ASSERT(grpc_server_add_secure_http2_port(f->server, ffd->localaddr, - server_creds)); - grpc_server_credentials_release(server_creds); - grpc_server_start(f->server); -} - -static void chttp2_tear_down_secure_fullstack(grpc_end2end_test_fixture *f) { - fullstack_secure_fixture_data *ffd = f->fixture_data; - gpr_free(ffd->localaddr); - gpr_free(ffd); -} - -static void cronet_init_client_simple_ssl_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *client_args) { - grpc_arg ssl_name_override = {GRPC_ARG_STRING, - GRPC_SSL_TARGET_NAME_OVERRIDE_ARG, - {"foo.test.google.fr"}}; - - grpc_channel_args *new_client_args = - grpc_channel_args_copy_and_add(client_args, &ssl_name_override, 1); - [Cronet setHttp2Enabled:YES]; - [Cronet start]; - cronet_engine *cronetEngine = [Cronet getGlobalEngine]; - - cronet_init_client_secure_fullstack(f, new_client_args, cronetEngine); - grpc_channel_args_destroy(new_client_args); -} - -static int fail_server_auth_check(grpc_channel_args *server_args) { - size_t i; - if (server_args == NULL) return 0; - for (i = 0; i < server_args->num_args; i++) { - if (strcmp(server_args->args[i].key, FAIL_AUTH_CHECK_SERVER_ARG_NAME) == - 0) { - return 1; - } - } - return 0; -} - -static void chttp2_init_server_simple_ssl_secure_fullstack( - grpc_end2end_test_fixture *f, grpc_channel_args *server_args) { - grpc_ssl_pem_key_cert_pair pem_cert_key_pair = {test_server1_key, - test_server1_cert}; - grpc_server_credentials *ssl_creds = - grpc_ssl_server_credentials_create(NULL, &pem_cert_key_pair, 1, 0, NULL); - if (fail_server_auth_check(server_args)) { - grpc_auth_metadata_processor processor = {process_auth_failure, NULL, NULL}; - grpc_server_credentials_set_auth_metadata_processor(ssl_creds, processor); - } - chttp2_init_server_secure_fullstack(f, server_args, ssl_creds); -} - -/* All test configurations */ - -static grpc_end2end_test_config configs[] = { - {"chttp2/simple_ssl_fullstack", - FEATURE_MASK_SUPPORTS_DELAYED_CONNECTION | - FEATURE_MASK_SUPPORTS_PER_CALL_CREDENTIALS, - chttp2_create_fixture_secure_fullstack, - cronet_init_client_simple_ssl_secure_fullstack, - chttp2_init_server_simple_ssl_secure_fullstack, - chttp2_tear_down_secure_fullstack}, -}; - -int main(int argc, char **argv) { - size_t i; - FILE *roots_file; - size_t roots_size = strlen(test_root_cert); - char *roots_filename; - - grpc_test_init(argc, argv); - grpc_end2end_tests_pre_init(); - - /* Set the SSL roots env var. */ - roots_file = gpr_tmpfile("chttp2_simple_ssl_fullstack_test", &roots_filename); - GPR_ASSERT(roots_filename != NULL); - GPR_ASSERT(roots_file != NULL); - GPR_ASSERT(fwrite(test_root_cert, 1, roots_size, roots_file) == roots_size); - fclose(roots_file); - gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_filename); - - grpc_init(); - - for (i = 0; i < sizeof(configs) / sizeof(*configs); i++) { - grpc_end2end_tests(argc, argv, configs[i]); - } - - grpc_shutdown(); - - /* Cleanup. */ - remove(roots_filename); - gpr_free(roots_filename); - - return 0; -} diff --git a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj index 8de0fdcb8e5..2c80b08d030 100644 --- a/src/objective-c/tests/Tests.xcodeproj/project.pbxproj +++ b/src/objective-c/tests/Tests.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 20DFDF829DD993A4A00D5662 /* libPods-RxLibraryUnitTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A58BE6DF1C62D1739EBB2C78 /* libPods-RxLibraryUnitTests.a */; }; 333E8FC01C8285B7C547D799 /* libPods-InteropTestsLocalCleartext.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FD346DB2C23F676C4842F3FF /* libPods-InteropTestsLocalCleartext.a */; }; 3D7C85F6AA68C4A205E3BA16 /* libPods-Tests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20DFF2F3C97EF098FE5A3171 /* libPods-Tests.a */; }; - 5E1BD3B51D3D74EC00A47325 /* fixture_h2_ssl_cronet.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */; }; 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */; }; 5E8A5DA91D3840B4000F8BC4 /* libTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 635697C71B14FC11007A7283 /* libTests.a */; }; 60D2A57ED559F34428C2EEC5 /* libPods-CoreCronetEnd2EndTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FBD98AC417B9882D32B19F28 /* libPods-CoreCronetEnd2EndTests.a */; }; @@ -110,8 +109,6 @@ 51A275E86C141416ED63FF76 /* Pods-InteropTestsLocalCleartext.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalCleartext.release.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalCleartext/Pods-InteropTestsLocalCleartext.release.xcconfig"; sourceTree = ""; }; 553BBBED24E4162D1F769D65 /* Pods-InteropTestsLocalSSL.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-InteropTestsLocalSSL.debug.xcconfig"; path = "Pods/Target Support Files/Pods-InteropTestsLocalSSL/Pods-InteropTestsLocalSSL.debug.xcconfig"; sourceTree = ""; }; 5761E98978DDDF136A58CB7E /* Pods-AllTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AllTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AllTests/Pods-AllTests.release.xcconfig"; sourceTree = ""; }; - 5E1BD3B31D3D736C00A47325 /* fixture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fixture.h; sourceTree = ""; }; - 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = fixture_h2_ssl_cronet.m; sourceTree = ""; }; 5E8A5DA41D3840B4000F8BC4 /* CoreCronetEnd2EndTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreCronetEnd2EndTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CoreCronetEnd2EndTests.m; sourceTree = ""; }; 6312AE4D1B1BF49B00341DEE /* GRPCClientTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GRPCClientTests.m; sourceTree = ""; }; @@ -252,8 +249,6 @@ 5E8A5DA51D3840B4000F8BC4 /* CoreCronetEnd2EndTests */ = { isa = PBXGroup; children = ( - 5E1BD3B41D3D74EC00A47325 /* fixture_h2_ssl_cronet.m */, - 5E1BD3B31D3D736C00A47325 /* fixture.h */, 5E8A5DA61D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m */, ); path = CoreCronetEnd2EndTests; @@ -868,7 +863,6 @@ buildActionMask = 2147483647; files = ( 5E8A5DA71D3840B4000F8BC4 /* CoreCronetEnd2EndTests.m in Sources */, - 5E1BD3B51D3D74EC00A47325 /* fixture_h2_ssl_cronet.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From b6b0932c28ff636d2e4405ef54d5a62349c2cd18 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Tue, 19 Jul 2016 14:33:23 -0700 Subject: [PATCH 20/30] Add fixture description and TODO for test name inferral from test case method name --- .../CoreCronetEnd2EndTests.m | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index ec1d222fbd0..122200a294b 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -31,6 +31,18 @@ * */ +/* + * This test file is derived from fixture h2_ssl.c in core end2end test + * (test/core/end2end/fixture/h2_ssl.c). The structure of the fixture is + * preserved as much as possible + * + * This fixture creates a server full stack using chttp2 and a client + * full stack using Cronet. End-to-end tests are run against this + * configuration + * + */ + + #import #include "test/core/end2end/end2end_tests.h" @@ -166,6 +178,7 @@ static grpc_end2end_test_config configs[] = { +char *roots_filename; @interface CoreCronetEnd2EndTests : XCTestCase @@ -173,7 +186,6 @@ static grpc_end2end_test_config configs[] = { @implementation CoreCronetEnd2EndTests -char *roots_filename; // The setUp() function is run before the test cases run and only run once + (void)setUp { @@ -217,6 +229,8 @@ char *roots_filename; } } +// TODO(mxyan): Use NSStringFromSelector(_cmd) to acquire test name from the +// test case method name, so that bodies of test cases can stay identical - (void)testBadHostname { [self testIndividualCase:"bad_hostname"]; } From e6c04aa6126d3de7e27c86b2086b00d5353011ed Mon Sep 17 00:00:00 2001 From: yang-g Date: Tue, 19 Jul 2016 14:46:50 -0700 Subject: [PATCH 21/30] Add a macro to support protobuf lite --- include/grpc++/impl/codegen/config_protobuf.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/grpc++/impl/codegen/config_protobuf.h b/include/grpc++/impl/codegen/config_protobuf.h index 4bee1bc4227..e1198f0ea03 100644 --- a/include/grpc++/impl/codegen/config_protobuf.h +++ b/include/grpc++/impl/codegen/config_protobuf.h @@ -40,9 +40,14 @@ #endif #ifndef GRPC_CUSTOM_MESSAGE +#ifdef GRPC_USE_PROTO_LITE +#include +#define GRPC_CUSTOM_MESSAGE ::google::protobuf::MessageLite +#else #include #define GRPC_CUSTOM_MESSAGE ::google::protobuf::Message #endif +#endif #ifndef GRPC_CUSTOM_DESCRIPTOR #include From 5a14bbec66c75e877f3744b859c723911ae8b129 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Tue, 19 Jul 2016 15:22:01 -0700 Subject: [PATCH 22/30] Let roots_filename be a static variable --- .../tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m index 122200a294b..58abb492cea 100644 --- a/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m +++ b/src/objective-c/tests/CoreCronetEnd2EndTests/CoreCronetEnd2EndTests.m @@ -178,7 +178,7 @@ static grpc_end2end_test_config configs[] = { -char *roots_filename; +static char *roots_filename; @interface CoreCronetEnd2EndTests : XCTestCase From 4c82af59d588930bb37b39e629bcc2450b138178 Mon Sep 17 00:00:00 2001 From: Muxi Yan Date: Tue, 19 Jul 2016 17:40:39 -0700 Subject: [PATCH 23/30] Add test target file to repo --- .../xcschemes/CoreCronetEnd2EndTests.xcscheme | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme diff --git a/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme new file mode 100644 index 00000000000..a1da2e0c97c --- /dev/null +++ b/src/objective-c/tests/Tests.xcodeproj/xcshareddata/xcschemes/CoreCronetEnd2EndTests.xcscheme @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From cc7eb8ef88dcbd11acb3f24000e1628026b975c5 Mon Sep 17 00:00:00 2001 From: Vijay Pai Date: Tue, 19 Jul 2016 19:03:55 -0700 Subject: [PATCH 24/30] Makefile modifications and instructions to enable cross-compilation --- Makefile | 196 +++++++++++++++++++++--------------- templates/Makefile.template | 52 ++++++++-- 2 files changed, 156 insertions(+), 92 deletions(-) diff --git a/Makefile b/Makefile index 4ce22678d7e..e156bc25a20 100644 --- a/Makefile +++ b/Makefile @@ -405,6 +405,29 @@ LIBS = m pthread ws2_32 LDFLAGS += -pthread endif +# +# The steps for cross-compiling are as follows: +# First, clone and make install of grpc using the native compilers for the host. +# Also, install protoc (e.g., from a package like apt-get) +# Then clone a fresh grpc for the actual cross-compiled build +# Set the environment variable GRPC_CROSS_COMPILE to true +# Set CC, CXX, LD, LDXX, AR, and STRIP to the cross-compiling binaries +# Also set PROTOBUF_CONFIG_OPTS to indicate cross-compilation to protobuf (e.g., +# PROTOBUF_CONFIG_OPTS="--host=arm-linux --with-protoc=/usr/local/bin/protoc" ) +# Set HAS_PKG_CONFIG=false +# To build tests, go to third_party/gflags and follow its ccmake instructions +# Make sure that you enable building shared libraries and set your prefix to +# something useful like /usr/local/cross +# You will also need to set GRPC_CROSS_LDOPTS and GRPC_CROSS_AROPTS to hold +# additional required arguments for LD and AR (examples below) +# Then you can do a make from the cross-compiling fresh clone! +# +ifeq ($(GRPC_CROSS_COMPILE),true) +LDFLAGS += $(GRPC_CROSS_LDOPTS) # e.g. -L/usr/local/lib -L/usr/local/cross/lib +AROPTS = $(GRPC_CROSS_AROPTS) # e.g., rc --target=elf32-little +USE_BUILT_PROTOC = false +endif + GTEST_LIB = -Ithird_party/googletest/include -Ithird_party/googletest third_party/googletest/src/gtest-all.cc GTEST_LIB += -lgflags ifeq ($(V),1) @@ -709,6 +732,9 @@ PC_LIBS_GRPCXX = CPPFLAGS := -Ithird_party/googletest/include $(CPPFLAGS) +PROTOC_PLUGINS_ALL = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin +PROTOC_PLUGINS_DIR = $(BINDIR)/$(CONFIG) + ifeq ($(HAS_SYSTEM_PROTOBUF),true) ifeq ($(HAS_PKG_CONFIG),true) PROTOBUF_PKG_CONFIG = true @@ -722,13 +748,20 @@ endif endif else PC_LIBS_GRPCXX = -lprotobuf +PROTOC_PLUGINS = $(PROTOC_PLUGINS_ALL) endif else ifeq ($(HAS_EMBEDDED_PROTOBUF),true) PROTOBUF_DEP = $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a CPPFLAGS := -Ithird_party/protobuf/src $(CPPFLAGS) LDFLAGS := -L$(LIBDIR)/$(CONFIG)/protobuf $(LDFLAGS) +ifneq ($(USE_BUILT_PROTOC),false) PROTOC = $(BINDIR)/$(CONFIG)/protobuf/protoc +PROTOC_PLUGINS = $(PROTOC_PLUGINS_ALL) +else +PROTOC_PLUGINS = +PROTOC_PLUGINS_DIR = $(prefix)/bin +endif else NO_PROTOBUF = true endif @@ -776,7 +809,6 @@ endif .SECONDARY = %.pb.h %.pb.cc -PROTOC_PLUGINS = $(BINDIR)/$(CONFIG)/grpc_cpp_plugin $(BINDIR)/$(CONFIG)/grpc_csharp_plugin $(BINDIR)/$(CONFIG)/grpc_node_plugin $(BINDIR)/$(CONFIG)/grpc_objective_c_plugin $(BINDIR)/$(CONFIG)/grpc_python_plugin $(BINDIR)/$(CONFIG)/grpc_ruby_plugin ifeq ($(DEP_MISSING),) all: static shared plugins dep_error: @@ -1147,7 +1179,7 @@ third_party/protobuf/configure: $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a: third_party/protobuf/configure $(E) "[MAKE] Building protobuf" - $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static) + $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static $(PROTOBUF_CONFIG_OPTS)) $(Q)$(MAKE) -C third_party/protobuf clean $(Q)$(MAKE) -C third_party/protobuf $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/protobuf @@ -1886,7 +1918,7 @@ $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.pb.cc: src/proto/grpc/lb/v1/load_ba $(GENDIR)/src/proto/grpc/lb/v1/load_balancer.grpc.pb.cc: src/proto/grpc/lb/v1/load_balancer.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1901,7 +1933,7 @@ $(GENDIR)/src/proto/grpc/testing/compiler_test.pb.cc: src/proto/grpc/testing/com $(GENDIR)/src/proto/grpc/testing/compiler_test.grpc.pb.cc: src/proto/grpc/testing/compiler_test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1916,7 +1948,7 @@ $(GENDIR)/src/proto/grpc/testing/control.pb.cc: src/proto/grpc/testing/control.p $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc: src/proto/grpc/testing/control.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.pb.cc $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1931,7 +1963,7 @@ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.pb.cc: src/proto/grpc/ $(GENDIR)/src/proto/grpc/testing/duplicate/echo_duplicate.grpc.pb.cc: src/proto/grpc/testing/duplicate/echo_duplicate.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1946,7 +1978,7 @@ $(GENDIR)/src/proto/grpc/testing/echo.pb.cc: src/proto/grpc/testing/echo.proto $ $(GENDIR)/src/proto/grpc/testing/echo.grpc.pb.cc: src/proto/grpc/testing/echo.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1961,7 +1993,7 @@ $(GENDIR)/src/proto/grpc/testing/echo_messages.pb.cc: src/proto/grpc/testing/ech $(GENDIR)/src/proto/grpc/testing/echo_messages.grpc.pb.cc: src/proto/grpc/testing/echo_messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1976,7 +2008,7 @@ $(GENDIR)/src/proto/grpc/testing/empty.pb.cc: src/proto/grpc/testing/empty.proto $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc: src/proto/grpc/testing/empty.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -1991,7 +2023,7 @@ $(GENDIR)/src/proto/grpc/testing/messages.pb.cc: src/proto/grpc/testing/messages $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc: src/proto/grpc/testing/messages.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -2006,7 +2038,7 @@ $(GENDIR)/src/proto/grpc/testing/metrics.pb.cc: src/proto/grpc/testing/metrics.p $(GENDIR)/src/proto/grpc/testing/metrics.grpc.pb.cc: src/proto/grpc/testing/metrics.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -2021,7 +2053,7 @@ $(GENDIR)/src/proto/grpc/testing/payloads.pb.cc: src/proto/grpc/testing/payloads $(GENDIR)/src/proto/grpc/testing/payloads.grpc.pb.cc: src/proto/grpc/testing/payloads.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -2036,7 +2068,7 @@ $(GENDIR)/src/proto/grpc/testing/services.pb.cc: src/proto/grpc/testing/services $(GENDIR)/src/proto/grpc/testing/services.grpc.pb.cc: src/proto/grpc/testing/services.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/control.pb.cc $(GENDIR)/src/proto/grpc/testing/control.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -2051,7 +2083,7 @@ $(GENDIR)/src/proto/grpc/testing/stats.pb.cc: src/proto/grpc/testing/stats.proto $(GENDIR)/src/proto/grpc/testing/stats.grpc.pb.cc: src/proto/grpc/testing/stats.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif ifeq ($(NO_PROTOC),true) @@ -2066,7 +2098,7 @@ $(GENDIR)/src/proto/grpc/testing/test.pb.cc: src/proto/grpc/testing/test.proto $ $(GENDIR)/src/proto/grpc/testing/test.grpc.pb.cc: src/proto/grpc/testing/test.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) $(GENDIR)/src/proto/grpc/testing/empty.pb.cc $(GENDIR)/src/proto/grpc/testing/empty.grpc.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.pb.cc $(GENDIR)/src/proto/grpc/testing/messages.grpc.pb.cc $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif @@ -2413,7 +2445,7 @@ $(LIBDIR)/$(CONFIG)/libgpr.a: $(ZLIB_DEP) $(LIBGPR_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgpr.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBGPR_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBGPR_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgpr.a endif @@ -2455,7 +2487,7 @@ $(LIBDIR)/$(CONFIG)/libgpr_test_util.a: $(ZLIB_DEP) $(LIBGPR_TEST_UTIL_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgpr_test_util.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBGPR_TEST_UTIL_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBGPR_TEST_UTIL_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgpr_test_util.a endif @@ -2706,7 +2738,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_OBJS) $(LIB $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBGRPC_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) $(OPENSSL_MERGE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc.a $(LIBGRPC_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) $(OPENSSL_MERGE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc.a endif @@ -2955,7 +2987,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_CRONE $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a $(LIBGRPC_CRONET_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) $(OPENSSL_MERGE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a $(LIBGRPC_CRONET_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) $(OPENSSL_MERGE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_cronet.a endif @@ -3027,7 +3059,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_TE $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a $(LIBGRPC_TEST_UTIL_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util.a endif @@ -3067,7 +3099,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a: $(ZLIB_DEP) $(LIBGRPC_TEST_UT $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a $(LIBGRPC_TEST_UTIL_UNSECURE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_test_util_unsecure.a endif @@ -3276,7 +3308,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a: $(ZLIB_DEP) $(LIBGRPC_UNSECURE_OBJS) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBGRPC_UNSECURE_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a $(LIBGRPC_UNSECURE_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_unsecure.a endif @@ -3328,7 +3360,7 @@ $(LIBDIR)/$(CONFIG)/libreconnect_server.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBRECON $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libreconnect_server.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBRECONNECT_SERVER_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libreconnect_server.a $(LIBRECONNECT_SERVER_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libreconnect_server.a endif @@ -3367,7 +3399,7 @@ $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBTEST_T $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBTEST_TCP_SERVER_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a $(LIBTEST_TCP_SERVER_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libtest_tcp_server.a endif @@ -3546,7 +3578,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc++.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LI $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBGRPC++_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc++.a $(LIBGRPC++_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc++.a endif @@ -3673,7 +3705,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBU $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBGRPC++_REFLECTION_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a $(LIBGRPC++_REFLECTION_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc++_reflection.a endif @@ -3739,7 +3771,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOB $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBGRPC++_TEST_CONFIG_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a $(LIBGRPC++_TEST_CONFIG_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc++_test_config.a endif @@ -3850,7 +3882,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBGRPC++_TEST_UTIL_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a $(LIBGRPC++_TEST_UTIL_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc++_test_util.a endif @@ -4023,7 +4055,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIBGRPC $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBGRPC++_UNSECURE_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a $(LIBGRPC++_UNSECURE_OBJS) $(LIBGPR_OBJS) $(ZLIB_MERGE_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc++_unsecure.a endif @@ -4086,7 +4118,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DE $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBGRPC_CLI_LIBS_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a $(LIBGRPC_CLI_LIBS_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_cli_libs.a endif @@ -4132,7 +4164,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIB $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(LIBGRPC_PLUGIN_SUPPORT_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a $(LIBGRPC_PLUGIN_SUPPORT_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_plugin_support.a endif @@ -4178,7 +4210,7 @@ $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PRO $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBINTEROP_CLIENT_HELPER_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a $(LIBINTEROP_CLIENT_HELPER_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libinterop_client_helper.a endif @@ -4232,7 +4264,7 @@ $(LIBDIR)/$(CONFIG)/libinterop_client_main.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTO $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_client_main.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBINTEROP_CLIENT_MAIN_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libinterop_client_main.a $(LIBINTEROP_CLIENT_MAIN_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libinterop_client_main.a endif @@ -4283,7 +4315,7 @@ $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PRO $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBINTEROP_SERVER_HELPER_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a $(LIBINTEROP_SERVER_HELPER_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libinterop_server_helper.a endif @@ -4335,7 +4367,7 @@ $(LIBDIR)/$(CONFIG)/libinterop_server_main.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTO $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libinterop_server_main.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBINTEROP_SERVER_MAIN_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libinterop_server_main.a $(LIBINTEROP_SERVER_MAIN_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libinterop_server_main.a endif @@ -4400,7 +4432,7 @@ $(LIBDIR)/$(CONFIG)/libqps.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(PROTOBUF_DEP) $(LIBQP $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libqps.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libqps.a $(LIBQPS_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libqps.a $(LIBQPS_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libqps.a endif @@ -4453,7 +4485,7 @@ $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBGRPC_C $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a $(LIBGRPC_CSHARP_EXT_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a $(LIBGRPC_CSHARP_EXT_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libgrpc_csharp_ext.a endif @@ -4797,7 +4829,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl.a: $(ZLIB_DEP) $(LIBBORINGSSL_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl.a $(LIBBORINGSSL_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl.a $(LIBBORINGSSL_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl.a endif @@ -4835,7 +4867,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(LIB $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBBORINGSSL_TEST_UTIL_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a $(LIBBORINGSSL_TEST_UTIL_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_test_util.a endif @@ -4873,7 +4905,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBBORINGSSL_AES_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a $(LIBBORINGSSL_AES_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_aes_test_lib.a endif @@ -4911,7 +4943,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBBORINGSSL_ASN1_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a $(LIBBORINGSSL_ASN1_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_asn1_test_lib.a endif @@ -4949,7 +4981,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBBORINGSSL_BASE64_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a $(LIBBORINGSSL_BASE64_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_base64_test_lib.a endif @@ -4987,7 +5019,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBBORINGSSL_BIO_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a $(LIBBORINGSSL_BIO_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_bio_test_lib.a endif @@ -5025,7 +5057,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(L $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBBORINGSSL_BN_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a $(LIBBORINGSSL_BN_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_bn_test_lib.a endif @@ -5063,7 +5095,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBBORINGSSL_BYTESTRING_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a $(LIBBORINGSSL_BYTESTRING_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_bytestring_test_lib.a endif @@ -5101,7 +5133,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBBORINGSSL_AEAD_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a $(LIBBORINGSSL_AEAD_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_aead_test_lib.a endif @@ -5139,7 +5171,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBBORINGSSL_CIPHER_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a $(LIBBORINGSSL_CIPHER_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_cipher_test_lib.a endif @@ -5177,7 +5209,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBBORINGSSL_CMAC_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a $(LIBBORINGSSL_CMAC_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_cmac_test_lib.a endif @@ -5206,7 +5238,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_constant_time_test_lib.a: $(ZLIB_DEP) $(LIBBOR $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_constant_time_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_constant_time_test_lib.a $(LIBBORINGSSL_CONSTANT_TIME_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_constant_time_test_lib.a $(LIBBORINGSSL_CONSTANT_TIME_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_constant_time_test_lib.a endif @@ -5242,7 +5274,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBBORINGSSL_ED25519_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a $(LIBBORINGSSL_ED25519_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_ed25519_test_lib.a endif @@ -5280,7 +5312,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBBORINGSSL_X25519_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a $(LIBBORINGSSL_X25519_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_x25519_test_lib.a endif @@ -5318,7 +5350,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(L $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBBORINGSSL_DH_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a $(LIBBORINGSSL_DH_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_dh_test_lib.a endif @@ -5356,7 +5388,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBBORINGSSL_DIGEST_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a $(LIBBORINGSSL_DIGEST_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_digest_test_lib.a endif @@ -5385,7 +5417,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_dsa_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_DSA $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_dsa_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_dsa_test_lib.a $(LIBBORINGSSL_DSA_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_dsa_test_lib.a $(LIBBORINGSSL_DSA_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_dsa_test_lib.a endif @@ -5421,7 +5453,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(L $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBBORINGSSL_EC_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a $(LIBBORINGSSL_EC_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_ec_test_lib.a endif @@ -5450,7 +5482,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_example_mul_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_example_mul_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_example_mul_lib.a $(LIBBORINGSSL_EXAMPLE_MUL_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_example_mul_lib.a $(LIBBORINGSSL_EXAMPLE_MUL_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_example_mul_lib.a endif @@ -5486,7 +5518,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBBORINGSSL_ECDSA_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a $(LIBBORINGSSL_ECDSA_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_ecdsa_test_lib.a endif @@ -5524,7 +5556,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBBORINGSSL_ERR_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a $(LIBBORINGSSL_ERR_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_err_test_lib.a endif @@ -5562,7 +5594,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_D $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBBORINGSSL_EVP_EXTRA_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a $(LIBBORINGSSL_EVP_EXTRA_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_evp_extra_test_lib.a endif @@ -5600,7 +5632,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBBORINGSSL_EVP_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a $(LIBBORINGSSL_EVP_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_evp_test_lib.a endif @@ -5638,7 +5670,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBBORINGSSL_PBKDF_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a $(LIBBORINGSSL_PBKDF_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_pbkdf_test_lib.a endif @@ -5667,7 +5699,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_hkdf_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_HK $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_hkdf_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_hkdf_test_lib.a $(LIBBORINGSSL_HKDF_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_hkdf_test_lib.a $(LIBBORINGSSL_HKDF_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_hkdf_test_lib.a endif @@ -5703,7 +5735,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBBORINGSSL_HMAC_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a $(LIBBORINGSSL_HMAC_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_hmac_test_lib.a endif @@ -5732,7 +5764,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_lhash_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_L $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_lhash_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_lhash_test_lib.a $(LIBBORINGSSL_LHASH_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_lhash_test_lib.a $(LIBBORINGSSL_LHASH_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_lhash_test_lib.a endif @@ -5759,7 +5791,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_gcm_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_GCM $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_gcm_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_gcm_test_lib.a $(LIBBORINGSSL_GCM_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_gcm_test_lib.a $(LIBBORINGSSL_GCM_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_gcm_test_lib.a endif @@ -5795,7 +5827,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBBORINGSSL_PKCS12_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a $(LIBBORINGSSL_PKCS12_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_pkcs12_test_lib.a endif @@ -5833,7 +5865,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBBORINGSSL_PKCS8_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a $(LIBBORINGSSL_PKCS8_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_pkcs8_test_lib.a endif @@ -5871,7 +5903,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DE $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBBORINGSSL_POLY1305_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a $(LIBBORINGSSL_POLY1305_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_poly1305_test_lib.a endif @@ -5900,7 +5932,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_refcount_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSS $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_refcount_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_refcount_test_lib.a $(LIBBORINGSSL_REFCOUNT_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_refcount_test_lib.a $(LIBBORINGSSL_REFCOUNT_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_refcount_test_lib.a endif @@ -5936,7 +5968,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBBORINGSSL_RSA_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a $(LIBBORINGSSL_RSA_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_rsa_test_lib.a endif @@ -5965,7 +5997,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_thread_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_thread_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_thread_test_lib.a $(LIBBORINGSSL_THREAD_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_thread_test_lib.a $(LIBBORINGSSL_THREAD_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_thread_test_lib.a endif @@ -5992,7 +6024,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_pkcs7_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_P $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_pkcs7_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs7_test_lib.a $(LIBBORINGSSL_PKCS7_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_pkcs7_test_lib.a $(LIBBORINGSSL_PKCS7_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_pkcs7_test_lib.a endif @@ -6028,7 +6060,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBBORINGSSL_X509_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a $(LIBBORINGSSL_X509_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_x509_test_lib.a endif @@ -6057,7 +6089,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_tab_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_TAB $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_tab_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_tab_test_lib.a $(LIBBORINGSSL_TAB_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_tab_test_lib.a $(LIBBORINGSSL_TAB_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_tab_test_lib.a endif @@ -6084,7 +6116,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_v3name_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_v3name_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_v3name_test_lib.a $(LIBBORINGSSL_V3NAME_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_v3name_test_lib.a $(LIBBORINGSSL_V3NAME_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_v3name_test_lib.a endif @@ -6111,7 +6143,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_pqueue_test_lib.a: $(ZLIB_DEP) $(LIBBORINGSSL_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_pqueue_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_pqueue_test_lib.a $(LIBBORINGSSL_PQUEUE_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_pqueue_test_lib.a $(LIBBORINGSSL_PQUEUE_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_pqueue_test_lib.a endif @@ -6147,7 +6179,7 @@ $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a: $(ZLIB_DEP) $(PROTOBUF_DEP) $( $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a $(LIBBORINGSSL_SSL_TEST_LIB_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a $(LIBBORINGSSL_SSL_TEST_LIB_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libboringssl_ssl_test_lib.a endif @@ -6189,7 +6221,7 @@ $(LIBDIR)/$(CONFIG)/libz.a: $(LIBZ_OBJS) $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libz.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libz.a $(LIBZ_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libz.a $(LIBZ_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libz.a endif @@ -6224,7 +6256,7 @@ $(LIBDIR)/$(CONFIG)/libbad_client_test.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBBAD_CL $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libbad_client_test.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBBAD_CLIENT_TEST_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libbad_client_test.a $(LIBBAD_CLIENT_TEST_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libbad_client_test.a endif @@ -6263,7 +6295,7 @@ $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBBA $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBBAD_SSL_TEST_SERVER_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a $(LIBBAD_SSL_TEST_SERVER_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libbad_ssl_test_server.a endif @@ -6342,7 +6374,7 @@ $(LIBDIR)/$(CONFIG)/libend2end_tests.a: $(ZLIB_DEP) $(OPENSSL_DEP) $(LIBEND2END_ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_tests.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBEND2END_TESTS_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libend2end_tests.a $(LIBEND2END_TESTS_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libend2end_tests.a endif @@ -6410,7 +6442,7 @@ $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a: $(ZLIB_DEP) $(LIBEND2END_NOSEC_TE $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBEND2END_NOSEC_TESTS_OBJS) + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a $(LIBEND2END_NOSEC_TESTS_OBJS) ifeq ($(SYSTEM),Darwin) $(Q) ranlib -no_warning_for_no_symbols $(LIBDIR)/$(CONFIG)/libend2end_nosec_tests.a endif diff --git a/templates/Makefile.template b/templates/Makefile.template index 0cbd8bfdd55..cb33643dd2a 100644 --- a/templates/Makefile.template +++ b/templates/Makefile.template @@ -282,6 +282,29 @@ LDFLAGS += -pthread endif + # + # The steps for cross-compiling are as follows: + # First, clone and make install of grpc using the native compilers for the host. + # Also, install protoc (e.g., from a package like apt-get) + # Then clone a fresh grpc for the actual cross-compiled build + # Set the environment variable GRPC_CROSS_COMPILE to true + # Set CC, CXX, LD, LDXX, AR, and STRIP to the cross-compiling binaries + # Also set PROTOBUF_CONFIG_OPTS to indicate cross-compilation to protobuf (e.g., + # PROTOBUF_CONFIG_OPTS="--host=arm-linux --with-protoc=/usr/local/bin/protoc" ) + # Set HAS_PKG_CONFIG=false + # To build tests, go to third_party/gflags and follow its ccmake instructions + # Make sure that you enable building shared libraries and set your prefix to + # something useful like /usr/local/cross + # You will also need to set GRPC_CROSS_LDOPTS and GRPC_CROSS_AROPTS to hold + # additional required arguments for LD and AR (examples below) + # Then you can do a make from the cross-compiling fresh clone! + # + ifeq ($(GRPC_CROSS_COMPILE),true) + LDFLAGS += $(GRPC_CROSS_LDOPTS) # e.g. -L/usr/local/lib -L/usr/local/cross/lib + AROPTS = $(GRPC_CROSS_AROPTS) # e.g., rc --target=elf32-little + USE_BUILT_PROTOC = false + endif + GTEST_LIB = -Ithird_party/googletest/include -Ithird_party/googletest third_party/googletest/src/gtest-all.cc GTEST_LIB += -lgflags ifeq ($(V),1) @@ -597,6 +620,15 @@ CPPFLAGS := -Ithird_party/googletest/include $(CPPFLAGS) + PROTOC_PLUGINS_ALL =\ + % for tgt in targets: + % if tgt.build == 'protoc': + $(BINDIR)/$(CONFIG)/${tgt.name}\ + % endif + % endfor + + PROTOC_PLUGINS_DIR = $(BINDIR)/$(CONFIG) + ifeq ($(HAS_SYSTEM_PROTOBUF),true) ifeq ($(HAS_PKG_CONFIG),true) PROTOBUF_PKG_CONFIG = true @@ -610,13 +642,20 @@ endif else PC_LIBS_GRPCXX = -lprotobuf + PROTOC_PLUGINS = $(PROTOC_PLUGINS_ALL) endif else ifeq ($(HAS_EMBEDDED_PROTOBUF),true) PROTOBUF_DEP = $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a CPPFLAGS := -Ithird_party/protobuf/src $(CPPFLAGS) LDFLAGS := -L$(LIBDIR)/$(CONFIG)/protobuf $(LDFLAGS) + ifneq ($(USE_BUILT_PROTOC),false) PROTOC = $(BINDIR)/$(CONFIG)/protobuf/protoc + PROTOC_PLUGINS = $(PROTOC_PLUGINS_ALL) + else + PROTOC_PLUGINS = + PROTOC_PLUGINS_DIR = $(prefix)/bin + endif else NO_PROTOBUF = true endif @@ -664,13 +703,6 @@ .SECONDARY = %.pb.h %.pb.cc - PROTOC_PLUGINS =\ - % for tgt in targets: - % if tgt.build == 'protoc': - $(BINDIR)/$(CONFIG)/${tgt.name}\ - % endif - % endfor - ifeq ($(DEP_MISSING),) all: static shared plugins\ % for tgt in targets: @@ -792,7 +824,7 @@ $(LIBDIR)/$(CONFIG)/protobuf/libprotobuf.a: third_party/protobuf/configure $(E) "[MAKE] Building protobuf" - $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static) + $(Q)(cd third_party/protobuf ; CC="$(CC)" CXX="$(CXX)" LDFLAGS="$(LDFLAGS_$(CONFIG)) -g $(PROTOBUF_LDFLAGS_EXTRA)" CPPFLAGS="$(PIC_CPPFLAGS) $(CPPFLAGS_$(CONFIG)) -g $(PROTOBUF_CPPFLAGS_EXTRA)" ./configure --disable-shared --enable-static $(PROTOBUF_CONFIG_OPTS)) $(Q)$(MAKE) -C third_party/protobuf clean $(Q)$(MAKE) -C third_party/protobuf $(Q)mkdir -p $(LIBDIR)/$(CONFIG)/protobuf @@ -1124,7 +1156,7 @@ $(GENDIR)/${p}.grpc.pb.cc: ${p}.proto $(PROTOBUF_DEP) $(PROTOC_PLUGINS) ${' '.join('$(GENDIR)/%s.pb.cc $(GENDIR)/%s.grpc.pb.cc' % (q,q) for q in proto_deps.get(p, []))} $(E) "[GRPC] Generating gRPC's protobuf service CC file from $<" $(Q) mkdir -p `dirname $@` - $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(BINDIR)/$(CONFIG)/grpc_cpp_plugin $< + $(Q) $(PROTOC) -Ithird_party/protobuf/src -I. --grpc_out=$(GENDIR) --plugin=protoc-gen-grpc=$(PROTOC_PLUGINS_DIR)/grpc_cpp_plugin $< endif % endfor @@ -1416,7 +1448,7 @@ $(E) "[AR] Creating $@" $(Q) mkdir -p `dirname $@` $(Q) rm -f $(LIBDIR)/$(CONFIG)/lib${lib.name}.a - $(Q) $(AR) $(LIBDIR)/$(CONFIG)/lib${lib.name}.a $(LIB${lib.name.upper()}_OBJS) \ + $(Q) $(AR) $(AROPTS) $(LIBDIR)/$(CONFIG)/lib${lib.name}.a $(LIB${lib.name.upper()}_OBJS) \ % if lib.get('baselib', False): $(LIBGPR_OBJS) \ $(ZLIB_MERGE_OBJS) \ From a3730b75f8cf086f7eedc2f34ad6d928b69b28d0 Mon Sep 17 00:00:00 2001 From: thinkerou Date: Wed, 20 Jul 2016 16:59:54 +0800 Subject: [PATCH 25/30] support php7 by marco --- src/php/ext/grpc/call.c | 692 +++++++++++++----- src/php/ext/grpc/call.h | 36 +- src/php/ext/grpc/call_credentials.c | 103 ++- src/php/ext/grpc/call_credentials.h | 23 + src/php/ext/grpc/channel.c | 191 ++++- src/php/ext/grpc/channel.h | 23 +- src/php/ext/grpc/channel_credentials.c | 89 ++- src/php/ext/grpc/channel_credentials.h | 23 + src/php/ext/grpc/php_grpc.c | 54 +- src/php/ext/grpc/php_grpc.h | 4 +- src/php/ext/grpc/server.c | 136 +++- src/php/ext/grpc/server.h | 20 + src/php/ext/grpc/server_credentials.c | 64 +- src/php/ext/grpc/server_credentials.h | 20 + src/php/ext/grpc/timeval.c | 138 +++- src/php/ext/grpc/timeval.h | 23 + src/php/lib/Grpc/BaseStub.php | 4 +- src/php/lib/Grpc/BidiStreamingCall.php | 1 + src/php/lib/Grpc/ClientStreamingCall.php | 1 + src/php/lib/Grpc/ServerStreamingCall.php | 1 + src/php/lib/Grpc/UnaryCall.php | 1 + src/php/tests/unit_tests/CallTest.php | 47 ++ .../unit_tests/ChannelCredentialsTest.php | 24 +- src/php/tests/unit_tests/ChannelTest.php | 107 +++ src/php/tests/unit_tests/EndToEndTest.php | 0 .../tests/unit_tests/SecureEndToEndTest.php | 0 src/php/tests/unit_tests/ServerTest.php | 142 +++- src/php/tests/unit_tests/TimevalTest.php | 53 ++ 28 files changed, 1726 insertions(+), 294 deletions(-) mode change 100755 => 100644 src/php/lib/Grpc/BaseStub.php mode change 100755 => 100644 src/php/tests/unit_tests/CallTest.php mode change 100755 => 100644 src/php/tests/unit_tests/EndToEndTest.php mode change 100755 => 100644 src/php/tests/unit_tests/SecureEndToEndTest.php mode change 100755 => 100644 src/php/tests/unit_tests/TimevalTest.php diff --git a/src/php/ext/grpc/call.c b/src/php/ext/grpc/call.c index 2cd45f10dc4..bd1eccb01b4 100644 --- a/src/php/ext/grpc/call.c +++ b/src/php/ext/grpc/call.c @@ -59,12 +59,15 @@ zend_class_entry *grpc_ce_call; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_call */ void free_wrapped_grpc_call(void *object TSRMLS_DC) { wrapped_grpc_call *call = (wrapped_grpc_call *)object; if (call->owned && call->wrapped != NULL) { grpc_call_destroy(call->wrapped); } + zend_object_std_dtor(&call->std TSRMLS_CC); efree(call); } @@ -203,6 +206,131 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { return true; } +#else + +static zend_object_handlers call_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_call */ +static void free_wrapped_grpc_call(zend_object *object) { + wrapped_grpc_call *call = wrapped_grpc_call_from_obj(object); + if (call->owned && call->wrapped != NULL) { + grpc_call_destroy(call->wrapped); + } + zend_object_std_dtor(&call->std); +} + +/* Initializes an instance of wrapped_grpc_call to be associated with an + * object of a class specified by class_type */ +zend_object *create_wrapped_grpc_call(zend_class_entry *class_type) { + wrapped_grpc_call *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_call) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &call_ce_handlers; + return &intern->std; +} + +/* Wraps a grpc_call struct in a PHP object. Owned indicates whether the + struct should be destroyed at the end of the object's lifecycle */ +void grpc_php_wrap_call(grpc_call *wrapped, bool owned, zval *call_object) { + object_init_ex(call_object, grpc_ce_call); + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(call_object); + call->wrapped = wrapped; + call->owned = owned; +} + +/* Creates and returns a PHP array object with the data in a + * grpc_metadata_array. Returns NULL on failure */ +void grpc_parse_metadata_array(grpc_metadata_array *metadata_array, + zval *array) { + int count = metadata_array->count; + grpc_metadata *elements = metadata_array->metadata; + int i; + zval *data; + HashTable *array_hash; + zval inner_array; + char *str_key; + char *str_val; + size_t key_len; + + array_init(array); + array_hash = HASH_OF(array); + grpc_metadata *elem; + for (i = 0; i < count; i++) { + elem = &elements[i]; + key_len = strlen(elem->key); + str_key = ecalloc(key_len + 1, sizeof(char)); + memcpy(str_key, elem->key, key_len); + str_val = ecalloc(elem->value_length + 1, sizeof(char)); + memcpy(str_val, elem->value, elem->value_length); + if ((data = zend_hash_str_find(array_hash, str_key, key_len)) != NULL) { + if (Z_TYPE_P(data) != IS_ARRAY) { + zend_throw_exception(zend_exception_get_default(), + "Metadata hash somehow contains wrong types.", + 1); + efree(str_key); + efree(str_val); + return; + } + add_next_index_stringl(data, str_val, elem->value_length); + } else { + array_init(&inner_array); + add_next_index_stringl(&inner_array, str_val, elem->value_length); + add_assoc_zval(array, str_key, &inner_array); + } + } +} + +/* Populates a grpc_metadata_array with the data in a PHP array object. + Returns true on success and false on failure */ +bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { + zval *inner_array; + zval *value; + HashTable *array_hash; + HashTable *inner_array_hash; + zend_string *key; + if (Z_TYPE_P(array) != IS_ARRAY) { + return false; + } + grpc_metadata_array_init(metadata); + array_hash = HASH_OF(array); + + ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, inner_array) { + if (key == NULL) { + return false; + } + if (Z_TYPE_P(inner_array) != IS_ARRAY) { + return false; + } + inner_array_hash = HASH_OF(inner_array); + metadata->capacity += zend_hash_num_elements(inner_array_hash); + } + ZEND_HASH_FOREACH_END(); + + metadata->metadata = gpr_malloc(metadata->capacity * sizeof(grpc_metadata)); + + ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, inner_array) { + if (key == NULL) { + return false; + } + inner_array_hash = HASH_OF(inner_array); + + ZEND_HASH_FOREACH_VAL(inner_array_hash, value) { + if (Z_TYPE_P(value) != IS_STRING) { + return false; + } + metadata->metadata[metadata->count].key = ZSTR_VAL(key); + metadata->metadata[metadata->count].value = Z_STRVAL_P(value); + metadata->metadata[metadata->count].value_length = Z_STRLEN_P(value); + metadata->count += 1; + } ZEND_HASH_FOREACH_END(); + } ZEND_HASH_FOREACH_END(); + return true; +} + +#endif + /** * Constructs a new instance of the Call class. * @param Channel $channel The channel to associate the call with. Must not be @@ -211,30 +339,38 @@ bool create_metadata_array(zval *array, grpc_metadata_array *metadata) { * @param Timeval $absolute_deadline The deadline for completing the call */ PHP_METHOD(Call, __construct) { - wrapped_grpc_call *call = - (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); zval *channel_obj; char *method; - int method_len; zval *deadline_obj; char *host_override = NULL; +#if PHP_MAJOR_VERSION < 7 + int method_len; int host_override_len = 0; + wrapped_grpc_call *call = + (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + size_t method_len; + size_t host_override_len = 0; + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); +#endif + /* "OsO|s" == 1 Object, 1 string, 1 Object, 1 optional string */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OsO|s", - &channel_obj, grpc_ce_channel, - &method, &method_len, - &deadline_obj, grpc_ce_timeval, - &host_override, &host_override_len) - == FAILURE) { - zend_throw_exception( - spl_ce_InvalidArgumentException, - "Call expects a Channel, a String, a Timeval and an optional String", - 1 TSRMLS_CC); + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OsO|s", &channel_obj, + grpc_ce_channel, &method, &method_len, + &deadline_obj, grpc_ce_timeval, &host_override, + &host_override_len) == FAILURE) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Call expects a Channel, a String, a Timeval and " + "an optional String", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel *channel = (wrapped_grpc_channel *)zend_object_store_get_object( channel_obj TSRMLS_CC); +#else + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(channel_obj); +#endif if (channel->wrapped == NULL) { zend_throw_exception(spl_ce_InvalidArgumentException, "Call cannot be constructed from a closed Channel", @@ -242,12 +378,17 @@ PHP_METHOD(Call, __construct) { return; } add_property_zval(getThis(), "channel", channel_obj); +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *deadline = (wrapped_grpc_timeval *)zend_object_store_get_object( deadline_obj TSRMLS_CC); - call->wrapped = grpc_channel_create_call( - channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, completion_queue, method, - host_override, deadline->wrapped, NULL); +#else + wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj); +#endif + call->wrapped = + grpc_channel_create_call(channel->wrapped, NULL, GRPC_PROPAGATE_DEFAULTS, + completion_queue, method, host_override, + deadline->wrapped, NULL); call->owned = true; } @@ -257,22 +398,40 @@ PHP_METHOD(Call, __construct) { * @return object Object with results of all actions */ PHP_METHOD(Call, startBatch) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_call *call = (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); - grpc_op ops[8]; - size_t op_num = 0; - zval *array; zval **value; zval **inner_value; - HashTable *array_hash; HashPosition array_pointer; - HashTable *status_hash; - HashTable *message_hash; zval **message_value; zval **message_flags; char *key; uint key_len; ulong index; + zval *result; + zval *recv_status; + MAKE_STD_ZVAL(result); + object_init(result); +#else + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); + zval *value; + zval *inner_value; + zval *message_value; + zval *message_flags; + zend_string *key; + zend_ulong index; + zval recv_status; + object_init(return_value); +#endif + + grpc_op ops[8]; + size_t op_num = 0; + zval *array; + HashTable *array_hash; + HashTable *status_hash; + HashTable *message_hash; + grpc_metadata_array metadata; grpc_metadata_array trailing_metadata; grpc_metadata_array recv_metadata; @@ -283,17 +442,16 @@ PHP_METHOD(Call, startBatch) { grpc_byte_buffer *message; int cancelled; grpc_call_error error; - zval *result; char *message_str; size_t message_len; - zval *recv_status; + + grpc_metadata_array_init(&metadata); grpc_metadata_array_init(&trailing_metadata); grpc_metadata_array_init(&recv_metadata); grpc_metadata_array_init(&recv_trailing_metadata); - MAKE_STD_ZVAL(result); - object_init(result); memset(ops, 0, sizeof(ops)); + /* "a" == 1 array */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { @@ -301,6 +459,9 @@ PHP_METHOD(Call, startBatch) { "start_batch expects an array", 1 TSRMLS_CC); goto cleanup; } + +#if PHP_MAJOR_VERSION < 7 + array_hash = Z_ARRVAL_P(array); for (zend_hash_internal_pointer_reset_ex(array_hash, &array_pointer); zend_hash_get_current_data_ex(array_hash, (void**)&value, @@ -313,124 +474,250 @@ PHP_METHOD(Call, startBatch) { goto cleanup; } switch(index) { - case GRPC_OP_SEND_INITIAL_METADATA: - if (!create_metadata_array(*value, &metadata)) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!create_metadata_array(*value, &metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Bad metadata value given", 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_initial_metadata.count = + metadata.count; + ops[op_num].data.send_initial_metadata.metadata = + metadata.metadata; + break; + case GRPC_OP_SEND_MESSAGE: + if (Z_TYPE_PP(value) != IS_ARRAY) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an array for send message", + 1 TSRMLS_CC); + goto cleanup; + } + message_hash = Z_ARRVAL_PP(value); + if (zend_hash_find(message_hash, "flags", sizeof("flags"), + (void **)&message_flags) == SUCCESS) { + if (Z_TYPE_PP(message_flags) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Bad metadata value given", 1 TSRMLS_CC); - goto cleanup; + "Expected an int for message flags", + 1 TSRMLS_CC); } - ops[op_num].data.send_initial_metadata.count = - metadata.count; - ops[op_num].data.send_initial_metadata.metadata = - metadata.metadata; - break; - case GRPC_OP_SEND_MESSAGE: - if (Z_TYPE_PP(value) != IS_ARRAY) { + ops[op_num].flags = Z_LVAL_PP(message_flags) & GRPC_WRITE_USED_MASK; + } + if (zend_hash_find(message_hash, "message", sizeof("message"), + (void **)&message_value) != SUCCESS || + Z_TYPE_PP(message_value) != IS_STRING) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected a string for send message", + 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].data.send_message = + string_to_byte_buffer(Z_STRVAL_PP(message_value), + Z_STRLEN_PP(message_value)); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + status_hash = Z_ARRVAL_PP(value); + if (zend_hash_find(status_hash, "metadata", sizeof("metadata"), + (void **)&inner_value) == SUCCESS) { + if (!create_metadata_array(*inner_value, &trailing_metadata)) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Expected an array for send message", + "Bad trailing metadata value given", 1 TSRMLS_CC); goto cleanup; } - message_hash = Z_ARRVAL_PP(value); - if (zend_hash_find(message_hash, "flags", sizeof("flags"), - (void **)&message_flags) == SUCCESS) { - if (Z_TYPE_PP(message_flags) != IS_LONG) { - zend_throw_exception(spl_ce_InvalidArgumentException, - "Expected an int for message flags", - 1 TSRMLS_CC); - } - ops[op_num].flags = Z_LVAL_PP(message_flags) & GRPC_WRITE_USED_MASK; + ops[op_num].data.send_status_from_server.trailing_metadata = + trailing_metadata.metadata; + ops[op_num].data.send_status_from_server.trailing_metadata_count = + trailing_metadata.count; + } + if (zend_hash_find(status_hash, "code", sizeof("code"), + (void**)&inner_value) == SUCCESS) { + if (Z_TYPE_PP(inner_value) != IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Status code must be an integer", + 1 TSRMLS_CC); + goto cleanup; } - if (zend_hash_find(message_hash, "message", sizeof("message"), - (void **)&message_value) != SUCCESS || - Z_TYPE_PP(message_value) != IS_STRING) { + ops[op_num].data.send_status_from_server.status = + Z_LVAL_PP(inner_value); + } else { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Integer status code is required", + 1 TSRMLS_CC); + goto cleanup; + } + if (zend_hash_find(status_hash, "details", sizeof("details"), + (void**)&inner_value) == SUCCESS) { + if (Z_TYPE_PP(inner_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Expected a string for send message", + "Status details must be a string", 1 TSRMLS_CC); goto cleanup; } - ops[op_num].data.send_message = - string_to_byte_buffer(Z_STRVAL_PP(message_value), - Z_STRLEN_PP(message_value)); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - status_hash = Z_ARRVAL_PP(value); - if (zend_hash_find(status_hash, "metadata", sizeof("metadata"), - (void **)&inner_value) == SUCCESS) { - if (!create_metadata_array(*inner_value, &trailing_metadata)) { - zend_throw_exception(spl_ce_InvalidArgumentException, - "Bad trailing metadata value given", - 1 TSRMLS_CC); - goto cleanup; - } - ops[op_num].data.send_status_from_server.trailing_metadata = - trailing_metadata.metadata; - ops[op_num].data.send_status_from_server.trailing_metadata_count = - trailing_metadata.count; + ops[op_num].data.send_status_from_server.status_details = + Z_STRVAL_PP(inner_value); + } else { + zend_throw_exception(spl_ce_InvalidArgumentException, + "String status details is required", + 1 TSRMLS_CC); + goto cleanup; + } + break; + case GRPC_OP_RECV_INITIAL_METADATA: + ops[op_num].data.recv_initial_metadata = &recv_metadata; + break; + case GRPC_OP_RECV_MESSAGE: + ops[op_num].data.recv_message = &message; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + ops[op_num].data.recv_status_on_client.trailing_metadata = + &recv_trailing_metadata; + ops[op_num].data.recv_status_on_client.status = &status; + ops[op_num].data.recv_status_on_client.status_details = + &status_details; + ops[op_num].data.recv_status_on_client.status_details_capacity = + &status_details_capacity; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + ops[op_num].data.recv_close_on_server.cancelled = &cancelled; + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "Unrecognized key in batch", 1 TSRMLS_CC); + goto cleanup; + } + ops[op_num].op = (grpc_op_type)index; + ops[op_num].flags = 0; + ops[op_num].reserved = NULL; + op_num++; + } + +#else + +array_hash = HASH_OF(array); + ZEND_HASH_FOREACH_KEY_VAL(array_hash, index, key, value) { + if (key) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "batch keys must be integers", 1); + goto cleanup; + } + + switch(index) { + case GRPC_OP_SEND_INITIAL_METADATA: + if (!create_metadata_array(value, &metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Bad metadata value given", 1); + goto cleanup; + } + ops[op_num].data.send_initial_metadata.count = metadata.count; + ops[op_num].data.send_initial_metadata.metadata = metadata.metadata; + break; + case GRPC_OP_SEND_MESSAGE: + if (Z_TYPE_P(value) != IS_ARRAY) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an array for send message", 1); + goto cleanup; + } + message_hash = HASH_OF(value); + if ((message_flags = + zend_hash_str_find(message_hash, "flags", + sizeof("flags") - 1)) != NULL) { + if (Z_TYPE_P(message_flags) != IS_LONG) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected an int for message flags", 1); + } + ops[op_num].flags = Z_LVAL_P(message_flags) & GRPC_WRITE_USED_MASK; + } + if ((message_value = zend_hash_str_find(message_hash, "message", + sizeof("message") - 1)) + == NULL || Z_TYPE_P(message_value) != IS_STRING) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Expected a string for send message", 1); + goto cleanup; + } + ops[op_num].data.send_message = + string_to_byte_buffer(Z_STRVAL_P(message_value), + Z_STRLEN_P(message_value)); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + status_hash = HASH_OF(value); + if ((inner_value = zend_hash_str_find(status_hash, "metadata", + sizeof("metadata") - 1)) + != NULL) { + if (!create_metadata_array(inner_value, &trailing_metadata)) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Bad trailing metadata value given", 1); + goto cleanup; } - if (zend_hash_find(status_hash, "code", sizeof("code"), - (void**)&inner_value) == SUCCESS) { - if (Z_TYPE_PP(inner_value) != IS_LONG) { - zend_throw_exception(spl_ce_InvalidArgumentException, - "Status code must be an integer", - 1 TSRMLS_CC); - goto cleanup; - } - ops[op_num].data.send_status_from_server.status = - Z_LVAL_PP(inner_value); - } else { + ops[op_num].data.send_status_from_server.trailing_metadata = + trailing_metadata.metadata; + ops[op_num].data.send_status_from_server.trailing_metadata_count = + trailing_metadata.count; + } + if ((inner_value = zend_hash_str_find(status_hash, "code", + sizeof("code") - 1)) != NULL) { + if (Z_TYPE_P(inner_value) != IS_LONG) { zend_throw_exception(spl_ce_InvalidArgumentException, - "Integer status code is required", - 1 TSRMLS_CC); + "Status code must be an integer", 1); goto cleanup; } - if (zend_hash_find(status_hash, "details", sizeof("details"), - (void**)&inner_value) == SUCCESS) { - if (Z_TYPE_PP(inner_value) != IS_STRING) { - zend_throw_exception(spl_ce_InvalidArgumentException, - "Status details must be a string", - 1 TSRMLS_CC); - goto cleanup; - } - ops[op_num].data.send_status_from_server.status_details = - Z_STRVAL_PP(inner_value); - } else { + ops[op_num].data.send_status_from_server.status = + Z_LVAL_P(inner_value); + } else { + zend_throw_exception(spl_ce_InvalidArgumentException, + "Integer status code is required", 1); + goto cleanup; + } + if ((inner_value = zend_hash_str_find(status_hash, "details", + sizeof("details") - 1)) != NULL) { + if (Z_TYPE_P(inner_value) != IS_STRING) { zend_throw_exception(spl_ce_InvalidArgumentException, - "String status details is required", - 1 TSRMLS_CC); + "Status details must be a string", 1); goto cleanup; } - break; - case GRPC_OP_RECV_INITIAL_METADATA: - ops[op_num].data.recv_initial_metadata = &recv_metadata; - break; - case GRPC_OP_RECV_MESSAGE: - ops[op_num].data.recv_message = &message; - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - ops[op_num].data.recv_status_on_client.trailing_metadata = - &recv_trailing_metadata; - ops[op_num].data.recv_status_on_client.status = &status; - ops[op_num].data.recv_status_on_client.status_details = - &status_details; - ops[op_num].data.recv_status_on_client.status_details_capacity = - &status_details_capacity; - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - ops[op_num].data.recv_close_on_server.cancelled = &cancelled; - break; - default: + ops[op_num].data.send_status_from_server.status_details = + Z_STRVAL_P(inner_value); + } else { zend_throw_exception(spl_ce_InvalidArgumentException, - "Unrecognized key in batch", 1 TSRMLS_CC); + "String status details is required", 1); goto cleanup; + } + break; + case GRPC_OP_RECV_INITIAL_METADATA: + ops[op_num].data.recv_initial_metadata = &recv_metadata; + break; + case GRPC_OP_RECV_MESSAGE: + ops[op_num].data.recv_message = &message; + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + ops[op_num].data.recv_status_on_client.trailing_metadata = + &recv_trailing_metadata; + ops[op_num].data.recv_status_on_client.status = &status; + ops[op_num].data.recv_status_on_client.status_details = + &status_details; + ops[op_num].data.recv_status_on_client.status_details_capacity = + &status_details_capacity; + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + ops[op_num].data.recv_close_on_server.cancelled = &cancelled; + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "Unrecognized key in batch", 1); + goto cleanup; } ops[op_num].op = (grpc_op_type)index; ops[op_num].flags = 0; ops[op_num].reserved = NULL; op_num++; } + ZEND_HASH_FOREACH_END(); + +#endif + error = grpc_call_start_batch(call->wrapped, ops, op_num, call->wrapped, NULL); if (error != GRPC_CALL_OK) { @@ -441,52 +728,98 @@ PHP_METHOD(Call, startBatch) { } grpc_completion_queue_pluck(completion_queue, call->wrapped, gpr_inf_future(GPR_CLOCK_REALTIME), NULL); +#if PHP_MAJOR_VERSION < 7 for (int i = 0; i < op_num; i++) { switch(ops[i].op) { - case GRPC_OP_SEND_INITIAL_METADATA: - add_property_bool(result, "send_metadata", true); - break; - case GRPC_OP_SEND_MESSAGE: - add_property_bool(result, "send_message", true); - break; - case GRPC_OP_SEND_CLOSE_FROM_CLIENT: - add_property_bool(result, "send_close", true); - break; - case GRPC_OP_SEND_STATUS_FROM_SERVER: - add_property_bool(result, "send_status", true); - break; - case GRPC_OP_RECV_INITIAL_METADATA: - array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC); - add_property_zval(result, "metadata", array); - Z_DELREF_P(array); - break; - case GRPC_OP_RECV_MESSAGE: - byte_buffer_to_string(message, &message_str, &message_len); - if (message_str == NULL) { - add_property_null(result, "message"); - } else { - add_property_stringl(result, "message", message_str, message_len, - false); - } - break; - case GRPC_OP_RECV_STATUS_ON_CLIENT: - MAKE_STD_ZVAL(recv_status); - object_init(recv_status); - array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC); - add_property_zval(recv_status, "metadata", array); - Z_DELREF_P(array); - add_property_long(recv_status, "code", status); - add_property_string(recv_status, "details", status_details, true); - add_property_zval(result, "status", recv_status); - Z_DELREF_P(recv_status); - break; - case GRPC_OP_RECV_CLOSE_ON_SERVER: - add_property_bool(result, "cancelled", cancelled); - break; - default: - break; + case GRPC_OP_SEND_INITIAL_METADATA: + add_property_bool(result, "send_metadata", true); + break; + case GRPC_OP_SEND_MESSAGE: + add_property_bool(result, "send_message", true); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + add_property_bool(result, "send_close", true); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + add_property_bool(result, "send_status", true); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + array = grpc_parse_metadata_array(&recv_metadata TSRMLS_CC); + add_property_zval(result, "metadata", array); + Z_DELREF_P(array); + break; + case GRPC_OP_RECV_MESSAGE: + byte_buffer_to_string(message, &message_str, &message_len); + if (message_str == NULL) { + add_property_null(result, "message"); + } else { + add_property_stringl(result, "message", message_str, message_len, + false); + } + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + MAKE_STD_ZVAL(recv_status); + object_init(recv_status); + array = grpc_parse_metadata_array(&recv_trailing_metadata TSRMLS_CC); + add_property_zval(recv_status, "metadata", array); + Z_DELREF_P(array); + add_property_long(recv_status, "code", status); + add_property_string(recv_status, "details", status_details, true); + add_property_zval(result, "status", recv_status); + Z_DELREF_P(recv_status); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + add_property_bool(result, "cancelled", cancelled); + break; + default: + break; + } + } +#else + for (int i = 0; i < op_num; i++) { + switch(ops[i].op) { + case GRPC_OP_SEND_INITIAL_METADATA: + add_property_bool(return_value, "send_metadata", true); + break; + case GRPC_OP_SEND_MESSAGE: + add_property_bool(return_value, "send_message", true); + break; + case GRPC_OP_SEND_CLOSE_FROM_CLIENT: + add_property_bool(return_value, "send_close", true); + break; + case GRPC_OP_SEND_STATUS_FROM_SERVER: + add_property_bool(return_value, "send_status", true); + break; + case GRPC_OP_RECV_INITIAL_METADATA: + grpc_parse_metadata_array(&recv_metadata, array); + add_property_zval(return_value, "metadata", array); + break; + case GRPC_OP_RECV_MESSAGE: + byte_buffer_to_string(message, &message_str, &message_len); + if (message_str == NULL) { + add_property_null(return_value, "message"); + } else { + add_property_stringl(return_value, "message", message_str, + message_len); + } + break; + case GRPC_OP_RECV_STATUS_ON_CLIENT: + object_init(&recv_status); + grpc_parse_metadata_array(&recv_trailing_metadata, array); + add_property_zval(&recv_status, "metadata", array); + add_property_long(&recv_status, "code", status); + add_property_string(&recv_status, "details", status_details); + add_property_zval(return_value, "status", &recv_status); + break; + case GRPC_OP_RECV_CLOSE_ON_SERVER: + add_property_bool(return_value, "cancelled", cancelled); + break; + default: + break; } } +#endif + cleanup: grpc_metadata_array_destroy(&metadata); grpc_metadata_array_destroy(&trailing_metadata); @@ -503,7 +836,11 @@ cleanup: grpc_byte_buffer_destroy(message); } } +#if PHP_MAJOR_VERSION < 7 RETURN_DESTROY_ZVAL(result); +#else + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -511,9 +848,14 @@ cleanup: * @return string The URI of the endpoint */ PHP_METHOD(Call, getPeer) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_call *call = (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); RETURN_STRING(grpc_call_get_peer(call->wrapped), 1); +#else + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); + RETURN_STRING(grpc_call_get_peer(call->wrapped)); +#endif } /** @@ -521,8 +863,12 @@ PHP_METHOD(Call, getPeer) { * has not already ended with another status. */ PHP_METHOD(Call, cancel) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_call *call = (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); +#endif grpc_call_cancel(call->wrapped, NULL); } @@ -543,12 +889,17 @@ PHP_METHOD(Call, setCredentials) { return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_call_credentials *creds = (wrapped_grpc_call_credentials *)zend_object_store_get_object( creds_obj TSRMLS_CC); - wrapped_grpc_call *call = (wrapped_grpc_call *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_call_credentials *creds = + Z_WRAPPED_GRPC_CALL_CREDS_P(creds_obj); + wrapped_grpc_call *call = Z_WRAPPED_GRPC_CALL_P(getThis()); +#endif grpc_call_error error = GRPC_CALL_ERROR; error = grpc_call_set_credentials(call->wrapped, creds->wrapped); @@ -556,16 +907,23 @@ PHP_METHOD(Call, setCredentials) { } static zend_function_entry call_methods[] = { - PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Call, startBatch, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, getPeer, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Call, setCredentials, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END}; + PHP_ME(Call, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Call, startBatch, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, getPeer, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, cancel, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Call, setCredentials, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END +}; void grpc_init_call(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Call", call_methods); ce.create_object = create_wrapped_grpc_call; grpc_ce_call = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&call_ce_handlers, zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + call_ce_handlers.offset = XtOffsetOf(wrapped_grpc_call, std); + call_ce_handlers.free_obj = free_wrapped_grpc_call; +#endif } diff --git a/src/php/ext/grpc/call.h b/src/php/ext/grpc/call.h index 36c5f2d2724..9fc52d78209 100644 --- a/src/php/ext/grpc/call.h +++ b/src/php/ext/grpc/call.h @@ -48,17 +48,15 @@ /* Class entry for the Call PHP class */ extern zend_class_entry *grpc_ce_call; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_call that can be associated with a PHP object */ typedef struct wrapped_grpc_call { zend_object std; - bool owned; grpc_call *wrapped; } wrapped_grpc_call; -/* Initializes the Call PHP class */ -void grpc_init_call(TSRMLS_D); - /* Creates a Call object that wraps the given grpc_call struct */ zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC); @@ -66,6 +64,36 @@ zval *grpc_php_wrap_call(grpc_call *wrapped, bool owned TSRMLS_DC); * call metadata */ zval *grpc_parse_metadata_array(grpc_metadata_array *metadata_array TSRMLS_DC); +#else + +/* Wrapper struct for grpc_call that can be associated with a PHP object */ +typedef struct wrapped_grpc_call { + bool owned; + grpc_call *wrapped; + zend_object std; +} wrapped_grpc_call; + +static inline wrapped_grpc_call +*wrapped_grpc_call_from_obj(zend_object *obj) { + return (wrapped_grpc_call*)((char*)(obj) - + XtOffsetOf(wrapped_grpc_call, std)); +} + +#define Z_WRAPPED_GRPC_CALL_P(zv) wrapped_grpc_call_from_obj(Z_OBJ_P((zv))) + +/* Creates a Call object that wraps the given grpc_call struct */ +void grpc_php_wrap_call(grpc_call *wrapped, bool owned, zval *call_object); + +/* Creates and returns a PHP associative array of metadata from a C array of + * call metadata */ +void grpc_parse_metadata_array(grpc_metadata_array *metadata_array, + zval *array); + +#endif /* PHP_MAJOR_VERSION */ + +/* Initializes the Call PHP class */ +void grpc_init_call(TSRMLS_D); + /* Populates a grpc_metadata_array with the data in a PHP array object. Returns true on success and false on failure */ bool create_metadata_array(zval *array, grpc_metadata_array *metadata); diff --git a/src/php/ext/grpc/call_credentials.c b/src/php/ext/grpc/call_credentials.c index ec0e6b91813..70aaffb848a 100644 --- a/src/php/ext/grpc/call_credentials.c +++ b/src/php/ext/grpc/call_credentials.c @@ -53,6 +53,8 @@ zend_class_entry *grpc_ce_call_credentials; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_call_credentials */ void free_wrapped_grpc_call_credentials(void *object TSRMLS_DC) { wrapped_grpc_call_credentials *creds = @@ -60,6 +62,7 @@ void free_wrapped_grpc_call_credentials(void *object TSRMLS_DC) { if (creds->wrapped != NULL) { grpc_call_credentials_release(creds->wrapped); } + zend_object_std_dtor(&creds->std TSRMLS_CC); efree(creds); } @@ -94,6 +97,43 @@ zval *grpc_php_wrap_call_credentials(grpc_call_credentials *wrapped TSRMLS_DC) { return credentials_object; } +#else + +static zend_object_handlers call_credentials_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_call_credentials */ +static void free_wrapped_grpc_call_credentials(zend_object *object) { + wrapped_grpc_call_credentials *creds = + wrapped_grpc_call_creds_from_obj(object); + if (creds->wrapped != NULL) { + grpc_call_credentials_release(creds->wrapped); + } + zend_object_std_dtor(&creds->std); +} + +/* Initializes an instance of wrapped_grpc_call_credentials to be + * associated with an object of a class specified by class_type */ +zend_object *create_wrapped_grpc_call_credentials(zend_class_entry + *class_type) { + wrapped_grpc_call_credentials *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_call_credentials) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &call_credentials_ce_handlers; + return &intern->std; +} + +void grpc_php_wrap_call_credentials(grpc_call_credentials *wrapped, + zval *credentials_object) { + object_init_ex(credentials_object, grpc_ce_call_credentials); + wrapped_grpc_call_credentials *credentials = + Z_WRAPPED_GRPC_CALL_CREDS_P(credentials_object); + credentials->wrapped = wrapped; +} + +#endif + /** * Create composite credentials from two existing credentials. * @param CallCredentials cred1 The first credential @@ -113,6 +153,7 @@ PHP_METHOD(CallCredentials, createComposite) { 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_call_credentials *cred1 = (wrapped_grpc_call_credentials *)zend_object_store_get_object( cred1_obj TSRMLS_CC); @@ -124,6 +165,17 @@ PHP_METHOD(CallCredentials, createComposite) { NULL); zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + wrapped_grpc_call_credentials *cred1 = + Z_WRAPPED_GRPC_CALL_CREDS_P(cred1_obj); + wrapped_grpc_call_credentials *cred2 = + Z_WRAPPED_GRPC_CALL_CREDS_P(cred2_obj); + grpc_call_credentials *creds = + grpc_composite_call_credentials_create(cred1->wrapped, + cred2->wrapped, NULL); + grpc_php_wrap_call_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -141,13 +193,10 @@ PHP_METHOD(CallCredentials, createFromPlugin) { memset(fci_cache, 0, sizeof(zend_fcall_info_cache)); /* "f" == 1 function */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f", fci, - fci_cache, - fci->params, - fci->param_count) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f*", fci, fci_cache, + fci->params, fci->param_count) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, - "createFromPlugin expects 1 callback", - 1 TSRMLS_CC); + "createFromPlugin expects 1 callback", 1 TSRMLS_CC); return; } @@ -165,10 +214,15 @@ PHP_METHOD(CallCredentials, createFromPlugin) { plugin.state = (void *)state; plugin.type = ""; - grpc_call_credentials *creds = grpc_metadata_credentials_create_from_plugin( - plugin, NULL); + grpc_call_credentials *creds = + grpc_metadata_credentials_create_from_plugin(plugin, NULL); +#if PHP_MAJOR_VERSION < 7 zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + grpc_php_wrap_call_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /* Callback function for plugin creds API */ @@ -181,6 +235,7 @@ void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context, /* prepare to call the user callback function with info from the * grpc_auth_metadata_context */ +#if PHP_MAJOR_VERSION < 7 zval **params[1]; zval *arg; zval *retval; @@ -192,21 +247,41 @@ void plugin_get_metadata(void *ptr, grpc_auth_metadata_context context, state->fci->param_count = 1; state->fci->params = params; state->fci->retval_ptr_ptr = &retval; +#else + zval arg; + zval retval; + object_init(&arg); + add_property_string(&arg, "service_url", context.service_url); + add_property_string(&arg, "method_name", context.method_name); + state->fci->param_count = 1; + state->fci->params = &arg; + state->fci->retval = &retval; +#endif /* call the user callback function */ zend_call_function(state->fci, state->fci_cache TSRMLS_CC); +#if PHP_MAJOR_VERSION < 7 if (Z_TYPE_P(retval) != IS_ARRAY) { +#else + if (Z_TYPE_P(&retval) != IS_ARRAY) { +#endif zend_throw_exception(spl_ce_InvalidArgumentException, "plugin callback must return metadata array", 1 TSRMLS_CC); + return; } grpc_metadata_array metadata; +#if PHP_MAJOR_VERSION < 7 if (!create_metadata_array(retval, &metadata)) { +#else + if (!create_metadata_array(&retval, &metadata)) { +#endif zend_throw_exception(spl_ce_InvalidArgumentException, "invalid metadata", 1 TSRMLS_CC); grpc_metadata_array_destroy(&metadata); + return; } /* TODO: handle error */ @@ -229,11 +304,21 @@ static zend_function_entry call_credentials_methods[] = { ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(CallCredentials, createFromPlugin, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_FE_END}; + PHP_FE_END +}; void grpc_init_call_credentials(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\CallCredentials", call_credentials_methods); ce.create_object = create_wrapped_grpc_call_credentials; grpc_ce_call_credentials = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&call_credentials_ce_handlers, + zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + call_credentials_ce_handlers.offset = + XtOffsetOf(wrapped_grpc_call_credentials, std); + call_credentials_ce_handlers.free_obj = + free_wrapped_grpc_call_credentials; +#endif } diff --git a/src/php/ext/grpc/call_credentials.h b/src/php/ext/grpc/call_credentials.h index d2f6a92449b..e05e14698b2 100755 --- a/src/php/ext/grpc/call_credentials.h +++ b/src/php/ext/grpc/call_credentials.h @@ -49,14 +49,37 @@ /* Class entry for the CallCredentials PHP class */ extern zend_class_entry *grpc_ce_call_credentials; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_call_credentials that can be associated * with a PHP object */ typedef struct wrapped_grpc_call_credentials { zend_object std; + grpc_call_credentials *wrapped; +} wrapped_grpc_call_credentials; +#else + +/* Wrapper struct for grpc_call_credentials that can be associated + * with a PHP object */ +typedef struct wrapped_grpc_call_credentials { grpc_call_credentials *wrapped; + zend_object std; } wrapped_grpc_call_credentials; +static inline wrapped_grpc_call_credentials +*wrapped_grpc_call_creds_from_obj(zend_object *obj) { + return + (wrapped_grpc_call_credentials*)((char*)(obj) - + XtOffsetOf(wrapped_grpc_call_credentials, + std)); +} + +#define Z_WRAPPED_GRPC_CALL_CREDS_P(zv) \ + wrapped_grpc_call_creds_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Struct to hold callback function for plugin creds API */ typedef struct plugin_state { zend_fcall_info *fci; diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index 8d94c59683e..ce96457c7c3 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -57,12 +57,15 @@ zend_class_entry *grpc_ce_channel; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_channel */ void free_wrapped_grpc_channel(void *object TSRMLS_DC) { wrapped_grpc_channel *channel = (wrapped_grpc_channel *)object; if (channel->wrapped != NULL) { grpc_channel_destroy(channel->wrapped); } + zend_object_std_dtor(&channel->std TSRMLS_CC); efree(channel); } @@ -107,23 +110,88 @@ void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args TSRMLS_D } args->args[args_index].key = key; switch (Z_TYPE_P(*data)) { - case IS_LONG: - args->args[args_index].value.integer = (int)Z_LVAL_P(*data); - args->args[args_index].type = GRPC_ARG_INTEGER; - break; - case IS_STRING: - args->args[args_index].value.string = Z_STRVAL_P(*data); - args->args[args_index].type = GRPC_ARG_STRING; - break; - default: - zend_throw_exception(spl_ce_InvalidArgumentException, - "args values must be int or string", 1 TSRMLS_CC); - return; + case IS_LONG: + args->args[args_index].value.integer = (int)Z_LVAL_P(*data); + args->args[args_index].type = GRPC_ARG_INTEGER; + break; + case IS_STRING: + args->args[args_index].value.string = Z_STRVAL_P(*data); + args->args[args_index].type = GRPC_ARG_STRING; + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "args values must be int or string", 1 TSRMLS_CC); + return; } args_index++; } } +#else + +static zend_object_handlers channel_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_channel */ +static void free_wrapped_grpc_channel(zend_object *object) { + wrapped_grpc_channel *channel = wrapped_grpc_channel_from_obj(object); + if (channel->wrapped != NULL) { + grpc_channel_destroy(channel->wrapped); + } + zend_object_std_dtor(&channel->std); +} + +/* Initializes an instance of wrapped_grpc_channel to be associated with an + * object of a class specified by class_type */ +zend_object *create_wrapped_grpc_channel(zend_class_entry *class_type) { + wrapped_grpc_channel *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_channel) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &channel_ce_handlers; + return &intern->std; +} + +void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args) { + HashTable *array_hash; + int args_index; + zval *data; + zend_string *key; + array_hash = HASH_OF(args_array); + if (!array_hash) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "array_hash is NULL", 1); + return; + } + args->num_args = zend_hash_num_elements(array_hash); + args->args = ecalloc(args->num_args, sizeof(grpc_arg)); + args_index = 0; + ZEND_HASH_FOREACH_STR_KEY_VAL(array_hash, key, data) { + if (key == NULL) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "args keys must be strings", 1); + } + args->args[args_index].key = ZSTR_VAL(key); + switch (Z_TYPE_P(data)) { + case IS_LONG: + args->args[args_index].value.integer = (int)Z_LVAL_P(data); + args->args[args_index].type = GRPC_ARG_INTEGER; + break; + case IS_STRING: + args->args[args_index].value.string = Z_STRVAL_P(data); + args->args[args_index].type = GRPC_ARG_STRING; + break; + default: + zend_throw_exception(spl_ce_InvalidArgumentException, + "args values must be int or string", 1); + return; + } + args_index++; + } ZEND_HASH_FOREACH_END(); +} + +#endif + /** * Construct an instance of the Channel class. If the $args array contains a * "credentials" key mapping to a ChannelCredentials object, a secure channel @@ -132,16 +200,23 @@ void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args TSRMLS_D * @param array $args The arguments to pass to the Channel (optional) */ PHP_METHOD(Channel, __construct) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel *channel = (wrapped_grpc_channel *)zend_object_store_get_object( getThis() TSRMLS_CC); - char *target; + zval **creds_obj = NULL; int target_length; +#else + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); + zval *creds_obj = NULL; + size_t target_length; +#endif + char *target; zval *args_array = NULL; grpc_channel_args args; HashTable *array_hash; - zval **creds_obj = NULL; wrapped_grpc_channel_credentials *creds = NULL; + /* "sa" == 1 string, 1 array */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa", &target, &target_length, &args_array) == FAILURE) { @@ -149,6 +224,7 @@ PHP_METHOD(Channel, __construct) { "Channel expects a string and an array", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 array_hash = Z_ARRVAL_P(args_array); if (zend_hash_find(array_hash, "credentials", sizeof("credentials"), (void **)&creds_obj) == SUCCESS) { @@ -167,6 +243,24 @@ PHP_METHOD(Channel, __construct) { zend_hash_del(array_hash, "credentials", 12); } } +#else + array_hash = HASH_OF(args_array); + if ((creds_obj = zend_hash_str_find(array_hash, "credentials", + sizeof("credentials") - 1)) != NULL) { + if (Z_TYPE_P(creds_obj) == IS_NULL) { + creds = NULL; + zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1); + } else if (Z_OBJ_P(creds_obj)->ce != grpc_ce_channel_credentials) { + zend_throw_exception(spl_ce_InvalidArgumentException, + "credentials must be a ChannelCredentials object", + 1); + return; + } else { + creds = Z_WRAPPED_GRPC_CHANNEL_CREDS_P(creds_obj); + zend_hash_str_del(array_hash, "credentials", sizeof("credentials") - 1); + } + } +#endif php_grpc_read_args_array(args_array, &args TSRMLS_CC); if (creds == NULL) { channel->wrapped = grpc_insecure_channel_create(target, &args, NULL); @@ -182,9 +276,14 @@ PHP_METHOD(Channel, __construct) { * @return string The URI of the endpoint */ PHP_METHOD(Channel, getTarget) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel *channel = - (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); RETURN_STRING(grpc_channel_get_target(channel->wrapped), 1); +#else + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); + RETURN_STRING(grpc_channel_get_target(channel->wrapped)); +#endif } /** @@ -193,12 +292,17 @@ PHP_METHOD(Channel, getTarget) { * @return long The grpc connectivity state */ PHP_METHOD(Channel, getConnectivityState) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel *channel = (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); - bool try_to_connect; +#else + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); +#endif + bool try_to_connect = false; + /* "|b" == 1 optional bool */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &try_to_connect) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &try_to_connect) + == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "getConnectivityState expects a bool", 1 TSRMLS_CC); return; @@ -215,10 +319,16 @@ PHP_METHOD(Channel, getConnectivityState) { * before deadline */ PHP_METHOD(Channel, watchConnectivityState) { +#if PHP_MAJOR_VERSION < 7 + long last_state; wrapped_grpc_channel *channel = (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); - long last_state; +#else + zend_long last_state; + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); +#endif zval *deadline_obj; + /* "lO" == 1 long 1 object */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lO", &last_state, &deadline_obj, grpc_ce_timeval) == FAILURE) { @@ -228,15 +338,20 @@ PHP_METHOD(Channel, watchConnectivityState) { return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *deadline = (wrapped_grpc_timeval *)zend_object_store_get_object( deadline_obj TSRMLS_CC); - grpc_channel_watch_connectivity_state( - channel->wrapped, (grpc_connectivity_state)last_state, - deadline->wrapped, completion_queue, NULL); - grpc_event event = grpc_completion_queue_pluck( - completion_queue, NULL, - gpr_inf_future(GPR_CLOCK_REALTIME), NULL); +#else + wrapped_grpc_timeval *deadline = Z_WRAPPED_GRPC_TIMEVAL_P(deadline_obj); +#endif + grpc_channel_watch_connectivity_state(channel->wrapped, + (grpc_connectivity_state)last_state, + deadline->wrapped, completion_queue, + NULL); + grpc_event event = + grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); RETURN_BOOL(event.success); } @@ -244,8 +359,12 @@ PHP_METHOD(Channel, watchConnectivityState) { * Close the channel */ PHP_METHOD(Channel, close) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel *channel = - (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); + (wrapped_grpc_channel *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_channel *channel = Z_WRAPPED_GRPC_CHANNEL_P(getThis()); +#endif if (channel->wrapped != NULL) { grpc_channel_destroy(channel->wrapped); channel->wrapped = NULL; @@ -253,16 +372,24 @@ PHP_METHOD(Channel, close) { } static zend_function_entry channel_methods[] = { - PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Channel, getTarget, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Channel, getConnectivityState, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Channel, watchConnectivityState, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) - PHP_FE_END}; + PHP_ME(Channel, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Channel, getTarget, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, getConnectivityState, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, watchConnectivityState, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Channel, close, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END +}; void grpc_init_channel(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Channel", channel_methods); ce.create_object = create_wrapped_grpc_channel; grpc_ce_channel = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&channel_ce_handlers, zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + channel_ce_handlers.offset = + XtOffsetOf(wrapped_grpc_channel, std); + channel_ce_handlers.free_obj = free_wrapped_grpc_channel; +#endif } diff --git a/src/php/ext/grpc/channel.h b/src/php/ext/grpc/channel.h index cc5823ee7f7..ea5efeaf864 100755 --- a/src/php/ext/grpc/channel.h +++ b/src/php/ext/grpc/channel.h @@ -48,17 +48,38 @@ /* Class entry for the PHP Channel class */ extern zend_class_entry *grpc_ce_channel; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_channel that can be associated with a PHP object */ typedef struct wrapped_grpc_channel { zend_object std; + grpc_channel *wrapped; +} wrapped_grpc_channel; +#else + +/* Wrapper struct for grpc_channel that can be associated with a PHP object */ +typedef struct wrapped_grpc_channel { grpc_channel *wrapped; + zend_object std; } wrapped_grpc_channel; +static inline wrapped_grpc_channel +*wrapped_grpc_channel_from_obj(zend_object *obj) { + return (wrapped_grpc_channel*)((char*)(obj) - + XtOffsetOf(wrapped_grpc_channel, std)); +} + +#define Z_WRAPPED_GRPC_CHANNEL_P(zv) \ + wrapped_grpc_channel_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Initializes the Channel class */ void grpc_init_channel(TSRMLS_D); /* Iterates through a PHP array and populates args with the contents */ -void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args TSRMLS_DC); +void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args + TSRMLS_DC); #endif /* NET_GRPC_PHP_GRPC_CHANNEL_H_ */ diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index b76fb105f36..10e0acafac4 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -52,7 +52,6 @@ #include zend_class_entry *grpc_ce_channel_credentials; - static char *default_pem_root_certs = NULL; static grpc_ssl_roots_override_result get_ssl_roots_override( @@ -64,6 +63,8 @@ static grpc_ssl_roots_override_result get_ssl_roots_override( return GRPC_SSL_ROOTS_OVERRIDE_OK; } +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_channel_credentials */ void free_wrapped_grpc_channel_credentials(void *object TSRMLS_DC) { wrapped_grpc_channel_credentials *creds = @@ -71,6 +72,7 @@ void free_wrapped_grpc_channel_credentials(void *object TSRMLS_DC) { if (creds->wrapped != NULL) { grpc_channel_credentials_release(creds->wrapped); } + zend_object_std_dtor(&creds->std TSRMLS_CC); efree(creds); } @@ -105,6 +107,43 @@ zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped TSRMLS return credentials_object; } +#else + +static zend_object_handlers channel_credentials_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_channel_credentials */ +static void free_wrapped_grpc_channel_credentials(zend_object *object) { + wrapped_grpc_channel_credentials *creds = + wrapped_grpc_channel_creds_from_obj(object); + if (creds->wrapped != NULL) { + grpc_channel_credentials_release(creds->wrapped); + } + zend_object_std_dtor(&creds->std); +} + +/* Initializes an instance of wrapped_grpc_channel_credentials to be + * associated with an object of a class specified by class_type */ +zend_object *create_wrapped_grpc_channel_credentials(zend_class_entry + *class_type) { + wrapped_grpc_channel_credentials *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_channel_credentials) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &channel_credentials_ce_handlers; + return &intern->std; +} + +void grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped, + zval *credentials_object) { + object_init_ex(credentials_object, grpc_ce_channel_credentials); + wrapped_grpc_channel_credentials *credentials = + Z_WRAPPED_GRPC_CHANNEL_CREDS_P(credentials_object); + credentials->wrapped = wrapped; +} + +#endif + /** * Set default roots pem. * @param string pem_roots PEM encoding of the server root certificates @@ -112,7 +151,13 @@ zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped TSRMLS */ PHP_METHOD(ChannelCredentials, setDefaultRootsPem) { char *pem_roots; +#if PHP_MAJOR_VERSION < 7 int pem_roots_length; +#else + size_t pem_roots_length; +#endif + + /* "s" == 1 string */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &pem_roots, &pem_roots_length) == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, @@ -129,8 +174,13 @@ PHP_METHOD(ChannelCredentials, setDefaultRootsPem) { */ PHP_METHOD(ChannelCredentials, createDefault) { grpc_channel_credentials *creds = grpc_google_default_credentials_create(); +#if PHP_MAJOR_VERSION < 7 zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + grpc_php_wrap_channel_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -146,11 +196,15 @@ PHP_METHOD(ChannelCredentials, createSsl) { char *pem_root_certs = NULL; grpc_ssl_pem_key_cert_pair pem_key_cert_pair; +#if PHP_MAJOR_VERSION < 7 int root_certs_length = 0, private_key_length = 0, cert_chain_length = 0; +#else + size_t root_certs_length = 0, private_key_length = 0, cert_chain_length = 0; +#endif pem_key_cert_pair.private_key = pem_key_cert_pair.cert_chain = NULL; - /* "|s!s!s! == 3 optional nullable strings */ + /* "|s!s!s!" == 3 optional nullable strings */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s!s!s!", &pem_root_certs, &root_certs_length, &pem_key_cert_pair.private_key, @@ -164,8 +218,13 @@ PHP_METHOD(ChannelCredentials, createSsl) { grpc_channel_credentials *creds = grpc_ssl_credentials_create( pem_root_certs, pem_key_cert_pair.private_key == NULL ? NULL : &pem_key_cert_pair, NULL); +#if PHP_MAJOR_VERSION < 7 zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + grpc_php_wrap_channel_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -178,7 +237,7 @@ PHP_METHOD(ChannelCredentials, createComposite) { zval *cred1_obj; zval *cred2_obj; - /* "OO" == 3 Objects */ + /* "OO" == 2 Objects */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &cred1_obj, grpc_ce_channel_credentials, &cred2_obj, grpc_ce_call_credentials) == FAILURE) { @@ -186,6 +245,7 @@ PHP_METHOD(ChannelCredentials, createComposite) { "createComposite expects 2 Credentials", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_channel_credentials *cred1 = (wrapped_grpc_channel_credentials *)zend_object_store_get_object( cred1_obj TSRMLS_CC); @@ -197,6 +257,17 @@ PHP_METHOD(ChannelCredentials, createComposite) { NULL); zval *creds_object = grpc_php_wrap_channel_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + wrapped_grpc_channel_credentials *cred1 = + Z_WRAPPED_GRPC_CHANNEL_CREDS_P(cred1_obj); + wrapped_grpc_call_credentials *cred2 = + Z_WRAPPED_GRPC_CALL_CREDS_P(cred2_obj); + grpc_channel_credentials *creds = + grpc_composite_channel_credentials_create(cred1->wrapped, + cred2->wrapped, NULL); + grpc_php_wrap_channel_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -218,7 +289,8 @@ static zend_function_entry channel_credentials_methods[] = { ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(ChannelCredentials, createInsecure, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_FE_END}; + PHP_FE_END +}; void grpc_init_channel_credentials(TSRMLS_D) { zend_class_entry ce; @@ -227,4 +299,13 @@ void grpc_init_channel_credentials(TSRMLS_D) { grpc_set_ssl_roots_override_callback(get_ssl_roots_override); ce.create_object = create_wrapped_grpc_channel_credentials; grpc_ce_channel_credentials = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&channel_credentials_ce_handlers, + zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + channel_credentials_ce_handlers.offset = + XtOffsetOf(wrapped_grpc_channel_credentials, std); + channel_credentials_ce_handlers.free_obj = + free_wrapped_grpc_channel_credentials; +#endif } diff --git a/src/php/ext/grpc/channel_credentials.h b/src/php/ext/grpc/channel_credentials.h index d89984ce604..44071b10f10 100755 --- a/src/php/ext/grpc/channel_credentials.h +++ b/src/php/ext/grpc/channel_credentials.h @@ -49,14 +49,37 @@ /* Class entry for the ChannelCredentials PHP class */ extern zend_class_entry *grpc_ce_channel_credentials; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_channel_credentials that can be associated * with a PHP object */ typedef struct wrapped_grpc_channel_credentials { zend_object std; + grpc_channel_credentials *wrapped; +} wrapped_grpc_channel_credentials; +#else + +/* Wrapper struct for grpc_channel_credentials that can be associated + * with a PHP object */ +typedef struct wrapped_grpc_channel_credentials { grpc_channel_credentials *wrapped; + zend_object std; } wrapped_grpc_channel_credentials; +static inline wrapped_grpc_channel_credentials +*wrapped_grpc_channel_creds_from_obj(zend_object *obj) { + return + (wrapped_grpc_channel_credentials *) + ((char*)(obj) - + XtOffsetOf(wrapped_grpc_channel_credentials, std)); +} + +#define Z_WRAPPED_GRPC_CHANNEL_CREDS_P(zv) \ + wrapped_grpc_channel_creds_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Initializes the ChannelCredentials PHP class */ void grpc_init_channel_credentials(TSRMLS_D); diff --git a/src/php/ext/grpc/php_grpc.c b/src/php/ext/grpc/php_grpc.c index 449ba3cd47b..5edfa2da7df 100644 --- a/src/php/ext/grpc/php_grpc.c +++ b/src/php/ext/grpc/php_grpc.c @@ -64,15 +64,19 @@ const zend_function_entry grpc_functions[] = { */ zend_module_entry grpc_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER, #endif - "grpc", grpc_functions, PHP_MINIT(grpc), - PHP_MSHUTDOWN(grpc), NULL, NULL, - PHP_MINFO(grpc), + "grpc", + grpc_functions, + PHP_MINIT(grpc), + PHP_MSHUTDOWN(grpc), + NULL, + NULL, + PHP_MINFO(grpc), #if ZEND_MODULE_API_NO >= 20010901 - PHP_GRPC_VERSION, + PHP_GRPC_VERSION, #endif - STANDARD_MODULE_PROPERTIES}; + STANDARD_MODULE_PROPERTIES}; /* }}} */ #ifdef COMPILE_DL_GRPC @@ -82,23 +86,24 @@ ZEND_GET_MODULE(grpc) /* {{{ PHP_INI */ /* Remove comments and fill if you need to have entries in php.ini -PHP_INI_BEGIN() - STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, -global_value, zend_grpc_globals, grpc_globals) - STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, -OnUpdateString, global_string, zend_grpc_globals, grpc_globals) -PHP_INI_END() + PHP_INI_BEGIN() + STD_PHP_INI_ENTRY("grpc.global_value", "42", PHP_INI_ALL, OnUpdateLong, + global_value, zend_grpc_globals, grpc_globals) + STD_PHP_INI_ENTRY("grpc.global_string", "foobar", PHP_INI_ALL, + OnUpdateString, global_string, zend_grpc_globals, + grpc_globals) + PHP_INI_END() */ /* }}} */ /* {{{ php_grpc_init_globals */ /* Uncomment this function if you have INI entries -static void php_grpc_init_globals(zend_grpc_globals *grpc_globals) -{ - grpc_globals->global_value = 0; - grpc_globals->global_string = NULL; -} + static void php_grpc_init_globals(zend_grpc_globals *grpc_globals) + { + grpc_globals->global_value = 0; + grpc_globals->global_string = NULL; + } */ /* }}} */ @@ -106,7 +111,7 @@ static void php_grpc_init_globals(zend_grpc_globals *grpc_globals) */ PHP_MINIT_FUNCTION(grpc) { /* If you have INI entries, uncomment these lines - REGISTER_INI_ENTRIES(); + REGISTER_INI_ENTRIES(); */ /* Register call error constants */ grpc_init(); @@ -246,7 +251,7 @@ PHP_MINIT_FUNCTION(grpc) { */ PHP_MSHUTDOWN_FUNCTION(grpc) { /* uncomment this line if you have INI entries - UNREGISTER_INI_ENTRIES(); + UNREGISTER_INI_ENTRIES(); */ // WARNING: This function IS being called by PHP when the extension // is unloaded but the logs were somehow suppressed. @@ -265,7 +270,7 @@ PHP_MINFO_FUNCTION(grpc) { php_info_print_table_end(); /* Remove comments if you have entries in php.ini - DISPLAY_INI_ENTRIES(); + DISPLAY_INI_ENTRIES(); */ } /* }}} */ @@ -274,12 +279,3 @@ PHP_MINFO_FUNCTION(grpc) { function definition, where the functions purpose is also documented. Please follow this convention for the convenience of others editing your code. */ - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim600: noet sw=4 ts=4 fdm=marker - * vim<600: noet sw=4 ts=4 - */ diff --git a/src/php/ext/grpc/php_grpc.h b/src/php/ext/grpc/php_grpc.h index 1d4834c50fa..bd7ee75a6fb 100644 --- a/src/php/ext/grpc/php_grpc.h +++ b/src/php/ext/grpc/php_grpc.h @@ -72,8 +72,8 @@ PHP_MSHUTDOWN_FUNCTION(grpc); PHP_MINFO_FUNCTION(grpc); /* - Declare any global variables you may need between the BEGIN - and END macros here: + Declare any global variables you may need between the BEGIN + and END macros here: ZEND_BEGIN_MODULE_GLOBALS(grpc) ZEND_END_MODULE_GLOBALS(grpc) diff --git a/src/php/ext/grpc/server.c b/src/php/ext/grpc/server.c index c13e7cd1f92..50fb2d0cf94 100644 --- a/src/php/ext/grpc/server.c +++ b/src/php/ext/grpc/server.c @@ -58,6 +58,8 @@ zend_class_entry *grpc_ce_server; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_server */ void free_wrapped_grpc_server(void *object TSRMLS_DC) { wrapped_grpc_server *server = (wrapped_grpc_server *)object; @@ -68,6 +70,7 @@ void free_wrapped_grpc_server(void *object TSRMLS_DC) { gpr_inf_future(GPR_CLOCK_REALTIME), NULL); grpc_server_destroy(server->wrapped); } + zend_object_std_dtor(&server->std TSRMLS_CC); efree(server); } @@ -90,15 +93,51 @@ zend_object_value create_wrapped_grpc_server(zend_class_entry *class_type return retval; } +#else + +static zend_object_handlers server_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_server */ +static void free_wrapped_grpc_server(zend_object *object) { + wrapped_grpc_server *server = wrapped_grpc_server_from_obj(object); + if (server->wrapped != NULL) { + grpc_server_shutdown_and_notify(server->wrapped, completion_queue, NULL); + grpc_server_cancel_all_calls(server->wrapped); + grpc_completion_queue_pluck(completion_queue, NULL, + gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + grpc_server_destroy(server->wrapped); + } + zend_object_std_dtor(&server->std); +} + +/* Initializes an instance of wrapped_grpc_call to be associated with an object + * of a class specified by class_type */ +zend_object *create_wrapped_grpc_server(zend_class_entry *class_type) { + wrapped_grpc_server *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_server) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &server_ce_handlers; + return &intern->std; +} + +#endif + /** * Constructs a new instance of the Server class * @param array $args The arguments to pass to the server (optional) */ PHP_METHOD(Server, __construct) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_server *server = (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis()); +#endif zval *args_array = NULL; grpc_channel_args args; + /* "|a" == 1 optional array */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a", &args_array) == FAILURE) { @@ -110,6 +149,8 @@ PHP_METHOD(Server, __construct) { if (args_array == NULL) { server->wrapped = grpc_server_create(NULL, NULL); } else { + //TODO(thinkerou): deal it if key of array is long, crash now on php7 + // and update unit test case php_grpc_read_args_array(args_array, &args TSRMLS_CC); server->wrapped = grpc_server_create(&args, NULL); efree(args.args); @@ -126,15 +167,22 @@ PHP_METHOD(Server, __construct) { */ PHP_METHOD(Server, requestCall) { grpc_call_error error_code; - wrapped_grpc_server *server = - (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); grpc_call *call; grpc_call_details details; grpc_metadata_array metadata; - zval *result; grpc_event event; + +#if PHP_MAJOR_VERSION < 7 + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); + zval *result; MAKE_STD_ZVAL(result); object_init(result); +#else + wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis()); + object_init(return_value); +#endif + grpc_call_details_init(&details); grpc_metadata_array_init(&metadata); error_code = @@ -146,23 +194,48 @@ PHP_METHOD(Server, requestCall) { goto cleanup; } event = grpc_completion_queue_pluck(completion_queue, NULL, - gpr_inf_future(GPR_CLOCK_REALTIME), NULL); + gpr_inf_future(GPR_CLOCK_REALTIME), + NULL); if (!event.success) { zend_throw_exception(spl_ce_LogicException, "Failed to request a call for some reason", 1 TSRMLS_CC); goto cleanup; } +#if PHP_MAJOR_VERSION < 7 add_property_zval(result, "call", grpc_php_wrap_call(call, true TSRMLS_CC)); add_property_string(result, "method", details.method, true); add_property_string(result, "host", details.host, true); add_property_zval(result, "absolute_deadline", grpc_php_wrap_timeval(details.deadline TSRMLS_CC)); - add_property_zval(result, "metadata", grpc_parse_metadata_array(&metadata TSRMLS_CC)); + add_property_zval(result, "metadata", grpc_parse_metadata_array(&metadata + TSRMLS_CC)); + cleanup: grpc_call_details_destroy(&details); grpc_metadata_array_destroy(&metadata); RETURN_DESTROY_ZVAL(result); + +#else + + zval zv_call; + zval zv_timeval; + zval zv_md; + grpc_php_wrap_call(call, true, &zv_call); + grpc_php_wrap_timeval(details.deadline, &zv_timeval); + grpc_parse_metadata_array(&metadata, &zv_md); + + add_property_zval(return_value, "call", &zv_call); + add_property_string(return_value, "method", details.method); + add_property_string(return_value, "host", details.host); + add_property_zval(return_value, "absolute_deadline", &zv_timeval); + add_property_zval(return_value, "metadata", &zv_md); + + cleanup: + grpc_call_details_destroy(&details); + grpc_metadata_array_destroy(&metadata); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -171,13 +244,19 @@ cleanup: * @return true on success, false on failure */ PHP_METHOD(Server, addHttp2Port) { - wrapped_grpc_server *server = - (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); const char *addr; +#if PHP_MAJOR_VERSION < 7 int addr_len; + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + size_t addr_len; + wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis()); +#endif + /* "s" == 1 string */ - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) == - FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &addr, &addr_len) + == FAILURE) { zend_throw_exception(spl_ce_InvalidArgumentException, "add_http2_port expects a string", 1 TSRMLS_CC); return; @@ -186,11 +265,17 @@ PHP_METHOD(Server, addHttp2Port) { } PHP_METHOD(Server, addSecureHttp2Port) { - wrapped_grpc_server *server = - (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); const char *addr; - int addr_len; zval *creds_obj; +#if PHP_MAJOR_VERSION < 7 + int addr_len; + wrapped_grpc_server *server = + (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + size_t addr_len; + wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis()); +#endif + /* "sO" == 1 string, 1 object */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sO", &addr, &addr_len, &creds_obj, grpc_ce_server_credentials) == @@ -200,9 +285,14 @@ PHP_METHOD(Server, addSecureHttp2Port) { "add_http2_port expects a string and a ServerCredentials", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_server_credentials *creds = (wrapped_grpc_server_credentials *)zend_object_store_get_object( creds_obj TSRMLS_CC); +#else + wrapped_grpc_server_credentials *creds = + Z_WRAPPED_GRPC_SERVER_CREDS_P(creds_obj); +#endif RETURN_LONG(grpc_server_add_secure_http2_port(server->wrapped, addr, creds->wrapped)); } @@ -212,21 +302,33 @@ PHP_METHOD(Server, addSecureHttp2Port) { * @return Void */ PHP_METHOD(Server, start) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_server *server = (wrapped_grpc_server *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_server *server = Z_WRAPPED_GRPC_SERVER_P(getThis()); +#endif grpc_server_start(server->wrapped); } static zend_function_entry server_methods[] = { - PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Server, requestCall, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, addHttp2Port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, addSecureHttp2Port, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) PHP_FE_END}; + PHP_ME(Server, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Server, requestCall, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, addHttp2Port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, addSecureHttp2Port, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Server, start, NULL, ZEND_ACC_PUBLIC) + PHP_FE_END +}; void grpc_init_server(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Server", server_methods); ce.create_object = create_wrapped_grpc_server; grpc_ce_server = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&server_ce_handlers, zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + server_ce_handlers.offset = XtOffsetOf(wrapped_grpc_server, std); + server_ce_handlers.free_obj = free_wrapped_grpc_server; +#endif } diff --git a/src/php/ext/grpc/server.h b/src/php/ext/grpc/server.h index 022257f37c3..a7df456a111 100755 --- a/src/php/ext/grpc/server.h +++ b/src/php/ext/grpc/server.h @@ -48,13 +48,33 @@ /* Class entry for the Server PHP class */ extern zend_class_entry *grpc_ce_server; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_server that can be associated with a PHP object */ typedef struct wrapped_grpc_server { zend_object std; + grpc_server *wrapped; +} wrapped_grpc_server; +#else + +/* Wrapper struct for grpc_server that can be associated with a PHP object */ +typedef struct wrapped_grpc_server { grpc_server *wrapped; + zend_object std; } wrapped_grpc_server; +static inline wrapped_grpc_server +*wrapped_grpc_server_from_obj(zend_object *obj) { + return (wrapped_grpc_server*)((char*)(obj) - + XtOffsetOf(wrapped_grpc_server, std)); +} + +#define Z_WRAPPED_GRPC_SERVER_P(zv) \ + wrapped_grpc_server_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Initializes the Server class */ void grpc_init_server(TSRMLS_D); diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index 505da10a282..962b5bc0833 100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -51,6 +51,8 @@ zend_class_entry *grpc_ce_server_credentials; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instace of wrapped_grpc_server_credentials */ void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC) { wrapped_grpc_server_credentials *creds = @@ -58,6 +60,7 @@ void free_wrapped_grpc_server_credentials(void *object TSRMLS_DC) { if (creds->wrapped != NULL) { grpc_server_credentials_release(creds->wrapped); } + zend_object_std_dtor(&creds->std TSRMLS_CC); efree(creds); } @@ -92,6 +95,43 @@ zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped TSRMLS_D return server_credentials_object; } +#else + +static zend_object_handlers server_credentials_ce_handlers; + +/* Frees and destroys an instace of wrapped_grpc_server_credentials */ +static void free_wrapped_grpc_server_credentials(zend_object *object) { + wrapped_grpc_server_credentials *creds = + wrapped_grpc_server_creds_from_obj(object); + if (creds->wrapped != NULL) { + grpc_server_credentials_release(creds->wrapped); + } + zend_object_std_dtor(&creds->std); +} + +/* Initializes an instace of wrapped_grpc_server_credentials to be associated + * with an object of a class specified by class_type */ +zend_object *create_wrapped_grpc_server_credentials(zend_class_entry + *class_type) { + wrapped_grpc_server_credentials *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_server_credentials) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &server_credentials_ce_handlers; + return &intern->std; +} + +void grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped, + zval *server_credentials_object) { + object_init_ex(server_credentials_object, grpc_ce_server_credentials); + wrapped_grpc_server_credentials *server_credentials = + Z_WRAPPED_GRPC_SERVER_CREDS_P(server_credentials_object); + server_credentials->wrapped = wrapped; +} + +#endif + /** * Create SSL credentials. * @param string pem_root_certs PEM encoding of the server root certificates @@ -103,7 +143,11 @@ PHP_METHOD(ServerCredentials, createSsl) { char *pem_root_certs = 0; grpc_ssl_pem_key_cert_pair pem_key_cert_pair; +#if PHP_MAJOR_VERSION < 7 int root_certs_length = 0, private_key_length, cert_chain_length; +#else + size_t root_certs_length = 0, private_key_length, cert_chain_length; +#endif /* "s!ss" == 1 nullable string, 2 strings */ /* TODO: support multiple key cert pairs. */ @@ -120,17 +164,33 @@ PHP_METHOD(ServerCredentials, createSsl) { grpc_server_credentials *creds = grpc_ssl_server_credentials_create_ex( pem_root_certs, &pem_key_cert_pair, 1, GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE, NULL); +#if PHP_MAJOR_VERSION < 7 zval *creds_object = grpc_php_wrap_server_credentials(creds TSRMLS_CC); RETURN_DESTROY_ZVAL(creds_object); +#else + grpc_php_wrap_server_credentials(creds, return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } static zend_function_entry server_credentials_methods[] = { - PHP_ME(ServerCredentials, createSsl, NULL, - ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + PHP_ME(ServerCredentials, createSsl, NULL, + ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END + }; void grpc_init_server_credentials(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\ServerCredentials", server_credentials_methods); ce.create_object = create_wrapped_grpc_server_credentials; grpc_ce_server_credentials = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&server_credentials_ce_handlers, + zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + server_credentials_ce_handlers.offset = + XtOffsetOf(wrapped_grpc_server_credentials, std); + server_credentials_ce_handlers.free_obj = + free_wrapped_grpc_server_credentials; +#endif } diff --git a/src/php/ext/grpc/server_credentials.h b/src/php/ext/grpc/server_credentials.h index 7101d650008..d37fafc0dc0 100755 --- a/src/php/ext/grpc/server_credentials.h +++ b/src/php/ext/grpc/server_credentials.h @@ -49,14 +49,34 @@ /* Class entry for the Server_Credentials PHP class */ extern zend_class_entry *grpc_ce_server_credentials; +#if PHP_MAJOR_VERSION < 7 + /* Wrapper struct for grpc_server_credentials that can be associated with a PHP * object */ typedef struct wrapped_grpc_server_credentials { zend_object std; + grpc_server_credentials *wrapped; +} wrapped_grpc_server_credentials; +#else + +typedef struct wrapped_grpc_server_credentials { grpc_server_credentials *wrapped; + zend_object std; } wrapped_grpc_server_credentials; +static inline wrapped_grpc_server_credentials +*wrapped_grpc_server_creds_from_obj(zend_object *obj) { + return (wrapped_grpc_server_credentials*) + ((char*)(obj) - + XtOffsetOf(wrapped_grpc_server_credentials, std)); +} + +#define Z_WRAPPED_GRPC_SERVER_CREDS_P(zv) \ + wrapped_grpc_server_creds_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Initializes the Server_Credentials PHP class */ void grpc_init_server_credentials(TSRMLS_D); diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c index 5e242162a8c..b54b0035358 100644 --- a/src/php/ext/grpc/timeval.c +++ b/src/php/ext/grpc/timeval.c @@ -52,8 +52,14 @@ zend_class_entry *grpc_ce_timeval; +#if PHP_MAJOR_VERSION < 7 + /* Frees and destroys an instance of wrapped_grpc_call */ -void free_wrapped_grpc_timeval(void *object TSRMLS_DC) { efree(object); } +void free_wrapped_grpc_timeval(void *object TSRMLS_DC) { + wrapped_grpc_timeval *timeval = (wrapped_grpc_timeval *)object; + zend_object_std_dtor(&timeval->std TSRMLS_CC); + efree(timeval); +} /* Initializes an instance of wrapped_grpc_timeval to be associated with an * object of a class specified by class_type */ @@ -83,14 +89,50 @@ zval *grpc_php_wrap_timeval(gpr_timespec wrapped TSRMLS_DC) { return timeval_object; } +#else + +static zend_object_handlers timeval_ce_handlers; + +/* Frees and destroys an instance of wrapped_grpc_call */ +static void free_wrapped_grpc_timeval(zend_object *object) { + wrapped_grpc_timeval *timeval = wrapped_grpc_timeval_from_obj(object); + zend_object_std_dtor(&timeval->std); +} + +/* Initializes an instance of wrapped_grpc_timeval to be associated with an + * object of a class specified by class_type */ +zend_object *create_wrapped_grpc_timeval(zend_class_entry *class_type) { + wrapped_grpc_timeval *intern; + intern = ecalloc(1, sizeof(wrapped_grpc_timeval) + + zend_object_properties_size(class_type)); + zend_object_std_init(&intern->std, class_type); + object_properties_init(&intern->std, class_type); + intern->std.handlers = &timeval_ce_handlers; + return &intern->std; +} + +void grpc_php_wrap_timeval(gpr_timespec wrapped, zval *timeval_object) { + object_init_ex(timeval_object, grpc_ce_timeval); + wrapped_grpc_timeval *timeval = Z_WRAPPED_GRPC_TIMEVAL_P(timeval_object); + memcpy(&timeval->wrapped, &wrapped, sizeof(gpr_timespec)); +} + +#endif + /** * Constructs a new instance of the Timeval class * @param long $usec The number of microseconds in the interval */ PHP_METHOD(Timeval, __construct) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *timeval = (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); long microseconds; +#else + wrapped_grpc_timeval *timeval = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); + zend_long microseconds; +#endif + /* "l" == 1 long */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", µseconds) == FAILURE) { @@ -110,6 +152,7 @@ PHP_METHOD(Timeval, __construct) { */ PHP_METHOD(Timeval, add) { zval *other_obj; + /* "O" == 1 Object */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, grpc_ce_timeval) == FAILURE) { @@ -117,6 +160,7 @@ PHP_METHOD(Timeval, add) { "add expects a Timeval", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *self = (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_timeval *other = @@ -124,6 +168,14 @@ PHP_METHOD(Timeval, add) { zval *sum = grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped) TSRMLS_CC); RETURN_DESTROY_ZVAL(sum); +#else + wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); + wrapped_grpc_timeval *other = Z_WRAPPED_GRPC_TIMEVAL_P(other_obj); + + grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped), + return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -134,6 +186,7 @@ PHP_METHOD(Timeval, add) { */ PHP_METHOD(Timeval, subtract) { zval *other_obj; + /* "O" == 1 Object */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &other_obj, grpc_ce_timeval) == FAILURE) { @@ -141,6 +194,7 @@ PHP_METHOD(Timeval, subtract) { "subtract expects a Timeval", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *self = (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); wrapped_grpc_timeval *other = @@ -148,6 +202,13 @@ PHP_METHOD(Timeval, subtract) { zval *diff = grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped) TSRMLS_CC); RETURN_DESTROY_ZVAL(diff); +#else + wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); + wrapped_grpc_timeval *other = Z_WRAPPED_GRPC_TIMEVAL_P(other_obj); + grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped), + return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -158,7 +219,9 @@ PHP_METHOD(Timeval, subtract) { * @return long */ PHP_METHOD(Timeval, compare) { - zval *a_obj, *b_obj; + zval *a_obj; + zval *b_obj; + /* "OO" == 2 Objects */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &a_obj, grpc_ce_timeval, &b_obj, @@ -167,10 +230,15 @@ PHP_METHOD(Timeval, compare) { "compare expects two Timevals", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *a = (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); wrapped_grpc_timeval *b = (wrapped_grpc_timeval *)zend_object_store_get_object(b_obj TSRMLS_CC); +#else + wrapped_grpc_timeval *a = Z_WRAPPED_GRPC_TIMEVAL_P(a_obj); + wrapped_grpc_timeval *b = Z_WRAPPED_GRPC_TIMEVAL_P(b_obj); +#endif long result = gpr_time_cmp(a->wrapped, b->wrapped); RETURN_LONG(result); } @@ -183,7 +251,10 @@ PHP_METHOD(Timeval, compare) { * @return bool True if $a and $b are within $threshold, False otherwise */ PHP_METHOD(Timeval, similar) { - zval *a_obj, *b_obj, *thresh_obj; + zval *a_obj; + zval *b_obj; + zval *thresh_obj; + /* "OOO" == 3 Objects */ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OOO", &a_obj, grpc_ce_timeval, &b_obj, grpc_ce_timeval, @@ -192,6 +263,7 @@ PHP_METHOD(Timeval, similar) { "compare expects three Timevals", 1 TSRMLS_CC); return; } +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *a = (wrapped_grpc_timeval *)zend_object_store_get_object(a_obj TSRMLS_CC); wrapped_grpc_timeval *b = @@ -199,6 +271,11 @@ PHP_METHOD(Timeval, similar) { wrapped_grpc_timeval *thresh = (wrapped_grpc_timeval *)zend_object_store_get_object( thresh_obj TSRMLS_CC); +#else + wrapped_grpc_timeval *a = Z_WRAPPED_GRPC_TIMEVAL_P(a_obj); + wrapped_grpc_timeval *b = Z_WRAPPED_GRPC_TIMEVAL_P(b_obj); + wrapped_grpc_timeval *thresh = Z_WRAPPED_GRPC_TIMEVAL_P(thresh_obj); +#endif int result = gpr_time_similar(a->wrapped, b->wrapped, thresh->wrapped); RETURN_BOOL(result); } @@ -208,8 +285,13 @@ PHP_METHOD(Timeval, similar) { * @return Timeval The current time */ PHP_METHOD(Timeval, now) { +#if PHP_MAJOR_VERSION < 7 zval *now = grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME) TSRMLS_CC); RETURN_DESTROY_ZVAL(now); +#else + grpc_php_wrap_timeval(gpr_now(GPR_CLOCK_REALTIME), return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -217,11 +299,18 @@ PHP_METHOD(Timeval, now) { * @return Timeval Zero length time interval */ PHP_METHOD(Timeval, zero) { +#if PHP_MAJOR_VERSION < 7 zval *grpc_php_timeval_zero = grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME) TSRMLS_CC); RETURN_ZVAL(grpc_php_timeval_zero, false, /* Copy original before returning? */ true /* Destroy original before returning */); +#else + grpc_php_wrap_timeval(gpr_time_0(GPR_CLOCK_REALTIME), return_value); + RETURN_ZVAL(return_value, + false, /* Copy original before returning? */ + true /* Destroy original before returning */); +#endif } /** @@ -229,9 +318,14 @@ PHP_METHOD(Timeval, zero) { * @return Timeval Infinite future time value */ PHP_METHOD(Timeval, infFuture) { +#if PHP_MAJOR_VERSION < 7 zval *grpc_php_timeval_inf_future = grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME) TSRMLS_CC); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_future); +#else + grpc_php_wrap_timeval(gpr_inf_future(GPR_CLOCK_REALTIME), return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -239,9 +333,14 @@ PHP_METHOD(Timeval, infFuture) { * @return Timeval Infinite past time value */ PHP_METHOD(Timeval, infPast) { +#if PHP_MAJOR_VERSION < 7 zval *grpc_php_timeval_inf_past = grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME) TSRMLS_CC); RETURN_DESTROY_ZVAL(grpc_php_timeval_inf_past); +#else + grpc_php_wrap_timeval(gpr_inf_past(GPR_CLOCK_REALTIME), return_value); + RETURN_DESTROY_ZVAL(return_value); +#endif } /** @@ -249,28 +348,41 @@ PHP_METHOD(Timeval, infPast) { * @return void */ PHP_METHOD(Timeval, sleepUntil) { +#if PHP_MAJOR_VERSION < 7 wrapped_grpc_timeval *this = (wrapped_grpc_timeval *)zend_object_store_get_object(getThis() TSRMLS_CC); +#else + wrapped_grpc_timeval *this = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); +#endif gpr_sleep_until(this->wrapped); } static zend_function_entry timeval_methods[] = { - PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) - PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, infFuture, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, infPast, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) - PHP_ME(Timeval, sleepUntil, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) - PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_FE_END}; + PHP_ME(Timeval, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR) + PHP_ME(Timeval, add, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, compare, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, infFuture, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, infPast, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, now, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, similar, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_ME(Timeval, sleepUntil, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, subtract, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Timeval, zero, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) + PHP_FE_END +}; void grpc_init_timeval(TSRMLS_D) { zend_class_entry ce; INIT_CLASS_ENTRY(ce, "Grpc\\Timeval", timeval_methods); ce.create_object = create_wrapped_grpc_timeval; grpc_ce_timeval = zend_register_internal_class(&ce TSRMLS_CC); +#if PHP_MAJOR_VERSION >= 7 + memcpy(&timeval_ce_handlers, zend_get_std_object_handlers(), + sizeof(zend_object_handlers)); + timeval_ce_handlers.offset = + XtOffsetOf(wrapped_grpc_timeval, std); + timeval_ce_handlers.free_obj = free_wrapped_grpc_timeval; +#endif } void grpc_shutdown_timeval(TSRMLS_D) {} diff --git a/src/php/ext/grpc/timeval.h b/src/php/ext/grpc/timeval.h index 7456eb6d58c..d4eb2facde9 100755 --- a/src/php/ext/grpc/timeval.h +++ b/src/php/ext/grpc/timeval.h @@ -50,12 +50,31 @@ extern zend_class_entry *grpc_ce_timeval; /* Wrapper struct for timeval that can be associated with a PHP object */ +#if PHP_MAJOR_VERSION < 7 + typedef struct wrapped_grpc_timeval { zend_object std; + gpr_timespec wrapped; +} wrapped_grpc_timeval; +#else + +typedef struct wrapped_grpc_timeval { gpr_timespec wrapped; + zend_object std; } wrapped_grpc_timeval; +static inline wrapped_grpc_timeval +*wrapped_grpc_timeval_from_obj(zend_object *obj) { + return (wrapped_grpc_timeval*)((char*)(obj) - + XtOffsetOf(wrapped_grpc_timeval, std)); +} + +#define Z_WRAPPED_GRPC_TIMEVAL_P(zv) \ + wrapped_grpc_timeval_from_obj(Z_OBJ_P((zv))) + +#endif /* PHP_MAJOR_VERSION */ + /* Initialize the Timeval PHP class */ void grpc_init_timeval(TSRMLS_D); @@ -63,6 +82,10 @@ void grpc_init_timeval(TSRMLS_D); void grpc_shutdown_timeval(TSRMLS_D); /* Creates a Timeval object that wraps the given timeval struct */ +#if PHP_MAJOR_VERSION < 7 zval *grpc_php_wrap_timeval(gpr_timespec wrapped TSRMLS_DC); +#else +void grpc_php_wrap_timeval(gpr_timespec wrapped, zval *timeval_object); +#endif /* PHP_MAJOR_VERSION */ #endif /* NET_GRPC_PHP_GRPC_TIMEVAL_H_ */ diff --git a/src/php/lib/Grpc/BaseStub.php b/src/php/lib/Grpc/BaseStub.php old mode 100755 new mode 100644 index df3fe85d447..2fec1bd9cc4 --- a/src/php/lib/Grpc/BaseStub.php +++ b/src/php/lib/Grpc/BaseStub.php @@ -84,8 +84,8 @@ class BaseStub } if ($channel) { if (!is_a($channel, 'Channel')) { - throw new \Exception("The channel argument is not a". - "Channel object"); + throw new \Exception('The channel argument is not a'. + 'Channel object'); } $this->channel = $channel; } else { diff --git a/src/php/lib/Grpc/BidiStreamingCall.php b/src/php/lib/Grpc/BidiStreamingCall.php index 95e51c5088d..c2fdb94b867 100644 --- a/src/php/lib/Grpc/BidiStreamingCall.php +++ b/src/php/lib/Grpc/BidiStreamingCall.php @@ -113,6 +113,7 @@ class BidiStreamingCall extends AbstractCall ]); $this->trailing_metadata = $status_event->status->metadata; + return $status_event->status; } } diff --git a/src/php/lib/Grpc/ClientStreamingCall.php b/src/php/lib/Grpc/ClientStreamingCall.php index 315a406735f..4050f7ed06d 100644 --- a/src/php/lib/Grpc/ClientStreamingCall.php +++ b/src/php/lib/Grpc/ClientStreamingCall.php @@ -88,6 +88,7 @@ class ClientStreamingCall extends AbstractCall $status = $event->status; $this->trailing_metadata = $status->metadata; + return [$this->deserializeResponse($event->message), $status]; } } diff --git a/src/php/lib/Grpc/ServerStreamingCall.php b/src/php/lib/Grpc/ServerStreamingCall.php index 53599fe4ae8..ba89d9f9721 100644 --- a/src/php/lib/Grpc/ServerStreamingCall.php +++ b/src/php/lib/Grpc/ServerStreamingCall.php @@ -92,6 +92,7 @@ class ServerStreamingCall extends AbstractCall ]); $this->trailing_metadata = $status_event->status->metadata; + return $status_event->status; } } diff --git a/src/php/lib/Grpc/UnaryCall.php b/src/php/lib/Grpc/UnaryCall.php index b114b771b84..a71b05dc93d 100644 --- a/src/php/lib/Grpc/UnaryCall.php +++ b/src/php/lib/Grpc/UnaryCall.php @@ -77,6 +77,7 @@ class UnaryCall extends AbstractCall $status = $event->status; $this->trailing_metadata = $status->metadata; + return [$this->deserializeResponse($event->message), $status]; } } diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php old mode 100755 new mode 100644 index fa026f09350..087d295ee0c --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -50,6 +50,18 @@ class CallTest extends PHPUnit_Framework_TestCase Grpc\Timeval::infFuture()); } + public function tearDown() + { + unset($this->call); + unset($this->channel); + } + + public function testConstructor() + { + $this->assertSame('Grpc\Call', get_class($this->call)); + $this->assertObjectHasAttribute('channel', $this->call); + } + public function testAddEmptyMetadata() { $batch = [ @@ -89,6 +101,7 @@ class CallTest extends PHPUnit_Framework_TestCase public function testGetPeer() { + $this->assertStringStartsWith('localhost:', $this->call->getPeer()); $this->assertTrue(is_string($this->call->getPeer())); } @@ -118,4 +131,38 @@ class CallTest extends PHPUnit_Framework_TestCase ]; $result = $this->call->startBatch($batch); } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidConstuctor() + { + $this->call = new Grpc\Call(); + $this->assertNull($this->call); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidConstuctor2() + { + $this->call = new Grpc\Call('hi', 'hi', 'hi'); + $this->assertNull($this->call); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidSetCredentials() + { + $this->call->setCredentials('hi'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidSetCredentials2() + { + $this->call->setCredentials([]); + } } diff --git a/src/php/tests/unit_tests/ChannelCredentialsTest.php b/src/php/tests/unit_tests/ChannelCredentialsTest.php index 56c1d8f006d..11779efaa15 100644 --- a/src/php/tests/unit_tests/ChannelCredentialsTest.php +++ b/src/php/tests/unit_tests/ChannelCredentialsTest.php @@ -42,10 +42,22 @@ class ChanellCredentialsTest extends PHPUnit_Framework_TestCase { } - public function testCreateDefault() + public function testCreateSslWith3Null() { - $channel_credentials = Grpc\ChannelCredentials::createDefault(); - $this->assertSame('Grpc\ChannelCredentials', get_class($channel_credentials)); + $channel_credentials = Grpc\ChannelCredentials::createSsl(null, null, null); + $this->assertNotNull($channel_credentials); + } + + public function testCreateSslWith3NullString() + { + $channel_credentials = Grpc\ChannelCredentials::createSsl('', '', ''); + $this->assertNotNull($channel_credentials); + } + + public function testCreateInsecure() + { + $channel_credentials = Grpc\ChannelCredentials::createInsecure(); + $this->assertNull($channel_credentials); } /** @@ -64,10 +76,4 @@ class ChanellCredentialsTest extends PHPUnit_Framework_TestCase $channel_credentials = Grpc\ChannelCredentials::createComposite( 'something', 'something'); } - - public function testCreateInsecure() - { - $channel_credentials = Grpc\ChannelCredentials::createInsecure(); - $this->assertNull($channel_credentials); - } } diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php index a1f9053c398..bf8540a44db 100644 --- a/src/php/tests/unit_tests/ChannelTest.php +++ b/src/php/tests/unit_tests/ChannelTest.php @@ -40,6 +40,7 @@ class ChannelTest extends PHPUnit_Framework_TestCase public function tearDown() { + unset($this->channel); } public function testInsecureCredentials() @@ -53,6 +54,82 @@ class ChannelTest extends PHPUnit_Framework_TestCase $this->assertSame('Grpc\Channel', get_class($this->channel)); } + public function testGetConnectivityState() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $state = $this->channel->getConnectivityState(); + $this->assertEquals(0, $state); + } + + public function testGetConnectivityStateWithInt() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $state = $this->channel->getConnectivityState(123); + $this->assertEquals(0, $state); + } + + public function testGetConnectivityStateWithString() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $state = $this->channel->getConnectivityState('hello'); + $this->assertEquals(0, $state); + } + + public function testGetConnectivityStateWithBool() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $state = $this->channel->getConnectivityState(true); + $this->assertEquals(0, $state); + } + + public function testGetTarget() + { + $this->channel = new Grpc\Channel('localhost:8888', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $target = $this->channel->getTarget(); + $this->assertSame('localhost:8888', $target); + } + + public function testWatchConnectivityState() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $time = new Grpc\Timeval(1000); + $state = $this->channel->watchConnectivityState(123, $time); + $this->assertTrue($state); + unset($time); + } + + public function testClose() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $this->assertNotNull($this->channel); + $this->channel->close(); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidConstructorWithNull() + { + $this->channel = new Grpc\Channel(); + $this->assertNull($this->channel); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidConstructorWith() + { + $this->channel = new Grpc\Channel('localhost', 'invalid'); + $this->assertNull($this->channel); + } + /** * @expectedException InvalidArgumentException */ @@ -78,4 +155,34 @@ class ChannelTest extends PHPUnit_Framework_TestCase ] ); } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidGetConnectivityStateWithArray() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $this->channel->getConnectivityState([]); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidWatchConnectivityState() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $this->channel->watchConnectivityState([]); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidWatchConnectivityState2() + { + $this->channel = new Grpc\Channel('localhost:0', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + $this->channel->watchConnectivityState(1, 'hi'); + } } diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php old mode 100755 new mode 100644 diff --git a/src/php/tests/unit_tests/SecureEndToEndTest.php b/src/php/tests/unit_tests/SecureEndToEndTest.php old mode 100755 new mode 100644 diff --git a/src/php/tests/unit_tests/ServerTest.php b/src/php/tests/unit_tests/ServerTest.php index 76aaa069704..6dd607a533c 100644 --- a/src/php/tests/unit_tests/ServerTest.php +++ b/src/php/tests/unit_tests/ServerTest.php @@ -36,27 +36,145 @@ class ServerTest extends PHPUnit_Framework_TestCase { public function setUp() { + $this->server = null; } public function tearDown() { + unset($this->server); } + public function testConstructorWithNull() + { + $this->server = new Grpc\Server(); + $this->assertNotNull($this->server); + } + + public function testConstructorWithNullArray() + { + $this->server = new Grpc\Server([]); + $this->assertNotNull($this->server); + } + + public function testConstructorWithArray() + { + // key of array must be string + $this->server = new Grpc\Server(['ip' => '127.0.0.1', + 'port' => '8080', ]); + $this->assertNotNull($this->server); + } + + public function testRequestCall() + { + $this->server = new Grpc\Server(); + $port = $this->server->addHttp2Port('0.0.0.0:8888'); + $this->server->start(); + $channel = new Grpc\Channel('localhost:8888', + ['credentials' => Grpc\ChannelCredentials::createInsecure()]); + + $deadline = Grpc\Timeval::infFuture(); + $call = new Grpc\Call($channel, 'dummy_method', $deadline); + + $event = $call->startBatch([Grpc\OP_SEND_INITIAL_METADATA => [], + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, ]); + + $c = $this->server->requestCall(); + $this->assertObjectHasAttribute('call', $c); + $this->assertObjectHasAttribute('method', $c); + $this->assertSame('dummy_method', $c->method); + $this->assertObjectHasAttribute('host', $c); + $this->assertSame('localhost:8888', $c->host); + $this->assertObjectHasAttribute('absolute_deadline', $c); + $this->assertObjectHasAttribute('metadata', $c); + + unset($call); + unset($channel); + } + + private function createSslObj() + { + $server_credentials = Grpc\ServerCredentials::createSsl( + null, + file_get_contents(dirname(__FILE__).'/../data/server1.key'), + file_get_contents(dirname(__FILE__).'/../data/server1.pem')); + + return $server_credentials; + } +/* + //TODO(thinkerou): make cases of addHttp2Port right + public function testAddHttp2Port() + { + $this->server = new Grpc\Server(); + $port = $this->server->addHttp2Port('127.0.0.1:8080'); + $this->assertEquals(8080, $port); + } + + public function testAddHttp2Port1() + { + $this->server = new Grpc\Server([]); + $port = $this->server->addHttp2Port('127.0.0.1:8080'); + $this->assertEquals(8080, $port); + } + + public function testAddHttp2Port2() + { + $this->server = new Grpc\Server(['ip' => '127.0.0.1', + 'port' => '8888', ]); + $port = $this->server->addHttp2Port('127.0.0.1:8080'); + $this->assertEquals(8080, $port); + } + + public function testAddSecureHttp2Port() + { + $this->server = new Grpc\Server(); + $cred = $this->createSslObj(); + $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); + $this->assertEquals(8080, $port); + } + + public function testAddSecureHttp2Port1() + { + $this->server = new Grpc\Server([]); + $cred = $this->createSslObj(); + $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); + $this->assertEquals(8080, $port); + } + + public function testAddSecureHttp2Port2() + { + $this->server = new Grpc\Server(['ip' => '127.0.0.1', + 'port' => '8888', ]); + $cred = $this->createSslObj(); + $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); + $this->assertEquals(8080, $port); + } +*/ /** * @expectedException InvalidArgumentException */ public function testInvalidConstructor() { - $server = new Grpc\Server('invalid_host'); + $this->server = new Grpc\Server('invalid_host'); + $this->assertNull($this->server); } + /** + * @expectedException InvalidArgumentException + */ +/* public function testInvalidConstructor2() + { + //TODO(thinkerou): it crash when key is long on php7 + $this->server = new Grpc\server(['0.0.0.0:0']); + $this->assertNull($this->server); + } +*/ /** * @expectedException InvalidArgumentException */ public function testInvalidAddHttp2Port() { $this->server = new Grpc\Server([]); - $this->port = $this->server->addHttp2Port(['0.0.0.0:0']); + $port = $this->server->addHttp2Port(['0.0.0.0:0']); } /** @@ -65,6 +183,24 @@ class ServerTest extends PHPUnit_Framework_TestCase public function testInvalidAddSecureHttp2Port() { $this->server = new Grpc\Server([]); - $this->port = $this->server->addSecureHttp2Port(['0.0.0.0:0']); + $port = $this->server->addSecureHttp2Port(['0.0.0.0:0']); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidAddSecureHttp2Port2() + { + $this->server = new Grpc\Server(); + $port = $this->server->addSecureHttp2Port('0.0.0.0:0'); + } + + /** + * @expectedException InvalidArgumentException + */ + public function testInvalidAddSecureHttp2Port3() + { + $this->server = new Grpc\Server(); + $port = $this->server->addSecureHttp2Port('0.0.0.0:0', 'invalid'); } } diff --git a/src/php/tests/unit_tests/TimevalTest.php b/src/php/tests/unit_tests/TimevalTest.php old mode 100755 new mode 100644 index a3dbce079f4..2d19f64c799 --- a/src/php/tests/unit_tests/TimevalTest.php +++ b/src/php/tests/unit_tests/TimevalTest.php @@ -33,6 +33,57 @@ */ class TimevalTest extends PHPUnit_Framework_TestCase { + public function setUp() + { + } + + public function tearDown() + { + unset($this->time); + } + + public function testConstructorWithInt() + { + $this->time = new Grpc\Timeval(1234); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + + public function testConstructorWithNegative() + { + $this->time = new Grpc\Timeval(-123); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + + public function testConstructorWithZero() + { + $this->time = new Grpc\Timeval(0); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + + public function testConstructorWithOct() + { + $this->time = new Grpc\Timeval(0123); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + + public function testConstructorWithHex() + { + $this->time = new Grpc\Timeval(0x1A); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + + public function testConstructorWithFloat() + { + $this->time = new Grpc\Timeval(123.456); + $this->assertNotNull($this->time); + $this->assertSame('Grpc\Timeval', get_class($this->time)); + } + public function testCompareSame() { $zero = Grpc\Timeval::zero(); @@ -70,6 +121,7 @@ class TimevalTest extends PHPUnit_Framework_TestCase public function testNowAndAdd() { $now = Grpc\Timeval::now(); + $this->assertNotNull($now); $delta = new Grpc\Timeval(1000); $deadline = $now->add($delta); $this->assertGreaterThan(0, Grpc\Timeval::compare($deadline, $now)); @@ -154,5 +206,6 @@ class TimevalTest extends PHPUnit_Framework_TestCase public function testSimilarInvalidParam() { $a = Grpc\Timeval::similar(1000, 1100, 1200); + $this->assertNull($delta); } } From bae0cf17065332220519fdd711aee6634bc999b4 Mon Sep 17 00:00:00 2001 From: Stanley Cheung Date: Wed, 20 Jul 2016 14:02:33 -0700 Subject: [PATCH 26/30] minor changes. cannot assume hostname unchanged --- src/php/ext/grpc/channel.c | 3 +- src/php/tests/unit_tests/CallTest.php | 1 - src/php/tests/unit_tests/ChannelTest.php | 2 +- src/php/tests/unit_tests/ServerTest.php | 60 +----------------------- 4 files changed, 4 insertions(+), 62 deletions(-) diff --git a/src/php/ext/grpc/channel.c b/src/php/ext/grpc/channel.c index ce96457c7c3..6737e340f91 100644 --- a/src/php/ext/grpc/channel.c +++ b/src/php/ext/grpc/channel.c @@ -86,7 +86,8 @@ zend_object_value create_wrapped_grpc_channel(zend_class_entry *class_type return retval; } -void php_grpc_read_args_array(zval *args_array, grpc_channel_args *args TSRMLS_DC) { +void php_grpc_read_args_array(zval *args_array, + grpc_channel_args *args TSRMLS_DC) { HashTable *array_hash; HashPosition array_pointer; int args_index; diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php index 087d295ee0c..8c83772d4c7 100644 --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -101,7 +101,6 @@ class CallTest extends PHPUnit_Framework_TestCase public function testGetPeer() { - $this->assertStringStartsWith('localhost:', $this->call->getPeer()); $this->assertTrue(is_string($this->call->getPeer())); } diff --git a/src/php/tests/unit_tests/ChannelTest.php b/src/php/tests/unit_tests/ChannelTest.php index bf8540a44db..4b35b1a28cc 100644 --- a/src/php/tests/unit_tests/ChannelTest.php +++ b/src/php/tests/unit_tests/ChannelTest.php @@ -91,7 +91,7 @@ class ChannelTest extends PHPUnit_Framework_TestCase $this->channel = new Grpc\Channel('localhost:8888', ['credentials' => Grpc\ChannelCredentials::createInsecure()]); $target = $this->channel->getTarget(); - $this->assertSame('localhost:8888', $target); + $this->assertTrue(is_string($target)); } public function testWatchConnectivityState() diff --git a/src/php/tests/unit_tests/ServerTest.php b/src/php/tests/unit_tests/ServerTest.php index 6dd607a533c..a806c72da72 100644 --- a/src/php/tests/unit_tests/ServerTest.php +++ b/src/php/tests/unit_tests/ServerTest.php @@ -83,7 +83,7 @@ class ServerTest extends PHPUnit_Framework_TestCase $this->assertObjectHasAttribute('method', $c); $this->assertSame('dummy_method', $c->method); $this->assertObjectHasAttribute('host', $c); - $this->assertSame('localhost:8888', $c->host); + $this->assertTrue(is_string($c->host)); $this->assertObjectHasAttribute('absolute_deadline', $c); $this->assertObjectHasAttribute('metadata', $c); @@ -100,55 +100,7 @@ class ServerTest extends PHPUnit_Framework_TestCase return $server_credentials; } -/* - //TODO(thinkerou): make cases of addHttp2Port right - public function testAddHttp2Port() - { - $this->server = new Grpc\Server(); - $port = $this->server->addHttp2Port('127.0.0.1:8080'); - $this->assertEquals(8080, $port); - } - - public function testAddHttp2Port1() - { - $this->server = new Grpc\Server([]); - $port = $this->server->addHttp2Port('127.0.0.1:8080'); - $this->assertEquals(8080, $port); - } - - public function testAddHttp2Port2() - { - $this->server = new Grpc\Server(['ip' => '127.0.0.1', - 'port' => '8888', ]); - $port = $this->server->addHttp2Port('127.0.0.1:8080'); - $this->assertEquals(8080, $port); - } - public function testAddSecureHttp2Port() - { - $this->server = new Grpc\Server(); - $cred = $this->createSslObj(); - $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); - $this->assertEquals(8080, $port); - } - - public function testAddSecureHttp2Port1() - { - $this->server = new Grpc\Server([]); - $cred = $this->createSslObj(); - $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); - $this->assertEquals(8080, $port); - } - - public function testAddSecureHttp2Port2() - { - $this->server = new Grpc\Server(['ip' => '127.0.0.1', - 'port' => '8888', ]); - $cred = $this->createSslObj(); - $port = $this->server->addSecureHttp2Port('127.0.0.1:8080', $cred); - $this->assertEquals(8080, $port); - } -*/ /** * @expectedException InvalidArgumentException */ @@ -158,16 +110,6 @@ class ServerTest extends PHPUnit_Framework_TestCase $this->assertNull($this->server); } - /** - * @expectedException InvalidArgumentException - */ -/* public function testInvalidConstructor2() - { - //TODO(thinkerou): it crash when key is long on php7 - $this->server = new Grpc\server(['0.0.0.0:0']); - $this->assertNull($this->server); - } -*/ /** * @expectedException InvalidArgumentException */ From 69c565bd60b1cc23f4d03ea8e56a250288d65a20 Mon Sep 17 00:00:00 2001 From: Stanley Cheung Date: Wed, 20 Jul 2016 14:21:35 -0700 Subject: [PATCH 27/30] wrap long lines; --- src/php/ext/grpc/channel_credentials.c | 3 ++- src/php/ext/grpc/server_credentials.c | 3 ++- src/php/ext/grpc/timeval.c | 6 ++++-- src/php/tests/unit_tests/CallCredentialsTest.php | 3 ++- src/php/tests/unit_tests/CallTest.php | 3 ++- src/php/tests/unit_tests/ChannelCredentialsTest.php | 3 ++- src/php/tests/unit_tests/EndToEndTest.php | 3 ++- src/php/tests/unit_tests/ServerTest.php | 3 ++- 8 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/php/ext/grpc/channel_credentials.c b/src/php/ext/grpc/channel_credentials.c index 10e0acafac4..16ba0368ebb 100644 --- a/src/php/ext/grpc/channel_credentials.c +++ b/src/php/ext/grpc/channel_credentials.c @@ -96,7 +96,8 @@ zend_object_value create_wrapped_grpc_channel_credentials( return retval; } -zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials *wrapped TSRMLS_DC) { +zval *grpc_php_wrap_channel_credentials(grpc_channel_credentials + *wrapped TSRMLS_DC) { zval *credentials_object; MAKE_STD_ZVAL(credentials_object); object_init_ex(credentials_object, grpc_ce_channel_credentials); diff --git a/src/php/ext/grpc/server_credentials.c b/src/php/ext/grpc/server_credentials.c index 962b5bc0833..296632d5bdd 100644 --- a/src/php/ext/grpc/server_credentials.c +++ b/src/php/ext/grpc/server_credentials.c @@ -84,7 +84,8 @@ zend_object_value create_wrapped_grpc_server_credentials( return retval; } -zval *grpc_php_wrap_server_credentials(grpc_server_credentials *wrapped TSRMLS_DC) { +zval *grpc_php_wrap_server_credentials(grpc_server_credentials + *wrapped TSRMLS_DC) { zval *server_credentials_object; MAKE_STD_ZVAL(server_credentials_object); object_init_ex(server_credentials_object, grpc_ce_server_credentials); diff --git a/src/php/ext/grpc/timeval.c b/src/php/ext/grpc/timeval.c index b54b0035358..8dc997c2dda 100644 --- a/src/php/ext/grpc/timeval.c +++ b/src/php/ext/grpc/timeval.c @@ -166,7 +166,8 @@ PHP_METHOD(Timeval, add) { wrapped_grpc_timeval *other = (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); zval *sum = - grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped) TSRMLS_CC); + grpc_php_wrap_timeval(gpr_time_add(self->wrapped, other->wrapped) + TSRMLS_CC); RETURN_DESTROY_ZVAL(sum); #else wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); @@ -200,7 +201,8 @@ PHP_METHOD(Timeval, subtract) { wrapped_grpc_timeval *other = (wrapped_grpc_timeval *)zend_object_store_get_object(other_obj TSRMLS_CC); zval *diff = - grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped) TSRMLS_CC); + grpc_php_wrap_timeval(gpr_time_sub(self->wrapped, other->wrapped) + TSRMLS_CC); RETURN_DESTROY_ZVAL(diff); #else wrapped_grpc_timeval *self = Z_WRAPPED_GRPC_TIMEVAL_P(getThis()); diff --git a/src/php/tests/unit_tests/CallCredentialsTest.php b/src/php/tests/unit_tests/CallCredentialsTest.php index 5fec06cd133..1994c8afe56 100644 --- a/src/php/tests/unit_tests/CallCredentialsTest.php +++ b/src/php/tests/unit_tests/CallCredentialsTest.php @@ -148,7 +148,8 @@ class CallCredentialsTest extends PHPUnit_Framework_TestCase $this->call_credentials, $call_credentials2 ); - $this->assertSame('Grpc\CallCredentials', get_class($call_credentials3)); + $this->assertSame('Grpc\CallCredentials', + get_class($call_credentials3)); } /** diff --git a/src/php/tests/unit_tests/CallTest.php b/src/php/tests/unit_tests/CallTest.php index 8c83772d4c7..d736f515460 100644 --- a/src/php/tests/unit_tests/CallTest.php +++ b/src/php/tests/unit_tests/CallTest.php @@ -93,7 +93,8 @@ class CallTest extends PHPUnit_Framework_TestCase { $batch = [ Grpc\OP_SEND_INITIAL_METADATA => ['key1' => ['value1'], - 'key2' => ['value2', 'value3'], ], + 'key2' => ['value2', + 'value3'], ], ]; $result = $this->call->startBatch($batch); $this->assertTrue($result->send_metadata); diff --git a/src/php/tests/unit_tests/ChannelCredentialsTest.php b/src/php/tests/unit_tests/ChannelCredentialsTest.php index 11779efaa15..e822929ccda 100644 --- a/src/php/tests/unit_tests/ChannelCredentialsTest.php +++ b/src/php/tests/unit_tests/ChannelCredentialsTest.php @@ -44,7 +44,8 @@ class ChanellCredentialsTest extends PHPUnit_Framework_TestCase public function testCreateSslWith3Null() { - $channel_credentials = Grpc\ChannelCredentials::createSsl(null, null, null); + $channel_credentials = Grpc\ChannelCredentials::createSsl(null, null, + null); $this->assertNotNull($channel_credentials); } diff --git a/src/php/tests/unit_tests/EndToEndTest.php b/src/php/tests/unit_tests/EndToEndTest.php index 2b09f9d112a..09364580c01 100644 --- a/src/php/tests/unit_tests/EndToEndTest.php +++ b/src/php/tests/unit_tests/EndToEndTest.php @@ -521,7 +521,8 @@ class EndToEndTest extends PHPUnit_Framework_TestCase public function testGetConnectivityState() { - $this->assertTrue($this->channel->getConnectivityState() == Grpc\CHANNEL_IDLE); + $this->assertTrue($this->channel->getConnectivityState() == + Grpc\CHANNEL_IDLE); } public function testWatchConnectivityStateFailed() diff --git a/src/php/tests/unit_tests/ServerTest.php b/src/php/tests/unit_tests/ServerTest.php index a806c72da72..f2346ab113e 100644 --- a/src/php/tests/unit_tests/ServerTest.php +++ b/src/php/tests/unit_tests/ServerTest.php @@ -76,7 +76,8 @@ class ServerTest extends PHPUnit_Framework_TestCase $call = new Grpc\Call($channel, 'dummy_method', $deadline); $event = $call->startBatch([Grpc\OP_SEND_INITIAL_METADATA => [], - Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, ]); + Grpc\OP_SEND_CLOSE_FROM_CLIENT => true, + ]); $c = $this->server->requestCall(); $this->assertObjectHasAttribute('call', $c); From b91cddb2fa41df52a9e7ddef2d5ba7097b7c4cdc Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Wed, 20 Jul 2016 17:40:01 -0700 Subject: [PATCH 28/30] fixed typo --- include/grpc++/impl/codegen/client_context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/grpc++/impl/codegen/client_context.h b/include/grpc++/impl/codegen/client_context.h index a132c9a57aa..012bcc2bbe5 100644 --- a/include/grpc++/impl/codegen/client_context.h +++ b/include/grpc++/impl/codegen/client_context.h @@ -41,7 +41,7 @@ /// /// Context settings are only relevant to the call they are invoked with, that /// is to say, they aren't sticky. Some of these settings, such as the -/// compression options, can be made persistant at channel construction time +/// compression options, can be made persistent at channel construction time /// (see \a grpc::CreateCustomChannel). /// /// \warning ClientContext instances should \em not be reused across rpcs. From 4f37dc3ca9c9fc80337f4e63188d21ad3e9059d6 Mon Sep 17 00:00:00 2001 From: Stanley Cheung Date: Thu, 21 Jul 2016 10:22:07 -0700 Subject: [PATCH 29/30] php: fix minor style discrepency --- src/php/tests/interop/interop_client.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/php/tests/interop/interop_client.php b/src/php/tests/interop/interop_client.php index 43b3199d92c..bf40549a04f 100755 --- a/src/php/tests/interop/interop_client.php +++ b/src/php/tests/interop/interop_client.php @@ -450,7 +450,7 @@ function statusCodeAndMessage($stub) { $echo_status = new grpc\testing\EchoStatus(); $echo_status->setCode(2); - $echo_status->setMessage("test status message"); + $echo_status->setMessage('test status message'); $request = new grpc\testing\SimpleRequest(); $request->setResponseStatus($echo_status); @@ -460,7 +460,7 @@ function statusCodeAndMessage($stub) hardAssert($status->code === 2, 'Received unexpected status code'); - hardAssert($status->details === "test status message", + hardAssert($status->details === 'test status message', 'Received unexpected status details'); $streaming_call = $stub->FullDuplexCall(); @@ -473,7 +473,7 @@ function statusCodeAndMessage($stub) $status = $streaming_call->getStatus(); hardAssert($status->code === 2, 'Received unexpected status code'); - hardAssert($status->details === "test status message", + hardAssert($status->details === 'test status message', 'Received unexpected status details'); } @@ -570,9 +570,9 @@ function _makeStub($args) } if ($test_case == 'unimplemented_method') { - $stub = new grpc\testing\UnimplementedServiceClient($server_address, $opts); + $stub = new grpc\testing\UnimplementedServiceClient($server_address, $opts); } else { - $stub = new grpc\testing\TestServiceClient($server_address, $opts); + $stub = new grpc\testing\TestServiceClient($server_address, $opts); } return $stub; From 20b8333efaa2bc76fbb3d03c62fd8d27b37117a6 Mon Sep 17 00:00:00 2001 From: "Nicolas \"Pixel\" Noble" Date: Thu, 21 Jul 2016 19:28:38 +0200 Subject: [PATCH 30/30] Sanitizing master. --- gRPC-Core.podspec | 3 ++- src/core/ext/client_config/channel_connectivity.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 0678f3b3ae7..f7d87017351 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -195,6 +195,7 @@ Pod::Spec.new do |s| ss.dependency "#{s.name}/Interface", version ss.dependency 'BoringSSL', '~> 4.0' + # To save you from scrolling, this is the last part of the podspec. ss.source_files = 'src/core/lib/profiling/timers.h', 'src/core/lib/support/backoff.h', 'src/core/lib/support/block_annotate.h', @@ -760,7 +761,7 @@ Pod::Spec.new do |s| 'src/core/ext/census/mlog.h', 'src/core/ext/census/rpc_metric_id.h' end - + s.subspec 'Cronet-Interface' do |ss| ss.header_mappings_dir = 'include/grpc' ss.source_files = 'include/grpc/grpc_cronet.h' diff --git a/src/core/ext/client_config/channel_connectivity.c b/src/core/ext/client_config/channel_connectivity.c index 03db06c2f74..ce3c13a4ee3 100644 --- a/src/core/ext/client_config/channel_connectivity.c +++ b/src/core/ext/client_config/channel_connectivity.c @@ -59,7 +59,8 @@ grpc_connectivity_state grpc_channel_check_connectivity_state( } gpr_log(GPR_ERROR, "grpc_channel_check_connectivity_state called on something that is " - "not a client channel, but '%s'", client_channel_elem->filter->name); + "not a client channel, but '%s'", + client_channel_elem->filter->name); grpc_exec_ctx_finish(&exec_ctx); return GRPC_CHANNEL_SHUTDOWN; }