From 43537980c3ff570ed565ef92cc1c14bfc2b4639c Mon Sep 17 00:00:00 2001 From: erititan <elemer.lelik@ericsson.com> Date: Tue, 11 Apr 2017 11:52:42 +0200 Subject: [PATCH] First version --- HTTP2_CNL113851.tpd | 42 + README.md | 9 + doc/HTTP2_CNL113851_1551.doc | Bin 0 -> 84992 bytes doc/HTTP2_CNL113851_PRI.doc | Bin 0 -> 59392 bytes src/HTTP2_EncDec.cc | 1928 ++++++++++++++++++++++++++++++++++ src/HTTP2_Types.ttcn | 291 +++++ 6 files changed, 2270 insertions(+) create mode 100644 HTTP2_CNL113851.tpd create mode 100644 README.md create mode 100644 doc/HTTP2_CNL113851_1551.doc create mode 100644 doc/HTTP2_CNL113851_PRI.doc create mode 100644 src/HTTP2_EncDec.cc create mode 100644 src/HTTP2_Types.ttcn diff --git a/HTTP2_CNL113851.tpd b/HTTP2_CNL113851.tpd new file mode 100644 index 0000000..a66c8d9 --- /dev/null +++ b/HTTP2_CNL113851.tpd @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + Copyright (c) 2017 Ericsson + + All rights reserved. This program and the accompanying materials + are made available under the terms of the Eclipse Public License v1.0 + which accompanies this distribution, and is available at + http://www.eclipse.org/legal/epl-v10.html + + + File: HTTP2_CNL113851.tpd + Description: tpd project file + Rev: R1A + Prodnr: CNL 113 851 + + --> +<TITAN_Project_File_Information version="1.0"> + <ProjectName>HTTP2_CNL113851</ProjectName> + <Folders> + <FolderResource projectRelativePath="src" relativeURI="src"/> + </Folders> + <Files> + <FileResource projectRelativePath="src/HTTP2_EncDec.cc" relativeURI="src/HTTP2_EncDec.cc"/> + <FileResource projectRelativePath="src/HTTP2_Types.ttcn" relativeURI="src/HTTP2_Types.ttcn"/> + </Files> + <ActiveConfiguration>Default</ActiveConfiguration> + <Configurations> + <Configuration name="Default"> + <ProjectProperties> + <MakefileSettings> + <generateInternalMakefile>true</generateInternalMakefile> + <GNUMake>true</GNUMake> + <incrementalDependencyRefresh>true</incrementalDependencyRefresh> + <targetExecutable>bin/HTTP2_CNL113851</targetExecutable> + </MakefileSettings> + <LocalBuildSettings> + <workingDirectory>bin</workingDirectory> + </LocalBuildSettings> + </ProjectProperties> + </Configuration> + </Configurations> +</TITAN_Project_File_Information> diff --git a/README.md b/README.md new file mode 100644 index 0000000..d8acd44 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# titan.ProtocolModules.HTTP2 + +Main project page: + +https://projects.eclipse.org/projects/tools.titan + +The source code of the TTCN-3 compiler and executor: + +https://github.com/eclipse/titan.core diff --git a/doc/HTTP2_CNL113851_1551.doc b/doc/HTTP2_CNL113851_1551.doc new file mode 100644 index 0000000000000000000000000000000000000000..9d532f1274f129983e463692214d4d37ac5b209c GIT binary patch literal 84992 zcmeEv2S60Z_x~P8hm($qq9-CYq+>4>3yNJdR^Ws~P?{7=>`D@Q?;2x`z4sP1#uiO% zC}N4d#fH5^<^Op*w|9HT9bl4w{C>Z`tbT5HW_RAqym{}<o7vgn)JeN*i#EDk=X8#S zoRs^RZ^4<0;*D_qoe;L=xW%}Z03Y-7^C|jUU;%*OzsG+G2Ocgvz;(H1Va#!F?DtV4 zavWL~hdIX`b>p}aT+iM;dv`n5?HC`c^<!b|%msGhxH?NLyAvd(*zLn4D0+_kd>eJF zp=;rR?fgYGcNV7eVqbP$*x6A?jx#j?1-GfviO;nW{}}NZIQR79xbC9oZq4Ml*{Hz} zh<}6gwm^<Mh5KLE#E+iyu38*77-~|pA;&$0I=BTP9iEE;9b$wMXF7^=rs@u!gFKPY zZ$7jM^u{2)6Hh_=N8p1?uy(u<qH@t8oQ3CvGtm_v0zKg@)K_?h!T~7H0;F?B<j-O3 zj_#T02S_#TCD75OOZ*WZ0v&N&aju2>7@|jXe*ix#e$8=?)F6M;VKJT&66z<&+rsef z_4aK%15U9+h;QAPg=>xGIFdi%Xahcw+-||y2WOH$I#Tf5T=4M}nN=K=em|&EI2B<b zfBxoQ_#rx7!51MPft1i*F%u!zW*jH*SvU*rC4_~uklxT)cuw~$$fIyJlwOE0(Ujx% z6r(+L^AW-VpM|iYvyh+Aj=JZf?(07PcZG%aGE{Cu@j`h&^=yb9+2)4U92XDatqb`y z;q38CJ<7kwe?AAy%*<pm2YKT*p>5l@Y1^cIL>IYRL_$WQ(oNpIn!Ht$_RX3!ZPUJ0 zaD?13EFz+<pS*2aN=8adN}{|~N^EALGF={*k|vLc2yI=%UmlT?l9;Z{koQl>h?j$k zBH2^kSeYJ^mXMl}kdo{q3+|bok*0{rkVV8Nq|5O?BVH+w)jYuyqd1mVs*qO_pO=?B z!qBaBxgw1y^;0IMq$*?8bPN`ZXd(aDl$gvUWpai*Jw7EfF;?DFDNj=>V&xery_6_N z8mkKG9Ge-FA#bnjmynK%$eSml)=3Jgl-xnyzDZMgRC{Hdf1qFOz(BviI`Zym^6v3+ ziDNha9!_4e(3Ip1<dF_ON3;o*cTbVK`PK*kgX87hzmz*V$iupT^DUdV4wrXNl)FVm zq{IZ&suNf%z~A4;4ODzBnr0@)P-cq6gp7d}4)V6a&6@DJiXUZJ`Z_rwAHx)Vd5T`X z7R{4V6O~kVuuEWcx&hk0jJ979wEa}HK^@YO5SIYbisV>%a!PWI1W|Ts8EV2xU?^L^ z;$`bEWb0?qBrPE(JssJlrz&GmfEam9ydt@m(ohcm#mfPWg=X;bvj|bd^y#lii<KuN z$D|~sLIQdwpiL4CW)M)k3<6mO{uUhure`RVAZdLw6VmwRF;w)x;!&<il>IC^rYm|W z4YY2};!&!_QzFSpPR~#zCi3EMfMl)Wk*v)~`dL6l;u3mgrWHoBcJXM|VKn_Mnha2o z%o*Yzw3JlTzi^oj;5E=6b<R+x#VKO6%$5Gi2ly0^GOvmL;3qS7P16)fN;#x8HZdW& zmjTi$MJ%e&ydr|M4sj)fHWtL?aVmRafHwgu#VaZsUh({)7GbcNFqWheY07jmkp{}I z(zl`r^ZFKG5ur>=N=QygOzCC7=mn_ME1-x*<kc$x+z9U3GYv+Z7c)bZS1DFeG<n4e zw1C}Lrh&1B<Sam?T|~}Fre(;{3mWL@<Z5fly2vCj75Xbj?}>gW6Fiou#POYHni6^_ zvf!fb$gj4KkB^^!Z8WCP9R>Dqg5{DcsDDgCXYwUd_E!w_lxL=sSWwRk)lQBj3ny4m zo>%fjSkX)>0_#y#-6yO-N`LgC%D9ANB0z;wrf0}w6&VV76&SKqDqVV2%9Mz!x??pl z>KtNH($bVM8Hoesf_QqzDphi-s&~4)f4nkTt{k9@$;{vjN|~f*CV;l6=)A`v<iVFi zncOcSEhU+HIWL(^Z;!`Z5%OA4c%dJZGlv5%iqLDNi|(<;fXl&`o{S+nccDRaJQoFV zhdiL3U^TeVzG-N!Kv)Qk*Py?dykrG+iX0|&fv9WMOWRjO8*YSufKP27$e<tsHOV9S zwBA|PIwe`@B{SH!s_q-^8~F!hdb&25+7l2MPHvn+u4)dIxTL3Kror$+CY18H1oAmE z;uRV47)3JqYnkbc6{(6ez7180`1V(cNJ?_zKreY1X{o2E9aRpjA`#VtCkQtsL!O!m z!4iCjG*K@shpWfy2kW3ISCCBU50^8|lUg<<jZ9!h3U38bN?tse$WoOG6iXzyhNO@3 zi%TILBcV@cIcpUvDHDnSCZt14)5+heE7Q_~2vU?Lyxzb~M^SnAHMD(8xv#JPLO&lL z??8F`fM9u*Krer<)I^`EsD?Z~p%=Nn39Or^dNxI@xYw6A%S=FHq$`yoVe_r&=Toz$ zZ!L6xg0T5P*yL>!m5OvFaaUQFdEG)eR5pxns?-!T9|W8lL2#|WM9okFjyX>#U9ME5 zB_<HB(OJq1)Wu82`#Lgld3j4~h|6L*LOZGk5ybHVZ6;V7Me;xxx&aw-Wo$x5N?JX6 zydVf@|0G2pB`+%dQ_}jtl0c%Bad1TwXq>?Wp`3p?WwN4YA|zKthN=%!n34p06^mO{ zXlSKmUW`1MvhtJ<P$ns2(yMyOJHP{F+2~jt5D`>DAs3WFkNkO}jC_W?r<pEPR$F2q zT52(XoS{H0Po}Pd3Q~JP;+eCr5_#P*R0@PSjW3VdRBBX|Nf^(nSY?JHAra3obyMZe zIw8o5s!6m<NpeWKLK4ys#u$Rh50}U$YYeRc`J~~Q#`hF#oyvDq1@(G=GJSrVOw_5z z@g5z|V{(hwP^%{-2Sw%6Mlqi+GZ_w(mt2dU&=+c4HCbE~OT^z&M)B0EKw!nBQWIHx zRqz6S0<2O@hB5<g1H>76LGqTu`dN|euwr;&&_PL<$H6keK$_J-i;$PPD11jP6vcv! zpe8#x%7ToQiX>0DmE6K21(sD`Q9ZS9GLmUg%CxkUv?#vsBub)SRZ3u?s$T;9@?Od` zb=_EUl{CaC6Z)abe6Fk}Uh?M5Z&GEWW-rt}140AiLitk1N<mUlNFscb*i2#}qM2tx z%A++D5W0UpIa6d}CUS?jsZ#+j8RN9PpCU06Hqlo;6VoJuYYyorlj|dnY!j}>9pEKR z$77-1tshJ?rey>dpYMnGX5{S=*_8MK?z)#OSWherG1v<;SC|Zw@<#~|lKDzQ={qQ4 zdEok~DQM<bk`!n&0IZ?1KrQ$o2{}_pjTQr+;r*oaCsBnZ<2hZh#C2u9Uh+_g7c!x? z5K6#z7((UiGMz&4G-Zr}tC~T2qY@o?d=%f#QR#4pqY_hM6p3{k1o+gc$@l7dAHu+# zIy3$$xmwkjaEr5&$^5+JP0-s76qwr+^!tb=n^bDXJYH~wFFjR9s6o2kDzIdzghr{v z`I5Y;_`z3#m5lK)iWLD*#Pe64a-yN5x4gJSkr%J7ouFt1YY8PK*Q|J@(G=KFaSB?I zLP~>&iCgF-`4I$Nr_(T+Ib(2;DMlqce0AAGA=zT`beO<_@06+FzX<{=PD|WXjcf%; z^OE^{L2VOZC<+LmKCz2SQx=OyRb^TZ6Xwofx|ArRAmQ+WVo@<T8W2N>7Q%fh;E;)j z%b02vjdW$IZWANwbbbIu+Q#>xq0sO&%;vUE$zXk~;G6lwNC_ibS(Atk&E-ux$o+hL z<+W@1*YK713-H2tT3!V)el>gos>-b*!0AsQ55JHccHg@4P;x3-s>YWxUvIf@o!WBW zS~XOj9WwRtt|{+`0iYFnur!tLNwy=Rc|>q)y)J>w*Qbu$&$mWsYu?k9*ADcp;cLY@ z0`-^!qoAT^u&wa8!O35lpSPjHQo3H~Jd>1ZQ{h}GW8#w&U|P`0s<_HqKaI0K6VvBn zXI+MoqMU|`R$&8Em1(>gj!07^rz5#$626nlqxX|np~`vtRTa{qwrzt$!|P($!_Uox ziX@nL#~zo-q%xVY@GFy<>AVUMO?11-b>9d$;pKruH$?}G(bF(X9h#^}PcQfqK^R10 zIDrW@OlZ?=bz||{hsG%}t)apNlQwSBA+&w-wh_(Sw01I+IdrE<a7_CvdOG?!$*VYY zZ{4vKMRkxn1~^qUGi#eheGv4fiXt&R1+xV`d!t8(%BQEMB&R1(&z=w`=iSn(X021a z%yera5GYuZCJ|xHf;)7TH@n^wob2$WB2kgxq??@TkpiZ<k!Xj)Ko+Fq8wjx!i6{v< zm>Qf)y|^;gEHqvj(+B6qiVUS0UOoUS;VDp9Ax+4@g4u+$Zql}SlaP>*;Ls3xw-%X+ z35w)og}nXqFVhu03Y1%wX+bimb(7Ez1)itE6?_<Wp%&~y4PT!E352&O1rr79c~({7 z0uO^vGSTs$|EBFfw1nnZr2<F+6TlRx22=+Ufg~Uq=nsqnCIYj7`M_FW9k2=54g_0p zTnG>fbO(9>k$?i|3B&-gKq8O?Bm*OXQ9u^(6|e|c3@ibb0?UB6kKf*Wd-Lkex0iCx zo;;p&<lymrN49TXv3>LQ%{kxAnKEI@@cvi(_Zr@x<H{SEkC67x0jo{TB~J0_vQ?aO z{M42?oKK6|Tt{<>t!^AwTH2c$MmHYMnmWfH9khz$Y}gYlgm_Em_>IXcxyTk+djNXT z?R}AlI>w1FL047+=lCBQ^Z9}nU1?}(;wQ)(ptR2Mz5B)T=flxw`E%8T@A>o39oqBf z<Ti=?IkrI){v1|rIe(_waZW-lG^IgF9mS<f3bQay2RLhSY!zDPB>@B(Fvc~>fzamE zUes3S0AEXvTLD;Faa<qZC2>wTv;ymeSURs22Ud#%>cxZ<IahpVhB%;FXv3ngibrz{ z*IaVkmj(k`kKrkklAP?Y2`3qW^=Kx}qH8C1?PbkbxZ={BGYXd-^x!NVm*#Ptu?^QN zmTxZOvbdrKuy*7uJvb|@M6t<wV-X4Tiak1{G*}AC5}eUiebBC0G372P=`Lv=N6UhY zW+rmx=Y42(SQW?LDsvuH#Ov1RX|(J%2X>+Pb$o@yF6YrW94&0iFQd6EtGRU5Tn3ys z<s_EUL{9MvKbECYNWLJf6p;vUB}l)%2Q~v+fUUqu;1qBgI0KvovH_BT^FR*p9C!h| z1YQAefp>rn<ii%Q1F8d_Kn=hP@CJMUk|RIBA7}%#1=<1afiHm0z}LVxz-VAGumo5N zECZh0xt??V=bY;~d!OvRv-8f%y?5qP%mj*=OEE)KF{wG%OY!;`CIv^$C3fZ`m^pxQ zsBFb?G=hFx!BVzn_q9@%b&9WTb4q1d+O&{z?SRM@Fmx@P1sil4ZKa<sbe-2(!<L8T z9Kk4P<!sONTr18(9V%$u%8N@HXg;DqVLgeT-vi45((RSND&R111UL#D1AYZg0{4LX z!0&)DY=8-13YY=rfCWJI!3wYjssL4iYCv_s3-AV-11*4XKncVFy?}Ti0q6~ojY$NO zfHA;WU>q<Wm;_7)mH<nESNAX8KmE(;z4uRFJiX)9j@7SrsAA@+VkW9%hVn5Ge%4#; zG%?gttR3pJ`)V$0WM`jo;VDDdeXW$5)?&FIVv?!9|Ka7NV5=4`e}?5skM#Hw@0J1k zfc?M$;78yf@Dp$dI1C&CZUVP}+rS;*E^rTc1Uv?w02WvYW(imU)_@Es0oVYxfE`c+ zfQjY20Uy902mpeBU?2qO4)g#bfhZsvPyjuFS5I%fdipBoXwJUf`&Q@do44=jtEUqw zW*)^1&DnR9V*2vWa2#tz@@e#2fVEiLs9#fAN*{J#s}+mVE7rD~O$|{NY{1{wwydl* zS&mv|)kvJ4_V*+mqc*1YA>M8SOl06WunVYA0v#*xGf)nlS`v@~3;`b4Vr3{`WQX1x zcm-6lhn@pXfVa>w_3>PNg$u1ah69GvJTgela2l;U_rwHliUYqH4CIRMWE%{e6yG^4 z4rtvG-%wqf+n|en4L{;F{0Qc0Na1PNr<1YOcppZHvuL+f6~{Y-#uCK+F$l)Q*dABB z<ErK5XgnJ&kH?wXJO+pbh65vjk-#Y6Yv3DTIxqv632Xp10-J!%z!qRDKytGk*a2h% z=YaD-4sa2;1l$B}0k?sCfP;KV03$#O7y~4Grhpkx5pV}80UkhQ;Mx7#_s>1MfB61^ z?FUwEKd}ATVTzfnikYB}8S?DD9tMk}n2yc^YYWr<a_qjAMzNGMQYkE@JG-xyQjZ35 z8vQKXuCrX1GduQ4{e|HkfvZkDtpYRv8Ul@gE<jhH8_*r-0Yn016QThHFa#J13<HJ( zqkt@6HZTX63v2<l0^5M?zz$$1Kz3y}um`vR<N_CgOTabYI*<?G;}cE-lmW^Du0T29 z)BQhdP}6VyCqWJiYejo@U#k_1(@ryBQ}xpoY`@`FVYa*!qo9>@;qp_kHz}&0;yj6; z<pDWBw%!e>2s8qMfM6g5XaY0^Is;vRu0RHm3G@T{0|S780QDDxfWg3QU=A=Bm<P-U zz6DkQD}hzOPrxDIFmMDo3LFEd&p8g90PX?zfx`T}apd_8B|mQ*`GR6)*L%(Q%%LYM zAR9&a%}f_Zp*;HAm{B%m_qAG3qwS2w7$%vYq|b%RpJBPuBR$oLr@sRafXBcS;63mG z_z08&cS-{;KpCJc;0lm0P#%y2^??RJL!c251Ox-&Kue$%&<ltM5`f-7A0QDRe<B$W zd<q(pe+@jjk^6J*q1}hp?LM^o#?Lo$SLM#Vkvo=RhEUAd^u+ksXtXM{Nth`SW=8mK z5VIWA399CB9CYSzX!3@&(F^!5f3EfWvy_Fj9LwPyYNMNjo|Y(fKTuH14YxRxG4j>) z%PCihc)b}o4V(cU01tsjKpyZE5c_%U;Nx`y5`h$;KQI6o2uua00n>q5z-)m0!m~g& za1MA3JOT26=fDf#B|yHT1AN9Z0Qr#B0Kt!Jj_VdcI1mT)2KoR)fnmV=H+gSv=e@~$ z|J%j$XWpDYb3X6Q5d_blIk@}ahTZr%vu4GbrQfbOGiU0=^WO{~OvV6mQ_calT|FBn zgsu*q0I8WZ$c2A~KjRs4!JPSDN}1OQ-XCJgMRVqVDK>zM<<ks|Brja`G@B2k(xL^N z<7-Epp?OI4oT4g>r|F7gPZN*$r>DDw($lyr$ha_D$h&_i0m-~G|Eo&dS0}AHCz@Ae zw1gQ(O6x;@y1-W|CGqo&1KJByQWt@{fV3pWZ9qvt4tN0dfp$Q9U_7uIct?yC4z2k> zEnn=G@Cco?(&UKmfE}9Sl;#qBf;gacN6XtjAbxC*IIvwD*dz|D6bF`y16p&H>Kn%7 zpYx4l?2fA!9;m6gJG}S{@ZvAPiw}(d0LPX*_Jndva7BK4qiVHqMgIJh^m?kh?26*} z`9uCL-%t`vK=G-TG|qz1d4YC}m0i}1kC8Y><J>E@uGKXa6%t}=8_v{S!q0DFfbFn! z0BCBS;02ne&d1qPd!I&oQ=7j8-UA;1`Xtea+8!_gtbiaO7zhEn0o{QfKs2BL4gg1i zW55aESKuUY8aNA(OyvTCT&;A%`vYJDunjl}90iWiHD=PT12=#_fcL-$fP<V#03$$< zOJ`iafBpXX6a0Jd<o(^NceAhFz54#t6a0Jd{@9@(wr^aDe+%btoO$)`INZ8Ba{Ew_ zf?n~>1B=S$&C5kDV)Ob;50&5puZN{8j3nK`%s*Z6Gyh22GV)iR2H*UpoZ?feE1-)M z+opvv<^+xLq%megSR9{9ZEfg%fyVZ<lwRj4Jt{SukENz@j^`T)`P^>=C1EAHht?8X z2DNspN{FZFifR_?Am`7H33vH3|Ng*&QN>Iko*??Z#5>aK@&M^}B|y;cwzw8_d;qRT z0pkEc-yh<-W+~VYpd}Co^ae%&-vBd!oxoARt~Blg89*k`4_E=L1a<>^0CU(}3!pyG z8n^~Tmcbk!u&*rUhF#IO04IPP;Qh;|k6zxn@!PrMKOEn3WA%;I%T)hvESk6I#-izq zCM+7WXt?U%Kr}disN0}a@1y)19J0L3{`$0N&GU*^WA$^q;vz)ttg_Q+Q%WT~&)aUq z(Gz?eB#J$)j<I)+C#s>CA#MZbqaD3Hg3M^1(z=7E>5f($Xa<$YK?&&Br$q=~(gpph z^JjWmBTp{gk$fyK-DqjvxTx#HhY)+Y&MeN0?6%O3)K1hc)F#v()E2XW-J-ULQniOK z+JoApKH5RtF1ZNb1<I6z90BhHw$u5~1YU{>JStw`w)nBn61XNV!6k7(tMk%&O!Snk z4c<N(OWs((+LyDj%ewe366a`~d&Qn>GEHUeuXi1x>Kk}bHnZWR?ox%jv{x)IkfJaz z(4t<5`WaqiN|GL30I1#W0MvFaXg8t#n&G-7&<B_TOa-O^%YhZZ8elV!16%;^0{4L5 zfk(hIKw2K-2A~2E21EcIflk0M;45G*uozeZoCeMS7lBJaKETOI-yi>e>(a}dmuFv| zIJk5D;u8x_%$+&)<;1ZsN4=ambr^o94$2riH8mqu!%E0`Vj?^NNWVtU$v#H+ZOH3L zUh-*?<}9)w%g3&80|`9EGYMy&p*Zhpvx1MW;Yya+Ieuuh&Ac9)hE9cMQi$w)QdkMR zYb{~SL60e=ZX8k3ii2)zQ_$!#p96B@n;2)l`SCoRS%@f6FGDf>HJ;){L#^@JA%W7b zb#8IhNJpt%sqGqp&(sdYd*ZhQkOIblDPRUz0G5C?AOlJOHh?YA5NHJaWi{8*c`e@& zeVOL?{}|9R8d~{iDf2l^VKBTA2mbzRQ(Jn8%--Ev+Oikb_pJ7-<2VThq!P4@i{_nl zu61Nqu63_iUImO@p!K{U^H+7dHzzMo&f3`sXKH5?fZEv{pmw$bsGWs2w!?LOpaDQ_ z9RvgeAwVdQ0;B>XfRVr`APZOptOm9L+kqXxPGA@C6L1JP3>*QD0Y3xTz&YSNkONp% zK<xk-5C8-MZ=XJ5|L$KufAajv!@Kcwe#`lj%fDMbYx#E+m`3sRJNEpL^PJ{D?@RC3 z*vgBfFwQs`NDp%`rd1u9JE1MCc}?fv#n}jVC{)jUggQ*CHxMo*ti#ZxM=V@N(q|UO z)4_U;_HcxSI7*F&bn?Xm>Qwyd5cMh#4Rxf_DnotVvlG^m&|7NFniC4)dC=S#_Z#X> zLg6j6A+;a1mC!D^xL&GZ7TefK9bTZm!i82~hWL(FKz!rB@!G-`-K&vW-4yOdVvX#E zE;9B81`OH@J(^*mM^kL2#ko3WRJ>7{GcAo))j3SRz#NOtVVZ2L6T|V#KT5rlT44<c zM3Sump>rASU|H>;t9DSeCZrjy326qwrgpvvm_e5W{R+qRG+;5PtB?8WD_m#=+KBHo z6bEXH1MFJNGtQRGn7z?8+x-=cTUq#8I#|83qQ}%sZbe*8gG-{#Rd6&wVm7@~zG@YX zt0S@2#nDuP8Ub|hXVae?gfNSR#iLa;DmI!UGM`Y~r8G?$+u{Nwre!px>_Mgc2xnNk zl+8G^>DMIx;q6a0v^mfMpmuKw3<ib(Q-G-e$-;DC2e1>^1>6K~0TMTi#Q`Z`3{(TE z1HOPC;12`<UjUtfo<Ix`3n+o{zyx3-FbS9pOaXQRyMW!m9^f!=1fV_1p5ME4FZ<pv z_p*P=-hD5711|1mFXMkHlHH>7w{vDp6q^0doV6?^d3(G)#hJISCD=K%g(KG_?0vpw z4IZ=3(bXrDwbilNbg!VLhdLKx`}rP2yC*Q*cj@G(>RHs!6D@IH=P9)9w`j{{z$#!J zKyAHA%(OLsH6Tv^zke;Z54;t7XUK|Wf7G&Ku6zd}SS`Vd2@DhB1uJI$XBGr%tQco1 z=rrjs=_=_Z=^*LdKp|H;r~lP}IQ>6;E$(%`CTl&poUXMN^(krC!-{)R!qw5r<)kb1 zEH6qB-v!>YHmPhPgsCzt?jh1?4i@(lI)9zR)B%eMH1z0NXiw_gNdWj>NqjD&Kf0{` zXjlEws?J~B{(^noi}wBj*az$f4gfy_2Z5h}Lx5m|2T_TILrX}s0{w+Y=$t7I7`~%L zpqOd29usGybB!kCBzQeS<D^j}1d$l$>UP<@V?ipZB?OOnAs~9xsOp9{_23(vc63w6 zn?6<xf#x}l5NP6D)j}Y|@j_5|`>QsOQ;Wx+YJZjU2QP-RR`;no%=$O@zr+E9a{>~7 z&eTWZ(bj_VZZGG`Zy4nz-*6<j!iMF>ZX0SDRh&H5#2u)#0WS$QL`X_)IAvr#`IOQB z5^MgC*QK+T1(e{+gbMvQqj*Whew?I=W5s?Fqiqg#J6Un`%>r+qO1=We0?*^?327Ba zoFzumZ4PzLOF4;z?_S9I@!uN^!A0Z0$X)YO$9^C9cGs_V<41C<xbT%$BziPzD*;4t z?KveE$N6)CoFBGS4dl;(Tpir|2QrV~Qn(l{fUAWJ1MwSxg81`0xc=YF-1l>`{j+B7 z_c?3+Su^+loHhTfnFoB%nt#^J13zcYKWpYSKWEK9Yv#2+XU#ur<}`)<+4}R(n)zpS zqdsHi0iV-{`^=etMmH*e`>dINMmH(|qnFQSWN6g#e>L;Z=tc!#xcC{_{u$k<01TEt zW6eLK8x_F)Lz%N_5t<V5;s5zT$7}ynM&>^tb2caA4;Ay_dGL?2-50Iwhm33DpD(6` zeEu0}XKdG(d9uz7Uo)EFld>5;SAK@?3!94ljHJoNY@V-~Xr7N>C&4EBN~<ROzOX^q zNQ%eR(|y`&O4eY0t}%TI!yo)h4%Ics*_$aP_oVj<z7kVE!7gk#<GlK~++=K-N7H>0 zS`7nIp`2A-DO?%{)61L@^ft$KA1-aVeEiarwt1?BGb8;tv%Ip1k{T~C_WP;guvM*E zzmi3T{Cc2cKKI59H1YIINBnRNxabOH^70U;`=l4<88cpRT;GrQ(%~(>Uc-x*Dr4=q zyi6(g1D@wT4w%1e*1)!%?m8Xzt~Wbg*40kZWy=Kr(>u-?2X>tme|fCa^s#-i<Fh>; z)jrqs!P4eO#w49-d8yI6W^vP-bhpiMIWm0lXv>vd7cZ0Vh(0oR@-Deo-h$VW6HeZL z-s4o)UUjFH{$bA5J+H5v9C&iz<3ao6m4EIV($lSM<r8+5^Q<bKG+w{|=b8J@Za%Os zy}_2PNpl+Z9+&-K(fk{O9m?L?@-d`&qfTyJezWOUF2C!gQSbc%R>X80Hv8)GRY@NX zw0_V#zh3p9ybso?_pS2D<h$Rt`>nm}qZ()X#u|OMd_c<Y0Z(M#Uf;XUe{Y?-$B(<0 zj&*5XaawM}NdLXQb01I04oKPY@o1UD0}p2S&ABBzmsYFA@yq579)8`&H@D5JO+TES zkZrVmRC3>Xy%&^OTPJx<-0ov@AG}gr8n^49>(W0iSLk}{``-K4H7jSl=gV8cE24f& zc<z4f?)K-ew>){`w*1(OGjTnS)%s~lq)XGq?oH}ViEn;BdFs-QQ&&8lx^RsBatr%4 z4Z_A}_5Q-*?63CgPKITD9lq4Gxns_l8(+5ja?)kL@k=doR(o`Q@JncR>EH<oKQ=ls zYemhw?fU!>w72RJr*!M6IqkdGsub^Mb~<tF^*L_Cvs&0K4y#;ev{#z>m1V{o(@HhC zWqzai$mtCa*u8j>`_tX}KTRn&^?03~_o~$y(0bG9DUGMt=8pa*uA=A8@Z`CP0h6nH zlq}h*+^=&6N6cBfBEY0}f@M2JzZ+S(6Jp}LC(a%6X8fy<hvpwhPB$6!OOJ=O_6NTk ze7SL-Wn<1&b69`3Wt+Y6_AkHlnA2<WVp;v0eU?tS+<VE&39p(&ydS-;?;YPSt&Z7m zn*V-$yY9iArUZ_^v-{SNrWvjkcP$uPrNPVX<8Re%`m90IzBQJ-DY4{@)%}HyDj&!5 zqdn!%kCuIP^45ixzs~WhFt@_Q?~jM#tD+OO<GvdmJabF*y2}TRc8)ugR_6Fpi<ILv z)=X}?X6n4o6>AM1`qKEx&w=lox@?GCcO!J?_(`{oK33`VwDQXR<x9Pt6kC31<v|zr zSv`m~JCb~4Q2YG@yKU^WEp_iN-5c2)FMa>3@p+z8gFQAkoLSmu{*aZoD#aa%_6vIE zJGzU1)>6MdU;D0Z>a%<3(Y=Q!_dT&AXX)(c<DSOEUCWr-<jz!+#BL{i!|U|gS@uwy z+qG9-d)|Cc)ft@|@4nmI>Btx9k+L&OogHr1&)IYIrFZ)J)t6siy}Zxv!@y1-1`d_3 zSbE@0^A5{aPn_P_wb`LoiZ$Q+#`%}p=V?DW<qyBxvxa_eoq70TWZfw(EBPmm{dL>1 zx>r5!m#Nuq)<xqXmk!^zEVH_LQmr0IyYF?q)Fr3ewb!pq8*U1Gv%lP-Ken0oc@(*3 z%U1)ACm-;eH|bDK8>=L*JGo_JFHQ*CT*ha?_*>7zYnJ(toAb@hsgG*9=Nh?Rd=z%= z{eay!efuU2J~C5&W<iszr2U&;HGFWuC8^@pd5@O%-BNYRwwH@142kd4=wkiR+m@f1 zWcQ--wWvQ@ullXg%I))8EEwdpveK+l<Ek$m(RXL?hs%qGTHnlWDZ5g!|B5jlXO4FM zZDG@O?b^t0h4;SWx3xpFRY$LOZglLG<-~fw4piDV9_}}Ac+8FKwKHGTd43}xFr(?? zg>zk2E&2NQm5ZuppZp>_{9uQGpL2Fp&L24=e8<T>gXGIQms5V=<|=DFZeOeaig_MY zZ(T6@HFx~+?1OcFUoy3B)ykD_bvSRea#qsAOCisimv}j~*|&+yT3fE1(%{6057|L> zRTq`}Ijl~d=GBgM+4e<xOWBe0?JH)^AGBcG{N)W--L^gwaosqxom*z@#$#7baQpoO zms|1Wh1QK~p8c--?-jev-8kjR%n9|ElwFYP(L1(G;HD0{9yA=2<92w_bl;Wo5uLYs z|32jXie~kycWeJ*WSgv^H`2>&ZFo0t%t=M}ykj@Fw;bMn>YBAn>wiDw{E2642Odbc z-a@e_xbEcEORdIKc<TOD{gY+B_bwe4S+n^&%hJEh>ov9csSJlDUGCKR_V}fQANpo2 z*f={Wx#HRG&Axu?(qUkkHojl@j;PgT*UF|d>x_82=eqNA=d!ILeRog(x!b@EEB9O7 z@%$lW)#R2pnvZ{5?^tMf_;B~6gzZPJ44$-P{<#;uN1gOobEm|JcW1YxEdT9e>u<j; z`$LV^b{l8J-yT;v?DXU0;q@=ynzuIlw@TTg+Ke@g|IR69aCQ4{zu4Q@wT;J=fc2I4 zRlfPQM{bYJd!GEB=sh>%e4WT<_RT}O^uO?$o3r3h#>wcpIU^k&`<CrD=?0hkXr^Lb zw~2#aJEmRnKiaR)(Mk=Y6y2nMtW$h5$YI2geQV9y3?1|_XH>w|QBUejOPv1wl#Cx1 z`6um&%WeCuGIYRZ;|FESEKOg#WZaqq_P@!)A1Z%y?ASTB&CSei6L;=u756OZJH@zx zF@wLhc*PywR5sa0F}lXVpg+FYb9COq@77k%=~8RJoCmU-?<(%dsZwUq`n0Q0FXspP zO)-9$@LjK!KlExazm&tGzV2gQcnk@P@*942TO*%E5#2YG2pp@pBeAm%{H0XLSF(WZ zr*Z<?`%Va}Fvfl2Pj4FEJG;euTDcG7N<aGc$bj`FrtRt4@@4PIk30H?SDSKs$iyy@ zRgb%!e_iq5rEvi%m5(^xn7-T8>2dF}=~oZf^>->URr<Ka!-|96>>2gwtA|$~Z#Fe? zzxp9?^L=UDxg(zWC*GOHbu8C!=e(yYcgvQ<HyeJsO<sO|>!%Ig+1&VX!SJ+C_Kj#s zr{+EApYe~r*2%M}=#4A$#}299xT=>V>CMnZ<&gd4;C<eTlhDFheiaHr5+ibM_>iuX zrdj(l{Zq@4{ZXqN=Bl^AuG;0GH?%@IINDiI9g-N4YgR<5PUtI@3G-H8l%V&~RZYlS zeT-kJ@lVo<{ftc<EScw8IKN($U~j7{n=l9VA)Teh(x0R_8=FWSEcNiwLMSor8POh@ z>e?8asB3DN$2WCq!~5Pv)TW%OHqF=~XEE1NP`q{*D&nE4$YY&~xU!0<i^o?>&3axu zjA$JZ9ejviO-_rD5HdC~6;;y6Se;I{lA=XKNbv9RU&?`xCT%!-jD^y%V<in}*kR0% zrAXY1f{>!Hb1FnpKw;16yGQx|lgFp#_bJc&kZR!&c*;Wm{t^fLz#AIb@5kK0Dc~BA zNAm<QK(>G!@BxB=wm>wH3JeD(1BTKO-71j33ovi@06CuhGj#s1r>hO#bq3x5=HO>3 zpc60@m<%igHURs9Q^0WG8juHYXh&N>4)_2;z`ytYIlj;&S5%n9n6`jUR&4{FL0duN zXv}fZPxFm%w#Kq(e&0aOlxvN(ZE0K*r{EIt3^id{t<4Bj)XcG=v^i0l$w@%v88;JD z8mg!`a$#7Wr@%~L0+-D7;^deP_2h!_x64xzpUlS(;A0^b=z*|l3BQ-J1mA?YaOR~< z-dNb$L^}qR?p!XlB6dku9S%#cNrF7MiX1kv2yIc83lA%+-cdoQB<*v}S1^b(3bNy# zaX~0oBUQO93Y3dpzuBUemL3wTrV?zK5|11(<rvzc6jz^<22}us`k)YGkOI9!v*x6r zU_=yxJmBim<_+~ZW7L|Z7Pwn5e|i;UjVCQVq%5^mO10yf;yZ+3q;4u|n}Vt7b&4&X zwDd5h)Q(H*BY$H{uDkh&9_AwyEnKin68k`-e(h<$YJ+v4S#n1_Y3X5N)zk=F$WzfW zv8U3sX@Si^0Fb3^j@!apAewCprAbRS9Py;3hp9GAe2_uh6^JTu-4^|#ilSN)3)YsF zL^$F}OAj+`irDR-CP_poeBY$n>AWpxQ4ET-)W8u>T6&mkQ^anZJVkpA#l~E4^t1d$ zq%E+6Ok120qGhNGeB+SFWk90a;yMlJ#s8*q@rY{*jY`H{Iy!L)LC^Cy<7}<|R=}Ez zK#HEApv2SK`X}dTd{goj%qH`~g$vc*-lU;4{HwzCk-x}4fHQr-!b)ex(~@&7p@r#u z{zi~BI;KlPc%7XtvF1WlPtV3x3N&*d$`FB)3_uFf*?u^aEeJu4Ug3U*`kwH-Cgv8e z+f@Hpq?L2p;U>_&wxI2)CnrBqtiR}5Qx-YTpMJ<a5tPZE(0h+`SQO%JI<7srYurvQ zhr7@Hf~$kLx`(Ufh}W(K)lyStg;tq+s(C<`D;89#<@A+U4nAT>CeDO5T!PWMYOLRA z(eCi1P_#q5UQ5bv%}*w)GgqGMv7|HXfzXDgYKx@=|FM@2^qt?Fo<5wF7&$COgO}Ga zg2LzQ%n)Yc3-r#M9F*;KD3e^K@SGsZGSC)LhLxqy--Qipn0YNoIiCBN@-*K~63<H( zlRIOz{NS5Vj4+DBp`y7H$9?pEn(rrx6LF^m^p93zXQIDiEk*K@fI5*b(Q6|I?4rVU z=l9?mR+ET#)zPUE-<g3~`?-d^ZtUlb)wak)i)ugE48FU{HxnAy5j|7G!x6|5hgz{& z@Gt&G#DPFQ@yA2x_ljtgM9u*<aFZnRC80J+HKH1{!8<h49PHr8srCf^t+J&}mY20% z*0B$?TUaEnqj6o0<)qoKi1rrI;TPY_f?o|e$Dno+H=L>7y17yR>WqIjStGEQ&ARsv zOR=vLuA_0ydb)<3Qz7Z8cXh_bUxlT27Sc-ut<lt$`d4R+E)1vd&P(uANq3Q4S%O=F zMeYD;8mN{h`a=pGQG<@WerOnEfH|<e-e`-(l9pQfp|Qv^PPJtRZP&KNfUhO85TtFJ zwDj1TxAGl$&ngk+j6t8H#BU$e+=wevOg-+zbKx2FOGqd5AKG3VeR@$A^(T84FCmF6 zuPY~v4Xh95y|A7t`_NOwU317G>k*mGrXxpt&J~nj^OPNVNqjBHdy!0T&3&|)BDo=H zEy}||Zwq!*+4MB<iR?SG5#^9GNhJ+dU?bp>`f-aS&m=w~UYM$TQ8~Yw!U9&8w*fK~ zp6kl_%3-;Ux{6X*iBh!YEmTkB$1JU!r6o_R$Tkl^?G8Y{0z|Z}VfR>{o`!QzP|v`b zcuH+v0l$%4U6jbpC{mP4W}rl@Ml#Ngl}cKiQblrZp#Kc{kd<VI8QN6=In<QJ9@59; zWdQF`lFU?u+wN$nUlEKjdMuLkXE<Bq8yhB%*6<$EP)p`pC2`uCNjonxOHaM5EplKR z@#%_WG~bW3H;U#t!%AL4r{r?@QD1<g)c;q-BlcV+P%mXdHj<p|NL-T#%QkaU<)9uB z4d8R=X*58@dq)xP$+E=25~m{v8XYmePa~0~IsDlf`qMII$TR^PK2E}hj|tfDaneWq zV==XT7|iFKW;D2noMXYoOtr_U$=Q{w%yOp9$5_q|OE06k#mapspZiFop`zSNit3## z(t^TjUxk%{jP@thK8??Rs!>|8@;Af-8Csm@erbLjR0PNS@p&#b>L<#xjHu3qw>8OG zf03M7z+d9?#>`a#U(rz*+>l{~6x&QO+N6?VrXS@*`D*i8e_adaUY+I6Z^Xi6=|?SD zGMs~cSuU7^C(8k@LV^16a}>hx{Y##&XN<lqg0ID^nT)H!@-2Zjy<Ak@kz7sm**6T@ z&Jf5k^|VS@I>B~&G5RHoL%$Q}L$=dM+}DbG3o(7{y2g1k+RjClJC^5}ku+AsGkcLt zi0Kw=86TDlCa4Q<873F@QbRUII~PB2NL$8T;eqlpZVCM!WlG!GG8;3>l(w`jBKHvZ z2@=jt8p4mlne~?OBWxPUB*U)J$WAkAr2fzk{GhE`1pNttKS{F<A)HLYD3H%C_{TJ- zVu@9gHd)h+A_3~F*z^a@R3wGD!rHnThrp*aOoQfAEU^aBCM#7M2^gYLx)2&Pe`1LR zvo={RjUxd=G+YXyK{F<nSd3_sRmCI{FhrwFAv9=?#1e}cZL*9_BLV$1e7KI79T_2I zJ`<8~KBhE-Gm!U71%HctQd9D=LaIUfKYBmQpCTnKaOZkhKF2*fgFU<)J#;+03-X@| z@83hX%5u!pkc7a><n}to(<P5HNi<4iYbMDbBOf#dzw~Z~{00S3n=1#7Y?9U%(SQu; z(1<`Pnk69_s|VkcWGsm5&25x!l490Tox4qelpXlqw>Q$qsM6O5Uog{#8MZKP4)Q)D z4HHqISsI#yNkj?A%hQ(c1{jYrkH-|#bf(w{(v+_S`618puS>}fk*IvAa_ad6&CINZ zw=`2yaE?}UAAcVLrNk(@h`CpIZ^-$nl)N`1Rn8>}<V?0f)2b@Ur3#<bWt1hDE1@}8 z*6$Uh?5~fqbK#VG<F62I<{XTBbJm!C*&bFG?J6Dz%DHBc7V-%>XhYpj!JGsBjvt2R z*rA(_;+>7aVeFDA`Iz!7zk=~Qi8W@B_>FwIn|fx&H2l$>!xGQLiJ#J)lM~+)&&G-4 zwdd)?cZ3-`ah#BYMh3<0sF^7fw}B>BlS+3!Pn?JD%%1q3{y9GJ6NYE|#7~Lm{lxM5 zX8^@d6y$(hOWq3aKQUuwBjNmv*^F5)`$v!m3-mtBmdN3ivsqY7_(D7QxwDd-nclfY zVfM+_ovZE6g}K8~e`8_JPt|9H<zs9{GiD*i;xQo0iQezjfHe{3g=rq!CM(<CF3bHj zo2$&WkH&T3HA&{_KZ8Be%o)f~X7L!XDe~wAYe#k#e_;lH6qDbbKCT4v+at5fs*f2V zVNA707L9O$ag};>lc>rsQIsE9WpRF*c^k^DsgT?LPtT2wCZ3_>n%a>i7w6W5>x;T6 zV8eycw;QYkU&H4mhTCL)SHdo<%O9E=epezIVZ$|KqlssjbJyf)r<13cqrw=K>KexK zy=)$2lXcl#o3oeA3+HTxinAG_eDRh>tF9W(igRr)<cjwE)Lfa5{|tY;Ra09Bom_P} zEY7<H%bVXRd!L0ir}tSD&gq6&`HvpnxS=R-!}BBL6IyWL9PhWfAOM*s-TKj3Ec@cI zuJ*V@|LBNlwq!KX;}ouIeq1B+3XI8@)y&U@@*aUeo%*X*XovZ>kpTAF=1dgm|Bc~E zIN~pHVw?E35?ii0%1%BqduJktMkb-pC*O+b&|Dn<?kl7khgXQH@!jT~!>WsLDf~}U ze)UQP?*4B!`)C)cvUoY{fOEId6#PN=<cz2Y{I&Y@9$o{I64y*Q-uj@AZIjnw??>IP z(PI9lAoEK1M;@7YWbxqrN9VXZAG@$=dB;Cqw>%ROvO?~h=$Y&PdO^LLX?x3?Z9O=9 z{_+;<pM_L+e^91bk7ZYDwaH!EY0L<hQuP(fY-YdM^~0VjwZ0kNe9YYT%Ld&^?bUKq zgNc*V%1>T;=3Q;0<AMD=hJP4d;+M>JmrFeuGo|*;W1%^>rQ0^Rc5d+a_S@O+Hzse{ z)u?9cH7!ETM{V9eHT&U;w@r^Zhj{(Gv+_%?rPaqQ`lZW{5pPRxxEHAmJRP{=H~BNG z@%9HdSdWQ1*-Ku!#+`lj7L0j#>3o;OJy#}fnmD*-<cZb?@*SgJHoE6<_QbGm!@!aJ ze5nq<mYrXIcR%=46OIB6t)tSEiRs>6^qV#5WbcDM*55q&YVWAiL)x7=SgKM#?`4xi zy_XE(u2&fr?^fngnFp8J95J=rZJxFC$?KI3KCT=0_WJa@a_70LOb^}&ZFD^TdcOwy zhK)S-)zama%)1Rp{d&ouOIxG%v^~9~{x7~|mW6HenPG1;I=9W9_Ul4oPam^A)1t|x zfS&HdmQU#$l~aE9g3{M&wmkdNxqVKp=yj7;9LwK)(C5U0HV1~S_q^T2IJwsJmI=3d zE?VJzv%}uWE0n9U%bnV~<L&tms7k?#_8B<Sc@!!*7QmLMTt>VyN$D;6ZKX(7^irmg zgjk<wWqD9;8+3ihqW+U}YsKuY(xm66<Lh?6;zH-U1Rq&==DSNdw{MQB_3Kr~bMwMw zT|DQT$;K*w@fy87Z?Na`GAk~Il|AH{c>Rdeo%;hy9osU&;cD}v>FtJ8JU98PwOg;q z&u^`MuEG<~E0H_hdp8)@?2CJ6Ust`+<Uw^0OQYKQ3%{y;rK^WA?=lYhjiGkI!uwLO zA9m?6?ow07A>J<<neVOlaq9bxKb1+${dHSztB{9FZ}m-_wD#D<fTsSxO|Td-wZ60E zvV{Ag(jgB*6Uu}w^uE77x8|}|6^1S9-*j93m`S_mRr+dKtZZCB-Pga)8MOXa*XFa< zU!JrgJ<FkW=gX<%W?8Nqb+ba5DFe==ro7(0bMAxG5jX4>dmfhcpSW|7^7TO*uO+T_ zH_Yormw)b>of=x<`+9Hdm;ce{#;gmDM^ckcOf|Qt-OA%_yO9$Qq%?lMEM(EtNw#Bp zRBmdwW3O+yX%+gtJF{%}?u_3SrMg&onSC8_zj@Au4Q(%+R<v__Gv6$r??l_A9X=;V zyS{!p<a%AlzLJtI$-y6XzP)mM;Firki+he+8<*2@nAPsD4&2+Y^iE5UQHkwOKHuBI z|M_|@BJoDubFG(kIQG6O=TbtktZK<C%df85e(|Hx>Jeu`$35w{wTG9>fM13U9F=}{ zRwL>9pq0Z?q*ouA+BU&;)!(k}9~8OOae!&$i1ve((x@5%mm<&YuiiGS`QRNRyxz=6 z+t5A9w6y=OUSH)5S>LTjjrr@_wv~0sJXZCvr}L%WH9}r@K4UW6xV?MW!_jXYC!Mxk zRXb?9wf&8U)0&%qRjY~bO`q;noOXTX_kLERHm)m2FLWIr(C3)d?7RsTjvcvvFso&g zgC9!m+2wNUm*eHH?iu`ee*G`|KD#$-+L1|Z%6*t$*Kf_Mw)Ic1jU1ML=xUornIFIW zc;!LmsNARBkK}(m+iTy4cgtMOtM~Zo+PR#Q9|t^rc&^rW`EL(8SnZj!a72kG)6CQF z)@|4?d84`W_aV`KM@HRx<oT|G+pZ@53sM~`)){wbShFnCckjx!i#@P)L+JzcEW*ai z24wa-DZgwrZI0Y|QqGS@xpF>DyLoPGdVTKvaI5rT&TSq}tL*RE-)d3COP3{MC%o?$ z*WpyBp6R8tE-V~4ZE@#?785t7-U{qDdZRM4zlHCOFN40exS8lVfA>!b6?)2=e`h_u zch}O7rkFqT+isUNxXi{nJ8o_%wRP>T36om7Uv?VZ_2_Hgz#j)V{bD>e(d&;Yi@)6e zdWw0H`=IBsD{j{99`t?X*?!CYf1H0QG1+}XWR3edL6Hv=8ZCT&w6Vv?vKI$Gu6Q^j zcAl$gg|Xk)Oq}%7qKO|ymk$}^cGtt9<{OFs6Wf6$1_nJnpK-Oqg|OH6d#9F}-f{Vs zYPJKFGdrgZbn;&^bWnQTrk$J1%=az2Yd5Bi)9b)4uk*WgF{?1-<)aSAS9z^kSj(a3 z-X*Wj#||!&++&Y)=J=zBavs;5d#IN0Wv_X%hkG;j%}Rbf%=^lv$9MaCU#LF8^zF=U zhcZ^LtNHl!`5}MIZhO%oeeCO<7k8!KYq`YBx<$R0C&q5RyCcE;n`f=Rwy?hJ9{ls+ z=Cj{qjowglan*-U#;wcnyKa@^@VI5^Z<6-8Y<o9zK;3aAE>s!Py@vDcB?;Bal^t_# zz=4y8ueS?bdap(1OY@mKzeue7d%J<ngD(Ccvq<ur);Kq0@vgGdgWKEOu??`Y*t)6z zkBgHP?WR4Rwen)LBzx1E{pXL3kbQCRWdk2c#Q}>$94EytI1}N$dG*w{UjBP)%x-G3 zzhTcVlGQD9D&?#_K5cb)_#^8%HN)a>$4zZJrCo#Z-^6E?JXf{Z$&i~#={=&BWiPB8 zy6DcRGhI)wIRA0Np@HeLeh)4N#q_8e|FCSW%Pn%hnOb^r!a>)AF^$SL?XbetYx|zj zt*>3lZR>ttHmsa-jz{+`b~a~+S3lkSoZQW*a*0aq4$e4!@=Q?7vI=9nZ8RR4syKEo zxRaMlov>YVV(XW?x#-l{brX(;EnmJcZ)K;>>#IF~K5AK|dBM8|UB7ps^r<dsR>5Q5 zo?C2vZFK5@KRUErQpYW?uD`>kjvv+re*e|O-=8>ilr6hCx^?K5dsTMLUY=U%c4^nl znTLYvbz4)~Xw_G-r_IeAa-&}^bh#4nz@_RpYkR!Ts93sAtAH;WoNeor8~CVN+=&f| z(mosOM@`G=TqD$a?)UxQMC{6`wEc(dU0a8QoUbIm*5$otkJr|+;3eDUo>=HnI_2jL z*$)q`$gMZis{U6OyUfk{{@sjam$vRuHu$lwtM!7_&qrPP^4yQ72JBoPF{RVh1~1w; zB&OQ@v_$4TqE|%fj23V5Cd)6_zRX_WK0~&n$>VW{)=HZF>RCbdI`}}!m%+zu(!QE| z_1Hk=`pK)`{*t!t{vWq{9`Sa*xb{qFyr<jvu|wbevdn7z;(LRi-fl7>W$=(OGgB^k z$+}GM_4|Vsk#=81MU7qdC@QL~(c<R|+y8$4_{&a}zVNQKyUzM6t8V`uylh6FFGlv> zeW}Fzu6zGDl{wMzLZ6w^^=DSzu69U%bl{AUOQTlbT@|@9`^BnXx*qA%uzO-m^zD^{ zJ$p*7cihvDzLYK4ZOcwi{%#w3yfV03S*Pb?z6#%ki#l(W@6yWhM<2(m*CYFzr=*<S zWjn>=%IH&Tv!z+L0%bKDxwm`xNVd+?-0K^~4|5WB&-(V5LyX^VzVWME%<JBt(Rc2F zq#X|@2DNu?Jtnta?1t!(6H{tEa_TiDyh<w%_x4r1d}pVXsN7=O{8M8i-VR?^du zx7?O|AAEH8sj%0XLl!=rCmDRmeQ!_OLFc&AGuzJ1-~6Gr%a~r?<(+$cY!{rK`;|$R z#fMK^t2l2}OXHur?b`LzRaA^sU1C%N6J~~-Sv)2>^tiTd!?g+MdUu-CKH#Uw*V9d? z>n&*#mWCTC@|u_jC1SrWC0*A=L+QLeO1vYZOG8%(G&D}(Ll|*s3K5C6;ru5gm`yj~ zdZ6~lEs0G1MsS?u{gi3x2`R}9+<d)!+~msSn3ULr<X#QjIz}|DQQJ+Po}oyNRV1b) zD;u~CRHnN%YACa;uc#Z7nAS>>DhGk&^ty@$Zt)ozsdc@*(_`RTD$>1DQkBWL7nhQj zq{u)pt(SLfnxa1lCnb9O`S{fIPEsT!yUBa@@@?RjnBnUt&lrHK*gm-G*~^dO{3s4r zv3+o*h>5{K22ZFigc#gsG3sahg&2R~nE)XsK!^zxVgiMjnnFxXH+g(wLUJF}h)!<u zxRk^&79m`@dGmiz;VAsk(zI|82Yt3fXDkAQ05i~MN;D=U(AJoH(d7Gp1HeJx5O5Tr z!P_!CybNu!6vL37NI-u9n*Itf{UuoXYoPQO0qL*8(O-t*<m|7k(60N4hRWcNmH^}% z(`tQ`32RqiH2`w<v8y)LMBpzy0TlrcfbyijGEaYLlK$El{l%e1a?HvD)9@heKu)_* z3p)|hXcCX%9S@&9W;O6vz=28tStcxY<KM?u!x_`-cw36UMhb*T@Qps80Fr=_z$jn| zumv~;oB_@P=YW@hEqLMycmdvkFVGJ723P_t1HK1V0K~uhfC(6H2ABhuKsBHR5C_Bq z2|yoU9IzDF5Bvxm1P%eWfyaO)#``qOECXx+Zy*@x0Ym}OKu=&KFay{KYzDRf+ko@H zEr6JSrCpp9Faat74S}vecc2Fl1q=h`09%3Wzz$#+a1kIuDhreY$^#XEV4w?-3G@dB z0AB)gftA1^;0SOO_!;;e_yCkPgFFCbfpS1Ypd}Cw^alC>Nx*5~Awc^s41i2d17-o) zz!Ts(5DfY34nzajA)oY@i7_?7kv=$d%+8-N+(8;azcR;shtAXU9V$8I&)7fX#DDws zjPVXq)C9F-zgtAth{*yA@U{v30g{2?z(`;vuoc(=90U#lM}e~drf@hb$h<!g2DAVM z0h56pzzqN|YB(dHD&Pb70rZuJ0*C>|0TX~pzz%?P!lB~~W4<l2RKFD*^PPq}xbVN! zMu?B#f2rgS9oa97XTLM>_ei<CAY>0g%#-6+0-FI9q2lmR6-|8|#FcwWOOBW&%;$y= zAI`moI^!cYqr5QOkaOm2;}Bev0m7fg%cB=Hw6RDPoF$w^9ud$z@DTu?j?$1`Ac0Zd zRGe)^1o3IWrxQdPB1i=Y!P#Dw5z!)(-GQ^pBkRd_GdWHbR_$qGEQKO)jieG38gEDQ zC;?sN=QqHwIoAd63TSSf=JeulZN|SprB%^vWu184stlVZhrwEA;JpHElw2F2{fq11 z{kY+K{`@_*-?2ZQq!hJ~o^}<gDY{p1FJz*rNk{i7oi7k5N~cS+mZ&V^&7Em?WpO&) z{Cu_21)!Av$cs|>BVSGZ#58G-pu+0F_@aNWXT}%y7Maz7>Q)Ezd~nvTyO?K%@iRb_ zPPcyIdt#okI#{cDO&d)GqJ<f^Yx5LYDgAhU)Wkhsl#%wVrcxHX$L<@hfw*?MrDQF{ zq)eOBeyaTRbF(JA7eQLYHK4j^O6kkXEZwJFt*B-7my+%Mq`9YCO2hI%TeBG|36m7Y zFSgebNf_<_O`G^>a?@@d;<e_UZb?}CXtYF-6l#MyqIB9c{R@>^aXRh%h%ZzY-8$6f zby-{o-E@ZAT)XbN=?t|H>7=2$lVvf?Gio6XKgE0@nuXOp5OeQB-NoxZ(na)i%g^MJ z@r$e`={U(XNg~M!yAz1=GRsKM&>I@f9oik9b|PcD#Orcjyj7Uyo>+7IkdoC(+-j80 z52&f)BD=@9K_#Gi)WTUzlQf@7;s<&H_XSNA?orN^Qj?BxM0-Cq)(26K7KP4;kODuB zFhqsNI6p);Is@Go;T2FF<Bo70^g&o3BH{C80~dAh5xu1p4ucpa5f<Qq>-s8;$9W|- z2|Iy7i-25Q6Utx^L+}8mh;RYdjW7V23!D+*7!7oAP~S^_3H*~F^u<BM1Gv5D*~v@5 z*Z^^;Hs0%TL8w{~z3o92u&#-Xp=hx<xv?~MqOp#6Ooi-dTooi9Q_(m`HLePh<GKRi z22=z*0C%7gP#LHOkb6@Vs1DF~9-crAzzgsO$PMxX{DE3PAP@l51Zo4c?^;~|YrcZW z$E1VmoCHMUf-o4BBs>8msOU@;+)?4ifKmnBb{&d%T||L7xhmYjS&V!z3V#b(3*`|e z)Kj4?&Z_`w=iNXK+Lmxf1#06;Don<CGtdx>CQv)i0jQlX0r>zmfY8>|wp4E#TlNHK zT)7yaG35(@#*l3x3^Zmu0+2sk6@o!y!m$920dD~`=JSHE&=_w7Kx4Xd0FB|?kqM32 zh5|H3%LQmm76hT9G1we{#$5LS8e=s8&uL7R4A2<rDL~_=*#M25UH~+1QUEI<Z0mpx zz&79{_>==&10DcXA+P~J8=x!D4TuH41V#eOfXzT0_?iJoA+SzB6`&rl7x)o44_pJ@ z01^na8DI}M0CFH4XbZ#w$-p<jWMBrc5Lg7P15(gt8Ng^@DzFl5wFc;fcIl#O8wuJ+ z3K#>XfEi!`SOV6746p?n03pCgU=*+&pua|c1ULrd0M<~#7C<;K71#+FLHVSBAK(wf z0I|R%U^1`^z_)(9jF@4?AA{yfi1{<;f>g?a{yzaD<f9954a{(#{)yvsuOa+6Ai7#Q z6AK+7y&-uO`UgR7sedqnyz0ttCD@5!fFQ@z58MUF=C=pPt_wQ92Xa{-vhoE$HvTRk z*!4k>9kT7D(|=WN%pe=O@-iK9t3E+)_UM%z6P~LkD((tr;;wK_=fa@daujB*L$p~+ zI%}@!J_`$HYJWO(+wVW!=ADK7ycF5IaM-JsKrf&-Fbenv*aGYXjshznGeUns@^f4z zJA(X>3@wM;tN`u-zXK7lKOF(`3l;-QfYSi=9Tx#{|3R`Qmbb!el?|RXWUqAPt+@8- z2a%jf(b5cpO#HR8hMqw8ubz!iLXtlAE7&0>zr`)95&Wsam>G8ok9!r)%dz-J!u^4J zN2B~U(t3TWfeDvy!~Ws#-<GRt^k!xJp_b}6;hrsLH~5N?r^%ZyE9<$pynBp^OVqmY z)%4t(9obYC`!XY^x}JL<+lQB|W)>alUEm(Pj1oVT%&5M&im#q~i5(ir7Y?s&>80o1 zK;IG`jv+s`bklQhOG1c8)8P3VT?*W@<)(c(-eX1cu3dy0YtFXZ>&_vSf6g8`UlS)> z*<#Jw-RieattczRXwJ4=Lbtg3EweAmJoV)Fd+kw;-2RvxT~*J$51AfKcG+L}riPw- zHHTbiT0d*-7M}w5;GKkpcRAeGaaKNh1?@w^JvKTNd4BTcTuu3eD_ic{8Xuy5Y&p_J zuBK@UIW#BFotxjei{h~5fQG`;nzJppHf(wF*rO3ctEkfncWt?9$w}!U&!$%^ujgJ$ zb-R9vN8|2#=(&du5(oR`FNGgi2dv69vCEqM(djU@f6F$(7Z}+l(Fl{h_F`%9eZO#A zg-=Nb*%bPk)-LPbJ3b}crAUOM5pGzGOR2(r6Hm{L4rBSMN&rul9U3~4V$-3-R3d>E z^(Ay@Xc-lo7NsRxUeH)IJ@^oGXy{3`>AkivSVu}K(DQ-~(9opz*3gudGB!w4i)oZh zOwbEvMMRXwed>aeQ)-j7!O||P^LxHAR#>B;8!V#{)*=cgrvOneSS!ki4vEyn%)$z; zybkFV_<sNBA3SN>5+J<--|!#(LyI&GI(5s-NSo@Chaw3qW+h*UMa;Ds`D?ZyzYvR< zYipB7w^|5!vjSPz>11)u)-EfWNotrqvbbg&jj&b?@CLDnEX+08;8}LWGtO}`l+Y$C z8sE~+#<#T5_?C9|M=kR79Km?FDg{f=e)$GmsL{iPq=yStdPq`AX^ci1N|}n)*e&z< z_DMBI%2cezZi#)DjD!Lk%rl5DFrPBW3Mqs5f>STX@-$ppTQr)=YVa~)i$)`>VO(Ts zsV!8c<(r$@f=a8FLxZK=;GoWPg9A#t!GYx|h?wEh4tG|k9PW&i!<|`5L2?Z9wry#3 zY1@`Y%C@ChX$4^r)Kc7>G=j9riU)5eGrJm3yp0EMCzDW6GZG9J_g_&^nWX=EQ6LB0 zCTk47LY>4!VT>t6VGO=Ro%B(>SIoa+!4!yR*Z-D<V9Ktlp2}%q7DB&R{3{Cov!Z~J ze1W4fFY-OnS<GY>q9=70J<(ar{1|ACUE=WRE$vVv!Dn|8e0qz?SA=zZi|u*Tq39!T zTZ%mF?eO4MGx9%^bGRKI-0F`7J@p!V$E1%=S!~K_lVycoe+{Eko;*P-^!jUv4*NWq zl~I4$$c0lKf*<2niahK=*tbbVbCL~c4#M6|YgAR|Dchs=Sa<-WRFR0s6{^zeq3p{0 z*(L1kveH>;OW1*O3A<>7>1+G8E!bC##7>>!Hso1=<sQ5<OeGeID=TG!;_gLDVWOIb zOM9uM_M)Y9h>&-#&l%<ksR)yots!h$Ji?SnA7L-NB+?=dA6rA5c6ljAscC>~h`1NV zuG$&ci83%RUIv&8D>MTy438-jed5QoWF-1gm6nX8esm=I(Z5kka?I3P^1B(x=a~ID zEvb%iu}Y#F(U`G1#>K_d5>#LJU|KRC!#fqt4VIueAHzE>EujsG3eyr@vIv*vwZs~> zLFK1flWnkuZJ5k!32n(~SVL69MKN8bAy?2!Dh;_pt#k#g^eGx58ixwRWu-_%ib1$Y z4H1ZorgLPaNJEN2yjU8d&Va<79kNoSA;rkxuWQKvseVM@Z!!CkKdm8#`;kAXA;qx9 zh4vwWj0p_DnQ2Hd2>*EvDF*SsrXj`1ptu^M>Hjo5DyGwor0RJ}A+ePAqV{8+<=6+l z>)%iFEc-Ffa_j^A2SZbnn&jj>(S$%}mRvm{(3z4Kn2qFp1R*gyOL<=@2maR-Hp!Pm z{#OqCuPHRir*{I9xf*<WVX{=pCLpiaWj$nMN?!$;*H@wu1{q5{T+jy)_xwc7L^V?? z<sG8DtN1dW5xajK#PY7<%XlJYc)F2@@ZTBGh4{cLTb{k~5-dhw9*k;IQe>~CyuH4D zQ$1@~@eZ=Rep5AT_~~<nLJ6e2ovwICJy$s9E(lk=qna!Hgc(9}_PUb@zh*PQ{L%rN ztXy=q<(UlRQfHfs&bB<sfX1(ta@~-u2VZ^T;fx7F3HdWWVEA{6h~yN9K@R;V&K8yw zja7uHV=KNrk3uk1^QlKkFpfenX2T0p52+-aYLGWsBMjOkW@8|wA~Bl=QnN9TA`)zy zO30W5E4YbgB@L2@#||wj67e)o9*<pGh=c_OpB6Myl5oq}P}3lZ+D>W`wVgnsw$mq9 z#JrT6gn22DFfaAV6}jM|CUL<9Brdpoaz$3Ss!6PH1&I}|pIngvay5woa*!Ax|Ky5< zRaBD*s|XTd6%AEHk7zj+PqZ|)r!XNa%@ZqJ$RZ(ZTE?s}kz=JRQsl&?Gc0_JSdqdf zCT3WlSXB!b!#`2gB85_1x}vIDq<D&n6;;*31@%u<wMelQmrhkxg91EAV;cvNZ5*U) z8^y$ms%p5#N`{k_4A-)ff1;{oG&Zvg*~~J!Hd9=>qFVKVqgve`ID*&%M;)UhCRS8c zcb8NX+g%dGc9#^JpFdI6aT=>Sj;!i9U8^cCT~Sr-rm?Tx$i8;dwXb4gMOC%B#?n?N zOIuyb(*B96{^6$P-5+is_=lU0-4&OPRaNj<HT^SNOls);58s`#MF0AE|6}ibL~CY* z0e~ytf3gjy`9V$sO&_z3uf?wd=}#f(^r41@gcc^-V5xvwSVc<*G!jaST$sD8??pGG zp??v9m_}jC2^bCi%L&9Z3R_sfXy{*9Af{2+5(7p<zc7hu6t>ua(a=A95Ys4Z*#V=W zf7yYUMqvvO7!Cak5X3YJTZ-`S`)7Jl^M87U%D=Bu#2qr{!cU|X<8_LT4L1C0^xxMg zJcIu?UZb$t7^TE6YaV-ZtdyXaR!X7~);Aj?8no0lW`b7uK$&gzuIIIyO29-DE5#$o zrgw_v;tB-+{K#8Rb^n%;w@|XbZR9NwWTpFyBX4bDe{tk35Mx#S8%Ev&K~~j2G4kdN zdaLxxLbY&B{mMdn;hI5j6+Wx_HHyXsro|}VVOwU^Yn1O~P_FN=E%UJtg}T6oUa)8g z(+ienSXrui!P1Nt5;w!j($0kwF7WDwf3?Q+SVNqascggIQ`M`NZM67w8y25Vd0%L$ z^}dv0@9y{<gbdg+=sc-imMwdak|#w5dD3WvwR)lAtP0mAI^5VUYdBk5J)Hi$!f@kg zgtdqk_f4ZVVOtD2^4N-MTUxDbi`B|`+AFF*<xQhDaT?F4mTRlWGdjz)KlM$cb_PCJ zqHyXhKMM1qB?>-RqHyZ17F&w@rcpel&*TPM;qarf9#9@tC(?t4dOfgGsK`!4AW?WH z@(DIyAo%BPzNkmiCiWL+TLof&o6Q#p{w+3No7i8p`2w-O&E^XP|GdrT-vlu-Z?s-f z8eF%o8FSK0B!a$aD>iEVcSeG-mVG*IgAY#BpAm>YdeCsR@XsIqTW2KLW)=b`*qN=u zc#EZWs`ub;X{p^?EVbj`ga7;XTg5KE3(+q&g$oOPkB-m5{!6}<VG~sPZI0NvLf>4N z)nS#^Z*>Zv{bVWi&wl><2^BAlyg=!n%cRW!49_ex-s+#hG{S0MFMfkh`g~e!Bk0pv zahN_o7KiDRYH^r82^NRx(`j*-K8+QJ>2qIkm_EZ6hv_qEahN{07KiDRXK|Q5%N2*| zQ)6+MJ|Px|>9b*Rm_7#<hw1ZYahN`p7KiDxYH?Ux9?}u|1YCTdKJ6BV=@Vmdm_C~p zhv`#yaTvkB{h)GAmf)j!y$5!^_!9>{&_^nJ12nZWD(+MLoZ7F}PFXNy&)JUrF~2lt zVO$2V0rXwlb3VPYv-RB(r?BTbziN27=%)&nqF_vM>sT;l!I<LGid(tCxnOGT7<*W9 z3u7r@0vNjfd!N!Sf&P#^cX-%&?9@|KJC1xcv}@5%>CZzumZWu8<omWo^FlkF{t(KK zjS7mUwWo8y!oR2d_T2p7qvwmFMcalYb*N%`!^*P_Q|q^~@#dP4YlGh`G@-00Co4^; zOVhowcU7EHS-LTi%S&kPtuk}eOh-znuU^@HhOOrQU`aD<wW2!s`#(5_J87s53pnJW zdHQUb1+>dFJNTz3eDRfBbN`~}d%19%<L_UuE^*V`pA>bbq9!C8vah-(r@2+Wt)aQM zKYWX~CKSfK_Vo%^sCuOLRlC*KgogCU()4V6p6vqL(9W#v;A7r*%M2DGSh+Om5zjyK zB<)O?Cbmk0x1x}jd|kMvq?&RGX^+)3>8eTlzP)=_%`+$KRPCV&Egrf)O7rBM&Z!E` zz4gH-V>F=~$sT<)d1|hNJTv9P(lwNx2As>(q}}Q>^Gi(%+9Zvp-LY_(yA9Amo2RXC z!-hOVu@RY(gga2qh-+me<(%bGF1ewU>ys+w{um_XZeeG%od4I}l|V;TrRgt~g#bwi zTiD@I>`V3?$Wo~!B#?wG6_&7sN>z~*RH{N%B_R+N7hC`r1RGEk96;rOpbcu<f}*yA zaukrITaG|OA5Jqe*sV6xq9bO$|5XYiE(65lnVyrIeD8nnF8{svzx#c!?$6V#8)gMy zdwM%O#Z%`pMg(F;PJ4{2Z;uYf4(Q_Q0Hsey=tguzjJ_kz<#a^<u0eQeUJ#aL2BT(d z2u{|8AhWa+{Hu5paAPNY#KQpIS{jOJ?K<ONac6}0>w?m`R8`K?&ll*r@gTPDunp^u zyY*q%TN;L<1z~t&X%8qoRby&xPka;I3-2xNg>{R1;{$CUgiq;%qn-MqbW%Sosp^Lp z2K2`kZGXJQ)6T7v`(yp^0hriMfw`-K!y^<dj90K@se&=t1F^qIi!$vXgpD7BhOoi7 zlrRJX2M@*X^M_)?)S;N_9tz)%!|`OyaP*lu9J=|#@s}PW&`>!Nk?lv}V(=(cedN;` zDpuBxMnwK-bn6k0S$)DWkMp-HaglgGJ`y2SkuVlTVRmseJ`9b)KO<xDggF*p^o_%s z#yD*BOTdQF39!scK-$PeTuDxZCO;8xWG2B;pTvWYl5wbavZ@yI+O#nU&KiqtIb*Tf zH5U4~aj>|@;UOgjZ`P+^_WV@nr=($LLpt{4>5yBYLr(V$6fMj^h>sqtG<vM<t;g~4 zdLDS5i7ksWVNS`yyR)*O9g~f<wb?ioFdhL3<MD`bJXWUWV9=5r^vTP`m>Ic<;jcQZ z^C#eN&xuHzJP{}6PsGNB6R{^Q4?||=!LcL{_f+O%gEJrZcQ3%yx&j=ZUWiNnMd%k& zgyMch2%b}f)R1Bv>r#xsUd3n}Qj9|-#R!{Qj4O#H$eCNh6BG^Do@{`R)qutI2D}hf zioDFpnAv^`nu4dGJa;P6rcZ^d`!w|HF%9~5Wk?@YhV{v1SQ|SXTN9^aobL?0uA71A z?3t+QIt#rcXCc`!3s%2!G)0s{nODw3&S&Gv;j=MVW5j<YSD<5A1zzZ6!jf(#B;=W} z$!bC$hlvL@nz6RM8TVPu(A&+JTW5wf(Sn&dbMVpNO4OMvv8#O*Y8P5zEt`uSUFKnj zG7rB?vcW#dhPO;MbXs7;AA+iJcvv-d7^``hq8*XNc3e%XK}vZIO8lMJ6X?V%IZnis zI+1O0q00m}J|0+yrww(e>0Xb@VfDy~tw-qMdUQ`|KzRR!IOw|wmq#z+A@GfCtBo*B zZp8k;#aJ4#7%xp&j9nFrq3gX2T_!GrW5F`)Z+92Ym)(V41MkM))9*(4oV#%};a+UX zycd3^<w&htj#CZGv9Ira=rHJhY-+q8+cgiMUDpS2DDXi%(`5xZhpj+XzZH0W=n4dk zU4gX|R^WnT1q!;ZWSd$EP3lSn%v*`>i&kRm*i|quT!lX`c?f@uei(hbJp$e2M=*QT zqtL}Z3ZL3Xk>R%*8+5C2E@L%{vsYtv!D_tH?=dWDe2fPPuEEKyH3$n{i*>1M@v86R zxF`N`oK9YclY#5;S<m&zOk9r>uJyRweFF|m*}(GIfL9l6K;6tIan`yK&a$U)rT!^= zOZ4o#85P-^(SO!6D5?K9ycxCy*8W>?FlP%oJGP=OejASDZ^OzZ+c0Cmv)DWRS!icI ziyfwCRYiU|{hxy_`Z?^I^gM>xpNCJo?daKSJD%^m9oj+Lp-JD4m+aeB1-Yx{>_Go< zJ2Ag-CwflX32V^{c+7tnUg)z6=Gt9IT(S!@!giy6z-}ar*^R67c4J1D7h#z2B3?;- z3EPJJ7E4C%LEy|ic+j*5-^|*JiTz$iqxNOAFM1ho#lC`=fv;j|)T?-P*lReO{~Gq^ z{2tHcy^jB|yv{@E_ray?Lsr&4oS(W6Z+F{|%EkL}a_}2?yyOjhUhpP1RlEtK{{d_t zbO0Aq55Q)73-9>9jV|eLqoe6<3@m>a5BnX&^F0n?{>=B#bMAY{4SgS3#qT3w=KHFe z=Lg~sp)unSy44)QJ82&vyZi$b>p#Sf*&kv}=waA;97f)(!zc_qf;CY`P?dB9Nu7@3 z+{mN&WYkAkp7aquw||6JVvixoa18hAjw86}IR1V1aU7p_9G_1<fpwM>xEg*EpT(X; zNZCnL=lv1sA%DVSd4Ix%)Q@54a|)j(okB+NCpg;u6HJdejg?8Ku{`xOwx*oHI~iwC zH|-1>iGvAe@mlIxe6!#zjyL`pV>+J0sDyKPB<UPXj&sQKJ&&@nf5GA83%FEs0m04- zINkoQI8ydkbhBQ>m7$*^a^$B7wO_&z$0g*={ZGsfz6{O4%W#dnj6;^oXzK78M8)jG z$)6$7`Zv7R_%{Sj|2r-gegXfwFIBbM^?CnbAASYf>aJjW{6F!9|5rFu{1t}eUqx-V zud%7-YkZpa4bD`5i^I{^@M6+6#8+KIuSM7JhR=66*7zO#3p`6w@4<I6&dVCIDLrVJ z&>O~kK-3MJ&xUEd^F-0@@E(t^LOO#sNx*g}<=OW02nXtU6<e81c#X%G_?FGgW}N^n z?&a;-`2M2jK%)up_4o7F`1&=C#5vsp9BAHFSI^oPeg^8ltmeu2E+{A<ttTyJywwn0 z!8|ROe7Lvg@1yZ)7ohR}%1S->SAvUKCYKUwJ+Fr7@wM?OHbRZPV}PGVj2gTCM`G9U z9Dgfgk}!#$FrEA*fG@*naK33L(!D9(B%-0wtNyKwOTzL@+Y~jfcE0U38h?%7PsSyh z%tdd2mAQ0Y&n?q>+mfPGuIG>9^<!EiaDMu192lXdkR{{eNv!CPt!kDgQ_h$kkW_ko zX|Va5Z;uS;VT-EqX%=fCHJW-fmFJO`((Oe{)Z&miC!XLbuZ($_$Gdu^l+OM4wS%0> zdCVr(2r>CA5rvu&#xr&|(_p36b)I1xqL!xWqs7Hr6~$_qTtwrnojQy;*s4_al5`$9 z`S~-;0iN<X*EE<4yqitS@{yY0DOrUn5naJm^rhvrI681uPkeq<bHUH^L!o!a8lJma zWEfVyDBwAnG*kZyU#zd<*|>ge0dn;X-Sh)nu@N)S7eA`j!sFU@{doy&3pmhjFZQy? zyTpCp=Wn=KClK@6<hV4?-R)gH^-#hD#oRMr$|>-4lc*v6aTA!6E)MNKZ|dX|=))K< zc&Y>Yx2C1yBD5uBdc^9Xp6xB}9^<2K-?C;jhxXbF4dr55eog8z3rGZBv$?rhO7AJ= zG$E;NYVx)%PU+cS8rZ60)VAeR+m?eZOL{%2>7vEFmiM0iRJ4;wPa|RU>|Mio2Dv;r zRR5x{#Lee=wlQfR;v#p_?~dkErpm>nwxb%_d$eu;Ur0mW7IKi37BY>}%lx<`#D_-% zN;IX}4Oz@*4*Mk?d#EzHjmLR)+z?5XP%pNO21e#KkbHHNslwhUhuU5W8TyB&hE@TD zXL~k?B^JaUFo-{Y4r1j9;_sM)=pMu|HHaL8c-CbQ>p&3oA_S>_!MuhLorqAPGtq_U zN^~Q-6JbOTq9@U2{<S6WT@&jIXEZk(A6lGSzUj-aCsl>+SRIH_!}gqtqFoNXO8Xc0 z^@N1nOmL9GWkR}{BsxmxF_l#6qRjr}JFBgvGH?4Gsmx+NAQfI8lS)5yl~j5gKQfh? z6-p{^bwtOw99}t`RK6*SB9$RJiIfM<Bb}80Y7g0;G?U0N7z$&QLZ`#+Fga{WzQbH= zv$&{X#;`hxIS#o%z*To$)dZ*^BG1}jw$j#e@)E|b$*<ED;6^7y5B^(4<ML(X_z zNtrVKd<BzIve0NV%A`$=OCeR$J^JQ^k5;1N<E6V${juAIJcsKG@|5W4SS2YwS}C>I zgNrS7iq7Ll<X0dX5p5A>hr3!0Cw7Ipd}T^buG)D>{-nHQu#<a9U%u7kbhsRI+{&am zbF3zdGR5IE(>|T>nsmAOa?9jP6F&4&@8@VA-L(6d=YHy-fp5NaO#QA<cuT#I8H`Yw z#RzRPZ4zjcK$`^GB+w>-HVL#zpiKg85@?e^n*`b<@as$9r`G?np4OcF@Z`pbKA~&Y zas5B~>s_+0_P@#QUar~G30bF;K=z+xjV?Qhvi@5|$X>u&LiPeS67sX1t%Pi-?j&T* zzn73T{XRn0^nW1aJ-`Qq@IFDv`c=zFTCVlQF3V{(xm@a!-Lt+d@KNa|y2*M|_RWRL z4z6eY*-4F)RzFGmGcl5%45IaW2|?s0sg_l?NP*i8HjA1TiNlYZAM#nGn2!mu6HVI^ zVht~nX3?}L^6bbB6p`luQsI<FDtjyCr2NM`USw}&5vlB{93&O_9w!x9T_hEGcA_WQ zQ%NM1@|r{{@}EvB<->1l)V-ONq*5MFl5+Wu-K6rx*6XBFJ_kvq96uqI{ez37viJB^ z^RwhZ;w>-C%`DH!%Pp8#qUJ~9F0q(uomO{)-ez>U)Z8&~$aI(rjMWx3mt=xMr=`Z| zw3yZGN|wcgsJZrJlzSdlm+aR_Sb@IEVwy+q6846yMHYf$%N&c-VmDb-sfZ*qjc$vY zL<v06(oioO9wH(Uo35tD>8P`qGa6KBiM7E}U1MVyHIX7Z5u4X?vc;(;M(o5h(^^|? zvAYcoH5OH_lBztTy|UI=X;B3%7TH#trO@cEQY9^^%&{2FCGG~+5s|sbFW1hkt7@aV zGbQpDxh_ykK=M#xU1(8@L5j|smRvI<rg+tIk<46o$W)6+(pX+#sTT=a>h%pKMkj3L zMT1Vflk*%>seZ{UbfHzv&m`)zS`~RORaVRZLW=f=B4<&^>rs3w3S<-L){<XE^E1gE z@r%ES-;7rBi@S;6<W};Fy@_98EBVFT#BV|?`N^JQ%ks!<CBLZa{JN-oQ(MI^R+P{- zr_aMLsg?Y^;gi|+B;Q_n@>YGmz4|QbroW%Q(aq0y%I`+|@T6~OEBW1MA0B>6EBW1M zA0B=(Y>6Dj2)$kT-Dn>ke$w5whM%`Qz2z%?QET|!Xdj;R$r#ZZe$qZ<5+M2XPFs{* zyW8Tl8*R;Q(%olfmFSCe3k|uG3a)R>|J<ts8G3*0l>n{P@G^CfK3(FLJdjtGzQpq^ zZNBwWoE!0wof?niBPJ?3F+3_RJenDoxE117UU7)OjDoDf+^md@44pnhnLeS`W;NRF zMy2>43th&UbQgZ1M7AzdiH=HEqM~9GBBNted&zHcv`)PjS(otjj@jrm7do77r_t)B zmFgyG%(7S7tgfn3ibJ&%`D7dUZm5Ny8w0Y(kMI{GX^?xVCvq?Rym9gNy1V!OX5L;t z;@)Q3B+w>-HVOQy5|D|zO!{R|lg>y6a9K6U@=7`@S)Ry>MAi?|xyhnN237CEM;09N z4!<iQ9j&ZQWL+q0LK&1~Ehv+OK7_ot?ng*RDGLRK7)WS|LBwEU2qBZ6VZ?A^1Tm5r zMSRE4ox({Yh)6;PQ|WkR(3gos93c}PnMBFNOeT9WfgD4OCB_lLBb79bNGEhe2B9Z1 zi7X<U7*FI7xr9uzClYx?K2bnSA_|EjqL`4u!$6c0vS^q>OeLlfWrUm;FN+fyxMmUM z#B9PyR1hY@Ovs=!ho~f~2rDs{m`B)%YQj!9h#F!(;UrvyjB2$+9Z^p#AR35;#3G`R zSWGM-mJ-W|yNJ7qdx(39<-~o&{lo*rgTxAAC9#SS`8`bf2=OSfns|&@L#!pl{WqlR zi1ow<;tAqOVk05$*P5}J&(Ad5iT&2*d)~I0Ur7S;ZL@r<rm*JQNu6qqVL{xY34_0n zH>ti16VP6xhCJX2FHPyX**tN^Sot<&3vJTHGfgI2fPCY3r=|lm*W;IT+zOx3^rTlj zKBL)cjckGPjpCh{5cZxv4Cs}?t?)>pr}U>iKBrLnPPY6^QWIORe0whCaI2;_^WTA< z{fS%6zwnNiPKw5#J(t8P-?vNI=Q4KBcba13{mRng_s6co??C<!38PRCH~$~x{Pxn5 z#Q6Ql>UQM+c#HV+*>gIs*TNF!Sjywp>q2jqzBlf6o-F;~dca($)_ZULZ|UJMN0L+A z?LNg(r<fy78Ap(_9F<D=8;CfZ!P^{hPO*-jpj`_8ZZ<LSxnBPRZ+2{c{<-*bnPZDO zs{BOY|Hazd{@T;u?bN>zX*4vRZ)Izk2B%uv<V)e(F=1?fUisf@{L)rRo8$N4$S=R{ zs9`x&s3Yy|#4d9mjr#Ru0E69X{2l36#_V6p+;09`#w%iw9$UI&X-6^+OJ1ZdOMQ}Z zmHtKSgPLi}_}cFOwIwhvZ9%n7sq?(HNYO?|L}?Yi9&wnh_R17(sUbT&NvpWj-4mO` zZb{MF9on?iaTQ}q9JNjpzo;#-*eoWu#q+u%MLR=RsEe3dlBZSF_ZRY#Bt=U*t-^Th zt}zuU+A6oZW=v$H%T#5lHo77lH5NPFJg+zS=&X!%sGpvC-<?FpL`5Y;R#+=+R!61N zSX0%|%<LCol{zl+dS+x{aGU1N<QLpwoY^(TY;+r4Zimy7>$X%Yw6K!qW~OKtWyK}L zWo2a~g~uhv$A!mbC&Y&*MaAjDlM*vBlalmV@v)gvi@hQ>yY5sFrdmc{J+<1VO_lU` zN`!C7T;G3`r13@FoyPLLl8dCgZ;DEuzbA^@DFz?*Oup=(q!r#g_f%MxH@;_<51EzZ n#BkQ8SjAhn)V?jhMBHelPD<oH!s{N9#Gk?cRu)<RPtE@TF_Jh; literal 0 HcmV?d00001 diff --git a/doc/HTTP2_CNL113851_PRI.doc b/doc/HTTP2_CNL113851_PRI.doc new file mode 100644 index 0000000000000000000000000000000000000000..65266313944d471438daaeed3731c9c7365f39e1 GIT binary patch literal 59392 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 GRsRpjE**FP literal 0 HcmV?d00001 diff --git a/src/HTTP2_EncDec.cc b/src/HTTP2_EncDec.cc new file mode 100644 index 0000000..df739c8 --- /dev/null +++ b/src/HTTP2_EncDec.cc @@ -0,0 +1,1928 @@ +/****************************************************************************** +* Copyright (c) 2017 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gabor Szalai - initial implementation and initial documentation +******************************************************************************/ +// +// File: HTTP2_EncDec.cc +// Description: Encoder/decoder function for HTTP2 +// Rev: R1A +// Prodnr: CNL 113 851 + + +#include "HTTP2_Types.hh" +#include <stdint.h> +#include "memory.h" +#include <string.h> +#include <stdio.h> +#include <deque> + +// header compression context classes +class HTTP2_hdr_data{ +public: + CHARSTRING name; + CHARSTRING value; + int size; + + HTTP2_hdr_data(){ + name=""; + value=""; + size=32; + }; + HTTP2_hdr_data(const CHARSTRING& p_name, const CHARSTRING& p_value){ + name=p_name; + value=p_value; + size=32+name.lengthof()+value.lengthof(); + }; + ~HTTP2_hdr_data(){}; + + void set(const CHARSTRING& p_name, const CHARSTRING& p_value){ + name=p_name; + value=p_value; + size=32+name.lengthof()+value.lengthof(); + } + +}; + + +class HTTP2_compression_ctx{ +public: + std::deque<HTTP2_hdr_data*> h_table; + int h_table_size; + int h_table_max_size; + + HTTP2_compression_ctx(){ + h_table_size=0; + h_table_max_size=4096; + }; + HTTP2_compression_ctx(int max_size){ + h_table_size=0; + h_table_max_size=max_size; + }; + + ~HTTP2_compression_ctx(){ + std::deque<HTTP2_hdr_data*>::iterator it = h_table.begin(); + while(it!=h_table.end()){ + delete *it; + it++; + } + }; + + void set_max_size(int new_size){ + while(new_size<h_table_size){ + h_table_size-= h_table.back()->size; + delete h_table.back(); + h_table.pop_back(); + } + h_table_max_size=new_size; + }; + + void add_hdr(const CHARSTRING& p_name, const CHARSTRING& p_value){ + HTTP2_hdr_data *hdata= new HTTP2_hdr_data(p_name,p_value); + h_table.push_front(hdata); + h_table_size+=hdata->size; + while(h_table_max_size<h_table_size){ + h_table_size-= h_table.back()->size; + delete h_table.back(); + h_table.pop_back(); + }; + + }; + + int find(const CHARSTRING& p_name) const { + for(size_t i=0;i<h_table.size();i++){ + if(h_table[i]->name==p_name){ + return i+1; + } + } + return -1; + }; + int find(const CHARSTRING& p_name, const CHARSTRING& p_value) const { + for(size_t i=0;i<h_table.size();i++){ + if( (h_table[i]->name==p_name) && (h_table[i]->value==p_value) ){ + return i+1; + } + } + return -1; + }; + + const HTTP2_hdr_data* operator[](int idx) const{ + if( (idx>0) && (idx<=(int)h_table.size()) ){ + return h_table[idx-1]; + } + return NULL; + }; + +}; + +class HTTP2_compression_ctxs{ +public: + HTTP2_compression_ctx local; + HTTP2_compression_ctx remote; + +}; + +class HTTP2_static_table_class{ +public: + HTTP2_compression_ctx ctx; + HTTP2_static_table_class(){ + ctx.add_hdr("www-authenticate",""); + ctx.add_hdr("via",""); + ctx.add_hdr("vary",""); + ctx.add_hdr("user-agent",""); + ctx.add_hdr("transfer-encoding",""); + ctx.add_hdr("strict-transport-security",""); + ctx.add_hdr("set-cookie",""); + ctx.add_hdr("server",""); + ctx.add_hdr("retry-after",""); + ctx.add_hdr("refresh",""); + ctx.add_hdr("referer",""); + ctx.add_hdr("range",""); + ctx.add_hdr("proxy-authorization",""); + ctx.add_hdr("proxy-authenticate",""); + ctx.add_hdr("max-forwards",""); + ctx.add_hdr("location",""); + ctx.add_hdr("link",""); + ctx.add_hdr("last-modified",""); + ctx.add_hdr("if-unmodified-since",""); + ctx.add_hdr("if-range",""); + ctx.add_hdr("if-none-match",""); + ctx.add_hdr("if-modified-since",""); + ctx.add_hdr("if-match",""); + ctx.add_hdr("host",""); + ctx.add_hdr("from",""); + ctx.add_hdr("expires",""); + ctx.add_hdr("expect",""); + ctx.add_hdr("etag",""); + ctx.add_hdr("date",""); + ctx.add_hdr("cookie",""); + ctx.add_hdr("content-type",""); + ctx.add_hdr("content-range",""); + ctx.add_hdr("content-location",""); + ctx.add_hdr("content-length",""); + ctx.add_hdr("content-language",""); + ctx.add_hdr("content-encoding",""); + ctx.add_hdr("content-disposition",""); + ctx.add_hdr("cache-control",""); + ctx.add_hdr("authorization",""); + ctx.add_hdr("allow",""); + ctx.add_hdr("age",""); + ctx.add_hdr("access-control-allow-origin",""); + ctx.add_hdr("accept",""); + ctx.add_hdr("accept-ranges",""); + ctx.add_hdr("accept-language",""); + ctx.add_hdr("accept-encoding","gzip, deflate"); + ctx.add_hdr("accept-charset",""); + ctx.add_hdr(":status","500"); + ctx.add_hdr(":status","404"); + ctx.add_hdr(":status","400"); + ctx.add_hdr(":status","304"); + ctx.add_hdr(":status","206"); + ctx.add_hdr(":status","204"); + ctx.add_hdr(":status","200"); + ctx.add_hdr(":scheme","https"); + ctx.add_hdr(":scheme","http"); + ctx.add_hdr(":path","/index.html"); + ctx.add_hdr(":path","/"); + ctx.add_hdr(":method","POST"); + ctx.add_hdr(":method","GET"); + ctx.add_hdr(":authority",""); + } +}; + +static const HTTP2_static_table_class HTTP2_static_table=HTTP2_static_table_class(); + +////// Indexed variable int representation functions +// Support max 32 bit unsigned val +// it means the buffer should be at least 6 octet to safely store the max value +// with the minimal prefix + +static const unsigned char twoN_minus_one_table[]={ // stores 2^N-1 for N=0..8 +0,1,3,7,15,31,63,127,255 +}; + +// return the number of used octets +static int encode_integer(unsigned char* buff, + const uint32_t val, + const int prefix_len, + const unsigned char filler){ // only the filler bits should be set + int ret_val=0; + if(val< twoN_minus_one_table[prefix_len]){ + // fits in one octet + buff[0]= filler | (val & 0xFF); + ret_val=1; + } else { + uint32_t act_val=val-twoN_minus_one_table[prefix_len]; + buff[0]= filler | ( twoN_minus_one_table[prefix_len] & 0xFF); + ret_val=1; + while(act_val>127){ + buff[ret_val]= 0x80 | (act_val%128); + act_val/=128; + ret_val++; + } + buff[ret_val]= act_val; + ret_val++; + } + return ret_val; +} + +// returns the number of the used octets +static int decode_integer(const unsigned char* buff, + uint32_t& val, + const int prefix_len, + unsigned char& filler){ // only the filler bits will be set + filler = buff[0] & ~twoN_minus_one_table[prefix_len]; + + val = buff[0] & twoN_minus_one_table[prefix_len]; + + int ret_val=0; + if(val==twoN_minus_one_table[prefix_len]){ + // The value is not fit into the prefix_len bits + do { + ret_val++; + val += ( ((uint32_t)(buff[ret_val] & 0x7F)) << (7*ret_val) ); + } while(buff[ret_val] & 0x80); + ret_val++; + } else { + ret_val=1; + } + return ret_val; +} + + + + + +//////////// Huffman tables See RFC7541 + +// Huffman code lengths in bits + +static const int huffman_code_length[]={ + 13, 23, 28, 28, 28, 28, 28, 28, 28, 24, 30, 28, 28, 30, 28, 28, + 28, 28, 28, 28, 28, 28, 30, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 6, 10, 10, 12, 13, 6, 8, 11, 10, 10, 8, 11, 8, 6, 6, 6, + 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 7, 8, 15, 6, 12, 10, + 13, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 8, 7, 8, 13, 19, 13, 14, 6, + 15, 5, 6, 5, 6, 5, 6, 6, 6, 5, 7, 7, 6, 6, 6, 5, + 6, 7, 6, 5, 5, 6, 7, 7, 7, 7, 7, 15, 11, 14, 13, 28, + 20, 22, 20, 20, 22, 22, 22, 23, 22, 23, 23, 23, 23, 23, 24, 23, + 24, 24, 22, 23, 24, 23, 23, 23, 23, 21, 22, 23, 22, 23, 23, 24, + 22, 21, 20, 22, 22, 23, 23, 21, 23, 22, 22, 24, 21, 22, 23, 23, + 21, 21, 22, 21, 23, 22, 23, 23, 20, 22, 22, 22, 23, 22, 22, 23, + 26, 26, 20, 19, 22, 23, 22, 25, 26, 26, 26, 27, 27, 26, 24, 25, + 19, 21, 26, 27, 27, 26, 27, 24, 21, 21, 26, 26, 28, 27, 27, 27, + 20, 24, 20, 21, 22, 21, 21, 23, 22, 22, 25, 25, 24, 24, 26, 23, + 26, 27, 26, 26, 27, 27, 27, 27, 27, 28, 27, 27, 27, 27, 27, 26, + 30 +}; + +static const unsigned int huffman_code_values[]={ + 0x1ff8, 0x7fffd8, 0xfffffe2, 0xfffffe3, 0xfffffe4, 0xfffffe5, 0xfffffe6, 0xfffffe7, 0xfffffe8, 0xffffea, 0x3ffffffc, 0xfffffe9, 0xfffffea, 0x3ffffffd, 0xfffffeb, 0xfffffec, + 0xfffffed, 0xfffffee, 0xfffffef, 0xffffff0, 0xffffff1, 0xffffff2, 0x3ffffffe, 0xffffff3, 0xffffff4, 0xffffff5, 0xffffff6, 0xffffff7, 0xffffff8, 0xffffff9, 0xffffffa, 0xffffffb, + 0x14, 0x3f8, 0x3f9, 0xffa, 0x1ff9, 0x15, 0xf8, 0x7fa, 0x3fa, 0x3fb, 0xf9, 0x7fb, 0xfa, 0x16, 0x17, 0x18, + 0x0, 0x1, 0x2, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x5c, 0xfb, 0x7ffc, 0x20, 0xffb, 0x3fc, + 0x1ffa, 0x21, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, + 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0xfc, 0x73, 0xfd, 0x1ffb, 0x7fff0, 0x1ffc, 0x3ffc, 0x22, + 0x7ffd, 0x3, 0x23, 0x4, 0x24, 0x5, 0x25, 0x26, 0x27, 0x6, 0x74, 0x75, 0x28, 0x29, 0x2a, 0x7, + 0x2b, 0x76, 0x2c, 0x8, 0x9, 0x2d, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7ffe, 0x7fc, 0x3ffd, 0x1ffd, 0xffffffc, + 0xfffe6, 0x3fffd2, 0xfffe7, 0xfffe8, 0x3fffd3, 0x3fffd4, 0x3fffd5, 0x7fffd9, 0x3fffd6, 0x7fffda, 0x7fffdb, 0x7fffdc, 0x7fffdd, 0x7fffde, 0xffffeb, 0x7fffdf, + 0xffffec, 0xffffed, 0x3fffd7, 0x7fffe0, 0xffffee, 0x7fffe1, 0x7fffe2, 0x7fffe3, 0x7fffe4, 0x1fffdc, 0x3fffd8, 0x7fffe5, 0x3fffd9, 0x7fffe6, 0x7fffe7, 0xffffef, + 0x3fffda, 0x1fffdd, 0xfffe9, 0x3fffdb, 0x3fffdc, 0x7fffe8, 0x7fffe9, 0x1fffde, 0x7fffea, 0x3fffdd, 0x3fffde, 0xfffff0, 0x1fffdf, 0x3fffdf, 0x7fffeb, 0x7fffec, + 0x1fffe0, 0x1fffe1, 0x3fffe0, 0x1fffe2, 0x7fffed, 0x3fffe1, 0x7fffee, 0x7fffef, 0xfffea, 0x3fffe2, 0x3fffe3, 0x3fffe4, 0x7ffff0, 0x3fffe5, 0x3fffe6, 0x7ffff1, + 0x3ffffe0, 0x3ffffe1, 0xfffeb, 0x7fff1, 0x3fffe7, 0x7ffff2, 0x3fffe8, 0x1ffffec, 0x3ffffe2, 0x3ffffe3, 0x3ffffe4, 0x7ffffde, 0x7ffffdf, 0x3ffffe5, 0xfffff1, 0x1ffffed, + 0x7fff2, 0x1fffe3, 0x3ffffe6, 0x7ffffe0, 0x7ffffe1, 0x3ffffe7, 0x7ffffe2, 0xfffff2, 0x1fffe4, 0x1fffe5, 0x3ffffe8, 0x3ffffe9, 0xffffffd, 0x7ffffe3, 0x7ffffe4, 0x7ffffe5, + 0xfffec, 0xfffff3, 0xfffed, 0x1fffe6, 0x3fffe9, 0x1fffe7, 0x1fffe8, 0x7ffff3, 0x3fffea, 0x3fffeb, 0x1ffffee, 0x1ffffef, 0xfffff4, 0xfffff5, 0x3ffffea, 0x7ffff4, + 0x3ffffeb, 0x7ffffe6, 0x3ffffec, 0x3ffffed, 0x7ffffe7, 0x7ffffe8, 0x7ffffe9, 0x7ffffea, 0x7ffffeb, 0xffffffe, 0x7ffffec, 0x7ffffed, 0x7ffffee, 0x7ffffef, 0x7fffff0, 0x3ffffee, + 0x3fffffff +}; + + +// inserts the huffman code of one character into teh buffer +static void put_code2buff(unsigned char* buff, int& bitpos, unsigned char code){ + int code_len = huffman_code_length[code]; + int bitpos_in_octet = bitpos%8; + + // moves to the first bit of the huffman code to the insertion point + int shift= (16-bitpos_in_octet-(code_len%8))%8; + uint64_t ins_val= ((uint64_t)huffman_code_values[code]) << shift; + + + buff+= bitpos/8; + for(int i=((code_len+shift+7)/8)-1;i>=0;i--) { + if(i==0){ + buff[i] |= (ins_val & 0xFF); + } else { + buff[i] = (ins_val & 0xFF); + } + ins_val >>= 8; + } + bitpos+=code_len; +} + +static const unsigned char get_mask_table[]={ +0x80, 0x40, 0x20, 0x10, +0x08, 0x04, 0x02, 0x01 +}; + +static int get_onebit(const unsigned char* buff, int& bitpos){ + if(buff[bitpos/8] & get_mask_table[bitpos%8]){ + bitpos++; + return 1; + } + bitpos++; + return 0; +} + +// return -1 or the char code or the EOF (256) +static int decompress_one_char(const unsigned char* buff, int& bitpos, int length /*in bits*/ ){ + unsigned int huffman_val=0; + + if((bitpos+5)>length){ + while(bitpos<length){ + if(get_onebit(buff,bitpos)!=1) {return -1;} // incomplete code + } + return 256; //EOF + } + + // get the first 5 bit, the minimum code length is 5 + for(int i=0;i<5;i++){ + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + } + // Check the 5 bit codes + // See RFC7541 for magic values + switch(huffman_val){ + case 0x0: + return 48; + case 0x1: + return 49; + case 0x2: + return 50; + case 0x3: + return 97; + case 0x4: + return 99; + case 0x5: + return 101; + case 0x6: + return 105; + case 0x7: + return 111; + case 0x8: + return 115; + case 0x9: + return 116; + } + + if(bitpos==length){ // no more bits + if(huffman_val == 0x1f){ // all bit is 1 + return 256; //EOF + } else { + return -1; // invalid code + } + } + + // 6 bit codes + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + + switch(huffman_val){ + case 0x14: + return 32; + case 0x15: + return 37; + case 0x16: + return 45; + case 0x17: + return 46; + case 0x18: + return 47; + case 0x19: + return 51; + case 0x1a: + return 52; + case 0x1b: + return 53; + case 0x1c: + return 54; + case 0x1d: + return 55; + case 0x1e: + return 56; + case 0x1f: + return 57; + case 0x20: + return 61; + case 0x21: + return 65; + case 0x22: + return 95; + case 0x23: + return 98; + case 0x24: + return 100; + case 0x25: + return 102; + case 0x26: + return 103; + case 0x27: + return 104; + case 0x28: + return 108; + case 0x29: + return 109; + case 0x2a: + return 110; + case 0x2b: + return 112; + case 0x2c: + return 114; + case 0x2d: + return 117; + } + + if(bitpos==length){ // no more bits + if(huffman_val == 0x3f){ // all bit is 1 + return 256; //EOF + } else { + return -1; // invalid code + } + } + + // 7 bit codes + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + switch(huffman_val){ + case 0x5c: + return 58; + case 0x5d: + return 66; + case 0x5e: + return 67; + case 0x5f: + return 68; + case 0x60: + return 69; + case 0x61: + return 70; + case 0x62: + return 71; + case 0x63: + return 72; + case 0x64: + return 73; + case 0x65: + return 74; + case 0x66: + return 75; + case 0x67: + return 76; + case 0x68: + return 77; + case 0x69: + return 78; + case 0x6a: + return 79; + case 0x6b: + return 80; + case 0x6c: + return 81; + case 0x6d: + return 82; + case 0x6e: + return 83; + case 0x6f: + return 84; + case 0x70: + return 85; + case 0x71: + return 86; + case 0x72: + return 87; + case 0x73: + return 89; + case 0x74: + return 106; + case 0x75: + return 107; + case 0x76: + return 113; + case 0x77: + return 118; + case 0x78: + return 119; + case 0x79: + return 120; + case 0x7a: + return 121; + case 0x7b: + return 122; + } + + if(bitpos==length){ // no more bits + if(huffman_val == 0x7f){ // all bit is 1 + return 256; //EOF + } else { + return -1; // invalid code + } + } + + // 8bit codes + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + switch(huffman_val){ + case 0xf8: + return 38; + case 0xf9: + return 42; + case 0xfa: + return 44; + case 0xfb: + return 59; + case 0xfc: + return 88; + case 0xfd: + return 90; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 10bit codes + switch(huffman_val){ + case 0x3f8: + return 33; + case 0x3f9: + return 34; + case 0x3fa: + return 40; + case 0x3fb: + return 41; + case 0x3fc: + return 63; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 11bit codes + switch(huffman_val){ + case 0x7fa: + return 39; + case 0x7fb: + return 43; + case 0x7fc: + return 124; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 12bit codes + + switch(huffman_val){ + case 0xffa: + return 35; + case 0xffb: + return 62; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 13bit codes + + switch(huffman_val){ + case 0x1ff8: + return 0; + case 0x1ff9: + return 36; + case 0x1ffa: + return 64; + case 0x1ffb: + return 91; + case 0x1ffc: + return 93; + case 0x1ffd: + return 126; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 14bit codes + switch(huffman_val){ + case 0x3ffc: + return 94; + case 0x3ffd: + return 125; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 15bit codes + switch(huffman_val){ + case 0x7ffc: + return 60; + case 0x7ffd: + return 96; + case 0x7ffe: + return 123; + } + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 19bit codes + switch(huffman_val){ + case 0x7fff0: + return 92; + case 0x7fff1: + return 195; + case 0x7ff2: + return 208; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 20bit codes + switch(huffman_val){ + case 0xfffe6: + return 128; + case 0xfffe7: + return 130; + case 0xfffe8: + return 131; + case 0xfffe9: + return 162; + case 0xfffea: + return 184; + case 0xfffeb: + return 194; + case 0xfffec: + return 224; + case 0xfffed: + return 226; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 21bit codes + switch(huffman_val){ + case 0x1fffdc: + return 153; + case 0x1fffdd: + return 161; + case 0x1fffde: + return 167; + case 0x1fffdf: + return 172; + case 0x1fffe0: + return 176; + case 0x1fffe1: + return 177; + case 0x1fffe2: + return 179; + case 0x1fffe3: + return 209; + case 0x1fffe4: + return 216; + case 0x1fffe5: + return 217; + case 0x1fffe6: + return 227; + case 0x1fffe7: + return 229; + case 0x1fffe8: + return 230; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 22bit codes + switch(huffman_val){ + case 0x3fffd2: + return 129; + case 0x3fffd3: + return 132; + case 0x3fffd4: + return 133; + case 0x3fffd5: + return 134; + case 0x3fffd6: + return 136; + case 0x3fffd7: + return 146; + case 0x3fffd8: + return 154; + case 0x3fffd9: + return 156; + case 0x3fffda: + return 160; + case 0x3fffdb: + return 163; + case 0x3fffdc: + return 164; + case 0x3fffdd: + return 169; + case 0x3fffde: + return 170; + case 0x3fffdf: + return 173; + case 0x3fffe0: + return 178; + case 0x3fffe1: + return 181; + case 0x3fffe2: + return 185; + case 0x3fffe3: + return 186; + case 0x3fffe4: + return 187; + case 0x3fffe5: + return 189; + case 0x3fffe6: + return 190; + case 0x3fffe7: + return 196; + case 0x3fffe8: + return 198; + case 0x3fffe9: + return 228; + case 0x3fffea: + return 232; + case 0x3fffeb: + return 233; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 23bit codes + switch(huffman_val){ + case 0x7fffd8: + return 1; + case 0x7fffd9: + return 135; + case 0x7fffda: + return 137; + case 0x7fffdb: + return 138; + case 0x7fffdc: + return 139; + case 0x7fffdd: + return 140; + case 0x7fffde: + return 141; + case 0x7fffdf: + return 143; + case 0x7fffe0: + return 147; + case 0x7fffe1: + return 149; + case 0x7fffe2: + return 150; + case 0x7fffe3: + return 151; + case 0x7fffe4: + return 152; + case 0x7fffe5: + return 155; + case 0x7fffe6: + return 157; + case 0x7fffe7: + return 158; + case 0x7fffe8: + return 165; + case 0x7fffe9: + return 166; + case 0x7fffea: + return 168; + case 0x7fffeb: + return 174; + case 0x7fffec: + return 175; + case 0x7fffed: + return 180; + case 0x7fffee: + return 182; + case 0x7fffef: + return 183; + case 0x7ffff0: + return 188; + case 0x7ffff1: + return 191; + case 0x7ffff2: + return 197; + case 0x7ffff3: + return 231; + case 0x7ffff4: + return 239; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 24bit codes + switch(huffman_val){ + case 0xffffea: + return 9; + case 0xffffeb: + return 142; + case 0xffffec: + return 144; + case 0xffffed: + return 145; + case 0xffffee: + return 148; + case 0xffffef: + return 159; + case 0xfffff0: + return 171; + case 0xfffff1: + return 206; + case 0xfffff2: + return 215; + case 0xfffff3: + return 225; + case 0xfffff4: + return 236; + case 0xfffff5: + return 237; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 25bit codes + switch(huffman_val){ + case 0x1ffffec: + return 199; + case 0x1ffffed: + return 207; + case 0x1ffffee: + return 234; + case 0x1ffffef: + return 235; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 26bit codes + switch(huffman_val){ + case 0x3ffffe0: + return 192; + case 0x3ffffe1: + return 193; + case 0x3ffffe2: + return 200; + case 0x3ffffe3: + return 201; + case 0x3ffffe4: + return 202; + case 0x3ffffe5: + return 205; + case 0x3ffffe6: + return 210; + case 0x3ffffe7: + return 213; + case 0x3ffffe8: + return 218; + case 0x3ffffe9: + return 219; + case 0x3ffffea: + return 238; + case 0x3ffffeb: + return 240; + case 0x3ffffec: + return 242; + case 0x3ffffed: + return 243; + case 0x3ffffee: + return 255; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 27bit codes + switch(huffman_val){ + case 0x3ffffde: + return 203; + case 0x3ffffdf: + return 204; + case 0x3ffffe0: + return 211; + case 0x3ffffe1: + return 212; + case 0x3ffffe2: + return 214; + case 0x3ffffe3: + return 221; + case 0x3ffffe4: + return 222; + case 0x3ffffe5: + return 223; + case 0x3ffffe6: + return 241; + case 0x3ffffe7: + return 244; + case 0x3ffffe8: + return 245; + case 0x3ffffe9: + return 246; + case 0x3ffffea: + return 247; + case 0x3ffffeb: + return 248; + case 0x3ffffec: + return 250; + case 0x3ffffed: + return 251; + case 0x3ffffee: + return 252; + case 0x3ffffef: + return 253; + case 0x3fffff0: + return 254; + } + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 28bit codes + switch(huffman_val){ + case 0xfffffe2: + return 2; + case 0xfffffe3: + return 3; + case 0xfffffe4: + return 4; + case 0xfffffe5: + return 5; + case 0xfffffe6: + return 6; + case 0xfffffe7: + return 7; + case 0xfffffe8: + return 8; + case 0xfffffe9: + return 11; + case 0xfffffea: + return 12; + case 0xfffffeb: + return 14; + case 0xfffffec: + return 15; + case 0xfffffed: + return 16; + case 0xfffffee: + return 17; + case 0xfffffef: + return 18; + case 0xffffff0: + return 19; + case 0xffffff1: + return 20; + case 0xffffff2: + return 21; + case 0xffffff3: + return 23; + case 0xffffff4: + return 24; + case 0xffffff5: + return 25; + case 0xffffff6: + return 26; + case 0xffffff7: + return 27; + case 0xffffff8: + return 28; + case 0xffffff9: + return 29; + case 0xffffffa: + return 30; + case 0xffffffb: + return 31; + case 0xffffffc: + return 127; + case 0xffffffd: + return 220; + case 0xffffffe: + return 249; + } + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + + if(bitpos==length){ // no more bits + return -1; // invalid code + } + huffman_val<<=1; + huffman_val+=get_onebit(buff,bitpos); + // 30bit codes + switch(huffman_val){ + case 0x3ffffffc: + return 10; + case 0x3ffffffd: + return 13; + case 0x3ffffffe: + return 22; + case 0x3fffffff: + return 256; + } + +return -1; +} + +static const unsigned char mask_table[]={ +0xff, 0x7f, 0x3f, 0x1f, +0x0f, 0x07, 0x03, 0x01 +}; + +static OCTETSTRING enc_huff(const OCTETSTRING& pl__stream){ + int enc_len=0; + const unsigned char* ptr=(const unsigned char*)pl__stream; + for(int i=0;i<pl__stream.lengthof();i++){ + enc_len+=huffman_code_length[ptr[i]]; + } + enc_len=(enc_len+7)/8; + + unsigned char* res=(unsigned char*)Malloc(enc_len*sizeof(unsigned char)); + memset(res,0,enc_len); + + int bitpos=0; + for(int i=0;i<pl__stream.lengthof();i++){ + put_code2buff(res,bitpos,ptr[i]); + } + + if(bitpos%8){ + res[bitpos/8]|=mask_table[bitpos%8]; + } + OCTETSTRING ret_val=OCTETSTRING(enc_len,res); + Free(res); + return ret_val; +} + +static OCTETSTRING dec_huff(const OCTETSTRING& pl__stream){ + int bitpos=0; + int len=pl__stream.lengthof()*8; + const unsigned char* ptr=(const unsigned char*)pl__stream; + + OCTETSTRING ret_val=OCTETSTRING(0,NULL); + int res=decompress_one_char(ptr,bitpos,len); + while( (res!=-1) && (res!=256) ){ + ret_val = ret_val + int2oct(res,1); + res=decompress_one_char(ptr,bitpos,len); + } + return ret_val; +} + +// Encode the string according to the RFC7541 5.2 +static OCTETSTRING enc_http2_string(const OCTETSTRING& pl__stream, const int use_huffman){ + unsigned char len_field[6]; + + OCTETSTRING str= use_huffman?enc_huff(pl__stream):pl__stream; + + int len_len= encode_integer(len_field,str.lengthof(),7,use_huffman?0x80:0); + + return OCTETSTRING(len_len,len_field) + str; + +} + +static int dec_http2_string(const unsigned char* ptr, int ptr_len, OCTETSTRING& pl__stream){ + uint32_t str_len=0; + unsigned char huff=0; + int len_len=decode_integer(ptr,str_len,7,huff); + ptr+=len_len; + if(ptr_len<len_len+(int)str_len){ + return -1; + } + + pl__stream=huff?dec_huff(OCTETSTRING(str_len,ptr)):OCTETSTRING(str_len,ptr); + return len_len+str_len; +} + +static HTTP2_compression_ctxs* get_ptr(const INTEGER& val){ + return (HTTP2_compression_ctxs*)(val.get_long_long_val()); +} + +static OCTETSTRING HTTP2_encode_one_header(const CHARSTRING& hname, const CHARSTRING& hval, HTTP2_compression_ctx* comp_ctx){ + + // first search in the dynamic table + OCTETSTRING ret_val=OCTETSTRING(0,NULL); + unsigned char int_encode_buff[6]; + + int idx=comp_ctx->find(hname,hval); + int idx_offset=61; // size of the fixed table + + if(idx==-1){ // not found it + // check the fixed table + idx=HTTP2_static_table.ctx.find(hname,hval); + idx_offset=0; + } + int idx_len=0; + if(idx>0){ // found it + idx_len=encode_integer(int_encode_buff,idx+idx_offset,7,0x80); + return OCTETSTRING(idx_len,int_encode_buff); + } + + // not found, lets check for the header name + idx_offset=61; + comp_ctx->find(hname); + if(idx==-1){ // not found it + // check the fixed table + idx=HTTP2_static_table.ctx.find(hname); + idx_offset=0; + } + + int can_be_added=((32+hname.lengthof()+hval.lengthof())>(comp_ctx->h_table_max_size))?0:1; // Do not add if the header size is more than the max table size + + if(idx>0){ // found it + if(can_be_added){ + idx_len=encode_integer(int_encode_buff,idx+idx_offset,6,0x40); + ret_val=OCTETSTRING(idx_len,int_encode_buff); + } else { + idx_len=encode_integer(int_encode_buff,idx+idx_offset,4,0x00); + ret_val=OCTETSTRING(idx_len,int_encode_buff); + } + ret_val=ret_val+enc_http2_string(char2oct(hval),1); + } else { + if(can_be_added){ + idx_len=encode_integer(int_encode_buff,0,6,0x40); + ret_val=OCTETSTRING(idx_len,int_encode_buff); + } else { + idx_len=encode_integer(int_encode_buff,0,4,0x00); + ret_val=OCTETSTRING(idx_len,int_encode_buff); + } + ret_val=ret_val+enc_http2_string(char2oct(hname),1); + ret_val=ret_val+enc_http2_string(char2oct(hval),1); + } + + if(can_be_added){ + comp_ctx->add_hdr(hname,hval); + } + + return ret_val; +} + +static int HTTP2_decode_one_header(const unsigned char* buff, int buff_len, CHARSTRING& hname, CHARSTRING& hval, HTTP2_compression_ctx* comp_ctx){ + if(buff_len<1){ + return -1; //not enough data + } + + int ret_val=0; + uint32_t idx=0; + + if((buff[0] & 0xE0)== 0x20){ // Dynamic Table Size Update + unsigned char filler; + ret_val=decode_integer(buff,idx,5,filler); + if(ret_val>buff_len){ + return -1; + } + + comp_ctx->set_max_size(idx); +//printf("Dynamic Table Size Update %d\r\n", idx); + return ret_val; + + } + + if(buff[0] & 0x80){ // indexed field representation + unsigned char filler; + ret_val=decode_integer(buff,idx,7,filler); +//printf("indexed field representation %d\r\n", idx); + + if(ret_val>buff_len){ + return -1; + } + + if(idx==0){ + // invalid value + return -1; + } + + if(idx<62){ + hname=HTTP2_static_table.ctx[idx]->name; + hval=HTTP2_static_table.ctx[idx]->value; + } else { + idx-=61; + const HTTP2_hdr_data* d=((*comp_ctx)[idx]); + if(d==NULL){ + return -1; + } + hname=d->name; + hval=d->value; + } + return ret_val; + } + + int add_hdr=0; + + if((buff[0] & 0xC0)== 0x40){ // Literal Header Field with Incremental Indexing + add_hdr=1; + unsigned char filler; + ret_val=decode_integer(buff,idx,6,filler); +//printf("Literal Header Field with Incremental Indexing %d\r\n", idx); + } else if(((buff[0] & 0xF0)== 0x10) || ((buff[0] & 0xF0)== 0x00)){ // Literal Header Field without Indexing + unsigned char filler; + ret_val=decode_integer(buff,idx,4,filler); +//printf("Literal Header Field withot Indexing %d %x\r\n", idx,buff[0]); + } else { + // not possible + return -1; + } + if(ret_val>buff_len){ + return -1; + } + + buff+=ret_val; // skip the index + + if(idx!=0){ // indexed header name + if(idx<62){ + hname=HTTP2_static_table.ctx[idx]->name; + } else { + idx-=61; + const HTTP2_hdr_data* d=((*comp_ctx)[idx]); + if(d==NULL){ + return -1; + } + hname=d->name; + } + } else { // new header name + OCTETSTRING str; + int str_len=dec_http2_string(buff,buff_len-ret_val,str); + if(str_len==-1){ + return -1; + } + buff+=str_len; + ret_val+=str_len; + hname=CHARSTRING(str.lengthof(),(const char*)(const unsigned char*)str); + } + + OCTETSTRING str; + int str_len=dec_http2_string(buff,buff_len-ret_val,str); + if(str_len==-1){ + return -1; + } + + buff+=str_len; + ret_val+=str_len; + hval=CHARSTRING(str.lengthof(),(const char*)(const unsigned char*)str); + + if(add_hdr){ + comp_ctx->add_hdr(hname,hval); + } + + return ret_val; + +} +namespace HTTP2__Types { + + +HTTP2__comp__context HTTP2__comp__context__init( const INTEGER& h__table__size__local , + const INTEGER& h__table__size__remote){ + + HTTP2_compression_ctxs* ctx=new HTTP2_compression_ctxs; + ctx->local.set_max_size(h__table__size__local); + ctx->remote.set_max_size(h__table__size__remote); + + HTTP2__comp__context ret_val; + ret_val.internal__id1().set_long_long_val((long long int)ctx); + return ret_val; +} + +void HTTP2__comp__context__free(HTTP2__Types::HTTP2__comp__context& ctx){ + delete get_ptr(ctx.internal__id1()); + ctx.internal__id1()=0; +} + +void HTTP2__comp__context__set__table__size__remote(HTTP2__Types::HTTP2__comp__context& ctx, const INTEGER& new_size){ + HTTP2_compression_ctxs* c=get_ptr(ctx.internal__id1()); + c->remote.set_max_size(new_size); +} + +void HTTP2__comp__context__set__table__size__local(HTTP2__Types::HTTP2__comp__context& ctx, const INTEGER& new_size){ + HTTP2_compression_ctxs* c=get_ptr(ctx.internal__id1()); + c->local.set_max_size(new_size); +} + +OCTETSTRING encode_prio(const HTTP2__Priority__data& p_data){ + return bit2oct(int2bit(p_data.exclusive__flag()?1:0,1)+int2bit(p_data.stream__dependency(),31))+int2oct(p_data.weight(),1); +} + + +OCTETSTRING enc__huff(const OCTETSTRING& pl__stream){ + return enc_huff(pl__stream); +} + +OCTETSTRING dec__huff(const OCTETSTRING& pl__stream){ + return dec_huff(pl__stream); +} + +INTEGER HTTP2__comp__context__encode(HTTP2__comp__context& pl_context, const HTTP2__header__block& pl_hblock, OCTETSTRING& pl_frame_data){ + + HTTP2_compression_ctxs* ctx=get_ptr(pl_context.internal__id1()); + pl_frame_data= OCTETSTRING(0,NULL); + + if(pl_hblock.pseudo__headers().ispresent()){ + if(pl_hblock.pseudo__headers()().method().ispresent()){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(":method",pl_hblock.pseudo__headers()().method()(),&ctx->local); + } + if(pl_hblock.pseudo__headers()().authority().ispresent()){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(":authority",pl_hblock.pseudo__headers()().authority()(),&ctx->local); + } + if(pl_hblock.pseudo__headers()().scheme().ispresent()){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(":scheme",pl_hblock.pseudo__headers()().scheme()(),&ctx->local); + } + if(pl_hblock.pseudo__headers()().path().ispresent()){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(":path",pl_hblock.pseudo__headers()().path()(),&ctx->local); + } + if(pl_hblock.pseudo__headers()().status().ispresent()){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(":status",int2str(pl_hblock.pseudo__headers()().status()()),&ctx->local); + } + } + + if(pl_hblock.headers().ispresent()){ + for(int i=0;i<pl_hblock.headers()().lengthof();i++){ + pl_frame_data= pl_frame_data+HTTP2_encode_one_header(pl_hblock.headers()()[i].header__name(), + pl_hblock.headers()()[i].header__value().ispresent()?pl_hblock.headers()()[i].header__value()():"",&ctx->local); + } + + } + + return 0; +} + +INTEGER HTTP2__comp__context__decode(HTTP2__Types::HTTP2__comp__context& pl_context, HTTP2__Types::HTTP2__header__block& pl_hblock, const OCTETSTRING& pl_stream){ + + const unsigned char* ptr=(const unsigned char*)pl_stream; + int ptr_len=pl_stream.lengthof(); + HTTP2_compression_ctxs* ctx=get_ptr(pl_context.internal__id1()); + + pl_hblock.pseudo__headers()=OMIT_VALUE; + pl_hblock.headers()=OMIT_VALUE; + + while(ptr_len>0){ + CHARSTRING hname=""; + CHARSTRING hval=""; + int hlen =-1; + hlen=HTTP2_decode_one_header(ptr,ptr_len,hname, hval, &(ctx->remote)); + if(hlen==-1){ + return 1; + } +printf("decoded header len %d %s %s\r\n",hlen,(const char*)hname,(const char*)hval); + if(hname==":method"){ + if(!pl_hblock.pseudo__headers().ispresent()) pl_hblock.pseudo__headers()() = HTTP2__pseudo__headers(OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE); + pl_hblock.pseudo__headers()().method()=hval; + } else if(hname==":scheme") { + if(!pl_hblock.pseudo__headers().ispresent()) pl_hblock.pseudo__headers()() = HTTP2__pseudo__headers(OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE); + pl_hblock.pseudo__headers()().scheme()=hval; + } else if(hname==":authority") { + if(!pl_hblock.pseudo__headers().ispresent()) pl_hblock.pseudo__headers()() = HTTP2__pseudo__headers(OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE); + pl_hblock.pseudo__headers()().authority()=hval; + } else if(hname==":path") { + if(!pl_hblock.pseudo__headers().ispresent()) pl_hblock.pseudo__headers()() = HTTP2__pseudo__headers(OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE); + pl_hblock.pseudo__headers()().path()=hval; + } else if(hname==":status") { + if(!pl_hblock.pseudo__headers().ispresent()) pl_hblock.pseudo__headers()() = HTTP2__pseudo__headers(OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE,OMIT_VALUE); + pl_hblock.pseudo__headers()().status()=str2int(hval); + } else { + if(!pl_hblock.headers().ispresent()) pl_hblock.headers()() = NULL_VALUE; + pl_hblock.headers()()[pl_hblock.headers()().lengthof()]=HTTP2__header__field(hname,hval); + + } + ptr+=hlen; + ptr_len-=hlen; + + + } + + return 0; +} + +OCTETSTRING f__HTTP2__encode__frame(const HTTP2__Frame& pl__frame){ + + unsigned char flags=0; + + OCTETSTRING ret_val=OCTETSTRING(0,NULL); + + switch(pl__frame.get_selection()){ + case HTTP2__Frame::ALT_data__frame: + if(pl__frame.data__frame().end__stream__flag()){ + flags|=0x01; + } + if(pl__frame.data__frame().padding().ispresent()){ + flags|=0x08; + } + ret_val=int2oct(0,1) // Type: DATA frames (type=0x0) + + int2oct(flags,1) + + int2oct(pl__frame.data__frame().stream__id(),4); + + if(pl__frame.data__frame().padding().ispresent()){ + ret_val=ret_val+int2oct(pl__frame.data__frame().padding()().lengthof(),1); + } + ret_val=ret_val+pl__frame.data__frame().data(); + if(pl__frame.data__frame().padding().ispresent()){ + ret_val=ret_val+pl__frame.data__frame().padding()(); + } + + break; + case HTTP2__Frame::ALT_header__frame: + if(pl__frame.header__frame().end__stream__flag()){ + flags|=0x01; + } + if(pl__frame.header__frame().end__header__flag()){ + flags|=0x04; + } + if(pl__frame.header__frame().padding().ispresent()){ + flags|=0x08; + } + if(pl__frame.header__frame().priority__data().ispresent()){ + flags|=0x20; + } + ret_val=int2oct(0x1,1) // Type: HEADERS frame (type=0x1) + + int2oct(flags,1) + + int2oct(pl__frame.header__frame().stream__id(),4); + + if(pl__frame.header__frame().padding().ispresent()){ + ret_val=ret_val+int2oct(pl__frame.header__frame().padding()().lengthof(),1); + } + + if(pl__frame.header__frame().priority__data().ispresent()){ + ret_val=ret_val+encode_prio(pl__frame.header__frame().priority__data()()); + } + ret_val=ret_val+pl__frame.header__frame().header__block__fragment(); + + if(pl__frame.header__frame().padding().ispresent()){ + ret_val=ret_val+pl__frame.header__frame().padding()(); + } + + + break; + case HTTP2__Frame::ALT_priority__frame: + ret_val=int2oct(0x2,1) // Type: PRIORITY frame (type=0x2) + + int2oct(flags,1) + + int2oct(pl__frame.priority__frame().stream__id(),4); + ret_val=ret_val+encode_prio(pl__frame.priority__frame().priority__data()); + + + break; + case HTTP2__Frame::ALT_rst__frame: + ret_val=int2oct(0x3,1) // Type: RST_STREAM frame (type=0x3) + + int2oct(flags,1) + + int2oct(pl__frame.rst__frame().stream__id(),4) + + int2oct(pl__frame.rst__frame().error__code(),4); + + break; + case HTTP2__Frame::ALT_settings__frame: + if(pl__frame.settings__frame().ack__flag()){ + flags|=0x01; + } + ret_val=int2oct(0x4,1) // Type: SETTINGS frame (type=0x4) + + int2oct(flags,1) + + int2oct(0,4); // The stream identifier for a SETTINGS frame MUST be zero + + if(pl__frame.settings__frame().settings().ispresent()){ + for(int i=0;i<pl__frame.settings__frame().settings()().lengthof();i++){ + ret_val=ret_val+int2oct(pl__frame.settings__frame().settings()()[i].setting__id(),2)+int2oct(pl__frame.settings__frame().settings()()[i].setting__value(),4); + } + } + break; + case HTTP2__Frame::ALT_push__promise__frame: + if(pl__frame.push__promise__frame().end__header__flag()){ + flags|=0x04; + } + if(pl__frame.push__promise__frame().padding().ispresent()){ + flags|=0x08; + } + ret_val=int2oct(0x5,1) // Type: PUSH_PROMISE frame (type=0x5) + + int2oct(flags,1) + + int2oct(pl__frame.push__promise__frame().stream__id(),4); + + if(pl__frame.push__promise__frame().padding().ispresent()){ + ret_val=ret_val+int2oct(pl__frame.push__promise__frame().padding()().lengthof(),1); + } + + ret_val=ret_val+int2oct(pl__frame.push__promise__frame().promised__stream__id(),4) + + pl__frame.push__promise__frame().header__block__fragment(); + + if(pl__frame.header__frame().padding().ispresent()){ + ret_val=ret_val+pl__frame.push__promise__frame().padding()(); + } + + break; + case HTTP2__Frame::ALT_ping__frame: + if(pl__frame.ping__frame().ack__flag()){ + flags|=0x01; + } + ret_val=int2oct(0x6,1) // Type: PING frame (type=0x6) + + int2oct(flags,1) + + int2oct(0,4) // The stream identifier for a PING frame MUST be zero + + pl__frame.ping__frame().opaque__data() ; + + + + break; + case HTTP2__Frame::ALT_goaway__frame: + ret_val=int2oct(0x7,1) // Type: GOAWAY frame (type=0x7) + + int2oct(flags,1) + + int2oct(0,4) // The stream identifier for a GOAWAY frame MUST be zero + + int2oct(pl__frame.goaway__frame().last__stream__id(),4) ; + + int2oct(pl__frame.goaway__frame().error__code(),4) ; + if(pl__frame.goaway__frame().debug__data().ispresent()){ + ret_val=ret_val+pl__frame.goaway__frame().debug__data()(); + } + + break; + case HTTP2__Frame::ALT_window__update__frame: + ret_val=int2oct(0x8,1) // Type: WINDOW_UPDATE frame (type=0x8) + + int2oct(flags,1) + + int2oct(pl__frame.window__update__frame().stream__id(),4) + + int2oct(pl__frame.window__update__frame().window__size__increment(),4); + + break; + case HTTP2__Frame::ALT_continuation__frame: + if(pl__frame.continuation__frame().end__header__flag()){ + flags|=0x04; + } + ret_val=int2oct(0x9,1) // Type: CONTINUATION frame (type=0x9) + + int2oct(flags,1) + + int2oct(pl__frame.continuation__frame().stream__id(),4) + + pl__frame.continuation__frame().header__block__fragment(); + + break; + case HTTP2__Frame::ALT_generic__frame: + ret_val=int2oct(pl__frame.generic__frame().frame__type(),1) + + bit2oct(pl__frame.generic__frame().flags()) + + int2oct(pl__frame.generic__frame().stream__id(),4) + + pl__frame.generic__frame().payload(); + break; + default: + break; + } + + // length: The 9 octets of the frame header are not included in this value + return int2oct(ret_val.lengthof()-6,3) + ret_val; +} + +INTEGER decode_uint32(const unsigned char* ptr){ + return oct2int(OCTETSTRING(4,ptr)); +} +INTEGER decode_uint16(const unsigned char* ptr){ + return oct2int(OCTETSTRING(2,ptr)); +} + +INTEGER decode_uint31(const unsigned char* ptr){ + unsigned int ret_val= (*ptr) & 0x7F; + ret_val<<=8; + ptr++; + ret_val+=*ptr; + ret_val<<=8; + ptr++; + ret_val+=*ptr; + ret_val<<=8; + ptr++; + ret_val+=*ptr; + return ret_val; +} + +void decode_prio_data(const unsigned char* ptr, HTTP2__Priority__data& p_data){ + p_data.exclusive__flag()=((*ptr) & 0x80); + p_data.stream__dependency()=decode_uint31(ptr); + ptr+=4; + p_data.weight()=*ptr; +} + +INTEGER f__HTTP2__decode__frame(const OCTETSTRING& pl__stream, + HTTP2__Frame& pl__frame, + HTTP2__decoder__error__descr& pl__error__descr){ + + pl__error__descr.error__class() = c__connection__no__error__class; + pl__error__descr.error__type() =c__error__code__NO__ERROR; + pl__error__descr.error__text()= "OK"; + + if(pl__stream.lengthof()<9){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "The frame is shorter than the header size"; + return 1; // too short + } + const unsigned char* ptr=(const unsigned char*)pl__stream; + // decode length + int payload_len= *ptr; // payload length + payload_len<<=8; + ptr++; + payload_len+=*ptr; + payload_len<<=8; + ptr++; + payload_len+=*ptr; + ptr++; + if(pl__stream.lengthof()!=(payload_len+9)){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "The payload size not match the packet size"; + return 1; // length field error + } + const unsigned char type=*ptr; + ptr++; + const unsigned char flags=*ptr; + ptr++; + INTEGER stream_id=decode_uint31(ptr); + ptr+=4; + // now the ptr points to frame payload + switch(type){ // type + case 0x0: // Type: DATA frames (type=0x0) + if(flags & 0x8){ // padding + if(payload_len<1){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "DATA frame: missing padding length field"; + return 1; // too short + } + int pl=*ptr; // padding length + if(payload_len<(pl+1)){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= "DATA frame: wrong padding size"; + return 1; // too short + } + ptr++; + pl__frame.data__frame().data()=OCTETSTRING(payload_len-pl-1,ptr); + ptr+=payload_len-pl-1; + pl__frame.data__frame().padding()=OCTETSTRING(pl,ptr); + } else { + pl__frame.data__frame().data()=OCTETSTRING(payload_len,ptr); + pl__frame.data__frame().padding()=OMIT_VALUE; + } + pl__frame.data__frame().stream__id()=stream_id; + pl__frame.data__frame().end__stream__flag()=(flags & 0x1); + + break; + case 0x1: {// Type: HEADERS frame (type=0x1) + int pl=0; // padding length + if(flags & 0x8){ // padding + if(payload_len<1){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "HEADER frame: missing padding length field"; + return 1; // too short + } + pl=*ptr; + ptr++; + payload_len--; + if(payload_len<pl){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= "HEADER frame: wrong padding size"; + return 1; // too short + } + } + if(flags & 0x20){ // PRIORITY + if((payload_len - pl)<5){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "HEADER frame: not enough payload to decode priority data"; + return 1; // too short + } + decode_prio_data(ptr,pl__frame.header__frame().priority__data()()); + ptr+=5; + payload_len-=5; + } else { + pl__frame.header__frame().priority__data()=OMIT_VALUE; + } + pl__frame.header__frame().end__stream__flag()=(flags & 0x1); + pl__frame.header__frame().end__header__flag()=(flags & 0x4); + pl__frame.header__frame().stream__id()=stream_id; + pl__frame.header__frame().header__block__fragment()=OCTETSTRING(payload_len-pl,ptr); + if(flags & 0x8){ // padding + ptr+=(payload_len-pl); + pl__frame.header__frame().padding()=OCTETSTRING(pl,ptr); + } else { + pl__frame.header__frame().padding()=OMIT_VALUE; + } + + } + break; + case 0x2: // Type: PRIORITY frame (type=0x2) + if(payload_len!=5){ + pl__error__descr.error__class() = c__stream__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "PRIORITY frame: length is not 5 octet"; + return 1; // wrong size + } + decode_prio_data(ptr,pl__frame.priority__frame().priority__data()); + pl__frame.priority__frame().stream__id()=stream_id; + break; + case 0x3: // Type: RST_STREAM frame (type=0x3) + if(payload_len!=4){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= " RST_STREAMframe: length is not 4 octet"; + return 1; // wrong size + } + pl__frame.rst__frame().error__code()=decode_uint32(ptr); + pl__frame.rst__frame().stream__id()=stream_id; + break; + case 0x4: // Type: SETTINGS frame (type=0x4) + if(payload_len % 6){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= " SETTINGS frame: length is not a multiple of 6 octets"; + return 1; // wrong size + } + if(stream_id!=0){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= " SETTINGS frame: stream id is not 0."; + return 1; // wrong size + } + + pl__frame.settings__frame().settings()=OMIT_VALUE; + {int i=0; + while(payload_len){ + pl__frame.settings__frame().settings()()[i].setting__id()=decode_uint16(ptr); + ptr+=2; + pl__frame.settings__frame().settings()()[i].setting__value()=decode_uint32(ptr); + ptr+=4; + payload_len-=6; + i++; + }} + + pl__frame.settings__frame().ack__flag()=(flags & 0x1); + break; + case 0x5:{ // Type: PUSH_PROMISE frame (type=0x5) + int pl=0; // padding length + if(flags & 0x8){ // padding + if(payload_len<5){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "PUSH_PROMISE frame: missing padding length field or missing promised stream id"; + return 1; // too short + } + pl=*ptr; + ptr++; + payload_len--; + if(payload_len<=(pl+4)){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= "PUSH_PROMISE frame: wrong padding size"; + return 1; // too short + } + + } else { + if(payload_len<4){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "PUSH_PROMISE frame: missing promised stream id"; + return 1; // too short + } + } + pl__frame.push__promise__frame().promised__stream__id()=decode_uint31(ptr); + ptr+=4; + payload_len-=4; + + pl__frame.push__promise__frame().header__block__fragment()=OCTETSTRING(payload_len-pl,ptr); + if(flags & 0x8){ // padding + ptr+=(payload_len-pl); + pl__frame.push__promise__frame().padding()=OCTETSTRING(pl,ptr); + } else { + pl__frame.push__promise__frame().padding()=OMIT_VALUE; + } + pl__frame.push__promise__frame().end__header__flag()=(flags & 0x4); + + pl__frame.push__promise__frame().stream__id()=stream_id; + } + break; + case 0x6: // Type: PING frame (type=0x6) + if(payload_len !=8){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "PING frame: length is not 8 octets"; + return 1; // wrong size + } + if(stream_id!=0){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= "PING frame: stream id is not 0."; + return 1; // wrong size + } + pl__frame.ping__frame().opaque__data()=OCTETSTRING(8,ptr); + pl__frame.ping__frame().ack__flag()=(flags & 0x1); + break; + case 0x7: // Type: GOAWAY frame (type=0x7) + if(payload_len <8){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "GOAWAY frame: length is less than 8 octets"; + return 1; // wrong size + } + if(stream_id!=0){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__PROTOCOL__ERROR; + pl__error__descr.error__text()= "GOAWAY frame: stream id is not 0."; + return 1; // wrong size + } + pl__frame.goaway__frame().last__stream__id()=decode_uint31(ptr); + ptr+=4; + payload_len-=4; + pl__frame.goaway__frame().error__code()=decode_uint32(ptr); + ptr+=4; + payload_len-=4; + if(payload_len>0){ + pl__frame.goaway__frame().debug__data()=OCTETSTRING(payload_len,ptr); + } else { + pl__frame.goaway__frame().debug__data()=OMIT_VALUE; + } + break; + case 0x8: // Type: WINDOW_UPDATE frame (type=0x8) + if(payload_len !=4){ + pl__error__descr.error__class() = c__connection__error__class; + pl__error__descr.error__type() =c__error__code__FRAME__SIZE__ERROR; + pl__error__descr.error__text()= "WINDOW_UPDATE frame: length is not 4 octets"; + return 1; // wrong size + } + pl__frame.window__update__frame().window__size__increment()=decode_uint32(ptr); + pl__frame.window__update__frame().stream__id()=stream_id; + break; + case 0x9: // Type: CONTINUATION frame (type=0x9) + pl__frame.continuation__frame().header__block__fragment()=OCTETSTRING(payload_len,ptr); + pl__frame.continuation__frame().stream__id()=stream_id; + pl__frame.continuation__frame().end__header__flag()=(flags & 0x4); + break; + default: // unknown frame + pl__frame.generic__frame().frame__type()=type; + pl__frame.generic__frame().flags()= int2bit(flags,8); + pl__frame.generic__frame().stream__id()=stream_id; + pl__frame.generic__frame().payload()=OCTETSTRING(payload_len,ptr); + break; + } + + return 0; +} + +} + diff --git a/src/HTTP2_Types.ttcn b/src/HTTP2_Types.ttcn new file mode 100644 index 0000000..8af8071 --- /dev/null +++ b/src/HTTP2_Types.ttcn @@ -0,0 +1,291 @@ +/****************************************************************************** +* Copyright (c) 2017 Ericsson AB +* All rights reserved. This program and the accompanying materials +* are made available under the terms of the Eclipse Public License v1.0 +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v10.html +* +* Contributors: +* Gabor Szalai - initial implementation and initial documentation +******************************************************************************/ +// +// File: HTTP2_Types.ttcn +// Description: Functions and types for HTTP2 +// Rev: R1A +// Prodnr: CNL 113 851 +module HTTP2_Types{ + +external function f_HTTP2_encode_frame(in HTTP2_Frame pl_frame) return octetstring +with { + extension "prototype(convert)" +} + +// return values: +// 1 - decoding failed +// 0 - OK +// +// The pl_error_descr contains the description of the error + + +external function f_HTTP2_decode_frame(in octetstring pl_stream, + out HTTP2_Frame pl_frame, + out HTTP2_decoder_error_descr pl_error_descr) return integer + +// Message dissector function for IPL4 test port +type record of integer HTTP2_ro_integer + +function f_HTTP2_msglen( + in octetstring stream, + inout HTTP2_ro_integer args +) return integer { + var integer pl_len:=lengthof(stream) + if(pl_len<3){ + return -1; + } + if(pl_len<lengthof(HTTP2_connection_preface)){ + if(substr(HTTP2_connection_preface,0,pl_len)==stream ){ + return -1 + } + } else { + if(HTTP2_connection_preface==substr(stream,0,lengthof(HTTP2_connection_preface))){ + return lengthof(HTTP2_connection_preface) + } + + } + return oct2int(substr(stream,0,3))+9 +} + +const octetstring HTTP2_connection_preface:='505249202a20485454502f322e300d0a0d0a534d0d0a0d0a'O + +// Error classes +const integer c_connection_no_error_class := 0 +const integer c_connection_error_class := 1 +const integer c_stream_error_class := 2 + +// Error codes +const integer c_error_code_NO_ERROR:= 0 +const integer c_error_code_PROTOCOL_ERROR := 1 +const integer c_error_code_INTERNAL_ERROR :=2 +const integer c_error_code_FLOW_CONTROL_ERROR := 3 +const integer c_error_code_SETTINGS_TIMEOUT := 4 +const integer c_error_code_STREAM_CLOSED := 5 +const integer c_error_code_FRAME_SIZE_ERROR := 6 +const integer c_error_code_REFUSED_STREAM := 7 +const integer c_error_code_CANCEL := 8 +const integer c_error_code_COMPRESSION_ERROR := 9 +const integer c_error_code_CONNECT_ERROR := 10 +const integer c_error_code_ENHANCE_YOUR_CALM := 11 +const integer c_error_code_INADEQUATE_SECURITY := 12 +const integer c_error_code_HTTP_1_1_REQUIRED := 13 + +// Settings parameters +const integer c_SETTINGS_HEADER_TABLE_SIZE := 1 +const integer c_SETTINGS_ENABLE_PUSH := 2 +const integer c_SETTINGS_MAX_CONCURRENT_STREAMS := 3 +const integer c_SETTINGS_INITIAL_WINDOW_SIZE := 4 +const integer c_SETTINGS_MAX_FRAME_SIZE := 5 +const integer c_SETTINGS_MAX_HEADER_LIST_SIZE := 6 + +type record HTTP2_decoder_error_descr{ + integer error_class, + integer error_type, + charstring error_text +} + +// HTTP/2 Frame definition +// The length field handled automatically +type union HTTP2_Frame { + HTTP2_Data_frame data_frame, + HTTP2_Header_frame header_frame, + HTTP2_Priority_frame priority_frame, + HTTP2_RST_Stream_frame rst_frame, + HTTP2_Settings_frame settings_frame, + HTTP2_Push_Promise_frame push_promise_frame, + HTTP2_Ping_frame ping_frame, + HTTP2_Goaway_frame goaway_frame, + HTTP2_Window_Update_frame window_update_frame, + HTTP2_Continuation_frame continuation_frame, + HTTP2_Generic_frame generic_frame +} + +// Data Frame definition +// The padding flag are handled automatically +type record HTTP2_Data_frame{ + integer stream_id, + boolean end_stream_flag, + octetstring data, + octetstring padding optional +} + +// Header Frame definition +// The padding flag are handled automatically +// The priority flag are handled automatically +type record HTTP2_Header_frame{ + integer stream_id, + boolean end_stream_flag, + boolean end_header_flag, + HTTP2_Priority_data priority_data optional, + octetstring header_block_fragment, + octetstring padding optional +} + +type record HTTP2_Priority_data{ + boolean exclusive_flag, + integer stream_dependency, + integer weight +} + +type record HTTP2_Priority_frame{ + integer stream_id, + HTTP2_Priority_data priority_data +} + +type record HTTP2_RST_Stream_frame { + integer stream_id, + integer error_code +} + +type record HTTP2_Setting_data{ + integer setting_id, + integer setting_value +} + +type record of HTTP2_Setting_data HTTP2_Setting_list + +// Always use 0 stream +type record HTTP2_Settings_frame{ + boolean ack_flag, + HTTP2_Setting_list settings optional +} + +// The padding flag are handled automatically +type record HTTP2_Push_Promise_frame{ + integer stream_id, + boolean end_header_flag, + integer promised_stream_id, + octetstring header_block_fragment, + octetstring padding optional +} + + +// Always use 0 stream +type record HTTP2_Ping_frame{ + boolean ack_flag, + octetstring opaque_data length(8) +} + +// Always use 0 stream +type record HTTP2_Goaway_frame{ + integer last_stream_id, + integer error_code, + octetstring debug_data optional +} + +type record HTTP2_Window_Update_frame{ + integer stream_id, + integer window_size_increment +} + +type record HTTP2_Continuation_frame{ + integer stream_id, + boolean end_header_flag, + octetstring header_block_fragment +} + +// Use it to send/receive non-defined or errornous frame +// Only the length calculated by the encoder +type record HTTP2_Generic_frame{ + integer frame_type, + bitstring flags length(8), + integer stream_id, + octetstring payload +} + + +// HTTP2 request/response and header definitions +// RFC7540 8.1.2.1 +// Note: the encoder doesn't enforces validity +// of the specified pseudo header field combination + +type record HTTP2_pseudo_headers{ + charstring method optional, + charstring scheme optional, + charstring authority optional, + charstring path optional, + integer status optional +} + +type record HTTP2_header_field{ + charstring header_name, + charstring header_value optional +} + +type record of HTTP2_header_field HTTP2_header_list + +// This type should be used for request/response headers +type record HTTP2_header_block { + HTTP2_pseudo_headers pseudo_headers optional, + HTTP2_header_list headers optional +} + + +// Header compression releated type definitions +// For each connection (not streams) a separate +// compression context should be used. + +// Never touch the fields of the HTTP2_comp_context +// Don't even think about it !!!!! + +// 1. Create the context with function: +// HTTP2_comp_context_init + +// 2. Every header block should be encoded/decoded with the functions: +// HTTP2_comp_context_encode +// HTTP2_comp_context_decode +// in the exactly the same order as the header blocks are sent or received +// in order to maintain the header compression context + +// 3. Delete the context with +// HTTP2_comp_context_free +// Please note: non freed context leads to memory leak!!!! + +// Creates a new header compression context +external function HTTP2_comp_context_init(in integer h_table_size_local:=4096, // The initial size of the header table + in integer h_table_size_remote:=4096 + ) return HTTP2_comp_context + +// Retur value: +// 0 - OK +// otherwise - error code +external function HTTP2_comp_context_encode(inout HTTP2_comp_context pl_context, + in HTTP2_header_block pl_hblock, + out octetstring pl_frame_data) return integer + +// Retur value: +// 0 - OK +// otherwise - error code +external function HTTP2_comp_context_decode(inout HTTP2_comp_context pl_context, + out HTTP2_header_block pl_hblock, + in octetstring pl_frame_data) return integer + + +// Free and release the header compression context +external function HTTP2_comp_context_free(inout HTTP2_comp_context pl_context) + + +// Set header table size +external function HTTP2_comp_context_set_table_size_remote(inout HTTP2_comp_context pl_context, + in integer h_table_size) + +external function HTTP2_comp_context_set_table_size_local(inout HTTP2_comp_context pl_context, + in integer h_table_size) + +//**************************************************// +// DO NOT TOUCH THE FIELDS OF THE HTTP2_comp_context +//**************************************************// +type record HTTP2_comp_context{ + integer internal_id1 +} + + +} -- GitLab