From 58f72fdf18a92d4557998c54b709198181cde931 Mon Sep 17 00:00:00 2001 From: Eiffel-World Date: Thu, 30 Jun 2011 02:09:51 -0700 Subject: [PATCH 01/76] Initial Commit --- Home.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 Home.md diff --git a/Home.md b/Home.md new file mode 100644 index 00000000..e5a0dc6f --- /dev/null +++ b/Home.md @@ -0,0 +1 @@ +Welcome to the Eiffel-Web-Framework wiki! \ No newline at end of file From 509375f7e67727748afc5cde81cef7590e5638fa Mon Sep 17 00:00:00 2001 From: jocelyn Date: Thu, 30 Jun 2011 02:24:23 -0700 Subject: [PATCH 02/76] Updated Home (markdown) --- Home.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index e5a0dc6f..4dba1c7c 100644 --- a/Home.md +++ b/Home.md @@ -1 +1,5 @@ -Welcome to the Eiffel-Web-Framework wiki! \ No newline at end of file += Eiffel-Web-Framework = + +![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) + +**Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] \ No newline at end of file From 1782fc9f4debcc3583c6392f8a224b6ac268cf4c Mon Sep 17 00:00:00 2001 From: jocelyn Date: Thu, 30 Jun 2011 02:24:59 -0700 Subject: [PATCH 03/76] Updated Home (markdown) --- Home.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Home.md b/Home.md index 4dba1c7c..eed80a79 100644 --- a/Home.md +++ b/Home.md @@ -1,5 +1,3 @@ -= Eiffel-Web-Framework = +# Eiffel-Web-Framework -![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) - -**Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] \ No newline at end of file +**Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) \ No newline at end of file From aca99fe6d4947e0182ae2457b15777fcc8dc9540 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 02:37:56 -0700 Subject: [PATCH 04/76] Created Tasks (markdown) --- Tasks.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 Tasks.md diff --git a/Tasks.md b/Tasks.md new file mode 100644 index 00000000..8de57910 --- /dev/null +++ b/Tasks.md @@ -0,0 +1,36 @@ +## October 1st ## +* Source code , layout/handling EWR (**jfiat**) +* "Eiffel WSGI" spec (github wiki) (**paco**) +* Eiffel Web Nino (**jvelilla**) +* REST component (**jfiat**) +* Example/reference Eiffel Web Server App (**jvelilla**) + - Hello World + - Restbucks (from the book REST in Practice) +* WAMIE/apache based support for Eiffel WSGI (**daro**) + - SCOOP ... +* Some persistance solution (**daro**) +* XML, JSON support (**jvelilla**) + +## Maybe for October 1st ## +* Reference Client/REST-service consumer + - Mashup support, facebook, twitter, google+, ... (**jfiat**) +* Authentication support + - OpenID, Google Connect, Facebook Connect, OAuth, ... + - http authorization + +## December ## +* Session handling + - Cookie based + - REST-based session example +* Access Control +* Application builder + - Deployment + - Persistence chooser +* Dynamic update of running system (**daro**) + + +## Contributors ## + - jfiat = Jocelyn Fiat + - paco = Paul Cohen + - daro = Daniel Rodriguez + - jvelilla = Hector Javier Velilla \ No newline at end of file From 23d8adaa3df901a246174e449e4b4ae81bb37e48 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 02:38:05 -0700 Subject: [PATCH 05/76] Updated Tasks (markdown) --- Tasks.md => Tasks---Roadmap.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Tasks.md => Tasks---Roadmap.md (100%) diff --git a/Tasks.md b/Tasks---Roadmap.md similarity index 100% rename from Tasks.md rename to Tasks---Roadmap.md From 057cec2b4470126e778b5189b4c9f7a015dfc607 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 02:38:30 -0700 Subject: [PATCH 06/76] Updated Tasks Roadmap (markdown) --- Tasks---Roadmap.md => Tasks-Roadmap.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Tasks---Roadmap.md => Tasks-Roadmap.md (100%) diff --git a/Tasks---Roadmap.md b/Tasks-Roadmap.md similarity index 100% rename from Tasks---Roadmap.md rename to Tasks-Roadmap.md From 0b7165b2603df82d7f1b1bb99caeb615d70a448b Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 02:40:41 -0700 Subject: [PATCH 07/76] Updated Eiffel-Web-Framework (markdown) --- Home.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Home.md b/Home.md index eed80a79..ae51a3c5 100644 --- a/Home.md +++ b/Home.md @@ -1,3 +1,5 @@ -# Eiffel-Web-Framework +## Eiffel-Web-Framework ## -**Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) \ No newline at end of file +- **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) + +- See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] \ No newline at end of file From d9b52d2691da3a66819ea64d793a3a00a1d44ebb Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 02:43:47 -0700 Subject: [PATCH 08/76] Updated Tasks Roadmap (markdown) --- Tasks-Roadmap.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Tasks-Roadmap.md b/Tasks-Roadmap.md index 8de57910..19788960 100644 --- a/Tasks-Roadmap.md +++ b/Tasks-Roadmap.md @@ -12,9 +12,9 @@ * XML, JSON support (**jvelilla**) ## Maybe for October 1st ## -* Reference Client/REST-service consumer - - Mashup support, facebook, twitter, google+, ... (**jfiat**) -* Authentication support +* Reference Client/REST-service consumer (**jfiat**) +* Mashup support, facebook, twitter, google+, ... (**jfiat**) +* Authentication support (**jfiat** ?) - OpenID, Google Connect, Facebook Connect, OAuth, ... - http authorization @@ -30,7 +30,9 @@ ## Contributors ## - - jfiat = Jocelyn Fiat - - paco = Paul Cohen - - daro = Daniel Rodriguez - - jvelilla = Hector Javier Velilla \ No newline at end of file + - **jfiat** = Jocelyn Fiat + - **paco** = Paul Cohen + - **daro** = Daniel Rodriguez + - **jvelilla** = Hector Javier Velilla + +note: In bold, you see the responsible for each task, but contribution from other is possible as well. \ No newline at end of file From 71cdfe18abd9feca282757cfb6489001d7fbb216 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:01:45 -0700 Subject: [PATCH 09/76] Updated Home (markdown) --- Home.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Home.md b/Home.md index ae51a3c5..7fdb90c7 100644 --- a/Home.md +++ b/Home.md @@ -1,5 +1,8 @@ ## Eiffel-Web-Framework ## +The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the [[original wiki|https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki]]. + + - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) - See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] \ No newline at end of file From 7f35ca21e27ee3469f7f9a59eb0bd5b9a0643b87 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:22:01 -0700 Subject: [PATCH 10/76] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 7fdb90c7..6a6f305e 100644 --- a/Home.md +++ b/Home.md @@ -5,4 +5,5 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) -- See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] \ No newline at end of file +- See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] +- See also a [[General source structure of this project| Source-structure]] \ No newline at end of file From f9a79c080044d3d729c685e9b09b03530445046c Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:31:59 -0700 Subject: [PATCH 11/76] Created Source structure (markdown) --- Source-structure.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Source-structure.md diff --git a/Source-structure.md b/Source-structure.md new file mode 100644 index 00000000..80da56a1 --- /dev/null +++ b/Source-structure.md @@ -0,0 +1,27 @@ +## Currently ## + +- LICENSE : file containing the global license +- README : quick README to point to the github project +- doc/ +- doc/wiki : clone of the associated github wiki repository +- example/ +- test/ +- library/ +- library/ewsgi/ewsgi.ecf +- library/ewsgi/ewsgi.ecf + +Any library/component (in development mode) should follow the following structure (when not needed, there is no need to create the associated folder(s) ): +- **README** +- **COPYRIGHT** +- **LICENSE** +- **.ecf** and **-safe.ecf** +- **library/** : the place to put the source code of the related library/component +- **doc/** : notes, documentations, ... +- **test/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ ) +- **example/** : standard place to put the example +- **build/** : a convenient place to compile your project, using this convention, you can setup utilities such as backup to ignore this folder. +- **resource/** : contains pixmap files, .... +- **install/** : contains installation scripts for each platform +- **data/** : contains eventual data for the related tools + +See that we use the singular, since it is an Eiffel convention for naming cluster or folder. From e0eedef3a76394a371a60879a1bacf70175e68f5 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:33:22 -0700 Subject: [PATCH 12/76] Updated Source structure (markdown) --- Source-structure.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source-structure.md b/Source-structure.md index 80da56a1..1314e608 100644 --- a/Source-structure.md +++ b/Source-structure.md @@ -11,10 +11,11 @@ - library/ewsgi/ewsgi.ecf Any library/component (in development mode) should follow the following structure (when not needed, there is no need to create the associated folder(s) ): + - **README** - **COPYRIGHT** - **LICENSE** -- **.ecf** and **-safe.ecf** +- **.ecf** and **-safe.ecf** : configuration file - **library/** : the place to put the source code of the related library/component - **doc/** : notes, documentations, ... - **test/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ ) From 5f5a8c6dafc821b9ed57ba3a32aeb893b799b031 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:34:06 -0700 Subject: [PATCH 13/76] Updated Tasks Roadmap (markdown) --- Tasks-Roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tasks-Roadmap.md b/Tasks-Roadmap.md index 19788960..9f921e84 100644 --- a/Tasks-Roadmap.md +++ b/Tasks-Roadmap.md @@ -9,7 +9,7 @@ * WAMIE/apache based support for Eiffel WSGI (**daro**) - SCOOP ... * Some persistance solution (**daro**) -* XML, JSON support (**jvelilla**) +* XML, [[JSON|Task-JSON]] support (**jvelilla**) ## Maybe for October 1st ## * Reference Client/REST-service consumer (**jfiat**) From c9c2b37070c8acd2544669c071e0e00dc18a0f33 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 5 Jul 2011 05:37:20 -0700 Subject: [PATCH 14/76] Created Task json (markdown) --- Task-json.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Task-json.md diff --git a/Task-json.md b/Task-json.md new file mode 100644 index 00000000..e8c22725 --- /dev/null +++ b/Task-json.md @@ -0,0 +1,10 @@ +## Goal ## +- Make this JSON library the default one for the community + +## Steps ## +- restructure to follow the (doc,library,test,...) structure +- extract gobo from it, and provide json.ecf, json-safe.ecf, json_with_gobo.ecf (and json_with_gobo-safe.ecf : not possible right now, but we can _cheat_) +- and then let Eiffel Software include it, in the official libraries + +## Roadmap ## +- this should be done before 1st of October \ No newline at end of file From baae1daa855dd3c7395bf1d0b6b22e79fdd693b5 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Wed, 6 Jul 2011 11:03:21 +0200 Subject: [PATCH 15/76] Added server side architecture for EWSGI --- server_architecture.png | Bin 0 -> 89726 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 server_architecture.png diff --git a/server_architecture.png b/server_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..3128400d777b70d9dd8c13604af1de82d0b0a287 GIT binary patch literal 89726 zcmb@tWmH^U(=7@F5;Q;(Ab0}7Ex227cXxMfTq6XR;10pv-GfU*{0|SF5Aug;41M}h!3=G@|;!EI)o-1xO z49r&;31LBHx4He)SL(`FV?Ad%SGq%Cxfab9J<4z2hA~#)YZ(@gcD-S})GhB?8vXqN zTL50T;`@g{5=1y^5|ZyEh=_1aAJi4#QzCE=OPj<4bZVR1potXwvsGs3W={!chl9-^ zVp!n!{=Q8Ac-O=K=Z_Cx|K~cQ{(mmQz`g&^+pjRs>-_urfALXR)c<@*0OrMi-eUau z&pR0S&;RrG|IBx&8_R^Y2Z5pHkB2 z1^1=yFhw=e-Nz+Kd;Q=Vw)^s;glp6|pq*vW;+mJyTE=yk;|j%+?Lud_p_ojA11&2x zL)NkQv~Q@1%y4SX3N}o0IY4vxHbE2OmhQn|Cz3UFq0kl(d&i@>%_g$P>q-0YO;epE z<_zCzI)VUQbG#sXJx1?W0Qwskn14=)zH~Xq25p|5A2;W#nW)LM1{P0n!kf-1Go%It z?q7P@6e6DpN|a4wmIFkNV2aX3zRH3KM73TJYXcceNf2g@e z8l8FO>wg>2Fv1?7bUB~2BI%X06irJ0F_GpC=1oCmBbK*6*8O$9#=#;1^pvakbj_aG@N?@u*%b`6=bYh)}bA-4l z>8cubho?r%)V=w>ed}P>xL+q8KHRI=c&HAU%F)dM{wtsv~^!sM=V&e~-|K;q z59#lQDAk0QRE0ytTO2I&5LJfyf-c`hx^o}I1kMKkicghRgDVlQsNl|-t^TxGT9JLO zC4t*pt7tmm)9CaFuhWP@8tG2{iz(}YYW><}R&0kaactW4-N`^E8@t9G{=@u$I%cIL zluxAB1z&W)8-0Mhxl5X6NjfreLENw3yY#p%cV2mP^7)5vy9VO z=BG$x8Qi6R71M+$&^c(jS##R1`V71PQ!&#Q53n=Ezaga}HRsKlcg+d45!#*`MT?@_ zx-!e~qGC&^WMVG#x&Pvj;xM5C&l0j)B(}dT>q9>kh}oerwg@%q+cl;PLt2p1;nfk& zFI+1ssXbk8y{qj%L+N=UzWtJgK}AEloX8ES>hq^*hF=7Ioy}OK&9c^ zbQipkba}HhI~@*x1fvygNQ;Ea=qib?Kp+-heDEVuj2dZlMMy; zv~E+^;h`vr&FLzPMaRP!W23~ZpB)7^muTm+rdG3UJc63Kl+4T);DYt@aam6fvn-QU z2b=ua+KcKfH~X515@hTJ-HreL#ypJTvb>7=D>!$N#7KS7FoKar-A;l2eYe26~?C1#4^f_F2uQK>7ZOQ-}F_ z?%>Z`HmnV`*F0_ZH5pSsN!-kw{2)`uvlAgK)=%`7^%gnCRH8CFRz9=?V~ww4LbDy8#xfN* zV66I!CE-gL=Cif`4~?DVsmvYXDA6n zj!sh_Qq-kaQ8n>|z9RnhMqkJ4G9|~m(-!G~A4h4uH_dxSt+M}Cv$x4mHuyJsv$==A zU7}j-+T7qv@XVv#pyxYtx?a#nFC>lL)rIsaz*y z318uG&3}-$-f-=mIn8XZrHMQ)F-=zFbjoCs?Rk5$ujZ}YDH4J` z`s<_6VK|;5W#7O+FyHmci;LQ!WOk({mxBS`Dej|YAy`=0-CmtWr;P3p9Q=}Nm7v?# zgk0|rZa_g?YMA?|&K>+4e_h%VkKV>qkSte;yZ6243+GR_;G0ZeEO<+ zP>v(GU<(yx3|>pqEFKN+9zIOo{^^X;{VAmCB*h@2Ey^UuDxA5^+OkVj>9*0w`|dJF zrAXD{Y5rH7C_4Xr`R&O@f3(Fw4AmylUJymvZ$G~#J;&CoCC_&R1lALK;Es;(rjQhi ziMAJ4NKcLViGywc>;VFlB@DKd8oCxvEa{O}{#BH&%(M~W_W_fMkSo$Et7^l&5t8Zc zU(U+*dr7k;X4rB<_X}iIeVOrSx#y=pg;$~N8f#PBgrAH;Sv60J*?famh+X|verx9yOU=z zl*C&3kZjkQ;rsLu&-ZYy8?m)%BHwI=lp&Xc^LGMeF_D2RY|A_8YTGc zWblPQS~AqjCES(WNTIROFu$7l=>Av%gV_rDel?xo$(VULkR?WBA3w--rHg+{Yp{&y zif29<#KaVPYxT3Q?{{9BNATGA_~hpB-PI9JWTWGz?)-!zT0 z3CtGi9UNa%D-1#}AFh^_^!0!5cYd!0&b_0x&tOY;BAK#Oj7xao%Gvf(OB3R8G=xivBDK{9=_Ha4>#*1A%?^)vc|_1LnKaI4YNYmi>Wi~FveGYoO9nb}(%YeKLrIoG{g^rCC5rWJ?N zc)<(3LqlPjHI`)jpdhA@5x@oI;pyV#K;*IM>rY%99Qk_eR#UWioQ~PIj3Lt4$-4~X z$UhH2!@Lyy!^!Ls{;vrOFIv55WnhQCH#+Z4X-4#h6M7Ec#5so(Kp>Dx9l$ts)NhAy zO@=_m0sBiWp2-u-?PCYy{hWWW*%|-3E;X1e0%>jEdXOECXDO2;ix=YwAHigL_LCSz zMHRCdV%TO?c}-0$Euq;kM-{8XTGtz}0B6&5i59!ZNjFZ8Qw&;AaY;#y8N8TS^5im1 zM7*I;6&+L-cLI+m)+yz-#JuQguN^M7+svI@zPaZb3QhOb9f(vJb|5a3JU`_cbMiD@ z%}+epWKBp(Dc6x7gcHS5=@!4-Fwk+$yetepYli zM~GB4RXbV5vbByCODWiE9g@qJ(!GX%(bXl4$6}5llf<%)h&EuZFzYqDz$UN<_bIbPz?lrYEXJOA-80$N^JM$qq?pu5JT!> zWI#PJZPH$=KXJ05suzt6ZTC^lEk`~TUH(9Jjfi&!Yp6jU9eqm+&0LrH4v<5MeeWY{ z?b@{7KnxSBMw22M^ct>j;#|U$%GR%@S+3XGUc`QUZ&upClZ0^f0-fiBcYAL2BLz3= zGj*`g_m6|2S_gOKs$u*zokAx#ki(t#Pnys?Jlv#)fP3s8(1~~`P5G>0vKvx8@mM_x z8miAj7+NvjrhDERJgK=PB_&ZuXmH3SvkmP}IfmHaQyv={mXx+EG;6){WDE{ZMTv@< zNH_J#zuNn=C6}~To}I5%N1w{2vLOixAGDV4KJ&4$ww@sCOqsPA`Tb3m71qp~pgjKk3~_(A?8NtZ~K|vakl#n&XUfNm0}DR#`L~JS^p7@W3H+>W}JUrX&`GwlzdSVF>|jsbw~0mnMosBu2bI9%XB)=uiG~diMg~!fm%4Vj3Mn{4`)XrYw8L!x(La| zH>zq`6FCH_)ofw3=V6M)^GqF)Rd}eb>|JS1WNpiKa=+bx2f#D}d0X;lLTnx$`#bK;-SM%GV+74yS2w!i3d%*_HU_}#=uxq zw<)lppCbb0_{4swIp>_w_BoRCI-*P-;uF)zX-5&$>HqEnShscy-OZT z^~rO(qmMb@9yS-Y*A@I0)8KO9l)>d9l|NJ(#zHz0pe_4TVk(~S+f{d3ZruDq=vtNA z{Ymd+hbr@HVm@hNuj6mGjJ|?d7NXg>Pxoi&VYtja)@bE%eni>vQ*@t-qBFR(TzSU& zIxG@m*zd%y1^hl*((QpKUoM=1A;X0SNK_}MIiWdsv9En!vzTA_C@oZSLpdw?b42b3uZ=44^(g9NOKD0|8BweaD81VQ;H!%RFU^_E7`7Z zdb+O5=vX~F44*wI6?O`!awdAMP`pu~+PXbB-$qp2 z{H$|IQIaILuDWJDWgZo=sLp!_UT7YgbdORLOuB!f;A{hx zL;N6plb?s1Tj0Efy!;677XRkKT_-w!AFU*$`9vI@4XoMb(-IWVLqln!^GXq54HAPC zu405jUetdJ7g{Sm`u(EGc&ca`cbti4y%2%OsB1m+9WsK5h)93&BoVKN`9bf)?mpbm z!`}TSTD%M>E}^%{UPQ>)ID7WJe#J3>pwU|l>SB6u9@Q>P#DWzC`47wU_`O^wwr}xU zYhTehCBtEw_K;orV`w|->0St0D~1{e%mxwseKxa^o6N~Ex>eF!zK=Z9l}dTCB+AQP zXEF}v}6E-v8Ki)GFnBe-iM%H8C0% z+w4gLrV4}pksw4r4b}#SP?ywdn7t#_r_TBZpKciL_`zIFXU*pgU%5-Iargk(dL+1K z%yRZ!N@KiOD=?oi_Nf5W6IZ3f+G2O|o_orRP>VU-j=erooM$>#N=ruNp2U(boGdl( zHayOWnZImqFXCrcX=k#74(psWxm-`Po6u|epFP_8&&By?ZU6}>HtLu~-==dVYaPr~ zWc6H4qGAt)hlg)@p2grXJhc0F`F$YPB=EpsuMLzGp}3M^WqMnGc2E8ei`d|NdVSU` zJ=O)|advrGk6K4Lg4MGRoE!-uyL?M7{kta&U#hzUNDv|h24u<9bF;If_;$@7*SCS3 z{Q(OV%N@PcXGJn_6C6K2F2_%Yh0Zf4TlHqGm)Mu~O1(Rx%5(^?O5+KjwlRBwU;$9I zTd}aU7F4!vxu^W+)4HBvkW_B9^*fSk@b7U|R_6Sg!OdZwwU_DWnM?+l+cHueKMGqO zZ`EsaN5;R2`tm*(2sN)dR@#3QXjGY=suw5+iz_F>#j%s7O6_ah`}_IHJ32Cj5pXIB zn@JqqOeo0(wc``dFD>bsR;z9B)~7%8Q>14*toKOhc}0`URCk@l>-kVU9BmR7T{{d~ zJPbSH%5V#%+B3udT`R)y9hMR40xEuqMQrO3*bOz#P)Y8GX0H|`HS@BGpn?b^HW=wK zjg<*I$gC*oJM0tyHolaWmKvt@Xv_lzQXnioKK$juyjqK%2J_e~Oo;E+Qz6&}MTEla zyOhUptLxWxgn;@W$Ou<_!kvQes!OeCNT}BQlFq z8K`p!VbMSU)hp#&t^z2cjr1t_gNt}~6{?4ro-~B5!R;tAsZVd4JT5Hz698D3AR7Aq zBZ+#=KkF_6nH@~wvAuf+RgzxAgM-rU#1M`ul&2I<5H(9cYWLwthR|dL6p~64{zz-g zf49%f<$de)6H@oQbT{ocDZgQX=E)x(k>dk`fy-2}ojva0-%-Vxxc9@$V6fDJBh4Mx zAsNjft$shE-ms31`PX3=N#*yK_glEM*~CDGHJhAE&$ zugymja<5fujkyXgeKhI`9i6KyHp1c$r#BkB^G?UmYj%Ls8V{hCAp8%1 zJ=D&TK~#PVq7YMCOGtG^mmP}eIuLrvt|8cLK1zbeWGtSecLm`->I=&d4F4=C^I=nv zhOt0#KUL>aJWEp4)GvVGs+MFZt7C2!ZVXCer6~9(d(=tMq z*A534lEGiDr~BO2QPSNh6Y>~W3coKY0cnxzOzw#A-yBl=1hqNWT%vR5BH^GhF? zcFR5AIy<{Yotj4^hzGYOShHAFH(DLETrFj-tmtRm-zl4#jx`=*^dnCjtMYGq(XuX7 zSN52~;83vAB>KjlEQF9gXG{9%I|9m~dwONK%00*Jy9}V2 zHr`j$QgFbZ{wp;ic4=w=CDPjP+{sKv$i z7nSIB#+D^De9>;RASA;0T~@oL=KS9NVL8d8>z8SIy@$EYWs=9IjI#$ZZ4Y*%nFw@RwZ#jg z_}ZquYkv}KONHU>wv|s5T}-G=2pq_Ng7_7=t}?Wc1V!uONSVh)RvceeR4+$^vq+<( zKdBqvHs=qcb-t%Eu7Z#Zu+TCoo<%ibB$Lhdc&TGKc@W1btz%-OL` z(7?|M2v>ezP?PMsJ_+~ZeA0fSqtv^>pyB~E!A)eVj;qx%D|qn zQE2g$?;KRa#B}qStdg=mvls!$OS#TgXBR+02^lOFW7%J_*g2G-fa80uN6C6b8TL|Q zHd1W<(DFXa-b6w~yb*DvjbR@Xk?)@iamOt6U z4m+XFhSOPlAcue6ny%xy0U)ARm}-p^qIdWrT#oORK|sDXgUf2t4$BeEO;k2lZ%}Z= zFO9Z)GSsaHxM${$QUV4i_=8T^aDi+LEA@{%-00}&nv_(nzqHjbiCMAoaK#(z9XwBs-9 z6qlBTY=i-(!rVHeFBNlJ=()|r*2ES}Y1&zJs9Os3-V(y86)m07L@CnWjRI}^7lHx# zipLyu^Z~N6GKFJ-Srz!V;JxVsEzt(|I{P1gpX~2XrSR&(;Hr#8#OJ++{pg7xW?(-` z;av~Kx?(T1J=Ksuwz~FDlM+_3&0RPv;=JwX>=Z)%K7beBUZpk5I57e*D8l@QXKjFB zCY``;1XrKv?fRCgat~N<>bP-|_QLGdV${_06Z8UzkjnHz#g6gu=r?cQ?i{r}#YvZ% zZVFrY(rkuh7KCkY)`)nx>ZMl|$ zYOr+o=}r(!Q%qZR*aTu-+oRWfUSa)=AwPIMLmrrz-qd-{?3tNVrq{FYC(vXxvY3En zZ*HYCxAUBeY!=f%k_-$^3C!y1ho`5eREsvMuHA5#rbkCBCLC~6EVlTJCx~qNvBoDzJ!kS(|D^bfWnd`6E*w`-9(piTpg3D?OLmgL(%Op1(i5Zex zPqYp2@$sMgKh^N||JmKBI6z-aYHEN)7~}UDLWB?BvUh`+{M?TgxcRzn<2`FBvb(^b z1tGqLT8f%-bIQzVpw=fdBVRH753{Ghu1!)j!&Np`fzbTEWzcJFKEysYcfUH+)aV`P+Y-RBFF*TF*{Cho`AUi)ZBWw+vU&YJVw| zLdUj!HaPg9ducXmlD!tUl@&Ik$)cfC*F%)okfg#kjAv#t+0*-iw*7DtS z4GD7rtC`4|w0Y^vnR0_4#+oW79XUfGs?@QHT*Ll$Jc=RAMXz2H)Kge#%@yzLrYLDt zm4UfRGX_1Ok9v15P+ni~{#Qeen!$!8YeYef+*9A&zLG8Jx50eaEoa>V(!{X?^u#%B7gQnlW@F`R|Gu(&dXxtx8P|bl~Qg#FTp^volYu<+V0X>qn9s9DZGlNPT5`VE)}$c< zc0lPV@8Pd9+PxUm3euv$?RDX1Hynz?YBDqvj>9aHRW@|fdVXcN7k8>Z_v%0*7$BE~ML)VPm*W6a;C$a}ixkFFo-CWUB9&6v zu{THsr1e742wZPQjn-c)q27S#fN6U6{j(G*X9TEt3&tq%g_0zO6c#v!fEq{CCYGI{ zQLRi@({^Bg?0zkTLDhW9$r(~mJUhKfIE}H@;z>L&lL}>zbOs1QAacCF4O_;wlVo`3 zmJ+ma!o3huSm)ReW7QaA{^ubr5&7o(2Qa8GP^CYO`(AQD$4H{Vx4`&kLl@6_b$SeA z(Rxgswc5+Ms4=s&bJMDoO}+yuqPN?kF-=~uu$C_&Yyi22iP@vkH%I-zdOo({(!{11 zT~e!<*-W$bDOIM#i=2arQ{vW>;@VSEHLdFKo=n(g__QRF0RiQ-D?fj6zXyQ#?JJU* zG+fb**&3rl0k5$lHI_mxU7-Aq8-cec_cv!XOdTdIwf}iETJvX1r0X3fL-xZAFwcC5 zKJB{=DRRXfuCHSUP1s1|XB1A$TKKDi0KyM6XW{yc4ja4yUETeLwOe(#&_S<(A_9fv$p2h#p9w01C?TsTZ&_kQIO7m7RkiI z=d>Dbz`7>(f1rSLZ_<7q{=MbCw_sJ2)T_JXwfCP0Vrw((32j}DV_u?Y6PLSxf%;qu{ZVg7bqOxL zW{7Q7hM>~4m`2oQM^t48A7oM4JPa%1S%1z|%WKY&yR|H*!}i1MnC$~-vcUR1 zpu>NTv{!hff~Ea(;g16X0!0cl4#y3`ESG+AmEj7;1i3Dtp!F1^)u zA#!6>xNJyFH&flCOH?nxGGzsFP-6i@6N}H;$jL)8E9Hfb2foZo&t_d@I-FO(DlsJ*Dll&l}5vTkGpbs|K=Zt5-oANIfg711HqRHZ-z`D#2gPaWCL5|x>iX_61^tPL+^Uq zCu~;hCY(;SNPK|inv`$JVJ;TomX|F5@I%zgvR%n4_6V#|YgFE~l-W(xl8TGkC)-(n zI8yX&nQKRP9<^74PPh_)$aFOVkktS}EvnALv6PRk4we);C^`%#t+a*>`)WH2Vh1ck zhN@Kr9P_S3I^33WoTF(m$zIJHoORm%I@iqr1BHv!^-vS9_|}QR17-QgPo5AvhzF>j zDIx}r{L(~L@S8DHiL8I{2jfn1`kjZ7SxHH+CU*|0euk@B2;zAIh5Cjgd8`OaJS;y@ z^zk`p(5=mFO(;;kGL6LbmY|wdrN`YG1SJu9?y@c>XdOBIIy;k-_!Ce@s#q08#dY(E zFamuF870qvWB@hCsWG$tr53l#{^vr_>ky>e+9$6}pdIx{8-J8Q)!{mE%XixFkA~=X z&q~@XphJvl-4g46531F;^()%xC}yf%lP-I&+<+Es%`&{aNz1a3Fcfi3qd^kd;G0m@ z9Y^uK+oBPW6D|_DYWmrVh~AeU8ynFdRy}}SDGow-OU8@sM{d+{F}uH~b@QedQC&ju zPxFQ;s{|%kO||UsiO6Wa|8+~BXE?~BSZ!)c&pu`l>6 z57HWXsR~FbKNX>UrAJ`H7p>sLi8rX4ow#3lq2gk&C9a&^FP?=ZhAyGn*qW7N>C=NJ&r;!3-;^6c1%DSE;{K^q zR_KXv3fWeg^=ltO-`9n`3&TTS;t{GZ(nMHh4ayOhu(8oBj(6#e4{BfzA%6=*fddly z!4ufAWG}K2Nv7_k*MmvzWOKAm?s_f_5OAni9L@xu^_OmtBgLz`!m3W|iSG5}dQ`2= z=KHH}i@TLplWf=!ZPlT&0;{%AO@NyRWQtKWvW*_NIMZJZ)OL_wzYtDw5M^Ye%2oYI zn(XM1&V(Xlgw=M>ZSUXWCK?AGdm(%GYhBPP3Cboo5UcU&EL1m~{O$6uN1Ms@!;oa~ zy&1ihS;Zb4ug5S&T=ItdMS2~xQ#5HKK;`@0kui>-b7BlOU>*jD@0 zLmf5%SnQXdihF;_Q<*yVI39TUvjIQ#da-_4Q!;8a0OdbQ?a%fcO??`YE|{i__j{S9 zOokTIt3zC@5J~Z%_$d7}n?bnyI}0@A&701Jx`CH63*bWBB<~XIrLJ6gZQG%$kROJx#qF#$-259P z%4qQg9DGYYa;SlN;yrUUt|3cIFE#Z3^2IPQ5`ui2dJ;K+l2IG5O+Jiv(vYE+Fm$Wp z-FtU|vdvMRfk47U6==D6?>of*e22oTjA&lmqPB!;v6MNd#6~PQY;k9clV2PEH;&sQ zeX^!TDk|2CqToTamw$e(V9M@iF94eCehv^W>lN}Vu^TA~M;Rnt`dL#UdLpf^Su^_a zt^FE6l%A6Ck8WHZ)9pO6!})^=xoCl1r5m|@qqkZk=4Lnwywd>CTD@JE^w+eW|6mmA zCd&{(zlPfa^PY6tvGkXnUsH^75jEvS^bc^Kq+LOG|woFZ9wKu zPOeR7*38h-Z**`gVRr;b=eLgl`vPrO0h7L#_aDEoOC@^9<*q6kW*h%NajbO6_$s@? z*@nhm@(GY0mek+?dQzVS1G$ihS8F7&(O<>592!`zsLHN%=y@AyjT@XAvR9xQ0wC2y zvjfKn<|{5B$N}jbM)?QLqZA;U^)A?S0WcXAw3vbZ-3U_3CSk3wT96gPts&EM*Q<0H z()|+-26zGL%HOy4H=v%1k{o1xppbb^Y0qEb_v9EaO^D7Z<);sM-JB4J0xbWZ4O}$f zSxW)*h14)i6IvEPVb#kEP*j7Kg>}tjV;dj2_LqbKnX*I1^XGvy1C!4OB-*t{ubnT0 zylbTE)x26WCBPTyd$N2qwX>z<(}X6C_<-+kf~z+i#!JCNz~ zXwKjH6nUd(%{tIDG7n7Weik4vNkHutzRD_S%jQf~@#VfXi-?>1aR?C8m7NJS{}U!) zL_>hSuV#a%Y{8lu7BI#kVfQ?Z1DuxUSC$`hQ%2qkFxH>F7PXNvopFbD;Of7V4;(u@ zZAjgpgkx#G*1hBy*{3tI|L4E&x$5kU!S9n}^l>u3du85hHzr03d_2?4C-qW6Zv^cX zcFC9!q%r@3cBndK1lIT}R_}FX4x`IUqee!*RrvB{Rgu14sP61&BrsBVC}}w?%fOYA zy^Qi$`y%bG;jO~N4)^Y!?*ZP_htG7kW!7TfB3}(){Q-s}vbFJm)d77$d1Obsk&hvA z{hr65cRWp0sg(ypH#iYe3$n-`O-4MNwUHMCp{U0VA$(F){YJ058$)x=@7k4rxrZNW z%jBd~Gdn@N`4*dFGMg@SAlLFqJ&%ZNBCjlgwMK!Nn|gg<8?dOTqQ^_E5>W5Q94*`v?jlxH49gov#&85Bpsy21wtzBixyNz(aluV3p(Hz{on&+wCd!?V?- zQs}h};^1E%3;@%QFaXG{AMw)@V!C2?86cs?Yi>lL$NpQ%2(8GjqLHHLEn}9AS9F)` z^x*0R3Ns@;I{v8YD)1@Vh75sOYjbUBGN~3T0;bSba!?qf(57B_Uj8!16$_>My2A2Z zUbjW2!oijDbNO02V+8kFVjB@-WM!*j4D#;Qs&rNO^(wLRZx;lYTeSu|-6MYpaD zd^ZF`OFVIn47Rd>K)7E1AA@~^x0Awboi#}Qh&;rD%mh)a26Hqf5vY`~Iy3Wqea9_o zVJ66g6=ON64Bfk_TMpc1uNTsC*%Z{qQ`ledOx|slWwB&Y&_#%7ff@QT7 zjsaRrjL` zC*~*rR6jl6)N1WO%v?;)29pr|SfnYlzDsih&SYF{T_$)%?EaOesr z;=FM4bV(E7HEL{rRNdN|)WVOgsVfWGZ;~K`~A=2x#3oM=EWqpjj zTgTVZWrJj|iQ8mC;@+(ggXC$hMLY(?v;1P*YSuZ1ZwYHk(KS5j{vbSC1=dp%sRywYG zqSfbm=Z{H7#pRGJ*XbGg{@yao?>o33!wNA~*k#5vkwVA* z&a`3gmloMOHPxv96027B;F;%F`9=PG|)xqR_S~h7?e&TSP_w- zRU7*=>w@SfyFQJ(z60f4UKoGD<7i{2S!ai93+VZuADaJd3*DL4$0>})V51PNarbmz znrecEea=B~_|1nZMG=UFJL>HcQ z*++Rk0&m$-b9_@MM&!ov$44gHlmLqEkp1tkOtUTA;qzQ^2eSCtf`3PFjJ6{k?Ks;& zu`v98C!(x~C87S_!e+LZXf$ClSagK{V#m1P!F8c~%k4eW#>;!9#E#72^O7A9_B;?^-LFh4H>X9WM6Uk;}j|-&B=L6lI97Wh9O~9xPl8eNCjA1R zVGrYPGKMezhQX7En?qdfFCeu2u_K6D9nS46MX!e7cf(WNe3|P9_>W&de#Hc+wZG1e z>CRpjIs6NU>ZoJ%4FY(XnRayeXTCDe$&i3%i;vL`$@Qs7OYy-Ufz07H76Mp;WS3aB zJJFG2vgCpTaVg6Y6Q-84v#yRkR>ZwrKl58C9HKt_Ulam_sIv|7fLLl(JST!5$qvFf zMt?TQa!Lzc7%gR{pe_y@J3Eu@E|gqK{r2u$+o_|fJ@+~@P{)r&{jWPzz1xV_s4RT* zCkT2USnB@~!=xwI1_I>Aa+3o}vYDKP9Cy0G$kWO~`WQljK%YGwPdJWiCuC=Wv$D9~ zRx{q%&K35VqqVOrmTIG`CrJI-;B%uMR-ODr1z<=+KVAxRkMmqru#B}*Xb`8o`a4ew zH$z$37OwE49n^j1liJav7fMGTxk-x;EJ~YJT;dN3Dq~}T-@`X|=+5Td@2PJV^=>TySsgCI$L9r{*mIhalGLv+3|nL> z`R*Y3|D7&{wf^&*bXrMw0rQoQB$%6bn0xw>fGtz`12kzP@SEGv|a` z!-0GM?YSIy4tRmbs4L38T8R37x(LTL`Mgg0``jKH>$ezx(mtAPpV++|UDrd;l(-D| zs1KMP@h|Aa_~S1r=t2Vjx#JT;c^!LXH~$S6gZE?Rk@Za~x6C5q2{LS`z~71LuQ2{F zz#~=Dg#iY%ego4xD!TKxpW7Xexyak>Qft>OYy^$Jg4euR5^)YYTlF#-GN%G1>{jkfM$uG&XK) z!rY~b|60DdAr}2`2UzWYuVu;xeqTqb?aErtuI+}4_q4C`{J0Zc;fL$qSYVq^)>+;{ zk@NnsfEl7DDbAniQcRKbwh|B&N-R1jQ2hml6ysm#{P@-RZ5_i;#iu30oz}ZU8df6T zeTCB*OO2d$SIcGXa9BaDCZk`-$Rek)0!5Z34mynj?D*C4KGVR7|ET%jb_}lw7sTT_ z#=*>w;JrlDkR}j_{8$>3#UVXC{(_8Vp~8mQ{jq?8IB!DzNRn8OEc2te)inGbq-uC4F)uwEP}=|6eT1suab~~Lk>YlWxSm9v1T_=49cf+cinlqm zuH!{h`c|1$j?0S2yT3mM3*2JvUc@klQhLu-CjN-7(q#q=Gh9f+Ne(D2<^TS`_ z1#tTpJ?7uLGb6i~N_DG~}Zh_@dHVYM+YO_B&BZ z!LApTdRY;8=BZj|cKopq9P`$e^dkWCit#6>>-ffHB7aBj{2atECWOxa<;dq8;epMI zyyTKoYuAdrY)M3B-X$-7cOvT*u}A%9qD-}Aw*a4e6BxQBm+G$R{70e>|J$)K{&>8m zN83t1Ss47K=;X#SMv>Rmj=SN_NHR%;qQU8=(k`dJ9c zgyX9EjeAG_@+Rl74bA4s??;`DHZ6AgiGht$JyxAPbu3PPzS`Wrtub>57s#Cj#Wqy1 zF@(PN!(=W@?MYZxMAE(du7~g1j+sRloM*{&F}irJ-TZ@UF6g#1f=@IH$L{<8Ho6cD zelX0|qgE>}bl=JZ{&SO{?`6L)7^^=AN#7-fUW&&)mwhudK2U`|c5C`L)X*pOw7KjY z^=WQXY!*T`o`SV?P`Ivkem}N;+0WQ^ms`Q(QjttCToi62Y4pnyPNzX&Iu~ZEn89~z zxaAxVEul6_^)M(CEkNKm_Pf zC3++!oc6kv9qBAKWW)To*U`p1K5zajUU|GM3PJz>aQv}(>dKQ&=(l)93)tt2EAKTVmBkDn_D)o^dxavu3lC3t5oHtTN1W0 zLD@aj8D@LvXIgx0NcrumA9_FO{4RTmQ#wpjfbr)kWK=3;<7}%Ey2&jUQ!iK7mfOeo z^sukxejm~4b7SZa&dw`4qrR9)uiO4mws+pk@n2!Y!Vm}}A7AZnnx=GbME<~+T8sb( zv36$__Np@P;W=-V{+Xa9rk8Lk9ISgI=NaSfqbfx);A0eU756k(nnFDv&+xve#=ASy zf$Wk}ZR4IkZIR}Cv+GsuF420Z-vc?-S35F)ML{fZjx*=Wf?D`;$?V6=t5C(OiY3?Z z#@9*0MsZv?CcfNP(bLs?N|$}P?vQ}(WVW`kJ`w!?ilIO@VzP4C{-YN>z)I|`?qS&Z zBDI?(I5jCNqG5E`Bzk*X6s_jgEZw70T%W5nmF|uZ-RTW8)#MPbnYBtUbo!W$h#`B9 z&D~l7X@_0Uo3_m4ZE-b-kHyDWZx*s1;>N#}%ha-Vpp88=KTNP(3i)4LeRW)v+xIn$ zfFK~DfYL~JcPWAhNOyNLbT~93NOz~w-3&-{!?3&gbb7^2j&*4bl%{}pytiVhQl696B_}!?2FI; z7q`g>T|7vEzMa&=-O4DfW1sp1FZOj9OSexn8`*$2!`F1rj&wb@A!p8MXZ`a`xtEIV zNH%eo8n}%NZOWyltON4|+^({_O%Q)(8&f--TbF`?E4wmp6~S7)Dcel!*4UT62=GNj z3-k1`h@OEsp6HE&!HV?{i2vEqD}xX!wWRNB0X)yHmoJvE4QYl-nsv5}cNTuQCC2uP zs8=&LQa(%-;|LyI8bxGjKXsI0M^!-KTFF&$ihxygqZ(-5vLhp&FqQ9#mismg?hCIA2MDOta+tll8J8K-?<$#_=}x>3d+(8Q#=hu!GT)pM)ymZz z3Fsxl-~Kf8Tm5L0m}vOlXBmbtjcgJ=K< zhy4iy6wnJ$T@_7X6|~Bm$L|ldMT56m1=T6uY))bi8;b#XbFu+QsD+OZY&pA0ah^Zf z&NPLZ z0Ut7qz>Ez+f4`yAp-Fh*ZB znA7Sdf-MdrizxM90ixFiQaj^e>xlKVI-(T3pUFIJI~g8{fM5LS>bfF$WA49GZ)yI; zY%Ia^R0YcSo2&k%Qxkow1v+bo)p2QF2j&)|dnfkc`AWx(%P7uA&b2d6IbiV&W}HEv}Xo8ycNJuze%LrHxWmFgE9p5@+|WG0@o_ zOs%mGnX2mJ#65EIwQE)*9{4)Lo7K%pRIBUXZ-5IApLGfnW=sEJj34o9xOTy9a+q z&0nLDf~3Y4GVh}XW}FW6*X031Dw!Dww0sWufsEQZWTJ@{srpy3i0fOS%gjd*+zG_^ za0}=?fau0)7U|1#u@I}^z(C>s4%G)=sOjD(#{#TCPrzA$u+iNZiC!hT&eFmx$J$%~2 za_zKQ;8xMl6eqOL9(z5cA;CVyKgK8PGDoySy~}*O8i+l;8CjQkEKn-#b|`qv=jjnG zY%)SGjNW1j7CIUvz0P?cOu9W(>E+tmz*~6>4^$^WGcOQ+9lfSLtATObeSr2dtHOER z>U+#O-^Vmgo!3P-8u2&sTQ+m8mPz^D1lN;Xs(EK3Phff7y(7830MCA5?jTZl-Yy%p>ED5cul zV^I>y?3w;UF39M9n8eHq>o>DXVoT@q@{VAmzlJX~{GBuss%_KMF`V4Bigx zZ>v6=N_?uy0t52yeIj?z!eQan+?cC%FbO~BRI<>`dP_F3YBkBl$+6)OX8bw7>M5Z# z7!A8!uyp}d3ugrccAs6&Z6OlD+F~aK0=E!{IG4MBOQ&U?ruc)#y!?rIXt#B0K+y54 zODmeic<4e7KF2EJ@fXE$}@oQ5-n@GAE2iY{B8bokl!;)ZKA z$L+Wf=Q`=6L>};VCM`UYA!SE{Lnu2kq%(|=bZ{=ogY#UE{LM0k`8cr}OehsO&s=xFsxL}rogAIBV@!I6aT%!a3W6(^8=|l7Uq)5s3Ph(AsJqJK- z%EAlu4k@4&O{`Yj(a_3n7R5LFiJ4=i;?m9vGSxFN@d)j6zU|9We6C(6&uRyB3H90f zd6yJm7T`BiKiaVIm&yq?^{?l?>dF7;o&bOZ0dvkeNLv}ci0+Myz`lGjmSpxB9R2W3 zL9G~3J-xS$?vq$BL&VjQyjQnExb~<+$BV2+J+!DJUg4IL6H&g>rl;^B#4B`&jm%yX zmPOYlseY94q?I!q6n63odpu3L9c3ma2{&xBNAc*95fFB3w#w4c2*}gp!~)kL@lrKY zIo-Y(+=a)&lg^o^HFysh{;BzurAw`kf%OA)Ga0Jh0dxaw+U}B9D@{DF3(n~D=&)jm zbsqZC6~0G5ue=sUNAC?Iq;MxZx%7c+ ze2imkHIgd@rwf^2%~vd=pgZ0Td=(rvN8?ZusCU9BGy3V?EC6J6Hr#Ko zu(U~TRVaCCuw;TaY2ihdNQoaW?vr~_WW8s&Erf+G2ve6^2=G?d-z(je4MWseD)KQd z*zq&#XnOMk*lm(1#}>kSH${KqaJgeEvKviA>{J@Y+c7b6`BnGoPL!QMPQE{w8T+*m zu*il~%9R7Dx$L`v1(g~OR%`P9=J>o#vkZpNrFVeL%FehG^C z*QwMa@bO=f-u>mXr#~|>#^cjHUTYHKjug@Q8n-wXCly>s2iV7ZKm@Rp4ay;L$!o8Q z*HqVk$Fqt74UW=xPJGaet>NHz)YH3PlRL>d_2)}`S1F|4(%jBs4RZ13l~q~}vrei@N%Uv@k30hN$(DH@) zg1ZDS#;Pf70s)!8ek({EL!S0WBh2_mg+j?s4e{{B(ru&>_zO?eSO+6@wXO#bHd!1W z!4UU_{uHRL0$^{Cw7#NKukm;EY3`p6jMhzmIOdQ1%6^0jZ4D)iL%2!!7oW;~qXXdH z2vD60>lEvIF*NMD)$1%|-eyu0X+Aw4r$F_ChnJOmh!7oZq z&JyL%U`ti&8);7@6`YiS2v9c-&?R%f*y-i;qsc^0#qG0VYi6op&MC*A8%4>TJ7AW9 zF>(RifEMqS-QkyV4tEAQ6yrrD(ZGc(2PZFRPRoF`)hc|EiOSK|gzyXIf*Ox@zo7Vh zu$mnQxUE>&0E}AJ3!GfO2ncp-ziQR7;Iqw%)5jgQ5}T5)E!&Q^Xu>iT3{S;qcV*Bo zv3=luu;5L0z;J6lVpn?&EGA&Hl6p_#Jelo{sdnKr-Y1rljBk!9-;ix64k-CGH%&?)S&SyrbX%)x^ed|yisx+wD+_j)?80AS4s$zN7p z$7UEYHeHhqytfu)Fj?zJEj&$%U@P{i`?_h#WMFYN3*4=Uhe}QBwV9@NiWCo%Sh1IP zS%;So$_QxWs$Mn$0g@(*zq7~HHZ1YMO~N@$?W^djav8KI6@Ijhb6+Sx*Esnsd>|Cc zwa*)ZCCLdFzh3!L)8w2}3t;YYgb8|NXp4x~t}{6zKjyU%er0PL(p5KGO2Ktgm`XXT z?T^eEK9!!qMAio!**qhLCG$*Ta>G9Iu+txegN7DC@#(IJsU4wmIC?%^LtS)u$9^)& zfqj6Y**4HhEBuRGj4(v`Xwfd>p`z_pGGFH@*o5H>n ztFhcb=&PiGkS_BL$m5K3)3=^=JS1;N0MD?1+Pl6K$Kc!b_qC68UK*9RG1|cp6UGW_ z9K066T(v@}V`)fpY4hMhoYj&DiDP=+hQ*M&b6t`u;mzWg3Cb&2S_gJtzr!%1qKSd# zY`HXlMP)+@;e!IH3 zpY?s`+sTvuMBM;gK(Qi}mB*&r)7l)Vx24F7Ooe8N-ufK|_jU2I_sVpsabLS|swURk z1={GMYCZ{;X~ko=qdhe*eQ8mOAg;??Z}QKQfk1V1Kl>4a7t@plTWH*VmjwtMicTC` zOyhn5l|mCL;BoNcGrYFy5-cEvv(P;H6V7uZh`+Sl zY@C*vKf}rtpqYtPy$Su7-?C`AK7JU)7H5uT2;VF^T=%LiNOi1dAiQh#pKrepheoSl z@T)TXy46%<3^#eBnR`ZaW_7xuxhUDh_0NjrfYe9~BX+8puSpImZrU+R0Pi)B&q+7? z=Acp5;9L7C_QmYFP4Utf!_4J^D8!s`hBG0j9Vk#+p_%5@P_)W(WqlNu&Kl_U3hxk; z6<#qB<*0||AiQhKhHz*!$zPpt89jj-H1a%UupZH;AFAAIWO={Pf$ON^fhJff z;BF^Ayw?B<9 z=tX_HJrMp%mL`_N+i?ajvT1qhtQ>sqi0i*`Bh0)G=&bKsk(`w3-B`5!$fbQ@cAv3X z{IED|p~4ta>liywM)sodh_|d)t6M&9$HC}Fzk$m5kC%UUl+;@ar?ueYdZc*1M{bGH zR#RvcoApEjoagm?27%zf9WYiGj;kM)3jN8scl;`M3xzgKMfZ@d9Tbh5(4?FA>-wDH zWcs`bt2vxT4p$!~f?RGqv9tyN(^i*&)y?XNr{t?|~ zibEIjCLxQdyeJkq(m+Bb=u^N8`2mAALM$<{=Z`iCyIz{6l(9Fm-yH58T=B-|XAtng zFOG~XL((oHOB$RC3mabR96+}af+!@$ZuH5Lo>fR~$#X{B&3c^`UA{#RKc^Q;*1AZV zuKe;&Yj8q%s_gXm`7Nzw`^LHon%5!P$(d~BrHD6KD>4ztIO4Sot#6iuHx8{@9!sb_ zi7YjB3TjU$_LZcS1wnH)(Z`3W6jP`js6>x;$#Z1#e7}OY-a}O zSaW`V*Bfu2H%1B3&yJYa#OB>-Q69HvmaWC@KOg&U{N+@+&Hskii`@sfzXeY*CEGk} zEC8Qc>WKiZ4}xlheo~bus*!r8=JQN@zQ!-CS!`pqu3SK z=IhH$G=vwRkP*!HpAg6T(uEguYEh^sD)ggC)TfAEKk6F(rdlggurV0zW{_IaJfaLt zO0yX>m{c2JE%-6gq`ZOfLB(A+W?Klg?FsylV_OM@DWqn=P@?O9w~w;x^FCBxLylT+hT=P^M%!~`YT7gQmb z>L2V?%LK5Z?)m?6SdIUvQ`%2%j4hxXQ_HRu6(~O9>UI-sUQByqv<((?j!f7b;50gd zz$uSs#UIQ`K~0Kes&lki6XV8vC^_u)^VGx#6mPDsg6jx#Ppv9gq=u=2nc^o6W{ZVC z`izknQmD1omy`=VD}4joLu8f_a)$ctNNK}dyYbB`F1^a3T|e>+?2Zeu;_p(h?PK_+ z6+3o((#X>xZl#$iHStEq*9-GR=dQ=vOP?S zzg@rG%8OmCY8w~ljb@)_-o*NOiE&{&{3dBv*8#gYQVP^DOrp&GVhlC>xZ-QcLZxg< zH|8M;2o3(-Yj1c2HR_MWWtI=$pFiCC$|ILC9jZ^->Gansrf%-jrhDjDlvQN}RpXDs zmHIt*JgA>@xI!Sx+)TYu#Oh`!*3AnGR7DHuB1aO%9uW+N&2^OXLr9(a~QT!c!!r+DhblHe9N% z#t2yHv}z%b%Z0;3iNHKZa3CvDQK~)4M)#WItuotjv@6Rr;^LXOPE~9}T%G(>j~`i1 zZLkr01H``-QxCc9U4x6YSmu|DEMjeqm(_5p`AYt6z=gWY%}}a@P0r8P=N6VxprTT= zkuI86#i>0kFl6rJI`fEbzRq<37hJ$;MyA6&2a!j{l8sIyan%4Xqo+0ouDOOC`@<2u z1UGQ0svqYK=UGecBZciU9u}(9R@^#0RyECR7D5J^e}69<6seCi+Y8^e7yIdH_XX?f zvh1)7K`~tJdc3S^@7!FUQ6K5X5I6z9wTr4XD(h)N*=umJ^p>sy$%Q(H6ZqrUPn%M# z)dZ%uP>ypb!!16q(~fww%Kj(d`*MRDro*X?)QPXwoWLE4UspSy zjEAlGNp|1g9z8ibIAEZtw(lH!?#B5ce;GO-FaPb+Rc~8c%B!~No|}&v4&60_z$L0t ze03Z+cX_Ud!c9<`q2yT5!0lnWE;?N+qec}ecWv=R?sB+mw2mRJp4*qz495O~k~=(l zZ8BQlUnNqBY+?kUBCqpqiqgZ_viG;M_6$NF@)rZee;9!+izkKzj!b(Z>xlkmM_unv z92jEh)ML_?gE335bUg>>al|IUY8cmd{pfR5OXBW~`-LjmghWeFjS%8Wig~=A+=+)7 zd5@oRGj!J%;k7?kN@A5NT@kZA)$1OvHX0&yW21NaH>UWk#S7gbUPr2+jD}^Xzquhb5M~Kh(Tw*fBxfLNy(iCR(hTdo|7 zR7uHUvBextSKvDf+9P?PGA`#I89pkM*-4~t6mfxt(*op~jzT)#(twzw939J6L1 zhMK<@Ov$}bWzxMW_L;CS8FaUH(@&Qxdcv#bd9{1Etc9sqsYpCbJ}TH)lk;%n$}WX? zIgC+$dXp>e>JbOrW07xv+=6hKIkW|YDoH!lnd)`OI|zo# z?2v;`vO;5d<(IdxmMBn&H(avHS2TsW?YVM4N!FiJ`Poqk_ZRV5So-&>c3E*7j-!Ma z_WRo+8~t(bDO)!;>z#BS6i>X{EhltOhH!G$eEl zkavX}V&p{4z4F^vJSz=`!(&o<9YRyccW~CeCgPdiMZ|R}$VLS(l=87YE&ARP8#EwM zAd1aU>Sr-;n`1h3t<~)6Ax|t(@Gw6Pc5PTfMR)G_f=nulT7cEcTq4J)q4z?VZ-o6iouGSU%oaC#o%^BR(8~VK3j-%Mhp=jlI!A5^!fDhV(dZwFcezj$lf8d_{ zX>)2?Fk2UD#y;SdJ=1mOErz}#FcoO;{%);@^*_Tiy?OmItBSIvK)zVu?)oZe?pS29 zFb;2D?a35lx}sq8|9NqtGB_avJ6nF6xZG)tOtjem@a#PuuXQ_lVR`9EWxe~rzs zRy>`o-CMvgg1K_o^RP}%C*NE!ch-om;qom)$|g4KNB+qEO(>RqA?=U2x4rfpi-!QJ z{byI;5oTT>^5n+6k?d#HLNagtjHuM?D%fXeC_YsK$^YiSo%F*+Zp|*(S%)HCh`hJl zin5}r@2!bReQ)!7yMpC{$`{fm^6y?!z`+CUb;y8VM1x1d)FY?y&0?x>z%^4u0-^nT zWKW4{d=VdwvEC4HoY|Knn`#NqLQH7Lf?LExjnO64oQGF0c}Z9It;% zZIYXQ9l6*~nn;&i>W+C)qh~3L%mT1Tpn(oZOt7zXEJPo$McP3zya~1%iHpNkDtD*V zV6$oJQAte%xv#CuB(Ye);V`Marmc@6L*!|s{?4Y1OA>;2QFHEn1$)My^Ma}O`5vIt z_RVk?yNh8eOi465Pb*LD6SK{A-u9Tzz4t}^)mS%P^)ZfGnL$$$?5vN>@;>m(@=H6L zMp<-kxVJ!R{|ip1c<(>Oo*>T5%>{YhsMI?j)iMvuN4A|KL4?1#)kH$B;*^>k5krlg zNLybMftx;cK(!mz7+`byvy8CBgVca(8)~s(eqY3jlzcpJdo>OY5?B|1bmL5lii(=r z!Of1q(-{ATO{_-DE9Vo_vrZ_K@BtT;@^lk?nv=tXo~IWHis5&$uIMuq>y*Nyj3;I^ zUu^FDZwjy-!da=aY|PhsDNgs}=j&&JNF$?AOV|-}<-JJFV@X=&O?ZMw7eTeHoMkat zFp(oo;QM1DgOk&8RFe9Z3+xAH%KFTgIp{{i=9dWc_8TA@dD!%stM?W@tX*i9Xw{p| zWZnP!g*h*2d}B@!6Rr-K%N^I+oHc4%qv!a!$xu-p=7<`27Pn3{H>8#6mn5mk<2EFH?sA@d(q7RIAebH9-$is|-dWY@BRZZsz>*Hhup5$g8 zHJS|)>(Z%aS2eZhRBHS?VjwhZhn}s7AfAj1w`i%(ogb3e$ltp=yYdQCPpo47XsD*o zmE!YocmLTJOON!ud+P~rZR3Y8vN|Jynix8-y(^I)*qQU?=Ci6>-e8ToY8HYeK}DC{ zdE|Ed3i}vB7%BYZK z$H_^5u-TLOx|q2ds|Y$im56-HMv|rBpV3%mhuF^gP#dE`g5am5TAL4mrp$@bb4ma4Z(!u$ z^?=YTM7)`$3-x}P-u%*kH7ud_C}q+r@LIIedEqOU&KslYg|`db1k^70Eo><|?l*l|0Dv1@c=0Xv`-w=t>vFx(q9a-dSLkbR zC&Q(8Hrc|?M-9tPZg`3S6uz3BJSa>yJ66D;lcPyjc*XEX0bu<`Y91*BHPz9{s=MKI z>*(Up>MkyZ76ELlCm^dHw)YLK^EnEIh4IOi( zO0m{lI5t6mO1eoI4BBjN61w0O2Li()=BsZz1$>oNvT-J`SqJVa2 z8*2rSJMR#Wx1?*8Oyl4luE^p%A5Li$)9l_F`%H214Uo6g$VeoHp;o8TOm!foc|EH4 zgChueTRxxX9F4EBF+E3xF}TEn==ASW>U>3ux<{O8FUA0)BuII=le{$)Ka-$rt;`Ag z>g8FVWTHL*HU_s^$-m-`VS!-h4 zKE%3}!(!V=Ar^q3oL$7gGu*05; zyt~>BmzB-MULjf5Re@-^BB-_ffd`6MfUxCRs_#`y1MfQw$^A zf3iCO$7HR%D&7$A;={eCxcgmtx*?~e^*cl!=;6E)!c2rjnUb_y+|*6n9$&Nyp|p1o z5KjNHR93`3(rsPuU>rtKVUPC>s>=exhK?XBG>I~Mk(XI9A-XS_!f#82?q6V|!q~-M zRoaF7{*!S6XRBqrG?t||wCj9Y-P>V{D-&S&EO~I5Xi@q=jVEII_WGa&)yEIUHDEdt z7Dcw_xO&QY{h-ALF%NqNozLfBU8bq6omZ`b4OOq`>7}5kWv_w~bLEjFycE+58;7u_ z7i>m7d+7g%IY~c@`|;`g>cdWwbTQgkm#_SB!!o0MI%z2|z<_P!=EFJ%DT;Saa9!uG!U@c`cR?hhBF)hGX*7jX}$SohzRSs_UkJTyL&ZfnJQ`Ve@D6JL!qXguFYrFZxH%2&8hc1Wf1|!3F!c=*0Nl=;*L=Hm#D!5;_DF@ zq}6CbdEM7VcKayq?@1%zUPi`}$;>V0tKPj50XV z&yQE^bE$-~OPzE-l2V?Y=dtESv7^=2xs2nFORA%@{Pz0^r~`e>n?5)!d6ZQ?OLUzl z`T(_|#kz2ouWpE~4k~>mz}95Y?f!2BjiVa*3~9h`dNHS>Q30^Zhw^Iz#kVuYJPsy- z?*$gFN8-HeVI~EKvl2cSN{^#gJ)z&p$U z3OHe`=SJ47(D-Ds?_W@dk=I1#)(`2#Opd(vl`8P}fcaFdm@UP_H&U8j8dzF?Z`*gL zV|b%@lH>GQ6gXW+sJ}8&@%(-j5OmTj6;Tum<1*J`H2(^+;B=CoU*gh*PpUHzFF3IZ zy?)QwfHuNoV3ydOlvA{X4$sB*Nw!2e>3px+wyNt~XqQ@X`w69a&*$bNY?#Z+m&QkT=WBVzg&AH|>MlP=I|y0%=vJMPD{qa9BtO zyJ%x);M;FA4lX&S^Z}~roO5?Z)p{8C^dKNv@CB#SZ^@sT%~#7BoYw4?1;+Fbgbdgx zk2f*q#tf}3xEV#QYixWwAkQ6(Dx-gHe?vs`soA2jK`z7kKAw<0tgk!+f~^@KyR-3r4(ML(JDx7 ztG}Yq0{vHuXTUeUYP#{tMsYc$-X6rdufCa=Xb@2qO-%{8#~;+b)(qyHFz1KgFeEi_ z2|!M3r;+Wn;=k$O-{|6chNkeHWl9t}zwr)5w$!w|a^QL1NCZnGaDTkw%5XULMiJa> zO>bUQ$6egB-tv!FbG074iHV7{#y&z`0`J{D4%A4Uwnqq!BSN&i#fLQLOBapbY~%xm zzi@$-<)NHbm9e~ZKW|WM32E3G?UcC(%r6#a&7TB3EV_9!jd}EcK~(_&RbIT`=o?b; z6rh*eLx7_0{U&6jl=E%r8<-`M}*6epnP3!S@@c$=&v|jQY@|E?I7}!BG7MsSaI3TAs^7{ zfSMv-&;FPgRQ2Q1gEDfXL)mSrR63V%DbL#eCu;&mCZ1#yTPQ>u6eGmxkV-=B}FIS7pcL+ovYqF&c3iD zcQMp8<)7wx0XO^{wzDukTq!KDU$4}gVPtG9!Bk8TsPI0Ap>Wt1L-QE8sq6tWpIyiqufw-NaPbFeMaK*aF(bS%($uK3i%QpiDBi6&g~l*wzp6)ap*C%>KNMK>^7JzqFq zt9HMc@nd!?-H|1;saL9{%F>6h@R^{z2mMyKMu5VF>mz;Xg3WM+!B9*9n6u1exl@W>7iq8ftF(nQ<*8@rWLMc0zadl~E+*k3$l#4< zRQ8{}_V($xj+i_iPEi4C8g08BC9&#wGW)(eopy8csMPPrzq(Da!ykV3+x))*JSm`X zG)s?KMGLSxOuer?c}MNWfol}X>Q_2*&h~N3{ZUS+XG=QEG*m0cDVsijHXpF3;fy0- zl@s89b$w{hIWrDKU4pVI4!5HeIBIL^mP`6^)T*8dtu8O+Y}`wI7T}L#V6s5cRMQAh zkR)oX#tSS;b1&3nTz)r*Pob-*2A4(+v%0M20POqy_lL$n0khWvp*iME~y;sKl+V3@M1br#PW=rY8>Ok?U_{#PZ;AlB`w{y z1;T|Ev(L5Xa%hwJsjJ5*z6WKFiUsqp{E0EZasWaMxvZR(ay_W7HkBIu=S;l*fEY&+ zhR8}k%ZpZd$2}Z)DKXrwFC=TCi!0Am!vma}g2*S8 z3shD3iIT$O=Z+jL1Y}|H^xJd*JVk&}Gz=^-9qbQ;{t<~UknLh9!u;6ShuFafMzP@M z;%4zTj8k*DCHuJpX*9L{s-z@{2dCpR%;ALeGSQn1vjubLb_5i)KOFv!o{9uW)DAx$ z_LMw|cR2e%)iBh{&WT-CeNQS(;(FFyT^6`lx0e1M<>yg7l$ zS#e>SO&fZ4%nK_Gle$I!+D_~7EMkUNiFKsgfY@*JiMi->Wm!0!Y;)G93qXo~_HNza zFE^I#-=OW$)~?a^H!Kmzj8PJGXQbLctl6v@mzx-*>vppv!q6L7oj*IA(VLL0X z@&%@gzP4|jZ%cbBQl%=mq_J)E=UG)cd6g~XSo_XtEH^Ukt4C2v6wB2M2;LmaZMn7Y z`e)y(FDV{`*V(>a(rvJ!{kt3A|BcPICc)Yc!NF+*LdvEaESO02#w|&rm(p^XW>R-e z>lMESA-tW7a+_#4G2nWl>d^k(gVqb}{|;X%uRIBE2;(PgW`Ch5;IO59@g# zy~*Se{5TYq2t3I-ReOv>Sv~$-+tq0-7;dX}IK9AT`a5*vuF^es?f(nJ6*gK4N0`fF z)BNxlK@EfE-X8EH3C`ntRF^1Tm(O;^tC9f=1?YnfCit>2mSlOEfYi3!3W% zqN==xk5d2SAeiY4NYQ{Z?FAXil`g8o=VfK*%S?v>?DI;`iAM0YYt2ebK ztttBg=zE5Z5|6k#ChbSxB%N^|mSg)KbmZG<}`nq?cRU{8{0B zT+t>n5%XvS>}rR9w zzUZ98LCG5-^55%V^Zc2p-Mh5`p5Y8mRHYib?RAD)b)32nut%5b6ecYUdF}G0E*)md zx`^ddZPyJ6^54Pv4E6sL&OYmKwV!O_p5^@3yFL(qjl&JSpY20k^eTQCO zM1=Ro(&G{FHF7KSi@i~)Ci#6bqR}b7gJxM#Pp8wpGV7>qXm<|SLkHCFL*~vQbVka{ z42yOuTBiA;-w0}6fwI01f3n@-bKvJBX!PkjPmrD(TrfUjT%1hKxgr{K4L>96hS^m; zX$zjjjKVFzWx1!9$(IcL!=az)?FlRiEM2ctHxbm#&4&$2FQI(n)0%5;`v5v9xZ(bc z* z^C)>^t5yY?lxUyEcV<~=RSA*(x{%|!=Bjb?!SV(`YC4Y==fnBfr|I+J7{XnncP{0Q zD<6156Ip_eH!Bq_rkkADU5~Fk3+|nXXX$G5<#YM|1`?|--9N?3T*!iLu>}( zBcgg1r8v^YP2yLEEYz}dwmXPZ6G^{IKnOEc@*iGhGw*N~B$pT|4-skfgH;|gPG2vL z&!yF0ohFUh?Ua>Qe|w3VYo4M~%p>huzijXqW+K@D^nDQxm)?^2I*f?Ae|3J)l=>4Y@ushdJ((7Ci{_qI^f_=&XWp)yq!#_<@9yFEa{^aGK@A+eE zz4gYr1{5u*O99Dz?H;w_6u=h}Y~i3ZRPj!m4l@zdw?1>u{s%%(?NLB$x2aDCeT^8+t8Rt{< z;Zya+p7dx9VDvqAJYwv?fEj%Mj?o_-z!i1M*6N#K9B_Rza~AODzjog5XkC8~ud_8J zSLFcs*g$#8{}ldT{|FSqVg)3-o&vV_+Bc_fd&z&(Dp*(na|gP@>^*URW`Lohjg#FZ zZ!iBXR{m~DdNcHV#cg`HcRZcU4N#tc45Z(^0JN9aM|_=~sX2K+pS1=g){?y!U|#HE z%%IW||Gg8e_W-Qh;L$w(i!Vl#Mj8BU3dod0*0Mlvs?US<)nQs=wZ zN>p6R=<>xkOCf(|*|dc}|HU6|hm(q##> z4EOvf(QHDXN#J_D_$U+{#EYA4s!MEgv+H?$B?S3EoOuH6rJeqB#IfE{wr(+##EN@z z`!jqZgk|U7`+~GN6ig6P`np*OMA*PQLs8=p$yV^x&*%MS8i6k0LZ2iQYS3?f15g1tmHEe4EVNJQM+Ua-{9=Dj&I;-MjGWXrWV5q4r0#d4KH1 zgjXAA)aGjEEH*3=D1}>*v={}Cows#LQG#^|MdmYwgWco+_ieN^wwh5nZ`AwURaQxN z(~piv7E|S=bv6q%A7V8)tVd0*`fdAu-5P@Tc~#h`QAq<+wkMyk7dM@ZDl=I4U+(k4kFX-%T0TF}J;q%613GtuXn zM|A1Kt&}~DJfNCjQDZ9mF{v^(Hue@eUjRnE+Fg3&n$#g^HrfRQPs>O(@4c(9i%m?a z%jL|sOQGLgWC1`U<2YVezC4wH-_%+3i1tWJnGgKXj&pbC0r>g694;{ z*LgPmWL9S*6VS^eXp%EyVje3V;#xA};w!?`l9^^b-sw#IJvMA>RAs5lo}Zf{N~F{? zi}ySA`l!;<@P0#h!YdvA7zXi^x5EL>LT|b|%D4JJ`wn7y?vm2qVv4Bus!PYLVoH;FgTuLf7IlayF+M1(kiNkNxKevx->XT)uv{hH+D_m%v0uph>>O;DZ;Kt$n+?gc`e2mU?p5h|K z!5hz;+ha~Z6<8|lLbn(a0}lZt0s&nQL2U0C#fPx&z{p(<%PAi=4$>+*vf`_$(@oxc zt1o4N1weFQQ`)#(0$tZ=$#ueh70D#*pDuFwx`2T0JYO}Y3!0|vQ^qPaMIuzO;0D^< z8aB$5I(JspZAnGULc7CPqIopv|s1!SNg+xEYF zl;3^}Z~jU*S>6>(kNAgjwTI>@H~uq~BMlwp?h{LMN!FH4FXig~9(+CGZ&Ms+3F5BP zB*D2r+iqa~m^)gj{^lA;Mi6(`iJ;yQZywUe5&q=F*>lkHBhJm%sDWzu>~TQ(XGd)T z`x3{yzuZgW@DyXiAdAPX)hoL%|>y*N` zk1^bt|5k{oP=B2&!`t_gzt1oS8jl#Ey$)W(9P)WkPXr$5{AAF+Zf0jM$SQJz$?vtl z&Rv8xZ_H5fQ})V*3?kXU%~D8eX*F1X)G3!0ZATnsO0Ixv{=8EDLu=)*`6*#b z#~J~(l+)5JhkF-Y@ig-t@RBf)&}M@LyIuO0OY0qyM|<;-A-eTJy}Nh3Z|!B8F|58OufZ+z|?9 zw_qJgza4HGcn;XAx&+lhmHSPw8@go6Cr| z&-autUa7)h6TjSJBuh+k{;{l1Mol=P=5UbJ_}HA8xm)0|a&(UO4T-`K$G#Wx@3BWT zpj%K=i`bK7RF!I`GaWb6R#N8_rZG`ZMdip=YF!YGeUXf@r+|5#8Vf!Rjn3cRnqHWg zw|vMnL0@n7o$T=he3Iz-y`{YJmfdis@eoACe8{ee(#$s&bv2EM;#2@&wbGD62}yHO zb2l6s&i3#b)p);X@zr2W_c{eRByZtjYi2rg;;(WAoTo2kLJHhY?P!L5wWL9ikk^56Upy9N zk~M|a_X{qV!T(;@$mAsjlLrN5%O{RahASct>4Az)^E`6^g_@Q{{MU z!MjPd8!;aNYaDZBPptL)?D&F?+FO$M36IM*1H@~3DRo>~w$`=S!hiskBb=oWn@OjJ z6%wZ!ch>)EP=uDK`#7m$FxrMBqB^vC;a#ec;I(7?0(Aol**Hbw?UDdh_-I2-(!~iU zU9+%sm81EaX<{vf+4$*@BfhiW?oz2{!#S(YTxqKLSVno@O_AfU@N%vIB#J%ygarPD z%uEiCpfIK16TGbP>F_l-LRhgNi=D(7;A$ok~f;gJW<1xi$!1J3k{&YY3RW#c`;}((R0tCl*wRr}F!2Qx_fA)q@}xK=x&Bkx;uyNMsjGr8=vQWfA4o){J|e^&D`fcXYaN5UTd8( z#|4@F(>)c4*8xYfJjIJCnu*|`8P^H-t;iBXQO)?4D{PwK7XA)6@8gbotB0^?XHd6o zGOt6~WArRqzlp&5dyHmENpi#JHn|z0LH8lGim^Y-GMm)joyGnLc>oyn%7JZqego&j z!A+C$a-vD!Z?}6|pjRvj#=yg|Fd=O^Q|No2N^xcM>+r1s6F!5QQc5ze{-n)hVw|6@ zB7L>MCt2mW#g-+aisp!$VinJ(!Nr#y3Amo`_!knzQ#G&gVOQxMqRfrYXqs-3A0bq1 zp@wf43mV?tiQQvIlgo>sMDwM0Ixn6>1}f#MQcVMVGmfZ0=6bRA`B=)7TO&Woo3Cn{ zW)&$`iQ?$2O9JBKn{ou>>bbT2b1tVlH8MvoVr@zdRxr6C1{f1u|5csYQm$3u9ek-1 zeMc$gZQ4E5t{a64>~VA{U|Y$YhEnn#rUT=ce>BB(y@x@ z1kbiJ@()7$O<}Vh%P~z2qiSAG*RT@INt(JfSb0aR;8$gC2PMUO@Ep(&>#VB2*>*Oc zo)s%d&GR#oo?=!>%6zFy>0R62g_&3W!^-gnT)q8pTOzT$SXZR9B(abD2(QP;5;alG zBn`$`*`%jfeLif00>HyD8`DmyMMOIX&9!AXzP`HSOsxa?Rs}aC&zBi$2@g1g4}gQ=7lt2TJHW|kJrSuRjA z9StWBhda|smDb*r(w*F16~=0{cG@?6qrNuVSB5#Ui-=<@UF)DkkkI95#9y~#hEzx? zl1_0?_OO&Os&RKFLqe)trL7v`I9rw;Ydycw*T24lwwU@{h*<@P?e7A{>zkX$Xd8Dj zch*08hp|Lt;)~~;B0jy}7t(V5><$DFX-Dz!_HQL`$Pv-G_KX>)HI3&D&>A`@0imSbJccRPoU-gs?X2dSfO5dD~H*V z!2E@c*&;V|L*O*V+UY0~;#MZ;2+P=7^4RdbOu?wV_@}Pl@rg#8-KYWZ)3mi#L508@ z&Zs%L1VK5Ahdq&))2Dx8bgjC}Ma{H*E%LoI@A0F#j%55$jY^*E1FEfv<=oi6e8JbQ;Dx#(a zcsh}(>)cJ~ZzC?BB~>T%sTDBe*w}qC(~FqTo3_>R=2%?bf-iWhdrSfBmB7C(k{usu za(X$csn<+JzHy9UZDxDk6PGLYH`if%xtb{8W^3aMQo9wOu|`!@E1>lRajw1q%0nr{ z)VML}YvaK@{F9q}mE5d8E~uKCxpI-7Y}lPyqxEKA^QqKq0+@!M+51Q21IWNA)%@Wb z`-;>9YAKdpZjF4JGqWUnE32*_@#DCSI_4F7KhUOU*Y^p8TP3N~b~m61glUO-w_xXq z#}g<;yGd?X@>Z9otvJw0y_FB~BBXrzGf%=q)Mx9UfmVtN=;H}$_pwS)Qh1qkY%N&0 zz5H8q$x)}=O4#WZvvBHCvrxpq;!6}hS-5v5RWwS3aE#^GdvUSfoa?gdTNFV2i#GeL z>RZZJ1t~tpU#J%7&Av=0l|NtfG7Y&j>X`p3Xqd_tudrIvf17r-I73*ij?*P4z?;8s zaXh!)p)?ep^*bm8tM4UNLmP>+sInBYDwy%tDeOmPAt|04z z9YELuq8a2x98m*Qvz?c>j4}4pm4?b?jaGqTK5>6`Rw0{o(f7SyQb&yX-#ikhSl4*p zH*hR4E$uMCY!@^O22*I+V=s=cRn5{Nb%Be5jBze;x)i;#7Q{4N?u&S_cYxN->U~IM z9z0{GF%f9#_T8AUrbXuMtrbis{eZ7W~uD9!|kr)Iekgz z#*5kC$OocLTB_0t2@m-yTX*LTLRiw4CMFhQVj=6<^oLfR?vh}=L+>fSaFtTW&z040 zA#Ai5qbZW9I}zO9w2q$Cy`X))Z*}f$?h2;hC%tK-aT66RA@jdpX*2ti2e;S^cKC$?aB3ZVM=( zN&B0iTBh@W#-(cl0|SyrrnJXJSOD`-`R!vXCl6&hXE6Vk4LYO}pH!g(QJsc!nQbCn z)u-JxnrOA^Jd-M9F0vk^vr#5P*fPR{JCGahknQ%J<{(piR+m}tD4($>uox;Ipe_2| zjwt&5O=(5{xzkmBQ<)7zNw;cj`R>{I8t>Q6sf|WriiSx?L0OrUm4e!qxBAf@)~E9% zCggNV0Qj1J^Dv6E1pgbiB+EJ4s0*_u+Jb)>ik|D+_-^-owy7xjAegLzIFMatav_uT z;7%yY9X^bA(hu8`+l|^3goIhWsjaAPsc#VtU2MdbWZ+Y*DE1u#lTNURM3h~Q3F)Ey zi2M>sTw#ikYoW=@Xx4{Qwh<;IBwu{6VWh<&ca6>IIQX^({_Unjb5arNl7?<&iZF!s z++aN0Fde*FwdDF1{jvNp^AUe?PSqNEBg&m>-c?uxDMHsPD=6^$nR&gTC~GB|?W&?N zPNLr_)5&kelbt>(qi>Vz)X#seL5P4kw~?MwUZ`VMV&OI^3!ge~B;JWY4P~)#q)qb# z(=BE@?$75|#auY(8kkYW3y(QX=UuEm54wAx6G;T1`Bj?bIKqDy*LK*9ELQ=DxT5aWJW%j@EYy|Sr>n7OG^ zH6=@L2ZzMIn&y&ZRW(nOnemooRPL7TL3$5l5mbK_!oi zuZ?cwH)Y;xC_2shQZhoS7);b!nc|g5 z^^OV)DG9`6&&|PpN^@m<61iEf4zH~{Jvnz(;^B6$=yZjsJC$U1qrgu4iyyZ_fy0rk zIN;?8V6&m4*gX>I?&Bj7!lM+$8*!ie_Uqk=H`BL(*k$(F<>F&b0_L2|f39^YbXDPm zM=`IG?6Fw;vn7T;e@@#JWPZ5bdl&KC0(UvaN`--c7&!!){G;EB|S>nk-|` zc{eIolgTV4)g0i9MH^prCwvQP&%PU*Tl#7A&Mi_OIE6DR8g<~<+tXItNZ}m@EL`GL zC`z3uy?6phw+C(?tm#F!wedFlDcmMs37fLPSjp#Lnd&W|6ESXM&c7A5(0O~h-9IvH6V5kjAstZN52E{|JPky8_1gRAh_x}Oc$Uapg`?`Lwxz1Lk|4Ff9 zg4Rw6>aByruyGGG3rz>1#Xp9#)@3@Ga+v&fk=}1g#!TJ6r>@P~FOu$Z0(wZ$beP4S zI?M?&KsWfixI{iJZoVYK-V0AIAXl=OlU7$*^q2y`x1mSgf5YGX+gPTNF}H;y%GdEo zQuQf1j7LjH-1JYkHbll_!cFYf56&%Oz^paO9_(HF|qY8bO1=+cf96gDgqlOfy)6LlWADm6P)mNMR$C`W9L z(Ea$}Q4{$()H0jxX&ejU{@SGEVZ?B+WO+-NzQ}ktZOPqArOf=HbC&I?>DzP>#WAY3 z0Hd388?%E3c=htOMG_QF9ERa8V^k}YMU}i}reKScT_=!L!H47LQ|VfnP?;g+X&H8} zW=WaqsuRdrS8%rrONCI+i=AlWaJ92wHaP&3m0`+h?lHCB5npKqYmbG=W1=T8;aBI~e9Me;xDgv65 z(Z58ruZOJbAw7x8@?uyxNKScYE*sk|_VJXzhqV8uk_CU|veb;uyvKud6ylHr*e_WB z6pg;Dd!w<8DHY(VqmxXA*G5QCtzRr2GAQdJZ4KgU*Tgc=hL@%VN5eK~hzT$$+Z&jB;U9 zHS%%ehR?24YG=upg`)3j5_J{pC$F~=J2(8arZa<>Y)fv6Bx`~H| z&nA9IomzZdXN4`A%sA|ob&1`&BRWu|Z+cTBzfR&BKUZ8&e~kt`OB3{<+-orOd21n$8P-Y?HF9%@D@sT>1zL6p-1xu2*q6;Q2tDE2u z9>{i0VUmt79xH*>sL%@s9LDF$z#3P31$(FY9nW6Yc-l=FI)H^pCMYjS8)H0>^4#S$nRdkX?)Rw2@|v@7eF157_tI0{885IKxIP_NG$ zyc@5-EG}xX%-Mr!Y3I=Ob_oJ*PNUTRZW@e&uX{$VliDt8^s4y0O%E@d}fY?W88D* zqq|l5d9Fc*TGEL0axm4pTJ=Z(DhH4D1jz0ww;*2mK~Gz61vNby)JjU>$+K;mfCBTY1`4H zNWYFP(opkYVY;0sjOaSmI^<(Ds=kO!b6^brAg;$3oR09HCc#h|;(+ja<4c{qo2<17 z@D=TD^Pdl67E5Tm&|O|9O?#()NhrrL{hS?vgm23KUKRGPp7E%cQD*QtY1O z?sY#hdPjbB*rb2{I&duJv+POA!paNt`zHwUy_+}P-XNl?bHRpUsHCpC9)5c4s(?!( zo(v#8gamtT_Kx+>@HSx3hk%xPH5~sh4x$ z5+D2iGkU^$QkF`CU!Y;Dk!Un8eaVTcCCaB_=GnY0v#P!f92{?|Fru5Cs+JPHEW|P( za1!cP5|WolN^RQS(vN4)6fiXJ^^vvZj#D}JpKl=!9J#`CNY@lA=y5szN{2Ovsx-Wh zF=O`D#p3f+N3JbV^xfpir@NFOqK10+G_|rz4}P_}H#y~8=j0_nJYsie9&X{@>(EuT zq)Iy9&~es3EV}OZ%5hHt0yHA#S%l%j6u@5(ZTat-TRpD%&vU;+z&V`PG>Wy8$*f?U z{kmW1A!q5~Na)0ZV8wdRsQ2hryW&|a@>QqH!_Qp!-SpDVVpFTK=zQITbEj3atUC+a z7j|9mOr~+ZQs^t~={6M@F(fh26kfxpjf_rnOiI-Lf~-^5UdnV;42)`Na<%wS}w}(xa%G zi@;YR4ohM14iZDp9$lJo}l$`fC> z6#D{Ra59rRF}77cb`g0WUvPJu&EXEW0>ir4F$ z^QdC!dA@G8PjrvrdL3MHmL%>%>V5<<6sbddp~`P!Rc6ae0S|L+KHX(36auFcA#Zvs zU+fXsCI%EYeW-z6^uV&xJadi5vC(;+@!rTR3clk=%Cl_BJabdpK4-$$*YiE7J@_U8 z-0!?206ub;YQ9^;5em`&J6sh}D26p!pE6`05rQ5!-y^W}Wiknkh`78JzbVcX66zQ` zN~g%ZX}G(ee#q7fimKThAXQ9z!!8;jZnJd|BulUg7B?H8g&5*#*xmeo;^IFdRR$Xf z%2;-gr{CNdR&gCMZakH{x~olaO1PC5$Wx-OqYXKT+cU&#*s4@^y!d#6<{Ws{ZF<^Z zB2{)J00ezjf#cxc)*jhs-6(q53)G4~D829BJZkqGK*3Jh!}ISypWPuol4gKH(fkSE zpJn{>`I>mGoX#^7Oo3MSL@((d6ZB8x8fS1%gN!M?(h9P=sY|k-l>I1!FStr$K@JJ^ z-H)L3HWA=+cGZg+v3Lq^R=uMjuOg0JZIdxQUfa`DrPj_s|FN)mA;pzn$vcZTOWVeV zlr?VxFcdcnMuV-SBj!G-r`=|p@ovoNQ{Zz2(2ur=>yIUk+dMAnfArw>h?*JSsIE>$ z(0H&CX$0MmauHS&`|?5Q-e*s+FdEX$`>Lok?dj4QSp?}@4(tEa^B*Ka=%r!6ZAgkA zV1q)RNabGll--o_OG`gnjMh(Yc_3FMCVbse$}1=%-*a=4*zfBM+H|5s)C+~7y%&T@@G?dALx&uUHU^>c%v3+P#gbj4xJbcS&k$2K!z zT8N@P#jg@>daz17gyz(l;(TLNAJB!s;N4K=#8mM1_4>}JS7b!NO_T(I+r=};JI$AS6t#>QqiFi1+gDUie4<87Yzh)ADF~ANQz@iKA6SAN`R<)cU zzDm6Aj%MeL|2T$+EqE&x-Ht0tpY!zdQ$qoU2>!|rUDsP`UAOCKpZ?P?DmW3{o>@55 zwdha3+QrtMI&Mb=!8Fwf59L`LI_wK)!k>n?G2@s>mc8-Q$u@%-bxiwCrQsgX_MW3P zN}a+NM-xC6CsU+uqzIc#LyYpxFJ29JYiM|1$TohOiCDagqHDFQ-ksR9Ohl5VMARu# zbjqzgaH~;kc=V}4`A5hV*$W6V+Z4vrAWTWrf;M_^_sc)OREAYQReg{?UK6@!rb)1P zGAwla-Q$@+b_E}W;*sc6qe!EAW7{r^`Fo~MwqQ?|vdP%mbnqYM*>kL%B-{&yd41~9 zKpXGpd!G0(Nl#mW&t`tA?+WnyOZI}<4RbX6c$-iA7{i{Hzi)`6`qUKB?JWs~$aggJ zfgjvck$3_4eKH^WGD=^p!!zEh|K*Md*WjQ-h@jejf_&st_nn!$1%|pu#`?c(G=O55 ze99u|lOirRO4oXgw>%1?2=0O6O_?z`e{aSIH;NG;ikkPX$X%r|2}ta27DdI8)1%j{wHX>5tx+WH?KYgy*UEQ zkWT}~_2o#swl~^t@N?|!$faT&gMtWU`3tjolt;|)kID;(OO}1Ztlv9Kl7V^iWy*mz zSmCTwB6e8iQ~yiJUo}49m@%SCiY*oXc8bWG3)JEk?4N z1dn|hGc6OnsW>%T40w5bZko=CLTC^U*Y}1*X2Q*SHeKh4@934KsW-m3rk)fk+vIzX z2)Nzd9nDI)VWlETC%@gRw7OhJSaph>!>9kVEybJoTrQb^XQI8^`lsAO*S46sS)3+X{PGcyfo0Q&uRmm|C=cj`@=yW6Wzg~OVJ2vh3B zG_k{H=?}T}b*&?rGsEi*$IMP}Pbni8{ur~S-Pc%XjpB^2^taw4P~59CFIC5Q8h+j^ z9==8WX2(YvDUcc zQlscfo^W+yJhiU7=5o@dWQYM^G4X2SkQPUD95SA!`uG* zB}o$qkPDgO8C1<1|IcoCBwBCI>TOVvy4IT}od=T5B@XSSiG2H94rc3pmAF=Ur zMjE~4_S>@#FX{NS`y40PcM%zSuD962gw<>bZM>fylta|1OvwgdhSJMNz0AMs^1OAx$ZaJL8B8u{o2v0gD4?9H?%r{ICyYX zU!q9q|K%{~Dj>i*96~{QI5JN?+^O}2U^n!29{{cUR{!`^Djl3R_fwFhRcEuS(f?%5900B+*?lb~CmN$&KVtvq{Q#%+>obrS@Uw7#dY40htg2d78)iCwJeh#_uSE852>^Iq_!j~SnnxV`7?dBi zn;dL^AKV1+YkJ%z|5*_L7ppvKDwP?1k^TpN{8d-IRz-RGUv4)b>3XaOWGWw*?_VbM zi|5N^kG@U-P5*!XuDlni^1~A#1wYb7{${nunX}sSbkv9wQ#g2WieRWuh3cNVF%+|R^IxU9iR88tO2J?ccr_W# zr+T@W0}u&)sC}^vSRVO>4q+w4nJ>-Ews#6*NMjtA#UCZ8Sb0pz6Wk=#T0PR}|KsX6 zHA4GzYMSt!pElKEFH0KN31 z1ry*MgU8lu0)vwkX5quabgb+c1~R@29oE)*G9>iL#OzE^x2Lv0I-K_HpCQ&v5(XQExQcm|h4X=nX7d;#gTJhnn*8)CrNKE1TB0!e-4J$|Z*ZI>ZtSqp zN88WBZq?~Jn)elww|f4k_esYdLk*+S@@T9h;M>Y2UYbjSRmjj=P1EENj?cF_9u(;Q z=wnd%1Dj+Ca5MQ2Yyvbgc?@k#HRIg-kgj%*NHZ7`-OL=jGP3JgXr?f~d&EA2UKR9Y z??knqp{7WK+RTxg#y-_`!O9l(cnnv|{Dw<_FqyG2N16Tk#>j<|Qz3*A~!(B4hVe5yDCV6>U3EHNi94+7Y?_@vDh)wc9 zW$4;3JF1L*3s=v7&_C(4&}%B1u`UPPd4_%ul!0^N^~>gKK0pB<+XDORD$&JTXDLLF zNB>4|FXmX?$9B?xBGIcog;En1YFzL^4XHW=H{!Q{{z>&V;0e*;RiIbS z3z#6#W7r0Vj)||i1RdN8{uQtOBg+8hLhZMn zlsX;#y2JUQzpx6pT1`re*2L4RQvQp@P%0mH$vlsKHLL2P;-Wr~8)j-xl``bM{!}|U zR-GGC&Qg_a4gjsiYyB%vc>I{Ym!9x*!bfe&?efr)J%5;nQ_7=)1W(w9UCak|Zk`%b zGFuRLStnb!nFQmMHs2b{&V`l%fxn{UzkwfUqyllDHjIqBAd{wzwvT`?yDtSP1fodD z5*Ond5^eTBNcD^eh(e+CfGJ(;eY80|ow4jl*ZC6R%X7q(0>piuC0Vn^lh8>$1{h%6 z15n1gQzB0US@b)oe;4KnX>U|yg}q>mMM|LM`}%07k*7t9iO|Jb^DhcMdk)w{g?{vD zD30Vf(FPQZCqkzj@;xdl5CV+od>wB_EslNX@)G&309zacr2oceBqE^Zcxht%&6%V@^fqG? zNBpPI|4MU!mqU%5U~zYXy#vVoGHO}>n*shh822=M{mJW2d-sz95XU_#ivBHQ(cb>H z1!SQw8KVt6uTO9RwOF*S12%f&CBgsyN9BMO(aR#F1n;+}*xLctMO2>u&0Iiw0?LUk z^gjumuy=RFM5!Q~*5@A<0v&0(**Zn7^Z6>p)?O9%ha>?#Ugaup%Q;uL0X{uN9#9$A#&}bh-Q1xHR-=SYW$i4orNt4{Q%y$g;WHA7L(KuX%>&O^lq4C0WIAd=XBwjWbc985wv zU)`TM1A29kFO@14!>GgzWDU-a~Ey80yX`3IXsg`&y!!z>?m zdEU@b#KYdA)1*LQjk!gj;`|dGwBUMtv5$%>c-+CkFMniY{P?W!FN^2 z2FSc_@)`;IT)kmIUztD=iQIlmxNX!1xS7MKBF;380?Vi4tPHyYgr#~n(!6Awg$Q|Q z*(fuwLa+CpQwoskmRpe`Ja=+QtJv%RB2f< zoah|i@PL^i=1Zy@#}&0AoNag?3ipkY?%q>aEMzBk3Bt}SyVlsJWjOlmkbF8jIy!*Q zMQqFB02w%4l5@I~GzY!t!L4Ef1$e?TVi!S7aSb=1vcY0 zK*xwo95iFg=d$+d;QTzZ29@75agQRY)rl}nd#CvGRCqEtb(~6m4tzp)rE{A56ZND0 zF>_p=GcAJ;KWpl%t#;I_TVJ#Epv&pAInOR_>p9>}Bl+Z_ zsY&gB%x8_Y;7JlPRVEz5tH-U<0O>(t5tUm#lKz4WlHr1pD1jds`)grx<6E30P~Wuq zn4@;#iFy$sd)|UshQ1XIB+fl9KXgJyG0m#4kXN13%0O*vx=^8w*hY_J(Al4?k-qfg zBh1zFkiS92Xx8D2JdRA&m%%KAyt$is4?`@^pBqmKXD|8mN|!1Lgg&`HdwkYE?oG#Z zezjL*pV2O>+eK3|6wbsM3#W|uIf(?rv}9Angk-BSTS5p-HC&Rx(9*-V7T2XqG}rYD zrhUrp-b2)t@rrQ6D78XyqpGS)!xxl<#Z+nZuiBN08<)v~|0M)Hu4 z{_S1q{d5ecg<9njd-`eE-mM1pFRrfl;ki%#tT39GjtB=L=~e#@D`dPf{{_Ad($t38B26p?S}JIzcRh6*%-2`<)WP z`rQiz6X*wfU}4eb^@+n)0D4mn0gH4ZnN3 zlzP4}<(FRJs+5QvG#^G!P2c9;J^6)f=ew>u>|l(dS__y9Q-r5VA^)Jpk=T&Q;kaYB zuS)e7O^R+e1cP=2A~DFH>rA&3`DSfu0i`EM67cOjWGhJl~``SZiM@ zQqP+_40&(k0x83FqL_*6j^z@S{I;`S6V1`$Ku|v*Q6Ba#+$acyX~SyoX!o(~KJ`>G zaCE}t7oyfqji7E?x4KZb%O{`dQhU@rB#QdY>@A0(ymd3JI4Y8iEhcwCRF%(t%w18F z-MkR@N`qbe#*h#_J4f*b9LLK5i|Atd$qt-I6sFyNS7~uX_R>99A5eHX%jL(*1QfBzu&xO)26-r;XZ+9}IMk5()96xmO(mt+? z>k(P|9zxUZTA6i89@%V_Vhfb#>f!dvUNLQ+2dB-&SL=A$_bve)G@U;0n>4&@{S)7m zqb&!4XsX`Gn_|z9v)up`Ag)dfbG%ple(=*I^sDQSNqFGbP?P>N5zu>lvGPru6uFw@ zsSE&x*jY3Gj$%|}miO#(q=vUKKdp6X+7~9juFW&d4YsFaM^{=o{_}E=G^rSGhJ>TR zB@0Hh_C5~O)^ntaO)F-7_(N6N%*LP#A7vcHz-oDB9~`a~rj@zkDU!f(67!;wnec4$ zGhO}E=M$x{=Ub8&P0HkRDQbwJl&wU46lP9DpAtAg6v&P1pTa=p5}=tZkZV^|O<|lk zajdvG+Ps;Kg~VkwV!;%(wt~OC7_nM^JiYLEclDUh#QG*Kp?UWn20>5uyOyP|?<1(; zME$8+>RTJxe2cz^up2qd9-?k9%v9XkgUN!|N7)*qw;Y#SZfA6+nn_P=m$1mqIu0*T zG$W1ku-o>+m-4=gNke@hI@KKqQ6E7m^Y;k#jUXcbN+h50JoIXfO;m$6WGJMsLAiF2 z&TA<$K6!MQnjWO4z8|gMK`tLr@$+U|}|763h=Ykg>h4xRtmM3g&E zmG3+e11G}>Eme&~+$JO7x6FI({Va^vU3KHcBN{cb+XX!QC^NFXDRcLis^1UZ#>cNP zD&GlaT+ZfPv6~KFH<%%|O(nA`2o$MS?`~T~BN)u&`?K(e2M={28Th?70+00xBvFkx zm-6dn&A&lxLu?~JZbR#5nivVLW1caf!_uorc;>g?$1yCT4?L@_C}x;;OLXwVd#!dx ze#>Lt0MCXVZY&WYv+6FTMG+O_uCQmy1a0Kp<}JHiBJGT!qza}bG)GO3ulBt%W3E_v z+B`Ia!7Fw#HXV!kdn_K)9v{~dub@kv@i{u4qsQy!Am^O&aW0jkKq->tLTr%B!;br+ zaeTYMq{a>oGI;;jBrOzkn8{uOf5zWRlbBKoR&(G&pM)F!4Z7KS#Pf6MqXqttVKNx+ z*N$Mu2WZb)-T6eX?@P z7Qq0)AOvLK^Ky!Q3!{+bsIi)JTp%I_RNlBXIJ<^9xnq<35=qs7`jkZ2*RBfj2!D~i zZPkBS+t`I1-1mU8g;C`0ANk$q2lCU1e_k#==qBw?@bJ+-JC}&|P7Wc+`&^TlBBZhY zgAxCjjh9DA(p>C0%JSDL-cv5zfcu_)A9W^g<&xqq=Os`4qlIR>u7G&)3!9F9X%lC; z(UfO4lqD6Z!-toRnYg^@b)4%P+4)I=?_!UA;j8+vCZjoh4(yo4s$}(2{r6A8p(qdKM9v zrHR<~3c;WXv|PgHe2iN35JR7c=%d_N(qh!$@KqkC5`%D{2<|boA7WFZ)Vc|QLfaaj zuu{9Idp6Cktrd+TCRE(Eb5ow4?6^e8_om!Kvo#ek`lVpG6TfX*+@8!<7royq75Ir_ z#YN^LU9$(NVsQNR`Ux8?(i`Nqa=kS|B7C3N-4R(n>>Odr*c>L!D#c7g+5*iws_F~V z`rh_3P`QQ@2y;Y4NPmOuBlc1#lWlAosQ4MF4@32UqFx~SL6-xoW-soLhKP*`Eg2 zLlwr?3CyNgE6BbwVMVWdJSCR%nOBETrh=ia{ay@c&hPD}nGED* zKpHYapik*Kxe%MQIp2oHDtdlwqbKFguGMZVd8Ot7^6R2@rgpN+(Q{)AdVOUP&n@}?6 z2n;jSoW0tTJVeUPX?&B zeeYdbt^SO;(lE6ENaIELx3u58D-cghH9Nh+HuJeQn-YYoH4I-al3$(li^zTCI^})(t7r;u*ko9}ZhYgc z__7pvNX(w~>%emg3maAsbX&&(M~;x2uFHwCqZYP32VDO@G5JW3DJzl~v0P*GZNPGz z8i`d!=qzge-C4=evdfRQGXI1zIQZW9$PszD=7!KAvw@7KD*7(%D#%*BOluh$VC5$O} z9^61>bA38AuCn!dj6z9k5^8aD=^Q=970uDL#ga^(t~ON|B<8o?-X2bB0OSRraMd80 zE#eD5yG}Do0=>07IHvn1f>DlTVpO1zt$2H%-b4{y3Yu>ehr;XjmeZr=hk7k|IIju> z2b~j&-wAqdxLy|;bM_?*VFR~-#R>u!BhA6hBoDu7b1bVb#B>{ylGTYO8m8Fj6ssui z3Kl;hM+>((Vz*UdU2N%7kL3t&Fc`0!J8EBZwP$7;V8Ii|y$`axaEvzw;_9L2(tU@VT&>x=X3yu8CytrPrpJ zD$rTF(o!o4Rfij_{!2a#v2um8U8PGN``I;7fAsEY!H-`}9k6=4O%iQhR;RtBx&nGj zqOF!nGboZE4JICTE>MVOK$zp76?n_1bZ|j{z6e6SjK7kHUA6NAjxPf0%2-gY0Vod0 z;vWtn`YIC*Ep7*_JGKdoRy++C-Uow~g&Fu$S<<)^f$CZtoC2-ix)q@P0tQuj4!-B* zyF^Evn(scXHDINUC3nN%Rcs`Qf`jY#lj2!SX6_waF7)A^eDSecUfb(Y( z%R9IZ_X>l}I-(kYs9ApUmsxQW+1hXQc1Do%xIf>*G@=Zt>k5Qz45oAUKHqLJYAS*@ zp|uO*HAXiNOtuvDtr=Mbo;a(v-p9mmm82Ik%)~yldz^iY!>)fjeV=jJ*W)lHyLsSc zs<8GHgKynjV=-PLYT#V2$ye{r(+eHt?R zCl@D*^)cHM^waI}8Y>kp*SUrq&p#8-7_ql3PbF-HZ@^@7w(F;Np0&;1tJ@wL^n{B# zxZq-Z4j^w?kt(`i`C=f}lyef>_lK*8Q4ZaQQH8$SXzn0ZC%k0U4n!9hD7V+&uZvQ% zw$ZRx0&e&^op=0sEBik?QYbYX#=;PYfr7(`N;xs&`xBNfKsqI z4G!>0Rl(rClhNbPz7MdvgeycKrS}u0(VP=VS-oM(YiZVn)fNvA4}edurGgC}JHubAe~s%mNPA57 zIj^acVtHX98{|INltt=dmTm%<=y`nIR@#tKfFM|z-Tq#|9J>aU~j7Z8*VRe0vhs0kF%Ecl}Q~cplGhuU68xL<-D5zdqd;}FA zI_(K{Yi9c^g|)B~VGi=hU(SbxOn97mOgemODZ^{((-Bs6z;jR@{w%&Nh;Cf4^)#6j z^yK82ELP{t#ygFf+xRcI>N;SB8hb^2se4zATAlzZpK*6M_LSg6En7Zaj!w@T8N6X- z!}K|DhMkxYy-ZXgrSZeHXwQx^DRR~Q93HqRl7}N$jKLebs!it@J5LrkOx6Z--d}os zpJjdcoPv_ArmX9--0TiBE!hw@%*z(TZLKaJYXpT2YkrtQM9?7TGUnYM&nFApg|-uv zjWZIC$1Ed?wLQNssg8^kEKJny`5XwxeVI>Q;S;>DiPY9K>{IN(ih#iCA}jddwPG#X(g~dCo{Nk(z8$W}-=}hYeNm@dg6`5~f;Ei6 zoh1q+7!|3)4{x5;$?kU5Q610xKc@aNtg7exABPXEGziilC5Uu4N-EtQ(hbswM!HJ` zk?scR?(Xi+L-(QMzw!O~{_gvN>*5I<_MTZY>lJI(m{EfO-eLl;wND@ph?GsEG>2?a zj6p)uZ*F8>gFXg2i=O(%KW4Mz1^Aj)e>v+=ZZtn zC$(xc+&f`OSM1K;mFTnzF^xy#e+~R%l->+__`=kw_GkIv{=GP8ZbCRkf-iMVdzHUB z=-~vmEA)Nol4+H*L=WOODK4Vk4-bz44@Ry60OP==yUb+95@zRESl}S)#=G}rWoorO%2YuPk=^BF1bE}%R2!woVhZ)gdQ(;?Mvn@K3- zgxZQvSr-q`O810M87k}+-@b@$aOgjWlXTVtp$*U0Z{CNcvATfmb+sBlk zSpkqbCWDP5lV^)APgRgmwiw_>#Ntuf_I)AV3D;tbsX4ma+5&Dc5NvxpQ`;r+q~_*2 znfMGY15jh#y5Uh>qGp6nA45`=EqX1H<)}kU66=d1vWANEZ4P1dgEkJ*${g2tBqGfB z^lQ{?yZJi2F;ve(YanzzKe>t$T=ZF~!z1ZCDl|38bdxRJ_0a+aQ z02>%d5lX}Q^YaR834DI9UBj$NqJ%h@>~WO0sr+pmS2V886e2L(?Hr;@QoLR)aU!`A z4D%yAUHOdGW>h%m8+M&Y8N(pm{sZ9oO}8+jsyB{>~fX%Xw~_{l2eb7=Hd+LJatE~fO%=fpHk@VF zOEO3UTBexW1su%7B3@TVl!PohB|hnZYxk1#zW}~0_CIL?04;NJ!>u#4IEJX8RNlfI zaV&bA72%a~MR?1Nza}OZ3#A%D38N`@B@21C_07g83fHdEgx z0Uk@dLNn?7xAqQ9t2QDFDC)?dFwy_mR-gP=BC1Z98?QElfi$KB1sHq*pO0$vrIlzC zdAOnDOk9V<8%~~{qcH>3;Y_L(Rz@FTzOhuou4&4&%Hi_U7z5Z^d((Zr7aa|O9-!G2 z0D=1gQa8%aeigqCLe;Wf?92xhD+iNuq_g~DREVxH9ux+olFV|yq=^e=zDideypq&T zh)e|O%;hSX9c-I{2|`-|l$6GJzQQCS3dC|NqAW;}fsJaML8O8pJMHI2EHO7@8r!0{E6wEE+JgZPdWz!hR2lCsho1A zG|ND|01zMB*0nL}V?x$fDe?E<+G>g9FoZpCAHFhy;sNyE1HU#}sFoYC@^SCe80WtJGz)1c%C_8jxo59)l&$fV z3bkgVHwy{!@>f~i!GZ+<(0ddgjloADwY}dVQH^(igfMk+*&5AswP5_GCNzA`(ALht zNa8#PzCCgVV4PmQ*#404)QPH`-4@Bd`9N_RxBu^fDb!yc7;rk34I7zwFXoUm1ize@ zzZ6@P%Lo>6^ys{>DrJ`$uZM+4smnij=Reerbo##EDV@YCw6ZrMIzewe;yA)Be)*z~-=uoCp@4JiT({ycQKcmNXQ#w{t2 zcLapY>jcZM4p{b96tTdln=2d^Lh8O2lEuc0SShFKb$hki#XVRZpn$>>aX@6K^BFK{ z3yb@1R*~8~*k~$&pqkj2!}V(JH1J% zE9pJ|1>)TOC!BRb8}M`fWDtxxOvN5mnN{NXvFakV3F_j-0H)2GF97c%DC(PLoJE)# zvGCmwO8+AAQFFa6pzSL<96&;NuU1jP4U6){bBo#h=475$+U z#Hwu%U4ME@+dN_+9uVh$fr^C#xT9&4r@3x`V3vA z*~#>IMx*AAh@FxPs-Pnpvq?cc-tUKWDVNP(Vbo3(MY{~$i0_L&bDDRsKcRQ(d6GHM zoD-0p6zg%5c_;NEq+;v@+yWPwCqfnt=2%mOQYC)4sk#ZA*q6}A7b$$a2?XhKfm_db zLji*J9=>fDG1BnW+e-RM4bCt`xfDsAUg^v--iRqwl0vSy_ak~KZ3jtMAbRWQE>=RH zUzl4AG_$#d5sC&pZKZge`8~uK%}8s0vo7cOAcDb<%WtV%t^SL%3h6lkFlVq;@|A;` zeYf+^Sa<=j+5uGWlMYR34hxFQSO+u+pDHR1E$1NP8VzwiPE!)Zo#TAbBN7PJNyRfS zxWFQ81=B!(H${>Ylu0O-;WcfX5|290NdS5P?NvXM{REIxScyPP8V6QY(O4{#T%T;U zit4MpE;!xnFN7-~Kx)KhlK_byGsLGvEfn=MpqZD^r3Vlj;3O>U9f+nN)ok14Ovki? zlDS+S#Fk@Yh*={%)c}d5db#xC>|1!vmw~g|R86USzZ%8Gv?Ll@U*kUhZeY}QnEaV3 zJ@D$wULd2{ja`Z0MgFRz=~X1`10&$K*?do$2dR_!nG%$}v3b?DiN-BUk4m(1DabbK zV8bq)4~VxElpk`eqjgv6yD(AZ{{G&c_njs(JBZY&vP~n>5%3PzA|kMV`T~TQ1N@q= z7;=8km`shO~ zLwfd<%6qW=4nwEYhB7CX&)QV~N@m0=-I5NJ{96aPu9Nh%teTM)5tFhOzq?jm zR2oP|k^zc}2%6IL9PfcyAHwn}Q47z&HV#vmDb^t1{bg4>$T*c@YZ+)zEb_TvLB?_n zU_Kwvf2&sT63kTcP?fMV22uHfM+g$ zZN4EES#=Es=5RiLFcGL8MG3)sal?gt>0We&Lj;BmcjLLBGA3YO={u^ZxXF! zd5vtN!7$gs#qh!qfXof^?H7=le`f%w2H4tIUE1A)_lNTPiqTbQci=I$$kX2aL;!C8 zap`ug0h640nUECK zDxR~$%FyFb{vJ#;3K`LvrA{d_tK}}2xhrAyYz%SEq6gv` zddHHuY;_UWF;G1E2C1duGlPj(OfIz73nXMYLt{RiSf@PIXm|aaAYt7{`&ZE+IYOZ4 z#4GA2h5|IjxlSRocz5oICFhhYZ7-_RkN=P!b~ym?6ssA0dtIjE4Ncx`6c3unrVakR z7prF46aRt}1>@HGRLC0@MaDNzpH_R3T3P5U)wkDz=^-WS1xTI-zDPIF6gF66gFTaH zQN8TejpN)=9b1$FSnFzPe^s#9*jYOMe$^fFt-m1NODY3$o<`kE%JW=tB~0@^#m+Uw zSP8FCW!PM-gLlah--qOC2QbTbd_~a4^0LudFuh!8$N3XJYj`gdaCj=}^ zA#$c2-Ee?l00dEmBRLrkgp`g?3-+O7XXwvpx>kXrF>(X8-V#d2nB=Rf4>h8!QzLf) zMNKaus6cUdt?<_;QN1B?wTbtVeGB|2Z8lyR{@`j;$ZT(&k6m94-&NO>;{WEOfKD(g zATkH^{W66HwN(;}-iPVQfS|ivo@#JO&(EjGMMbRqCAdlM4z>-EHCENyfCxH#NY>Q; z!C$hyEjG2)`wS0{;DhilNZ+Ehp&>D{A9ScKAdcErl4)5~eHP|0tCFbjhpaknXugb+ zf<3PZ6zkdqm-~%W0`i#mV>}}S3Ox`_Q~ifM{+lvRP$zGsR<~FzJ%aDAG1I8#AeHI# z;pOi?4acnTrrjA|)D%hKb|OT@?4;_pMD>Nm&1e_XCGS0Z-BvFd9KxqEF(2Kbi`Lza zBfKr#hq%(-C&vHx${=pJBl3sqqEfd0$1gr&pa16IcP#z#YD3sZ1HuLLmO9T=KW}%b zstkUlYoh5<;m|zKQK0tO&G3n*4MO3Sag4535G3*rIH(q($=E7=fxeWq`=@_ik)Rtc303s__}sVKf_|{7 z&XThige^O}raLI$b&Th=!cnBhta=1&)T8KP9TD@V_C?sXXSlUYAx(vO&aAF3)~@qe zA_@@wfILXhlQ&rymMj{o$aR;0MvF_FAuyo`p>zaN*N?An4z3$523dafs-4|MCf54- zmM-N5NB^rZq40#B{A7mVC}%GR(7L0W0ks>J>j$0Dy^m_aEw?is7e3=BE~&j}xDSOQ z%>3;jBPql+mceVYE#Z(5<7@HPcfV*#8ugX-?Y z*NTj+qClMkZX?!1uxfpBx~l3&c6)LCG?zc(hHsD4tx54{DK^SMW{CK@*8;jv>|STG zg=du!J@l9FU@|u*3t|lj_@k#3bs^6vY3qUkx5Wa@$Ts00dPdoby;94;Af4+qE zshgdG5;I05D@hf-PuXb>JLwt!x|($OLju>-Ki9Gq39R=AXzlNy=KB8h3K^)23{xzS zlKSINn)@l~_xRyBM`rD_$sEt)27NMZ6GTz4n9t)|i4JD^lz>PmG0&E-kX}ROuwMRu98fj;qYmnBa&%$@*~aaf+Ef zcFb1_njU%Wv$&FUG767X>*;~FEuj@TSe%Pyi=?fh0?jX!mxA(@Zo?*})YxEKGpvn4 zK}{ZnYeLUXfxt^Q3x8%)Z@bO;kKY(x}xPGlRmVHBbkO?+(CtQv`|q}zxj($(%=V_(>LEtV>fcK&ug{} zzOd^TqM86R!r$y`o;ay+B~KAmXK`Qf)%VPOG#6LJ+;&(#`0u{gWJiA45kQ1KTZB)lo3|s9V832? zYl(g~jF4kr$s+Tj(mW!8!yMArrnRFNlCpz6Syo-m=zm*dUc)g z#&A%vcy&bDK@!;Fl*^vHad6I}x`PmtDl^tJ&Nf76%LZowmV*tFMFGhUXoVY+`!9^# z;;SVc8#e#=fstwmeJdTPS^hq2F09Z7BFw@-ho?yJviC!{Mv+1=^i;@C?UN)uHKHe|Y^xy$^N0{Z*;ZB;Ijm}}XH zBjl2pDDzMCGuy#RTf9}R!E>(M$7czxn!fAWFLA1F=cm-S_bcz2YC#KCcktGK`AS*o z4IefVUxn5anO+(`Kh6?`qI!#(Mh2YQ)$k_Ia6d&tP4{lvzagKu4qo7ZWc5HZy=zC8 zf}{hkB|aiJtFW{kKf^ICaW;gH&uW(;@&PkH6%mFDShfT=Z$K_XNEaOsYxUh8>&@K{ zXZ+}7Xc-?9j*b7!0^<#gIP&B52@$df5-XmS#l%aBchqYVEdxgBn^J7G|MBICY z_k#~je7*orZK_wR!;cLqSz}!oU&uUmx8QRxeVYq_52oyIjR~7regjFTAYoJG1&i=K zkg98s7kRHf(df-lgpIY@u1s{^x0?NCL69q2j7OQuo0de>NP6A4o4zR4;7+4zx>(&s z|Nf5LmFzSRXhEkK#EVj3xGaV`+4^{A8rys2*gUkhbW^E;TD3Tvmi^I-3yqf(^#W?& z+Ley_y~ja=(?Lk28IbCDlKy-C&_>t5r~9LIzOj0`zcjh}yF?@{)>J9@YN@Fb$y_NY zeJ+3D%~)0BnAcp!dMw#3ZSlp-t3M;xL8I?VTL*weP7bM+D674MSl*_-O(fguutdh! zW*<)ZfeF&c8ZxSJ=D;(E%8?4O?Vpt)t?DyzM8b@oLfZgL_<{&Q ze`(DhudhG4+1pU6-Eg@46*<0ON^l8=@V)M|^xFk_H>T~DB}B;_dpsRYH=_8{i6%L> zu<)EFRSNl9Ep=iykGbDaMK+Y8y(?SGGtoIBKq#Yvs2MTBVv<7ZBqxJT)=qW63|RY( z#wi%LU_}y)OWIUs5d>;_m>V1jV|d&aTK;i)#p8{;-50C>Av?e|=)!fF30Pc&=X1=` z6X74mJ|rd9`rIpylgBpMLT+1>iL)^kCXfQ#Y;i>u_jG@O2Ta>c_Nb|zUMAhiyE=`* z=Uw4+bAPd{VXThS6Cue3K6`%Jz&k#qBuVW@8|7LCbV2334(0s?GlJWc*D|w)ouplY z>!)Tu%*gKlaU6g;h3uyZ>5ef(xe{O9B_#gP_xJ_baD2YwdHUgTx5k)~q!mT6T+BcS zR(Qp57tL@xTqIHs;Z!ggg>s1Yp@^SlJIOweZ|boL!Mnrr=YDN0rE#~rra$K4&~gj0 z5#oGoX?vw2QODLX^L%s9(CkLN+DS2qXRm-&ihARy>D84#mh>BjJw`2z*=TFC!M3%p zHweNlu>OX8WVp2^!_WO8_R=CRhhm$AI%o3DCp(3$D-RN$ zcda?(ozL5+y9Y6eD2O^t0@*xY{e_CO@}|{jxp#i``<7)567BPyGW?>GnM%*_fP@Z- zymAspr%xYr&8Z!C(18vIkxbd4O_#9?=yXI@3rzT|hM(TOHvyxqy%y)L`A<>@C~K|S zP0I-<@(OeGi0pG{Y3Z@Bt)A%QVx89I;koR2lA4-v?Q$_N1zQh0($&Y8TLX^cEtNeO zhFgh8jw`wG{ro{w#TO~J3_*2BJeTRux~)`h_e_xMjmKOzMwUFoSNgz+GR5Syi^ zgw*G!z|`iuAYNxzDW`+yyo$el-eCUbnIqo&xc7%&R^T4C<{d1WPFbg|FW-yoBu+SS zoit@6c(>@w*{*ttepJI=Zedt0&#b~l{?tzC{?vtb70#I45Y+>9$k5jzE^z<3{W?^( z{jOF^W=5noHf8v;7wX;Q`&sX|2MRg_V!1ZU;Umm|%v7!P*qzLjywdBeZXaAEV*2(* zCFmQtM`@x0s4RLBo{sXT5ol)EnM5ArW+)`y^3<6v`gJJzR898!a`WQ{BvlJM zk#a{=YkTK5Lx_^OHs39nFyd&MuX;CXKVpdp)_zmuj~a}(D`V!lSZrk5@_$ieWXF1~ ztroL~>=~X{znqPw<@0sp@gXE5Z@C5*}F<*?FF%Pj$gY~az z+`S1mu!l0n{d7vlxA~a15U@3x#`YyXa0Vo6;ph#R)d_!r`oj>-6CGCEsR!b;VM_@L zHQ00)uv>R=w&?qwg16kVbf(B!pBesBH$ZO__$P+^CNnxxm|wU*bG zI`~m*XVX>s#j`j>b%&e!=;}O%oUDZ8I7u~g7lH(m4Uc>N-`<$0bb1c!C~`F+OXe-jW-^qXCzn=O zW9Q&NDJM6|&hr#O#Fg*3H^Do*+~mqO?sT`%?uS&M-xZ45Seb5~U>EQH9$9kMA?}0!^@+#(z^H=X_Cs0Za;|*-E@7hmdjJv*#Hyl}lsYwE zY2t|>=85g->?Cl5gM%|4OaIKS-vK|(ndWi+Iz_;pQzo+2a+c9zxuJT*skF2-q?E1e zZ`5+5i)DC;Zu6i0nG&feGNBz~2?>e7XJFhctxS!LyUWIX5m4B7mTIiEDxo-8PH)}# zXlCIw|8Jv^R9jxKm1^fJdGm;cvSdbm6e(&_me=|?ycU8yL7P0?bIgi2`lo>}F5Wd@ za%VE8>SROb>EQd}+eN|?t)`aez;_lchpa+~QP)j(zpoSGp1dyNVUJsk(#7o<`CAqj zK!mjd{#Mtk@+5G*%F;Z!5$^aAuC*jPA-xeRUMbomJ!8}Et@bd?F<-DWiD0}YDYtk` zN0Kj3Gfd{})z$HF88nrvlL0}n!JW%m6IDY?YjUbU$*HvSrpkO$(KGy`TB&@s#k8`n zH4+li-Q}d*-bl)I{ZwZVMji|t;+Scg=M^d9YDt+&(OaOB%pAnNgUVFT6C~Y96#m!j z((V7WlQ7nRW8&((q9k$ZyG)1% zT}AiMDM_>Bl%{C7eQV1hX(UJhxUgb7Xb zcNlIEDF6ax@Wu)>D&I3`)zG)sH8%1+i>d!L7-%5jb7uZTVMG{(fsP&-CFu5)JdnU7 z=j`mPoc;+0v3G1t-eN2r##XDs=&QZ6^9;9a*jom(^R0g6W;dtlCs|qB48VP(Z7hAV_Md0f$#RvAse)0dP4ij{;WNTy_+L$ zkqm};JwoiJg3*>9~0GKG$R+_vLRh+d?Y~+-hqrSe%M{+CuPCx97JKVpYp5f z??i39|2>lyY;?k$+UoPy7}+&pIf*_`AZaw0XbVI|#AP$2-vQG2R4gtNlKu`IO*P)r z!vi1t`eaqEyu6&<`A}UYlHnbTUW*62-G->!In`4j&Nrvq^X<#saY|m`AX%B(s*rsY zOe-FfZub54$>eVq!y6WLwL;|&BXELt8}PUvRl}oR+j6v3X;hj723!Fx_qPXrZEm`| zyVI!a<;##z}ooPJjd!e=4n8KrT&~{RFfG^}z9CPA&5n z*NWHXx|;|ll_h}XMM5V|$D$OM2aNmq#lrA1jmp5g>yt7~oB0arsDeVl-mdTX98asY zwqz>B>I!g~F#V@B6hUAhj!>iayR+>fBg5XmSU`+`kSA@dwY5lLsy2`KJ+znL`~fry zn!mk((5NGwN~BiCo}jAhR+(Y>)wH$>J)U-AnCO5$@`xOi7_(gi$`p_}07y;1r0m<` zY4*Vt<0J9v({75ZmS@-cI7LkPw#z-oHNfjH45!?Bvw?zW3U>QdI4H?dfKI|oNo82-CVh0Z~oVx?;TH81PED;zW72HYpj)qvlOz#QJYl?mDja* zf%Iju1ZqoOtTH!L0EN$}_I>pRKjZx$6ry$51$cz~(r~4ft!`^9UYD;wvyn1eV1jag zBLcgV#+x?Xt~-l%Qw}=sNYJFcX+?L> ze9%5$-k57QkYL<4C$4u)9)E|&Tl7WJHd?V?wEvNdwi;gwNpR+7h2*Wama(AQ=DC&A zf=$aKDw>S?v2pV=Qzjam4wMg&@ff9BT3QHid-j=2ihkpq{}{J7+Zjq^Hy*$t9R#lM z4wBE?;_0{u$F%n6`bcUGZmf>tv7DiQYPDT$sd73{RrzJ=52kcc_Z*qbldG*0>==ZA z#}>pS{ttCf#Lzzez$IC*;-=qvwqyQrfREU?c4*{0aS z{}sA!=kJ1dPh7*}pK&8U_U1*VRAb?EZYgOAG^CW6nPB8rSr4+&*LKo0S|ME0X8+hSgiOsBEzuRpm_3CKrJM<{C5`mY7N9_j*X zZ%)@c4_(EIsKGYV&Aqhmp|_AUhbJcC9JNyI{^>jg_#&M~XKf$k#ddc-_bV&(DDfwo z0A$=bwxp#CKN`I55}Q>oy^nCy#R~HpjhG3FQ^=oIvLrChPfz7Z76%8V47H$`c2ea4 z-JdEy{?FQbVe(FH_oJ3q3oC+k!M-|y8Jz=nKrj6wUE=O#0v`F`)7nPE1b= zM?!ezsIO$Uu5YS431b3YvRuyRtYNd(ye+V3%4(1LPc>I~bL-wOl#jWIc*}0L#+qJR z=xIoS%7bqc1HJB{au({whyqf1v`F32ri(>tOpF%47_>OiJrS4an(fy2O0?@M#77RM zi_{1l*I$eOK1~&HS3Up@?yneZ9qo*!rCnHzn_hQf2u*>$ZQW30yn=_Xt7}X!T>x{x^wirQ((-g?sY1#Y zYFi*gGo-;sti{t(>iYZVI`MFyf%^YalLBHJ9UYSpI)5KejgMkC=%*PE{O`vLjq}t$ zFZN4?j{C^0SbJf*^_ve5yPBcr7wJgrPnYi~qL1&~%a?iU6Yn2&D^7+M^?t7g-`^Vu zVxIfJRKQp^I53{h&vVoxecyee##!GSx(`OX*xXRRE=$YGmrBJsKKk-mt9Kj#&?@)g zg4LhDasPas(R=zpu1zo&W+1&616MStb-3e_5NM6O(IM|+3$uP%ZSF2?et2d1y3m-c z^+~!TYxomZ21dTtrm=l#-q?HT-|sHIAX%z8d=A`!L3xqyfgRGE-iO-fDt8g}DxH+{ zcaW@1fp~(d<2P_qjb+0C=8!FzacmMya;&GNZ+zOSW1 z&#OmAospjJatp?~>mQD7limhejJ`^U4?Di(0B=oS#M&vrt{>B423{Bq91|_YN*ev) zb)b(Ezg~Fu`$sPtJ)DeL2!F*$j#!f{Aqq(GYx|S{k+7){{cKg-N&Umg`A2tUB;jVXx-;RSF z(_oVk%Uy8SEO78$xmx9;0?e;OkWSdYJp$&By+KYE=5hG`@adItE()2C<#eG+z1#k9 z64&0|WS-;k63^Y!{Ym{%NXz|a(sy(f1Kmao^FgD0(IAj6!&lMI%6rlG+I^^`>j~aX zF&Mu>X#1WSq1Kg(Bl_jimJr#%vC-ip)=LKiE08K#Bf&$kEb&?1Noz^>!9wkU1@^b! zgrnsz;A0N{#pUR9ifIj9n*I5+FnYD3CiyuOIA#ZLsnwS{@8~p1i*6{+eG$`+P)Dom z-^<@nQAm`Mc$PBWrnX#aLm+hx8%b#G{Yp#rfUGe|_v7ADvZj4zj-UJ%vUOlFCz9!x zNL$4~(lic>Dfw#5ao|Mm-~6G%(U$X_;Xz#WZ3ET_0S>#-7kC^=6&!i5B={{+{xlQe zM$G;oOQlAa-EPgq;;uW{k;#|(eQw!s%C4jqiEI)BP~m=RTKrM73MiC%c@J)fSGQHP z+yS9IAldV6&TN(AW~taKqM}vhs1QGXU1_)3^A>t5K#VzzFa__+7G!YT6EjFhwm)e> ztu`6Z>+-ad2+}lMWGsF_V^wK$`sPdc_>cvtRwIo;70^c(^fVxA!-jQT3jf znIcnUesM^bYu&?T{I2Knlesb+cd#D=C5La4PgYx>A5ShWAle*+NAZ`1@itvNdyPy| zh`K8I#AyOX^I73&R8moB~!_NtsW_@kr%DIj_Ps4O7g{T^6q*CN+29C!r z_qmTJBg?+Z7eNhlC+ETXZ+17&(@r-pw)&-3J)k7RwdGQdvVr{K%czN!=lGwzQ#8?N zT}qcuyu48qMr|HxZ_afhAkI5rOeeG12xMIPp)#3mIiai^l$rP7$4k!=_!no^?bURM zLOAkm)eoXTMd{N5_S;yAY9nxPR0Q2{#$Y^p+w9d)<|l~$Ay1GCNtsZndj|EJQ2!9c zvY+~PQ`Xj>axWg}0=4z31jbFZd=AgI2kT8~?|gR#6XbvjAKG9hinHgCH#BwCuNa-z z1!`()a!rQ+6x2LkE!yrq++I99k7bAu*q4WgjRJpKrd)N9hSwqScDk+s(0WlCx5<1@i##pKUQ+XeR>$^~}PZKqB+_lqC_R8lh8fAFxQd z$0uYs@-@vTY4mhd(G7Ik?wRMR(~q44CRn=?tG0FQc%P1yUf8^9 zk>=SAxsbDNJiS?aqI@&VmC9>gI>JM6j&BW`aTyuFPgiqqv`KL<+Oc1Er1iXvIlj^h zvr;RlVcV+ySBgoUl}FyoLxzixGcm}PL`vTColt)WU5*fEhbpZmh>MAO6Gf!Zx*aboPMppURUXYmN*;G3Q5r-YmCXs4AZu zJ?cuC?WbIe@mQPCcx88dgHF7*2bec;hxd-xvh)C+Gg-JW+w6%b9Q{6$%=0E&&_5u+ zE{1;uPrxNo@cxi3Wo0pi&t*bR=rNlmFzFjWrHwFEdN{2Yx>gbHMn-=-NZRPd8(QNI{fR3XWiuF?jhVyiqKqe`A+ z(0GrXVoG&Fa@R$A&hYu02c8fwt2bEM7-~ATL4Eii^-6u)dvX7jh5$WHiU_>M2US7r z4g|uz<0E^8NI(B)<)41g{}JiF*76gG*e&#gO)f@ZS@||POfI?bsFL_-eMMyo^e{ueX2AW zHZTM3V=LS12taweENuRFkmI&3=V$aPpZB@$)r}k3DOb|PEAqBV)6t!0J456&g(K^w z<>g!3(NuvqGAW;U;cT)CqN>fteQn*g0g#7sq;IuUXUA^4!ru%r9pa&`Ws>q;?f2vA zoke)vER{k;p8-(wBs36>2?8=s_Tkc=pQO~EIDe(8uC3i`~ z#f@&sb8s_n#GHGWfmZ+Ldcml#s6Nn?%5(K70l`9R#xxX2IlV?jeh;08c1Z8lA$RQ7 zfkmI?zXMT{qAs2@b{Ta2y~9n2ay)lPS(x)lvt?V^9P(GanPvxxebSGlvjs{)9v+Uh zFiNAn-Q8Jkq$G^*aMuEUG3xwrcy;Dos$GvAV1bU{WWTYr8A-xdQHw~b+s4$v1XFcK zJUTiW#m~==dPR|#MPA!9o*8!jLkfvp!v1?uHM`-KGvX@;-Y7Tdez)$ok=M;$DU+z0 zwKg*C?d^r5IR?f9aT(FkT^EU!#)G-#;kyxwwYCnsEH-ZXsP*;xJnj8E1%!68{YQgK zM}XM|$)+^Y^L&^YHft&0JaJz<1aVu=SScT7VNaDh(VPo{KUZ6qCfCRM8*klNoT%}w zPTTCcMLfQnb$tKWZ`yD&HR zmqx8VU39$=km4$75b7On%EQ9LgWR{qaX%=EA~b_Ez_FMHyGKVdxOB?$I_jOECC~!7 zNMJsr`1(2mP7&UK!^LvFS^&q2Nd=kKBKorLssW_`rvF^ksLpI#ysfoimNk{ zOsJG4aKoNbJUksZh+wbJ2b~I`k#k%H1&4)g5*h3x`~~IRtj^4+;`aX#cHU1j_Z-H5 zS?oJY2YB_nFK(DXl2dIlpC^5AX9ip5J5{$WkR{PbQg%9{Wqh&Q(e(h(pn}_$+EeQh z2~DnYcqk`Ckh8v*>WbSFl04^8-TUj28mTR?N%bGYAZf6gO6@4T1volPLW}4Mr8jOla{QGJsvt%!tQ=3TPYBAN))hK*yV)*6EeVR170^t zEVVx4Ia+S46^EgpLXabFkgt0%FAwD@I68!oyi2lcWPtVg zb*e{AwSa%WTzi18CfQSS`e*+_T_@`6q21kxh8{>fl_{PD$*}pq0b$6PMlVD&4HQ=1 zb@-!xMyw5{WM?Zk7V*yig#uFIxYTQLn9`M9qq^LO5;eG;sPPh>$JL}p<+Kp_n$Kq> zVT(n-)o+zcWzH|ERjW|e+QF4&G&S6U`yD8p1F?xn*egD^Dhwo2mTae8it8Ar^M@@` z(^5m_s-NnOX-U`F(~-i`&!I0keD zU3V>b3<;>36hVqmzzr2HrAOw|;dV+vgp##yN)}~hUuo4yyOK&^6&2x~eD6C8rIzIn zG*D|GEgCiamd_bcZW#1{np)WcTB@DJoPLreYaGy@Yj#7GmWLmVo|Swl{>aY3i%Hl$ zJp7Zm(fbYXA=o|d98(U~&>G#ZZ88S~=Y|s5%z@06yUc1n>)24esw(@9qxoSIt@F4E zuC{K$VQ@RaJXnf(zAk7Sklw5ME)bzQ-YP!RbKP1iI#qH>oDqQ-WHx7I-HFlacl|4A zwk^REvO-+7lSLDi9|(k_*Hct9e|_)?&UE;~^lA4T*thFUkK;ZlDStqbM4_xA5{>{p zF!pqHjjc4h^E|>}5iKD6{VS`{GrY#s`VbpcaH1W-L*scxeTBk9&(qbtGDPA7_h6&c zjhwcRx+kW4>{a`w|JdcY*f=B*fy;bK7-9%@MJ@iao>^#jxx`^KatF)&uEuQKfcLuA zDnlex7dUeSFgi|lQ3NAGkobBE0?OOdsP`be5^d(vr;Hh0@ndp(!f{oYX4!RunPZ{Q z=m|ihJ|}UivS`QflI+Z53%ljJ%-K~U+Jg6@0lS(WqnNIxtiKg6g=EnlmL8AsDgS68 zg{m{=hilgp7NFF%Rd3C%fB(eZ$#e%2a=T6NV4nOUN7`a!`@23xsBcihjxJjueZQd) zbD~5`8xzGRyhGs?S}MD_qVK6eHhp?DPUs4`&N|>hZFwy(^tQA7l{0od+vtAK3MCpF z_Ii%LHp$m|u0Ez-PYiqx#6 z&;H9{q^o^Wj`wR6$m)|R?t`R6y5W%#w4de3mWEH6ZZxYc>{KLi#y__z8wvmDOC-+X>n(~(mz`8K2jKBxVz?H!q0gGYre4s@+&pzbzI zFu_*fDMj42Rd#s>k@-^41Doa(UHnsk)kf;%043IxevZ(p_UD{4PUnQZ7aP{^C*n&9 zOAmCB7NSeI;Q#uKkznE&=9Ty6g6B`TZ!D8Qi$I`D6y~`HCI60Hl-^}5+5&GR#i&)@ z9J3JOs?^6ug{^rVf>e=U$x5@~bmc;Ng-rNo47nHdsIpkdRHk0;o1vclLHv@mS8hTY^w~ z)a=)M!n-Se*SGnQPSVrc513XSuJs7t7b$t3Cwk=Vya0WcHBH*A?#z$q6L50vg~_iO z;W$~L)p8@N3;F-xs`M()!bP$7i!)YJQyWp)XiQyhFux>L(R%{KK#q2JF9B_uU_bERg=pw*t?fj$ z;A$u-q3L|ZXp5lnkd13;H|SqHx|cJb%Y7Fq_@%gl`yGDjx7R_Of*?5}#!plxuAJWI zHULKbUzIyZR#GkHZah&#Xv{CTP5QYza!C(LL44y4=w%N=-#$HVXnWR6{-SfdJvW|i z2tdKJ0Q;|_snS_?$mdH4v<^^(qBgrfGtyg&(9o!4(6(L+Ffq-$$g?tIq$`?Kzq7EkwuO4BIu z97+1ltS~R%C{h7MSMV?6tuu-=Jl!?GaT?DM395?@bbFovu&u)@T;U=bxm0PuWZ7=| z_VfIq#c$=n9YSrU0)3cQYH7Dybx9ISo_ub3j4I;mtTY+H;)9AFZ$A{HT(B&NDd%wr5vc+*Gz zbT-ISHNDRM_^5XNLHU2;;?D^o@1`}1uTfdj5ZDam0>vdH^=3ut_;HNC_S*x6(elu3 zI3#pt?ku)YZpPI@%ueHjiYpm*i)1Vu}z-|am60*pqs1G9?oa;U1;n_t31S6UXOR$)dF&< z{8LrtO6O3hj0&>2fjKT%b})g7Yrh2`zlnveiGJkgFj$yhUz6&6I^GXJ+WTzP!Jc9H zqt@K<1ns4YFVy`GD7pKm(cJFZt$l9zTbFYi1UH;IyrLu3+9qyxSvKBo;;oz2%j9wSKI_D(_JvgL@jHqcH(5luRa>I|gM$ zAGHAP7oCct0O?w2n5w8+y2|SswV4!Nr%;Cjmpa{me9A44f&?}(F*M}mWhMwqR@Ltu`6)3hN@ z=tGdEFDeZHj0k|-#%I4!wnYp|$+LX~+0OfsHV6v&pxh%h1C`lS;w;V#}bz!BN zO!=txV58XE5ElEOt&I_Gas%ai^XKphz+7uy3$K3u=Tn0?Q3TLE9a^tEEFQwnx|DsHJ}ly4`EegKNb_5$v~bdj!~j?eEg=24<=>6lQH^IzjiRPaJy5 z_`lg={B!Iigh9zFsr@8;?2NPupGojW`#$J4I+qlp5xA4`I<9$TH0&f#*>!uZdpO~A zViTybxNLIcEDS!ze$C^nvYLhL@L9o-+^H0lepwH@4K+%fS&V+0NWW$YIf?K=DOI_;)q7b!Ln0 z)JEHN*ts^~(qBsEm`xYrwyN&!*@+#~^A{Gq1)I>mS_KrL3)eJ$kHlTmbwHc;n}m&f ze7}RB3IgBqGg&A*y`BI6D0}OuDBrGa7z66B7HLK>vYAVf+;VCXK%0cnO3B!*Dw zRuB*vx;qDiA*8!w=#(5_-V1;CeLwfRp6^@h`{s{XEV!6CW1l;Yz4vjdP4|nYVN3zJ zwKpSt>z_MbIAJQ>!syQHq?b1-Z%MgbE7=8VI6d=(;nx8YY)!Zlu~iA6lUa>m9`> zl_z&QK5Kv4GBTc38Q~QRhhX{f88!a;97*SlVF-E4`G#GtPU&-oyYx8`P;A;xOX*-D zWUBLZ-uRdIhW2#L+{7XTD}|g3b{B*9D2lS`r%a-2s9Q*+2dpMZq(p}0H^*PTNBYbC z1yPF9TuGgKeiUA+0Z&;l#3X`(s#RC%=VD`J^L}3|NBX^F?sUVcX((sKWCO75kq&mS zl`$Y@T?dy1KF=2)j~7Q1y<~D&g|94=2Izf%ahI1QZ}p5(*3V!CBztv$lK*jkd=SSfry~^dxN0kAJ_d#2}h2RotIov@Fx4CQ~ESX#5nv#<2PLgZnfKAFQ zxZE?I^gpEbC*{e%Sk!9jjKLeCK=@}V6_6BOe49$P&X{6;8dQELxa4#h?SqrBsPLTv z??{Su88deAiC<)e5&lM=8PcL) zY4mpd34*iQd{>t*ml{i?NG`A6yHqZ_tvlm#=LG-ZFV&*>MyR&dBJYO|W*Ft7K7`^8 zbWqnC$2mYnl^D&Qr~fJ%ns!YF&`q!@2{-FXFM=)c8MLiHbdRCuZo`wfpy$tVu+%Rg zmgw&`_hnE*XL4st(K3udr?LLWBc-M%8J{5NNaFcfHe$TlhHW?|dW^k6 z7{GkM9SIYZnzBvypz~3bBjJKHLAyUv`@+k^?*#y^S+0e@3}KYK%^bQ zy*Q1rdVqmcoA6Sj@)h$Zc7L>gx20{1GQ?Eo9-<H)Ie$WX_c789V7$GgARB^2(X&o#-B)S4kl=?CoMaHP`1onv8n^!spWa zUg{JPaNCgL+U1B1G8HlG7qsBy+3cRlX#GRLL0_g&#DlvIO4o#(;aGltLky0T6n6Dm zS^L6Xuk6-4mzVlrBUxIq3q@rfb1(5(m}bel(}wwz;tluydY}C!QL6$qrP6{IoC9Jj zkyJ05gt$R!W!UDFj{D_#ss>a|G%1uV0Z6R&#;WeiBSlB(2zxyK;z$X8=4ESCRELy| zk1@^3Oph+i!+-I#v3R|rQmYT&q%h{S4@gT`MHOMiGf=-08vix9khfS66uLf$x^5?~6+a1(`a8IaJSiq z>I;PboWB_G_+(eoR4wnz@Akyi(#r5s-@w^!UDv2O`qIo+&2j~&zaqo+c^oqQ3|Q^? z%ki4=jJcC7@Dr-#z(Wq38f-sY;_fzD9579vZ4nadxzGHGFoogKi`R~NCsF>lZC9+G<6?CEz|$lS2-Dwn+2W0O3>|H z@3adQXURhL`?h6AS=LbwJcS-iQR55EBO*+R3@PFuu)KU6NlZS5dw#!ey{97mL)}t8 z^@cdb@}j~}7?Jgv9#u7??-bLz()v}26wL}`cScn$u)VHjk}PX0)}!na6dgcEbtn?mst+vX|ZVL$dWICg(c~a} zW;ncS7$KYrp2O|vB)IXZTs&I^Ej)3TJ$p?dz$$f=GoDjLP}XLA1J5FVKxq1TEs3fA5+oX)uwqEo{5!lE+zvOlQvD?j*oF1^2vwfC<{}W>UgAw2WwvQx2QvY7zRz z`COj+puWV(mZhkmmsVG2_ZNgJpQoSzp( zraIPh`mWF|gfX8wp--JUQiSc^Zx{4(a&5ok-tL;G&NVtgD-i4l9|P3$+tjny7h*9& zS&8Qk`GXqU%vSpVayqaZf2^}!7Zoj7x!#KBVwAVuc>7G0(A~prJ zuwU7)PILI~)3-dPm%1#F{iWGS+&osJ>iVs|wo=i`h71<3`>K#Cm-xkbxl>64n@HOF zPXNOxcRCFpH|+FEAtQX=Cy>k*X->2!U1+MSgrS~VEz@=GhfalbwD=yZZ@h;bCdUM` zqj=tMKM9b>oE2}gOjy-P-~Ar=IN^fu_?&6ki_Y@n6`)zBm_@=oIfD6m1e<~Ue9dHa zL0WmaS`@f(?< z%6*k0V6IT(69^B2hB;W=&CTrBuVL5oL7&{f1+Xq!#T(kcBr^Pv81vn5&`BSy(={}R zC~FPtw}LgXns$sOtE^cVJMt~*TP(p;m!_3*;1+uRXGqydg7GDkpNI(kq|c9hK^III z$6x0O@qEW-&uWG?VbFJ72>cqm{CT-a6lC9LVTPZMwdWa|YRWqMr(OMH}G?8NzHz0M~my}bL>=172kD6IvMD63sp0`Z`$6qRWAowPV-}2_S)wpnP zq}r=HSQ-{_keQizDjZ@)14Ltrb#PIEv99|`LF9hAX&tyT#aW|;aw+SED@&)X+cBM* zr*VQ%pPDhHo~3X4z~1walBX$s7-75rg_kEFx&0^iu^>SPvq0PQ<;l|E&CcNamLb{8 zY}tqrn!~Fne#L*p^lFN=i8R@k?!sH?T1@@+311_1zc<)TzC?9nsuM3#+|1x zYV&w!sAFOdn+KgToRIb`pr!g0f~O;1KWv{DBml({-L;-Zj)$LX{|qa4D9TvUAPc(*S3GTu_BB=N9G2#!hK z-S7cL0DjFwuIt8gv*d3)^60oMBQhjEFln%6YPaPa&i84?sild{9h%7%GQHR~go;1| z3I0FML-N$};HV8tjLVsY8+Rx-VorGHdsAYvrgqo!BuPI(!Fu7?IEUj)h~bS#4#r<2 zg{p-XrII4QJYE`ZM-Ad{cP}uxmn8e7J+vAyY3_bq=e_D6ecDDflbeoIH&W$<2@GK!OgFOG&tXjn=iL= zYX*_yJSYR&!pkmjaou{#xonFLPFuux8p5EH;L=d{bd=8`Ozf_P$4g8Y2Gw2Xnw5BK zk5C3pCL(2~ORJ=t?3R1#{>c6z=5S?R#46%Ohr-s?5p53|Ml~!gW@Ma^SiCX0@lB{AhX6!K@5EP#o%Hd*a=7Z+YPa;(VT#YCXJb zPPd*HH2aEslrmE&^Jj+IuFr@sd zL3R53C-0nX-8FkhM~8>{TZ{&dTRltJrkdhuN>xtAGP*v0SXb)5ox&(ypuAj~EPAza zPdDJq9z>S+J;lA>rz0}6aUocTp*131v4eVFpMo*DJb$F&1eb&<&#`-?Sw{CY!CHe#ULrS&?i(&f08@F#8L;(t^ibf7G7T9 z2jAJ!qFqowDL-pna$YY<2-_zaMI)TfVoVinA85%8StEAo2H z_-1WCZ@E`SCq(qenmYQAx;eeGZ*$Y5SL%|xl^bOJPJ(BX9G|40;`iLXYbNeh^7H)u zH0DFm%KKNj;U;}dxUeb1E}_3#A*#7^>x|M*mVI~a+dWf|W-~sm=0C0{mh(8As=0Cn zjibwHWSbn8CQF@;BUY%Ey~S~ZkFR{1>Cd_gnhO@-U^U}Sv80bD<=r}={ysLgG%Xg2 zNYw;EPiIdwYJtgWAdS|TLZ`%tVL)OVc5qVS-voDOEoIiF3ZmfR9UnVf>7FnQG0g7U0J)L$H7=GDiLL`3SKTzv$-36s>;AJ&Rp-}jD~0X-<|3C^TR1y&Ck84K za#B`0uLI)0z7>wG#pkk*SmL>rhA_5TRjiq)1zDT95(&>Z{>31PgaUs%A z3zR+dx`;BFn!%TIb4Q~8I@5|D8}`aWTNpwPj=D=CwELqyKfgKc%k(>sgPH{2!02Sj z;IjO(e8-$l4_xi+L+fgM8`qy?@VPo(f>fSXJ5I)A=jezcV+Utd2SfJ%=#nCBcO@NM zK@|@Ec|N9F3oZVhgFf@*ML%orlX>V@HUkwjPw|ziA9eA0d|7>*kT{j)_H(FSA$bVs z5TTC=f1Kp~82S_day*WyPYNz6D++ZiePu0mH#`^!k*B(hen+d92bWI%NaDQIXSs%7 zNmUseekg~rxI`IT+s5J!uSF3un*x>17+o6J{*utULKq z>(@)*J-hxx;@a=F4uP5D#!b3^%Zc@)#`QxcImG%OCbR=cc;~Xz^16|!Y{Tdz>ChUaq=Bp@ve73oB zuk@ha#+|NF-bLB|?+ikIH$AZKbpXqT_}&0*jCWuI zsY+sJTAM6W183TThb4t9Q?r-j72@e@+lzJx!2$^a+7a`qn8+i_kSPO*(;}V%=mj_7 zQ-hny9#@^m8q?y$d{BtV+&uNQ=?C-ZW@l~#HzTx7Ity^Cj9Nf^1H;4&&NdI)e-Vo* zZ0Vlu-yM9kM%!vBQTKGgAALP^7X{S5fz?bp59^Q_`l%jb|GgHagu9X~zH8Yv-Ob~M zb;!eT4ZQ2YK?72%zPVhn9ZW>Id_%iW%Vn(RTuH5U7%}2fm?i7AtGhh zz5kp$qd`2)wTrJkQPxG@b9Mfn=}f|0x(p^2;x@=1{v0ZMvm)!b6p`kCtLa@o=c)9? zg`OmJYWpAH`jZ%DlGfTrRGa;eK>z3)hKH;)uJJf=UK<#AJfZ$tv}9T8FcP?T?Ra+S z)Ul9xT*2{a-mfd(wEf3eM5kk8Us{n(DBldsrewQ!1X_jX9d|*QfpyvNmgW< z%zAS9*EVxB4h&FD-R&`a4>tB|7!nYCEsd%uhp9@g**_hR@}91r4QwmQA@&Cxq*+|A zZaNg-GoSaPJ^jw-qhjQ_z6X_|xVaK{?&-|Qa9`Do@D_(JoXnhEEWW`|wq6{|4$ZO? z)YgObc0L+x*(MMGQ<=FVc1(wMQ$kn`H~nYJbYdELteh1NmX{-|5-qfS)`=wtj=1lD zKB8B?tZB%(CBTNmI+bx@VN=kCBLttoBq3nz-8do*{T>>~n!T+2o#EFVl#~=PxsA)_ z7}-uEpll#QkR^UC>#$Z_%ecMAU_Q}pKfo%JO;{dymRu&(6$q{LjJ~=QHN6ciZuB#w zN7%jyUuG`2Le6W{=g0@Hhq0DY2yo=SAdYtVt^|EAA+q<8khMVPj((jyN zQNOK*PfhN%hh$oC^K2H#{;_4;^W$)XZAx@`nsGW7&$Nv z3)pE#WL?a3#O%>C&R^yu>?TeNb>^8eWG~B!3GSAD(0=u+%4sMpDVdgFFQ`G5$uL(p z_vWDFJ$mJRllNkdDdOCx{pPXxE&N-3_`}vY8SX|E_-S&w897?7K%O}pFVDZ6_`?Ek zbj7IL=t31#e;vXR!IWOdrKbL2?Q_-A*TBMM7gKb66zBNfBiaonIgAi34E#TU@U z%Rhz$9cMt`^wHWW>M)0W?(Y6U#ZnikUHcR<&!!8r=V4cqz~SVg7tsr)i2&R9uoL=8 zyNEk~DENHbnZkH?@P=b859QBY`Wl>QMv7t$s}jT!qMw9HF0{@&T4Zb3zTIpq@`hz? zMTd6T(e4E&%%d))$Zt+iE%@k_mBi6vS_Ez4JULsxzC#}1TZfGz#}=+c^g>8G!o+7C zl~UjzWbmZ0-z&1ZJgD(hto51ww80#ix?jVn%LwofXzy(O;VZ@^lBV+$H#Lv!PH)~T zO@CES(dq5Co4FV1ID~teO1~dx8O0>Rmv&lg*Ws2Z!^jq?H~DD7Z0v|gn0fQ>-U#b(Py(% z6F7GJMUd`K{S8Eu;gqe+%tfJ`2@2fv)Q!0y62Nc$63(*_>Vf>lke8a*|de> z#k>oReE8FqBonFQUp_$VB1oaKjMZ$u4QEFO`I6L~o>O+8iU_W)_oI2+!<<2-YZn|r z{$e&-6btFQA@OY(1L@C$GNtNdtmddTBO0%U3zw{Q2njM#t*Qc?+SU|dbQ8N_%SDnA z3+a-lpVV*q)jR%#7j`VpSG0RHJNut+Ncz#QRPWb;-%)giO=#cmYkKqzIjBcI6G`Kg z+qX<(fSekH-7i6-&vb2xI!8aMt3*-3(MWS@M)t7e*Y`YZ&D5B@>_3e>smh*Y;Q!!g zufu)de{xY6V~0W?E$x3&H}=)3HE$g;J+YE5r2v*>w{N@U+HlhQV9jRJ*KBE?COe`^ z3|_?i^|&sy<+Szdw1nKUG3{2{pp2S|5AO|GB@^Hp3pQJmAf0 zlt|pPTOhVZ*`D|u|LfFH<$c!~ zo%a5{L0QySh6~mT-;M!(=bbcXxb(|*TjaIWRg8c0k9Uo0%QR<(u&&y-=1Z*6C*c~< z72m2krP*a>8MD5x8@vP0S}!I}2#jmbn>}y5KCOG3C3adQ&RuwJNP1g=8r}8NT6AwW zO*&$3u!-!lf0}Y&zu`zW@9u+IL8+%+nrUxsMv6b~E>A}yPJUg?WdFXY2~Fr)HBqsC z>bKS=mRSDE1NV8?hu3&Zh~_Ct6~G@I>Fo8pgou9Pq4RKu)*8=#CEk%X3*G)?zu?26 z#}Urbd|n+m^!~!X*;y-Rk*i*-ezb)qQ{U;r*xG%{{k13wTGs=BvHem531K*W^m`RMUe&nmz=D1x3o=_f)Fq9}`mV39e zQc#_@xS7ch6=Tkv7aK27$3m4WnI&De@p|whw!TFwqllL&g)b<&M@qH5sK#&oC|BM^ zpDGvkMOyCTOx~X?39{F#Ifso&AE}iAXX+zc9~3$GFfi;^O=F869XVy$$kW6a)yodl zrJBVbuOZDv;cna@cWNg|GUPO5Y?Yrm=d(clASAY9DzjO%p4ptXDgK6{QRc^4?wiT}D$Ai=@_#zZS1 z-Nk5p4K-bo6qR`(|2V-?kjdO|fErf7&TK%s3>REDi`rg36#HlK$$~Xxxobu}u1_Il zH}zalVAOJQ^>3tdoJWW=!_0^RU*E}P@-0-a1)_2DQ&Tc+)zn&U7^s}Hi;Ox^ z-gN?gQhSHxM;R|mfzr90m&MDj`>7UhI@zd~d<-i08~xO>b@d(D3YoN73IpA4s_zq^WWGlA?4nX81CMvJB}`QVcqePDL80_rzNu2u zsZM?t?{LGe$3B;xV|u>w{~855wOK6qIUDrpof8C89Zx-*><8qCfMb%%FvyY4GKVdh z`uI`oBEfUW(gah)8U!$WzAMKEG-!Jlu77`P9$BYKp4_L+!hvn3JU3>wv;CW^Q1J6n zGhwze?Hh*i)mx1@dl>s=sLJb9$j;4)ic|wI#$jO}3`>{y=1$U%|3riV;0ds;!0VsI z0oLd5{DCF-XKujbxAOY`^^t#e4fvESKxO`Y{qvQ;r~I#P`maY?+5mi~V;K(Z0N`JP z{Y$kPKcI^QEbVW&?>%uFFnBa=-~Q;af5N~Ua0@g6we^&_s;TS1@9Iyk#6W|_|NR{B zFLOSSPy!R4&&3mWcK)x=XlNFK?K}UKE?}I)@o)aF$$w4r|IZx$_iFvGX)@z4Rv!T_ zb*NF3cL+e%Qghx6w7@Tkwp^%cTJ8XSr#n*>-CP{NSb$f$4hHZ?Y5YZi7-6DW(q;7( zjl>6=5lcc^K~{Ph-^u@a4ybk!Q%T}~Bz4$HUbPQ&?vdrHBoBX}jgh&u@jMyRX$t!L ziOj=ZcMpJ;pbnr%B?}?6_q)`jEvVEC9x*KfRJuZ`mJ}JiSR)ywCFrHSE31jN!Fm`T z8vuFEST*DjPOVQ*488_>47*R_k_}3XJ(oiGofn_^1m21TAQ-jg&ptFC^>S(8IZc*6 z@;d|gS0;it1F>-112D0ct<8c+yUn|!i*{pZ7^OU&9rSyZXUq(9XX%4aZ>~`tK*oy^ zUYe!zk91+qE}0y{9}s|i^VHHGpcN08BQVc40MKya)N8kD;>G@alU=5+sOx&)Il42P zyaS*ic)v~32Bhh2!79BSM-BE<w$EEQ|&<%ioF`&Drp?0p)pv=S&)YE&F|4c;=bN2f~3njhy)(2Os zvFZw@X*5DsDxgXmjStrIRdzZQJwW+Cbi0%a;2LRR^X`tKqc%*PN)R(P`zY!(Kbz%? zExN6%U2H%L6!h=KQ~&E6d2n-LYMTKaz(Wqnu)%xfLb3g{Ks!Ks4^4V&in5fGOLA8h z)Wk}^)9H0V{32*j>R6@n((FFl6xCYAq935yZeRcJr~7}4{ifHQq`v-==F9ic{nNcU zx(WsA7TogZLX>1X^G%YD-Si#u8IaSdT1S9UhNsyd5EpZiO`uq8wAp+*Ck`wDv05n4 z(a_!`7tc&n6Aq9+{(|J&Pf|LI2(VKDw?X%STuo@E4CAYi120F z^^KZCP^VE-QzK~Gz1`@a*`wA#M=Q9X7U)dq=Dt)R?WW&KPhN)PbD7HS89>(L_L8;E znyRH@k`urH2gRXBl2wLD3Vh+_w1%e|vR8*(J`U!j8?mQ?(>{5SuHRfdj|NWu{k#0c z_vMra0E&(ktb%iCzOBBCR9RTkkxf@5ZFhVF(4u?3_yoUD+;zRvDYu_yaAZg(Y5JJSds{%7(5d#QgGnu29~b5p;Y}$>&Jd)8XV5%@ z{CK?|%1kkY_z(d!fsTRRAHx8B+y=N--R%V}{ zFQqYfJu{J8*X8YCUPQUZPIQ(=gFAMkXJu8B`f7cR_786TYFNI5cw%*$dKpu-`@%SV zY@#SP4IC}GHTxqCIBD<&k3*S*VP!sff_K*I7W0YIspNIt%E(QwsA6;djBZa;eiE%K zX|v6{IQEKB545~xVA(sq=mL~?6Dt2&Q|?D9fe9=UU?eJlbRC7gGK~{K`vGNS8k4KU zA_}I)KD$-F0qU=n+1OGHW*813&F|&!?H;u}OJu1gor&c~gIX`bNte9&@*JcypMS)G z?>oPg|0z(=?^ma+!(627W=Zj&kcW@9Zbop%mp)0z9kN>jD;Ks zy8*bvVtkao&4F<{vO@H&neT0_zD=mv2RegSev0$4rM)!jemXqh7dClSA3v0`J>UV% zyC@z4Z~{@CtksUX0UHK>DpPj=Rt3i*P|7#rEiy&<{<^X-4+q4l1M42*@|`ZapupA7 z(PddBRfMNrQdIKIyOI`>EiSEXx|5;u1xMYM`~u^{8yx*=`*6n$`$0eYysdfsFes}O zjnpqg-3y2lzyOx-nk8*>22k^iRxeFj#*I}ZS0e_KMHK1R9C{%?rtIE~`}L2OZ5e5~ zsXMLvE$p1|ZgZXNsqR7+>=4IEuAGuq?`wdLo49%jQ6J+Hk9oMr&k0$?<%_Y3np)|> zuGyJTjSmHT2Z{l9fMA=o%(am)zKP=yL)DVM zJ(Y1sTx!wSYb~BC6T19O*Q2vKjhIhzl$^SdRV*KC+bf@=&}lj`bm}(q=N=au%(UC) z4*Fn=J8R&@udSO_2<(HD0l5E%)If#JBQ-vvl9n59DaZFdr1BESPX7mc4QYZaf}%%ygZX)`jRuEF%z@|26YvJ4*~hr#`n=VE&=Ek_Hj$G{Ot zm8m-CuAroWYz@eEm9dZ;tX@ZJZiH@bU*EL}kzs0iR-!OiSv`9;eJQ0tBDj$Zy~M_% zJi$>;sVhOQo{co#-#QuehJ2r|sIm7M;9=_f#z%!Kdh4Gt5VACJnv$}7>){Fi()&l6 zxbo_I7Xp*`0$V!{s4E|nIsR?d`QRL3!=Bv~*G3>KdL^fO2S%{uW*Vxm*g*Va~v6^GXbSLozCsCI+O$&~P z&F{2YmalmhLffc2U$G2EV{049d6LC@S|22LDsWmq&vsp|X+`=z%-iuB|CyoH&6y$O zrd;;@YW<+gHaD%{)b3rk!dXFqk9Rf-Vpr@@R`PBxbsE(?k$?o>L2xi9o3BzCOh57f zuh*pz9L%QZX?0)u>8S*0mH3Zu6P#o;SY}5T8*@BdibT%`kSfx$!tkr_ov%E3 zUmv@{u^-Y)MzsrVr8bBQJTb6@*pC*6Et#=x8N6Yk@v<=&&Q(j}a>G?Putf=ytX~jd zArrTHpz38Zn~w`40o!Qs;bp|!9T?|@#58YH>CtkW_1CU7m*F2uJjJg1TMHbd`hE^{ zyZQ?AP6>R*jK)C`Xwh5EGAwhD%GpJCXS_`+x~bqls>Qoy4zQU(?hU0MRIAJ#267P} zh@j+pP)+^fF1V}AYslK4tad1rw5>mld{?87Y;aMx3A5e%1SX+&m^Kya!(jp+n5q0L z*4Yvw|0UK?NY2*$==@(33@vrwHM{6Hv*r1S;v7G)+DckbhdR6|@lQAjDX?~$I;~aA z^QEd(5}-{29LRIlfLBXh1O2jaCroWo#?`O{DaO!U5(#lU3Zo}vodZAUq*(UjQW<%r z(mYLjjekyaVQT9S-~tK8@pZSwwp4;CUj}6IMEbsF?rWoON1+nLZ3WoMQPY%rQ4qez01f32@1XDUvVUwrqW*2#ndBeG;;ft&Fk2X;Q`P`hO4(~+d^|Yi5 zqcx?hv5C&!;w-xaj;cTq(~!v0MdjoLuTnT|bpgEV$7f zr!Ke^-WBcsR*acSH@#=KO8Z{BG9k-+T@{w|%CZk*XOH~nRH^JVOUr|R*T+`tO6;DW z`BT!#KnGcI+;`y)2pNyni&bLSRba~2XN9@r#Oy{t%bvuqQ0g+U)T?}AzNp61(bUi~ zjRrC-1w!uKU+79EKH`g)i9=`#A?OSZGKCvEjfvQ!cjayh=~*Qo%bXWktW^HJ3H;}& z`-uiMy|=pJ@ux)mUaQo1{wCPzUwpR)MbdmCLe%ymU@xTve}6W{G7=nl_QGzKVrW+HN6}4sh%KzdQ*tk{jRmZRfHM|f z{i_^YotyS3PyzCFy!zT#_Jn6wYkby1J6Q*VZHpjD3V$!vwORNPw zJo=TE84SX*xJD1WhF(uu(^<~j_qz>lcV4VA({E#RXYX=`O5`Q78gXArlba&E;Ua{NjK}?&5blI zco>)Zk`Mpp$WOUNKI-XfIhTn-P!xMk>=G&Jv?sH*a&kZHG~CqB{1zal&S_TSN?!2}JRT zO^`A}C28Qh7(*8j#l)mqVI|1~T0H7c(-)dKVGUEanjcw4r{~KGGFb@mU;hHSCGE^= z&g<}ViOQV*M(}_5F&jM(mpR0aE>KA|PQ7uQ>{X0F&kb~$J;~u--*0n#Jbv{qK0fi5Woc!r``I(H!cZcH_e&23OyJ{1`r8Sy#m3ST%T`+x8}-P_ z+?LVpn5bUf@s=uN?1aYO5Z*fsLm^IZ_f9)YzYf#}VIDNacCP{8WeCpK%u_Ihep_^1 z2FkGyg-~R&k)IV>MX<(ey^h!6Xktbw8hNhUK3?|})-)NuUHS1z-c!f+A2UPdY) zW0%AK0((C~@fM{>NvMp2Uz73J^0xb?CK`Ej4p=6RA5tSM?i4;4Z^q85@YtVM9SgZ; zQ_nEqVy}}nhpQ~Gn0=*T_H&)9cdNQ46(8l16_r6UhdPwvB(w>TzQNnG;I#h`32^aM zk$eG_0qlpg=x#C@0F0TgFDQ>#iwlgWhFX$M1LQK%nMxF0;O+QWW7%m>U@G0${|vlo zrOK*hxjAZ_dZiVbSvU~t{mzT!uTZ`crD*l~$}#&Ms_Xtpo$8&+o8ZM>p2o0LR(A|# z!}!IX@7%DeQF9>luG$Biula0+)GEjR(sUepn*?>(N(pwo!({<yZUsv|%BH)2h2=JEl^X;2a_#Pr3M)3-g?=ls=H&tG)*-RQh)3PLb~SKmx91ion=qT}%%fY|imsDL)E3M#mQ5um)Ww zK0eEWHO}K%8iWJVAn~~7ko7l7Gsqf@=4H$_(uKS9wvwqOTS#=JD4kkiGxc;7^!Ai6 zVbO8Ebh+`*z7($CJBgl1xsE`I=*756pNRkkbP`Nka&!NT5BHu)C+B!6_VRHLd!{|9 zYqC=Z8gxijS19;-1J5+@nYkqPNizhMQb2$*$w5?TVD0_#RTl)h&M22c8w63L4Ny0l zrR0g!@-Va|-K;6;q@_}+n3EjoOqq!i9DiQwgN0bz;A2I2|+DCGT`TW+qhT4-&SVTJxDLqzg0|m~(M8HK5=QQw?V8 zelbhIlR0|7EMD+!g#%b-2^$$~DXa@ox}%Uz$ACqetglg)RpFmeRiAuGh2Z>nQi;X8 z?A5S}e5b8eZw0EgCE#wkK=+T947rs|lr8HNZgNDT>}?;O~D7XkS~Wz%|x93 zf^nPc|Hj1sH){4D!25q5@qPW!j{ms(tEs4EpXKXszP*noE3DONB;DB&jc)Owb0~qrl1kAs9*J7YnBMg z&f609HaaHfc(LsxPxO~WDAz%$X-8HezneFJGAeJ+q*S-vX#iLj_{}=Q^y(@V{d{4+ zqM`>y0J5QyI)OZiGr-hUSLOha%L2fAev6?OF)GF|78TJwwRr<-aCg`MNR=jQ!RD%O zWKTvl!6*PD=C+@%%&(R{`O$2zo~oKDBMk#=me%e+z$yMB`JFNW$JyGAj8uX&Y;d7g z5cbfpy)L^R^|Mw+08_q+Niy?Uc9=@6KezyRAYw9g(G3gNyz~+uIEquHecUN>L1v_1 z#PLLCKN8c6k;DZ4?lg;7qdL@6u$%rQNZL{>zw$&H=pW`dJz62p(|7Y7z|x2^=^T5Y zA#TzdP#b;v!lb&&0gBQj?o~Sh0Y&qXu4+~zG~PxNV67I2wUIlp=aN{bt9Fxt=oUp^A4s3c zlA8TaTC6T}VL>qnYTzU<#m)=72lN~yoc@!iy6c>(#9JT^x;)x1OIx3cbah^l2C^?B zmgzQzN5IWfGnR;iv2jx4RQBX$*K!Fm+ z5Xi%jp$EyGwHTP7;EZ4I0b5k%D%ZEpTv6qqKbvV@rfLTCfeH^rs2yAHDLZo86e_MBJzEN8iqOXP7Q@SJ*!k&g2}frCC6 zBcJ*>kSJ^|tBO`*@FLcf?h69F>1_hB#t0BG+cUjkQ+BtT`S;Sk#f3oul8XJx6PD`g z_wRr1;d~?p_5bU%aPJcC!Rrb?Sl>Z#l~s93xp-jN@fG*i8nGvWiyHr`VtkWL3}?Op z!)!_N+AFhb%js`>_>R-SAztpQ+UOhi{A(uuV+#J06s(WK0CE@}Y4rf-q;Pi;%S&(* zVMmmWyKy;AY?XLutw!@8vf1Gjlt!$1Tq;Woo$)5x(c$wEp31PTsN|m`bL@w6|K61s zLbg6tfpbsfeI?(N+5%)N;7-lFaYY#d@u+;26{d{+8ru@ z8#%<{-_jCNQ3yR~xw+gHSQ^P+9#UkI9{B3_#CHAi?8oWfut5VO)2s`~fbpcOAUZ0A zL4vMbF-%NU|9JYO@VSkl?2g+IVWN!5ObCKrhVoTUdH^YyA^cybC+iIWO{Qg%w9hGc zVGyv3#a!cexwxq8I#u_|)1%`X9#c;U7YKdCfOW(J{(1k9x9(s_!YPLyb@CB=jh+sJ z9dO5nDUf0qQuP6%p2G$n{c5>H(`qKbV8GN5>9-s--RHHsOWq?!sR+YL`F4j?P?SA0 z49aLtb!jaGUO$GS*=2Q6NxfOA|?5gYYk5fxb)e4a5 z+nAm}j(XTTfYwbt`;X4@JEj`)tb>`dvJoVwmJBb##SFROVKMZKqI&D{UDacUp%#BMhr2it zbHYi#!Lj#5rc8%huGDnW@gS}#5XXQ}QtwB9PFqL!l#K{bbAx`jn+JHWYa3NtZWWsS zdxF%hVbpT(%mA}5jENgY4QsCplMglt?~q7wLY&#BJ_{bLxLki2*$acQmi5BfspNXq zk1vlB9o|Z7mOqAtc+J{27Z(fwvq^mxFa~{Y#s9E||1Hi(+|Ik~1<@fPsY&-NJ5vVdZbJnl!mdY|{7u~W5zE?h_x695vSvFb;dRAwBwhzHuD*qY!4 zRuznG(|9-j=eqJ-;AG0g`iGy$ymp*j@1y6LcF*R&a3AQ331Mosl*8_xqpMdKG;;TddO4T0|%*b!; zdMO__=)_$tc7=G0S%Z2XG13eLerrpwV%+Jjj%rOYh?MNQOd>e^Fx$w>s?#1KHYW5~ z|3&3GRfn&kR(igo%o^Dp;Tk;T!&2yBk36u{mgPy$pu4V6QLOtts$%Ic$3#Ff$70h3 zD>_w{y84SQst`KETN!yCZ7^zfG6?6Sa+4EVSPK#vdn{`W+ff1u;e9s7?R1hT`Oo@N zwuX_y!15&0cu!!F0l!W3n6UA;o&Dwe7JFaMvS~{;D?ZHW)TpE3#NQDV!!aZ0VtRo~ zD!0R-y}0b|RnEd44iX%Z;EsNF#c|Sero3IS6{$tGEpYOzmK3cY8LIY1E&IZT+u&1G+Am94bi*A5DqKe%yZN);+p|8s;XTWf_UT#5VhNSM{gP2&U) zm=(T0Cf;Vr)J#~+l8@!??)=GYLsEJ2jKZ`BAw-wg*Csytm|B&S^WFbhq3ogMJ7Ryu zgr+Ce{p@@EILiBP_q3NTURy$3uWnqCe9_9YQsrI}1CzJ*&EClpKFp8N%zmZ;RbM%Xq$dQ{{@!yDpf2-sgCKiSEmI z8Rfa*NG?{I%gH7suqHHEs}r1Lm?D;VH)k8ayO!#C5ZulHc402%9+*1Ya{lRO(a_cq zXju~oAo?(%)mT7)v}gy?=tr#Ufi0=QqlAb>M(_?5DJvB)D_RVjh){yL;tB2;Oh1tl zG8m4*w225McpL+7U%-sPbT5Vy_)?IGeNX>0>*ai_S8%=fh5-mXUHx3vIVCg!07^Cr A#sB~S literal 0 HcmV?d00001 From 6bdfb1d74a60de6005c99ee2d36bd98263a13ea9 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 02:05:17 -0700 Subject: [PATCH 16/76] Updated Home (markdown) --- Home.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 6a6f305e..34e6ebc0 100644 --- a/Home.md +++ b/Home.md @@ -6,4 +6,6 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) - See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] -- See also a [[General source structure of this project| Source-structure]] \ No newline at end of file +- See also + - [[General source structure of this project| Source-structure]] + - [[Overview of the server side architecture| Spec-Server-Architecture]] \ No newline at end of file From b86057e6a4a84a5afaa08b0e00ac163f44a2a08f Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 02:05:43 -0700 Subject: [PATCH 17/76] Created Spec server architecture (markdown) --- Spec-server-architecture.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Spec-server-architecture.md diff --git a/Spec-server-architecture.md b/Spec-server-architecture.md new file mode 100644 index 00000000..2960af9d --- /dev/null +++ b/Spec-server-architecture.md @@ -0,0 +1,3 @@ +## Diagram: Overview of the server architecture ## + +![server_architecture.png](server_architecture.png) \ No newline at end of file From dd7b62f7a1763e21625d059d0309e80f95af97ef Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 02:14:45 -0700 Subject: [PATCH 18/76] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 34e6ebc0..4800baac 100644 --- a/Home.md +++ b/Home.md @@ -5,7 +5,8 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) -- See a first [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - See also + - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] + - [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - [[General source structure of this project| Source-structure]] - [[Overview of the server side architecture| Spec-Server-Architecture]] \ No newline at end of file From fbbfc784b17c3ffd11a5ae5cc5d72dd583f3cb1b Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 02:22:41 -0700 Subject: [PATCH 19/76] Created Community collaboration (markdown) --- Community-collaboration.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 Community-collaboration.md diff --git a/Community-collaboration.md b/Community-collaboration.md new file mode 100644 index 00000000..2c4f551e --- /dev/null +++ b/Community-collaboration.md @@ -0,0 +1,19 @@ +This project is a community project + +## Mailing list ## +- Google group: http://groups.google.com/group/eiffel-web-framework + +## Materials ## +- wiki: github wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/Community-collaboration +- Shared documents: on google docs at http://goo.gl/M8WLP +- source code: git repository at https://github.com/Eiffel-World/Eiffel-Web-Framework + +## Main contributors ## +- **jfiat**: Jocelyn Fiat (Eiffel Software | http://eiffel.com/) +- **jvelilla**: Hector Javier Velilla (Seibo Software Studios | http://http://seibostudios.se/en/) +- **paco**: Paul Cohen (Seibo Software Studios | http://http://seibostudios.se/en/) +- **daro**: Daniel Rodriguez (Seibo Software Studios | http://http://seibostudios.se/en/) + +## You want to participate ## +- You are welcome to contribute (code, test, doc, code review, feedback, suggestion, spread the words ...) +- Feel free to subscribe to the mailing list From 5a4a749c0f7b03ba206fb2205e470ffcffd0dbc8 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 02:24:18 -0700 Subject: [PATCH 20/76] Updated Tasks Roadmap (markdown) --- Tasks-Roadmap.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Tasks-Roadmap.md b/Tasks-Roadmap.md index 9f921e84..69ba8900 100644 --- a/Tasks-Roadmap.md +++ b/Tasks-Roadmap.md @@ -28,11 +28,7 @@ - Persistence chooser * Dynamic update of running system (**daro**) - ## Contributors ## - - **jfiat** = Jocelyn Fiat - - **paco** = Paul Cohen - - **daro** = Daniel Rodriguez - - **jvelilla** = Hector Javier Velilla + - See [[the collaboration page|Community-collaboration]] note: In bold, you see the responsible for each task, but contribution from other is possible as well. \ No newline at end of file From 843a3efe0f60af0430326377e4625f0455bab66a Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 03:45:59 -0700 Subject: [PATCH 21/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 4800baac..1ed5c245 100644 --- a/Home.md +++ b/Home.md @@ -1,6 +1,6 @@ ## Eiffel-Web-Framework ## -The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the [[original wiki|https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki]]. +The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the original wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) From 398139ee1979b693236ac458ae0f20ae87a1a94e Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 03:47:02 -0700 Subject: [PATCH 22/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 1ed5c245..8c50451c 100644 --- a/Home.md +++ b/Home.md @@ -1,6 +1,6 @@ ## Eiffel-Web-Framework ## -The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the original wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki +The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki]]. - **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) From b57450d762dce22602e347e99b641fba3ce70c3d Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 6 Jul 2011 03:48:31 -0700 Subject: [PATCH 23/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 8c50451c..a37146d7 100644 --- a/Home.md +++ b/Home.md @@ -3,7 +3,7 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki]]. -- **Eiffel Web Framework**: Please visit the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) +- Mailing list: please visit and subscribe to the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif) - See also - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] From 36539997cd8a781b031da2185db1a362607345c2 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Thu, 7 Jul 2011 01:24:05 -0700 Subject: [PATCH 24/76] Updated Community collaboration (markdown) --- Community-collaboration.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Community-collaboration.md b/Community-collaboration.md index 2c4f551e..e8cf3f37 100644 --- a/Community-collaboration.md +++ b/Community-collaboration.md @@ -4,9 +4,10 @@ This project is a community project - Google group: http://groups.google.com/group/eiffel-web-framework ## Materials ## -- wiki: github wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki/Community-collaboration +- wiki: github wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki - Shared documents: on google docs at http://goo.gl/M8WLP - source code: git repository at https://github.com/Eiffel-World/Eiffel-Web-Framework +- temporary wiki for EWSGI spec at http://eiffel.seibostudios.se/wiki/EWSGI ## Main contributors ## - **jfiat**: Jocelyn Fiat (Eiffel Software | http://eiffel.com/) From 1f385658ed647b690076606e91bf276807da3c05 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Thu, 7 Jul 2011 06:41:47 -0700 Subject: [PATCH 25/76] Updated Source structure (markdown) --- Source-structure.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source-structure.md b/Source-structure.md index 1314e608..b49988ff 100644 --- a/Source-structure.md +++ b/Source-structure.md @@ -4,8 +4,8 @@ - README : quick README to point to the github project - doc/ - doc/wiki : clone of the associated github wiki repository -- example/ -- test/ +- examples/ +- tests/ - library/ - library/ewsgi/ewsgi.ecf - library/ewsgi/ewsgi.ecf @@ -19,9 +19,9 @@ Any library/component (in development mode) should follow the following structur - **library/** : the place to put the source code of the related library/component - **doc/** : notes, documentations, ... - **test/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ ) -- **example/** : standard place to put the example +- **examples/** : standard place to put the example - **build/** : a convenient place to compile your project, using this convention, you can setup utilities such as backup to ignore this folder. -- **resource/** : contains pixmap files, .... +- **resources/** : contains pixmap files, .... - **install/** : contains installation scripts for each platform - **data/** : contains eventual data for the related tools From 6cba9a912f14e29075fdce2948696e372e5c323e Mon Sep 17 00:00:00 2001 From: seibo Date: Thu, 7 Jul 2011 15:56:14 -0700 Subject: [PATCH 26/76] Created EWSGI/specification (markdown) --- EWSGI-specification.md | 109 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 EWSGI-specification.md diff --git a/EWSGI-specification.md b/EWSGI-specification.md new file mode 100644 index 00000000..c9c6fb67 --- /dev/null +++ b/EWSGI-specification.md @@ -0,0 +1,109 @@ +The Eiffel Web Server Gateway Interface# +Preface## + +This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers. + +Note: This document is based on [PEP 333](http://www.python.org/dev/peps/pep-0333/) -- Python Web Server Gateway Interface v1.0 but adapted to needs and properties of Eiffel. +Rationale and Goals + +The most important goal of EWSGI is to enable an amazingly simple and wonderful programmer experience to the Eiffel developer who wants to write Eiffel server applications for the web! + +Other programming laguages like Java boast a wide variety of web application frameworks. Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. Python also boasts many frameworks like Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices was a problem for new Python users, because there was no standard protocol or convention for how webservers were to interact with Python applications and frameworks and this meant that the frameworks only worked with some webservers and not with others. To resolve this the PEP 333 -- Python Web Server Gateway Interface v1.0 was written and subsequently implemented in many webserver environments and in Python frameworks. + +The availability and widespread use of such an API in web servers for Eiffel -- whether those servers are written in Eiffel (e.g. Niño), embed Eiffel in Apache (e.g. mod_eiffel), or invoke Eiffel via a gateway protocol (e.g. CGI, FastCGI, etc.) -- would separate choice of framework from choice of web server, freeing users to choose a pairing that suits them, while freeing server developers as well as developers of higher level web application framworks and tools, like AJC (Alex's Eiffel2JavaScript), to focus on their preferred area of specialization. + +This document, therefore, proposes a simple and universal interface between web servers and Eiffel web applications and frameworks: the Eiffel Web Server Gateway Interface (EWSGI). + +But the mere existence of a EWSGI spec does nothing to address the existing state of servers and frameworks for Eiffel web applications. Server and framework authors and maintainers must actually implement EWSGI for there to be any effect. + +However, since no existing servers or frameworks support EWSGI, there is little immediate reward for an author who implements EWSGI support. Thus, EWSGI must be easy to implement, so that an author's initial investment in the interface can be reasonably low. + +Thus, simplicity of implementation on both the server and framework sides of the interface is absolutely critical to the utility of the EWSGI interface, and is therefore the principal criterion for any design decisions. + +Note, however, that simplicity of implementation for a framework author is not the same thing as ease of use for a web application author. EWSGI presents an absolutely "no frills" interface to the framework author, because bells and whistles like response objects and cookie handling would just get in the way of existing frameworks' handling of these issues. Again, the goal of EWSGI is to facilitate easy interconnection of existing servers and applications or frameworks, not to create a new web framework. +Proof-of-concept and reference implementation ¶ + +We propose a proof-of-concept or reference implementation be done for Apache on Linux and Windows using EiffelWebReloaded and WAMIE. +Specification overview ¶ +The Server/Gateway Side ¶ + +This is the web server side. The server sends the request information in an environment object or structure to the Application/Framework side and expects information on what to return in the form of a response object. +The Application/Framework Side ¶ + +This is the Eiffel application side. Basically one should be able to write: + +class APPLICATION +Inherit + EW_APPLICATION +creation + make - - THIS IS PART OF THE EWSGI SPECIFICATION! +features + execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! + do + create Result + if env.path_info = ”/hello” then + Result.set_status (Http_ok) + Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) + Result.put_contents (”Hello World”) + else + Result.set_status (Http_not_found) + end + end +end + +Or to register handlers for specific URL paths (or regexp based URL path expressions): + +class APPLICATION +Inherit + EW_APPLICATION +creation + make +redefine + make +features + + make is + do + register_handler (”/hello”, ~hello_world_handler) + -- The default execute feature will dispatch calls to the appropriate handler! + end + + hello_world_handler (env: ENVIRON): RESPONSE is + do + create Result + if env.path_info = ”/hello” then + Result.set_status (Http_ok) + Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) + Result.put_contents (”Hello World”) + else + Result.set_status (Http_not_found) + end + end + end +end + +Specification Details ¶ + +Required information in the ENVIRONMENT class. + +The ENVIRONMENT class is required to provide features for accesing CGI environment variables, as defined by the Common Gateway Interface specification. The following variables must be present. +Variable Description +auth_type: STRING "Basic", "Digest" or some other token. +request_method: STRING The HTTP request method, such as "GET" or "POST". +script_name: STRING The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server. +path_info: STRING The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. +query_string: STRING The portion of the request URL that follows the "?", if any. +content_type: STRING The contents of any Content-Type fields in the HTTP request. May be empty. +content_length: STRING The contents of any Content-Length fields in the HTTP request. May be empty. +server_name, server_port: STRING When combined with script_name and path_info, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to server_name for reconstructing the request URL. See the URL Reconstruction section below for more detail. server_name and server_port can never be empty strings. +server_protocol: STRING The version of the protocol the client used to send the request. Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called request_protocol, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.) +http_variables: HASH_TABLE [STRING, STRING] Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. + +Required information in the RESPONSE class. +Implementation/Application Notes ¶ + +For an Eiffel system to run it needs to be compiled to a executable program or shared library (.so/.dll). For supporting CGI all that is needed is an executable program, For FCGI and EWSGI we need to compile the Eiffel system to a shared library. +Questions and Answers ¶ +Proposed/Under Discussion ¶ +Acknowledgements ¶ +References ¶ \ No newline at end of file From 831f73b3084742ceea388fc6a9807d426c1d8747 Mon Sep 17 00:00:00 2001 From: seibo Date: Thu, 7 Jul 2011 15:57:14 -0700 Subject: [PATCH 27/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index c9c6fb67..53d04a80 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -1,5 +1,5 @@ -The Eiffel Web Server Gateway Interface# -Preface## +# The Eiffel Web Server Gateway Interface # +## Preface ## This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers. From 13515504c3bde212c374c41d3d197ee458f5f514 Mon Sep 17 00:00:00 2001 From: seibo Date: Thu, 7 Jul 2011 15:58:16 -0700 Subject: [PATCH 28/76] Updated The Eiffel Web Server Gateway Interface (markdown) --- EWSGI-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 53d04a80..50913066 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -1,5 +1,5 @@ -# The Eiffel Web Server Gateway Interface # -## Preface ## +# The Eiffel Web Server Gateway Interface +## Preface This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers. From ab924d35965a8cd157505ca77942f451e6e93c8b Mon Sep 17 00:00:00 2001 From: seibo Date: Thu, 7 Jul 2011 16:11:10 -0700 Subject: [PATCH 29/76] Updated The Eiffel Web Server Gateway Interface (markdown) --- EWSGI-specification.md | 60 ++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 50913066..8d99ee27 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -4,11 +4,11 @@ This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers. Note: This document is based on [PEP 333](http://www.python.org/dev/peps/pep-0333/) -- Python Web Server Gateway Interface v1.0 but adapted to needs and properties of Eiffel. -Rationale and Goals +## Rationale and Goals The most important goal of EWSGI is to enable an amazingly simple and wonderful programmer experience to the Eiffel developer who wants to write Eiffel server applications for the web! -Other programming laguages like Java boast a wide variety of web application frameworks. Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. Python also boasts many frameworks like Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices was a problem for new Python users, because there was no standard protocol or convention for how webservers were to interact with Python applications and frameworks and this meant that the frameworks only worked with some webservers and not with others. To resolve this the PEP 333 -- Python Web Server Gateway Interface v1.0 was written and subsequently implemented in many webserver environments and in Python frameworks. +Other programming laguages like Java boast a wide variety of web application frameworks. Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. Python also boasts many frameworks like Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices was a problem for new Python users, because there was no standard protocol or convention for how webservers were to interact with Python applications and frameworks and this meant that the frameworks only worked with some webservers and not with others. To resolve this the [PEP 333](http://www.python.org/dev/peps/pep-0333/) -- Python Web Server Gateway Interface v1.0 was written and subsequently implemented in many webserver environments and in Python frameworks. The availability and widespread use of such an API in web servers for Eiffel -- whether those servers are written in Eiffel (e.g. Niño), embed Eiffel in Apache (e.g. mod_eiffel), or invoke Eiffel via a gateway protocol (e.g. CGI, FastCGI, etc.) -- would separate choice of framework from choice of web server, freeing users to choose a pairing that suits them, while freeing server developers as well as developers of higher level web application framworks and tools, like AJC (Alex's Eiffel2JavaScript), to focus on their preferred area of specialization. @@ -21,25 +21,29 @@ However, since no existing servers or frameworks support EWSGI, there is little Thus, simplicity of implementation on both the server and framework sides of the interface is absolutely critical to the utility of the EWSGI interface, and is therefore the principal criterion for any design decisions. Note, however, that simplicity of implementation for a framework author is not the same thing as ease of use for a web application author. EWSGI presents an absolutely "no frills" interface to the framework author, because bells and whistles like response objects and cookie handling would just get in the way of existing frameworks' handling of these issues. Again, the goal of EWSGI is to facilitate easy interconnection of existing servers and applications or frameworks, not to create a new web framework. -Proof-of-concept and reference implementation ¶ + +## Proof-of-concept and reference implementation We propose a proof-of-concept or reference implementation be done for Apache on Linux and Windows using EiffelWebReloaded and WAMIE. -Specification overview ¶ -The Server/Gateway Side ¶ + +## Specification overview + +## The Server/Gateway Side This is the web server side. The server sends the request information in an environment object or structure to the Application/Framework side and expects information on what to return in the form of a response object. -The Application/Framework Side ¶ + +## The Application/Framework Side This is the Eiffel application side. Basically one should be able to write: -class APPLICATION -Inherit - EW_APPLICATION -creation - make - - THIS IS PART OF THE EWSGI SPECIFICATION! -features - execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! - do + class APPLICATION + inherit + EW_APPLICATION + creation + make - - THIS IS PART OF THE EWSGI SPECIFICATION! + features + execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! + do create Result if env.path_info = ”/hello” then Result.set_status (Http_ok) @@ -48,27 +52,27 @@ features else Result.set_status (Http_not_found) end - end -end + end + end Or to register handlers for specific URL paths (or regexp based URL path expressions): -class APPLICATION -Inherit - EW_APPLICATION -creation - make -redefine - make -features + class APPLICATION + inherit + EW_APPLICATION + creation + make + redefine + make + features - make is + make is do register_handler (”/hello”, ~hello_world_handler) -- The default execute feature will dispatch calls to the appropriate handler! end - hello_world_handler (env: ENVIRON): RESPONSE is + hello_world_handler (env: ENVIRON): RESPONSE is do create Result if env.path_info = ”/hello” then @@ -79,8 +83,8 @@ features Result.set_status (Http_not_found) end end - end -end + end + end Specification Details ¶ From 6309bbfa52f9fd7f90ef494b5a0eaffc7f28a939 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Fri, 8 Jul 2011 05:39:44 -0700 Subject: [PATCH 30/76] Updated The Eiffel Web Server Gateway Interface (markdown) --- EWSGI-specification.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 8d99ee27..079166ce 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -1,3 +1,5 @@ +**WARNING** **THIS PAGE IS IN PROGRESS, AS IT IS NOW, IT NEEDS UPDATE SINCE IT DOES NOT REFLECT THE FUTURE INTERFACE** + # The Eiffel Web Server Gateway Interface ## Preface From d5288a4ebebcaa405215674b5ff469a00a76372b Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 12 Jul 2011 10:33:14 -0700 Subject: [PATCH 31/76] Updated Home (markdown) --- Home.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Home.md b/Home.md index a37146d7..5e6c68cf 100644 --- a/Home.md +++ b/Home.md @@ -9,4 +9,5 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] - [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - [[General source structure of this project| Source-structure]] - - [[Overview of the server side architecture| Spec-Server-Architecture]] \ No newline at end of file + - [[Overview of the server side architecture| Spec-Server-Architecture]] + - This project is also a collection of [[Libraries]] related to the Web \ No newline at end of file From 13993197d365b74d096c49b1f891afabf154e8d5 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 12 Jul 2011 10:39:15 -0700 Subject: [PATCH 32/76] Created Libraries (markdown) --- Libraries.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Libraries.md diff --git a/Libraries.md b/Libraries.md new file mode 100644 index 00000000..280a4961 --- /dev/null +++ b/Libraries.md @@ -0,0 +1,10 @@ +The libraries which are currently part of the Eiffel Web Framework +- [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix GW_ for **G**ate**W**ay) +- [[Library-libFCGI]]: Eiffel wrapper of libfcgi SDK (http://www.fastcgi.com/devkit/libfcgi/) +- [[Library-encoder]]: simple encoder for base64, url-encode, xml entities +- [[Library-error]]: simple framework to handle error in EWSGI and related +- [[Library-http]]: utility classes to handle HTTP protocol, status, ... + +The libraries which are needed but not (yet) in the collection of libraries +- [[Library-EiffelWebNino]]: Eiffel Web Server written in Eiffel +- [[Library-JSON]]: JSON format parser and converter \ No newline at end of file From d409eac9d625d70b1ba17ad0b44a5d179109b6bf Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 12 Jul 2011 10:41:22 -0700 Subject: [PATCH 33/76] Updated Libraries (markdown) --- Libraries.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/Libraries.md b/Libraries.md index 280a4961..015d5e20 100644 --- a/Libraries.md +++ b/Libraries.md @@ -1,10 +1,14 @@ -The libraries which are currently part of the Eiffel Web Framework -- [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix GW_ for **G**ate**W**ay) -- [[Library-libFCGI]]: Eiffel wrapper of libfcgi SDK (http://www.fastcgi.com/devkit/libfcgi/) -- [[Library-encoder]]: simple encoder for base64, url-encode, xml entities -- [[Library-error]]: simple framework to handle error in EWSGI and related -- [[Library-http]]: utility classes to handle HTTP protocol, status, ... +## libraries currently part of the Eiffel Web Framework ## +* [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix GW_ for **G**ate**W**ay) +* [[Library-libFCGI]]: Eiffel wrapper of libfcgi SDK (http://www.fastcgi.com/devkit/libfcgi/) +* [[Library-encoder]]: simple encoder for base64, url-encode, xml entities +* [[Library-error]]: simple framework to handle error in EWSGI and related +* [[Library-http]]: utility classes to handle HTTP protocol, status, ... -The libraries which are needed but not (yet) in the collection of libraries -- [[Library-EiffelWebNino]]: Eiffel Web Server written in Eiffel -- [[Library-JSON]]: JSON format parser and converter \ No newline at end of file +## libraries needed but not (yet) in the current framework ## +* [[Library-EiffelWebNino]]: Eiffel Web Server written in Eiffel +* [[Library-JSON]]: JSON format parser and converter + +## libraries that could be included, or or used ## +* eMIME +* \ No newline at end of file From 4880d23846206eb8ec45292814d3283cb07ef4ab Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 12 Jul 2011 10:42:09 -0700 Subject: [PATCH 34/76] Updated Libraries (markdown) --- Libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries.md b/Libraries.md index 015d5e20..377865ab 100644 --- a/Libraries.md +++ b/Libraries.md @@ -10,5 +10,5 @@ * [[Library-JSON]]: JSON format parser and converter ## libraries that could be included, or or used ## -* eMIME +* eMIME: MIME parser, and encoder ... * \ No newline at end of file From 838982906762225f41dce22de61b3efeae0f2150 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 12 Jul 2011 10:42:23 -0700 Subject: [PATCH 35/76] Updated Libraries (markdown) --- Libraries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries.md b/Libraries.md index 377865ab..08aa53fc 100644 --- a/Libraries.md +++ b/Libraries.md @@ -11,4 +11,4 @@ ## libraries that could be included, or or used ## * eMIME: MIME parser, and encoder ... -* \ No newline at end of file +* ... \ No newline at end of file From cc7e54a19b4cd0ad0a890ef029ed5b7820b24ab8 Mon Sep 17 00:00:00 2001 From: seibo Date: Tue, 12 Jul 2011 14:34:05 -0700 Subject: [PATCH 36/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 079166ce..0aa33cd8 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -26,9 +26,27 @@ Note, however, that simplicity of implementation for a framework author is not t ## Proof-of-concept and reference implementation -We propose a proof-of-concept or reference implementation be done for Apache on Linux and Windows using EiffelWebReloaded and WAMIE. +We propose a proof-of-concept or reference implementation be done with: -## Specification overview +* CGI +* FCGI +* Nino. A simple Web server written in Eiffel by Javier Velilla. +* mod_ewsgi. Apache module on Linux and Windows using Wamie. + +The reference implementation should enable a user to write an EWSGI "Hello World" application: + +1. Using just 1 Eiffel class of ~10 lines of code. +1. And easily switch deployment method (CGI, FCGI, Nino or mod_ewsgi). + +A reference implementation is being worked on by Jocelyn Fiat (Eiffel Software), Javier Velilla (independent), Daniel Rodríguez (Seibo) and Paul Cohen (Seibo). The reference implementation consists of + +1. EWSGI core library: [Eiffel-WebFramework](https://github.com/Eiffel-World/Eiffel-Web-Framework). +1. eJSON library: [eJSON](http://ejson.origo.ethz.ch/). +1. Nino Eiffel Web server: [Nino](http://code.google.com/p/webeiffel/source/browse/#svn%2Ftrunk%2Feiffelwebnino). +1. mod_ewsgi module. Based on [Wamie](http://eiffel.seibostudios.se/wiki/Wamie). +1. SOS. Simple Object Storage - a simple Eiffel persistance mechanism. + +# Specification overview ## The Server/Gateway Side @@ -82,13 +100,13 @@ Or to register handlers for specific URL paths (or regexp based URL path express Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) Result.put_contents (”Hello World”) else - Result.set_status (Http_not_found) + Result.set_status (Http_notund) end end - end + e ¶nd end -Specification Details ¶ +## Specification Details Required information in the ENVIRONMENT class. From 4904aba3601d1721a521cf1a228b4d41e5962d23 Mon Sep 17 00:00:00 2001 From: seibo Date: Tue, 12 Jul 2011 15:48:53 -0700 Subject: [PATCH 37/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 0aa33cd8..e4f10d1a 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -103,31 +103,46 @@ Or to register handlers for specific URL paths (or regexp based URL path express Result.set_status (Http_notund) end end - e ¶nd + end end ## Specification Details -Required information in the ENVIRONMENT class. +Required information in the **ENVIRONMENT** class. + +The **ENVIRONMENT** class is required to provide features for accesing CGI environment variables, as defined by the [Common Gateway Interface specification](http://ken.coar.org/cgi/draft-coar-cgi-v11-03.txt). The following variables must be present. -The ENVIRONMENT class is required to provide features for accesing CGI environment variables, as defined by the Common Gateway Interface specification. The following variables must be present. Variable Description + auth_type: STRING "Basic", "Digest" or some other token. + request_method: STRING The HTTP request method, such as "GET" or "POST". + script_name: STRING The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server. + path_info: STRING The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. + query_string: STRING The portion of the request URL that follows the "?", if any. + content_type: STRING The contents of any Content-Type fields in the HTTP request. May be empty. + content_length: STRING The contents of any Content-Length fields in the HTTP request. May be empty. + server_name, server_port: STRING When combined with script_name and path_info, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to server_name for reconstructing the request URL. See the URL Reconstruction section below for more detail. server_name and server_port can never be empty strings. + server_protocol: STRING The version of the protocol the client used to send the request. Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called request_protocol, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.) + http_variables: HASH_TABLE [STRING, STRING] Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. Required information in the RESPONSE class. -Implementation/Application Notes ¶ +## Implementation/Application Notes For an Eiffel system to run it needs to be compiled to a executable program or shared library (.so/.dll). For supporting CGI all that is needed is an executable program, For FCGI and EWSGI we need to compile the Eiffel system to a shared library. -Questions and Answers ¶ -Proposed/Under Discussion ¶ -Acknowledgements ¶ -References ¶ \ No newline at end of file + +## Questions and Answers + +## Proposed/Under Discussion + +## Acknowledgements + +## References \ No newline at end of file From e63553555da2886b8e4442ba9a7b28cde6455fca Mon Sep 17 00:00:00 2001 From: seibo Date: Tue, 12 Jul 2011 15:57:54 -0700 Subject: [PATCH 38/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index e4f10d1a..a2b1e608 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -56,24 +56,24 @@ This is the web server side. The server sends the request information in an envi This is the Eiffel application side. Basically one should be able to write: - class APPLICATION - inherit - EW_APPLICATION - creation - make - - THIS IS PART OF THE EWSGI SPECIFICATION! - features - execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! - do - create Result - if env.path_info = ”/hello” then - Result.set_status (Http_ok) - Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) - Result.put_contents (”Hello World”) - else - Result.set_status (Http_not_found) - end - end - end + class APPLICATION + inherit + EW_APPLICATION + creation + make - - THIS IS PART OF THE EWSGI SPECIFICATION! + features + execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! + do + create Result + if env.path_info = ”/hello” then + Result.set_status (Http_ok) + Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) + Result.put_contents (”Hello World”) + else + Result.set_status (Http_not_found) + end + end + end` Or to register handlers for specific URL paths (or regexp based URL path expressions): From e1120040bbe843cc4d67e14b0b52fe580a466d6b Mon Sep 17 00:00:00 2001 From: seibo Date: Tue, 12 Jul 2011 16:02:30 -0700 Subject: [PATCH 39/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 51 ++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index a2b1e608..dd9ec204 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -73,37 +73,34 @@ This is the Eiffel application side. Basically one should be able to write: Result.set_status (Http_not_found) end end - end` + end Or to register handlers for specific URL paths (or regexp based URL path expressions): class APPLICATION - inherit - EW_APPLICATION - creation - make - redefine - make - features - - make is - do - register_handler (”/hello”, ~hello_world_handler) - -- The default execute feature will dispatch calls to the appropriate handler! - end - - hello_world_handler (env: ENVIRON): RESPONSE is - do - create Result - if env.path_info = ”/hello” then - Result.set_status (Http_ok) - Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) - Result.put_contents (”Hello World”) - else - Result.set_status (Http_notund) - end - end - end + inherit + EW_APPLICATION + creation + make + redefine + make + feature + make is + do + register_handler (”/hello”, ~hello_world_handler) + -- The default execute feature will dispatch calls to the appropriate handler! + end + hello_world_handler (env: ENVIRON): RESPONSE is + do + create Result + if env.path_info = ”/hello” then + Result.set_status (Http_ok) + Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) + Result.put_contents (”Hello World”) + else + Result.set_status (Http_notund) + end + end end ## Specification Details From c7f4f45f5eab34092d633f9e9bca52a8e9f33a90 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Thu, 21 Jul 2011 10:01:04 -0700 Subject: [PATCH 40/76] Updated Libraries (markdown) --- Libraries.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Libraries.md b/Libraries.md index 08aa53fc..6b72e4d0 100644 --- a/Libraries.md +++ b/Libraries.md @@ -4,6 +4,7 @@ * [[Library-encoder]]: simple encoder for base64, url-encode, xml entities * [[Library-error]]: simple framework to handle error in EWSGI and related * [[Library-http]]: utility classes to handle HTTP protocol, status, ... +* [[Library-uri-template]]: URI Template parser and expander (follow draft spec 0.5) ## libraries needed but not (yet) in the current framework ## * [[Library-EiffelWebNino]]: Eiffel Web Server written in Eiffel From 7680d071f1807e0c3cf983a23f778fdb9ccf7b31 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Fri, 29 Jul 2011 06:15:51 -0700 Subject: [PATCH 41/76] Updated Community collaboration (markdown) --- Community-collaboration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Community-collaboration.md b/Community-collaboration.md index e8cf3f37..58098906 100644 --- a/Community-collaboration.md +++ b/Community-collaboration.md @@ -7,7 +7,7 @@ This project is a community project - wiki: github wiki at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki - Shared documents: on google docs at http://goo.gl/M8WLP - source code: git repository at https://github.com/Eiffel-World/Eiffel-Web-Framework -- temporary wiki for EWSGI spec at http://eiffel.seibostudios.se/wiki/EWSGI +- Proposal from Paul Cohen for a EWSGI spec at http://eiffel.seibostudios.se/wiki/EWSGI ## Main contributors ## - **jfiat**: Jocelyn Fiat (Eiffel Software | http://eiffel.com/) From b869eb73331894034129ec7ab6102ceaba936747 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 1 Aug 2011 00:36:52 -0700 Subject: [PATCH 42/76] Updated Home (markdown) --- Home.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Home.md b/Home.md index 5e6c68cf..db832b6a 100644 --- a/Home.md +++ b/Home.md @@ -9,5 +9,6 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] - [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - [[General source structure of this project| Source-structure]] + - [[EWSGI specification| EWSGI-specification]] - [[Overview of the server side architecture| Spec-Server-Architecture]] - This project is also a collection of [[Libraries]] related to the Web \ No newline at end of file From a2a1f89299a9fd1ff3078052e4c7917c329d27ed Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 1 Aug 2011 01:19:54 -0700 Subject: [PATCH 43/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 126 +++-------------------------------------- 1 file changed, 8 insertions(+), 118 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index dd9ec204..d63c375d 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -2,140 +2,30 @@ # The Eiffel Web Server Gateway Interface ## Preface +This specification is a proposition based on recent discussion on the mailing list. +This is work in progress, so far nothing had been decided. +You can find another proposal at http://eiffel.seibostudios.se/wiki/EWSGI , it has common background and goal, however still differ on specific parts. +The main goal for now is to unified those 2 specifications. -This document specifies a proposed standard interface between web servers and Eiffel web applications or frameworks, to promote web application portability across a variety of web servers. +--- +Note the following is work in progress, and reflect a specification proposal, rather than the final specification. +2011-08-01 +--- -Note: This document is based on [PEP 333](http://www.python.org/dev/peps/pep-0333/) -- Python Web Server Gateway Interface v1.0 but adapted to needs and properties of Eiffel. -## Rationale and Goals -The most important goal of EWSGI is to enable an amazingly simple and wonderful programmer experience to the Eiffel developer who wants to write Eiffel server applications for the web! - -Other programming laguages like Java boast a wide variety of web application frameworks. Java's "servlet" API makes it possible for applications written with any Java web application framework to run in any web server that supports the servlet API. Python also boasts many frameworks like Zope, Quixote, Webware, SkunkWeb, PSO, and Twisted Web -- to name just a few. This wide variety of choices was a problem for new Python users, because there was no standard protocol or convention for how webservers were to interact with Python applications and frameworks and this meant that the frameworks only worked with some webservers and not with others. To resolve this the [PEP 333](http://www.python.org/dev/peps/pep-0333/) -- Python Web Server Gateway Interface v1.0 was written and subsequently implemented in many webserver environments and in Python frameworks. - -The availability and widespread use of such an API in web servers for Eiffel -- whether those servers are written in Eiffel (e.g. Niño), embed Eiffel in Apache (e.g. mod_eiffel), or invoke Eiffel via a gateway protocol (e.g. CGI, FastCGI, etc.) -- would separate choice of framework from choice of web server, freeing users to choose a pairing that suits them, while freeing server developers as well as developers of higher level web application framworks and tools, like AJC (Alex's Eiffel2JavaScript), to focus on their preferred area of specialization. - -This document, therefore, proposes a simple and universal interface between web servers and Eiffel web applications and frameworks: the Eiffel Web Server Gateway Interface (EWSGI). - -But the mere existence of a EWSGI spec does nothing to address the existing state of servers and frameworks for Eiffel web applications. Server and framework authors and maintainers must actually implement EWSGI for there to be any effect. - -However, since no existing servers or frameworks support EWSGI, there is little immediate reward for an author who implements EWSGI support. Thus, EWSGI must be easy to implement, so that an author's initial investment in the interface can be reasonably low. - -Thus, simplicity of implementation on both the server and framework sides of the interface is absolutely critical to the utility of the EWSGI interface, and is therefore the principal criterion for any design decisions. - -Note, however, that simplicity of implementation for a framework author is not the same thing as ease of use for a web application author. EWSGI presents an absolutely "no frills" interface to the framework author, because bells and whistles like response objects and cookie handling would just get in the way of existing frameworks' handling of these issues. Again, the goal of EWSGI is to facilitate easy interconnection of existing servers and applications or frameworks, not to create a new web framework. ## Proof-of-concept and reference implementation -We propose a proof-of-concept or reference implementation be done with: - -* CGI -* FCGI -* Nino. A simple Web server written in Eiffel by Javier Velilla. -* mod_ewsgi. Apache module on Linux and Windows using Wamie. - -The reference implementation should enable a user to write an EWSGI "Hello World" application: - -1. Using just 1 Eiffel class of ~10 lines of code. -1. And easily switch deployment method (CGI, FCGI, Nino or mod_ewsgi). - -A reference implementation is being worked on by Jocelyn Fiat (Eiffel Software), Javier Velilla (independent), Daniel Rodríguez (Seibo) and Paul Cohen (Seibo). The reference implementation consists of - -1. EWSGI core library: [Eiffel-WebFramework](https://github.com/Eiffel-World/Eiffel-Web-Framework). -1. eJSON library: [eJSON](http://ejson.origo.ethz.ch/). -1. Nino Eiffel Web server: [Nino](http://code.google.com/p/webeiffel/source/browse/#svn%2Ftrunk%2Feiffelwebnino). -1. mod_ewsgi module. Based on [Wamie](http://eiffel.seibostudios.se/wiki/Wamie). -1. SOS. Simple Object Storage - a simple Eiffel persistance mechanism. - # Specification overview ## The Server/Gateway Side -This is the web server side. The server sends the request information in an environment object or structure to the Application/Framework side and expects information on what to return in the form of a response object. - ## The Application/Framework Side -This is the Eiffel application side. Basically one should be able to write: - - class APPLICATION - inherit - EW_APPLICATION - creation - make - - THIS IS PART OF THE EWSGI SPECIFICATION! - features - execute (env: ENVIRON): RESPONSE is - - THIS IS PART OF THE EWSGI SPECIFICATION! - do - create Result - if env.path_info = ”/hello” then - Result.set_status (Http_ok) - Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) - Result.put_contents (”Hello World”) - else - Result.set_status (Http_not_found) - end - end - end - -Or to register handlers for specific URL paths (or regexp based URL path expressions): - - class APPLICATION - inherit - EW_APPLICATION - creation - make - redefine - make - feature - make is - do - register_handler (”/hello”, ~hello_world_handler) - -- The default execute feature will dispatch calls to the appropriate handler! - end - hello_world_handler (env: ENVIRON): RESPONSE is - do - create Result - if env.path_info = ”/hello” then - Result.set_status (Http_ok) - Result.set_header (”Content-Type”, ”text/html; charset=utf-8”) - Result.put_contents (”Hello World”) - else - Result.set_status (Http_notund) - end - end - end - ## Specification Details -Required information in the **ENVIRONMENT** class. - -The **ENVIRONMENT** class is required to provide features for accesing CGI environment variables, as defined by the [Common Gateway Interface specification](http://ken.coar.org/cgi/draft-coar-cgi-v11-03.txt). The following variables must be present. - -Variable Description - -auth_type: STRING "Basic", "Digest" or some other token. - -request_method: STRING The HTTP request method, such as "GET" or "POST". - -script_name: STRING The initial portion of the request URL's "path" that corresponds to the application object, so that the application knows its virtual "location". This may be an empty string, if the application corresponds to the "root" of the server. - -path_info: STRING The remainder of the request URL's "path", designating the virtual "location" of the request's target within the application. This may be an empty string, if the request URL targets the application root and does not have a trailing slash. - -query_string: STRING The portion of the request URL that follows the "?", if any. - -content_type: STRING The contents of any Content-Type fields in the HTTP request. May be empty. - -content_length: STRING The contents of any Content-Length fields in the HTTP request. May be empty. - -server_name, server_port: STRING When combined with script_name and path_info, these variables can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to server_name for reconstructing the request URL. See the URL Reconstruction section below for more detail. server_name and server_port can never be empty strings. - -server_protocol: STRING The version of the protocol the client used to send the request. Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called request_protocol, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.) - -http_variables: HASH_TABLE [STRING, STRING] Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request. - -Required information in the RESPONSE class. ## Implementation/Application Notes -For an Eiffel system to run it needs to be compiled to a executable program or shared library (.so/.dll). For supporting CGI all that is needed is an executable program, For FCGI and EWSGI we need to compile the Eiffel system to a shared library. - ## Questions and Answers ## Proposed/Under Discussion From 01944508925a924fdc6a082eb640a92650123b19 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 00:21:53 -0700 Subject: [PATCH 44/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index d63c375d..b35a45f6 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -11,8 +11,8 @@ The main goal for now is to unified those 2 specifications. Note the following is work in progress, and reflect a specification proposal, rather than the final specification. 2011-08-01 --- - - +For now, the specification from EWF is done in Eiffel interface +please see: https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification ## Proof-of-concept and reference implementation From cefc95e56f56e2ae7a8d09732642fd6f7156a11a Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 01:02:01 -0700 Subject: [PATCH 45/76] added current eiffel interfaces for APPLICATION, REQUEST and RESPONSE_BUFFER --- EWSGI-specification.md | 271 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index b35a45f6..e771ef4c 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -14,6 +14,277 @@ Note the following is work in progress, and reflect a specification proposal, ra For now, the specification from EWF is done in Eiffel interface please see: https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification +EWSGI_APPLICATION + + deferred class + EWSGI_APPLICATION + + feature {NONE} -- Execution + + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) + -- Execute the request + -- See `req.input' for input stream + -- `req.environment' for the Gateway environment + -- and `res' for the output buffer + require + res_status_unset: not res.status_is_set + deferred + ensure + res_status_set: res.status_is_set + res_committed: res.message_committed + end + + end + +EWSGI_REQUEST + + deferred class + EWSGI_REQUEST + feature -- Access: Input + + input: EWSGI_INPUT_STREAM + -- Server input channel + deferred + end + + feature -- Access: extra values + + request_time: detachable DATE_TIME + -- Request time (UTC) + deferred + end + + feature -- Access: environment variables + + environment: EWSGI_ENVIRONMENT + -- Environment variables + deferred + end + + environment_variable (a_name: STRING_8): detachable STRING_8 + -- Environment variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + feature -- Access: execution variables + + execution_variables: EWSGI_VARIABLES [STRING_32] + -- Execution variables set by the application + deferred + end + + execution_variable (a_name: STRING_8): detachable STRING_32 + -- Execution variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + feature -- URL Parameters + + parameters: EWSGI_VARIABLES [STRING_32] + -- Variables extracted from QUERY_STRING + deferred + end + + parameter (a_name: STRING_8): detachable STRING_32 + -- Parameter for name `n'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + feature -- Form fields and related + + form_fields: EWSGI_VARIABLES [STRING_32] + -- Variables sent by POST request + deferred + end + + form_field (a_name: STRING_8): detachable STRING_32 + -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + uploaded_files: HASH_TABLE [EWSGI_UPLOADED_FILE_DATA, STRING_8] + -- Table of uploaded files information + deferred + end + + feature -- Cookies + + cookies_variables: HASH_TABLE [STRING_8, STRING_8] + -- Expanded cookies variable + deferred + end + + cookies_variable (a_name: STRING_8): detachable STRING_8 + -- Field for name `a_name'. + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + cookies: HASH_TABLE [EWSGI_COOKIE, STRING_8] + -- Cookies Information + deferred + end + + feature -- Access: global variable + + variables: HASH_TABLE [STRING_32, STRING_32] + -- Table containing all the various variables + -- Warning: this is computed each time, if you change the content of other containers + -- this won't update this Result's content, unless you query it again + deferred + end + + variable (a_name: STRING_8): detachable STRING_32 + -- Variable named `a_name' from any of the variables container + -- and following a specific order + -- execution, environment, get, post, cookies + require + a_name_valid: a_name /= Void and then not a_name.is_empty + deferred + end + + feature -- Uploaded File Handling + + is_uploaded_file (a_filename: STRING_8): BOOLEAN + -- Is `a_filename' a file uploaded via HTTP POST + deferred + end + + feature -- URL Utility + + absolute_script_url (a_path: STRING_8): STRING_8 + -- Absolute Url for the script if any, extended by `a_path' + deferred + end + + script_url (a_path: STRING_8): STRING_8 + -- Url relative to script name if any, extended by `a_path' + require + a_path_attached: a_path /= Void + deferred + end + + end + +EWSGI_RESPONSE_BUFFER + + deferred class + EWSGI_RESPONSE_BUFFER + + feature {EWSGI_APPLICATION} -- Commit + + commit + -- Commit the current response + deferred + ensure + status_is_set: status_is_set + header_committed: header_committed + message_committed: message_committed + end + + feature -- Status report + + header_committed: BOOLEAN + -- Header committed? + deferred + end + + message_committed: BOOLEAN + -- Message committed? + deferred + end + + message_writable: BOOLEAN + -- Can message be written? + deferred + end + + feature {NONE} -- Core output operation + + write (s: STRING_8) + -- Send the string `s' + -- this can be used for header and body + deferred + end + + feature -- Status setting + + status_is_set: BOOLEAN + -- Is status set? + deferred + end + + set_status_code (a_code: INTEGER_32) + -- Set response status code + -- Should be done before sending any data back to the client + require + status_not_set: not status_is_set + header_not_committed: not header_committed + deferred + ensure + status_code_set: status_code = a_code + status_set: status_is_set + end + + status_code: INTEGER_32 + -- Response status + deferred + end + + feature -- Output operation + + flush + -- Flush if it makes sense + deferred + end + + write_string (s: STRING_8) + -- Send the string `s' + require + message_writable: message_writable + deferred + end + + write_substring (s: STRING_8; a_begin_index, a_end_index: INTEGER_32) + -- Send the substring `s[a_begin_index:a_end_index]' + require + message_writable: message_writable + deferred + end + + write_file_content (fn: STRING_8) + -- Send the content of file `fn' + require + message_writable: message_writable + deferred + end + + feature -- Header output operation + + write_header (a_status_code: INTEGER_32; a_headers: detachable ARRAY [TUPLE [key: STRING_8; value: STRING_8]]) + -- Send headers with status `a_status', and headers from `a_headers' + require + status_not_set: not status_is_set + header_not_committed: not header_committed + deferred + ensure + header_committed: header_committed + status_set: status_is_set + end + + end + + + ## Proof-of-concept and reference implementation # Specification overview From 4ab99dcd4a76287846b74c9e5bfa05d3a8918cf5 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 02:52:31 -0700 Subject: [PATCH 46/76] Created EWSGI specification: difference in main proposals (markdown) --- ...ification:-difference-in-main-proposals.md | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 EWSGI-specification:-difference-in-main-proposals.md diff --git a/EWSGI-specification:-difference-in-main-proposals.md b/EWSGI-specification:-difference-in-main-proposals.md new file mode 100644 index 00000000..a2c2f092 --- /dev/null +++ b/EWSGI-specification:-difference-in-main-proposals.md @@ -0,0 +1,99 @@ +Currently the **design of the EWSGI** is not going very fast, mainly due to conflicts for the core design. + +Let's try to summary today's **points of conflict** between Paul's proposal, and Jocelyn's proposal. +Since Paul put the specification on the wiki from Seibo Solution Studio, +and Jocelyn is implementing the proposal withing the current Eiffel Web Framework. +(If other proposals are provided, we'll compare them to those 2 existing proposals.) + +Let's name the **Seibo-EWSGI** proposal and the **EWF-EWSGI** proposal. + +Of course, **the goal is to have only one specification**, but it is good to have more than one proposal. So that we really try to specify the best EWSGI as possible. + +Let's remind that EWSGI is meant to be a specification, and we are looking at **Specification-Compliance** for the future implementation (so this is mainly about class names, and feature signatures. I.e the Eiffel system interface). + +_Note_: to make the code shorter, we will not always include the prefix EWSGI_ to the class name. +_Note2_: we will use the term of "user" for the developer using the EWSGI specification and/or implementation(s) + +## General goal ## +At first, the main difference between Seibo-EWSGI and EWF-EWSGI is a tiny nuance on the general goal. +_Seibo-EWSGI_: get web application portability across a variety of web servers. +_EWF-EWSGI_: get web application portability across a variety of web servers including connectors. + +Both are following the CGI specification. + +To resume, the goal is to get an Eiffel Web ecosystem based on EWSGI specification which allow to write components, libraries, applications based on EWSGI, and which can be compiled and used without changes on the various EWSGI implementations. +EWF-EWSGI is also targetting to make the connector implementations portable on the various EWSGI implementation. However this is not the most critical point, and could be address in later specification version, or maybe a specific EWSGI/Connector specification ... + +**Conclusion**: the general goal is (merely) the same for both proposals. +That is a good point, otherwise no need to compare the 2 proposals + +## Main entry point for a Web application component ## +This is the first important difference in the class EWSGI_APPLICATION + +Seibo-EWSGI: + + response (request: EWSGI_REQUEST): EWSGI_RESPONSE is + -- The response to the given 'request'. + deferred + ensure + Result.status_is_set + Result.ready_to_transmit + end + +EWF-EWSGI: + + execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) + -- Execute the request + -- See `req.input' for input stream + -- `req.environment' for the Gateway environment + -- and `res' for the output buffer + require + res_status_unset: not res.status_is_set + deferred + ensure + res_status_set: res.status_is_set + end + +So the main difference is for the user/developer +Seibo-EWSGI: the user has to create the RESPONSE object. +EWF-EWSGI: the user has the RESPONSE_BUFFER object passed as argument, and it is ready to use. + +The consequences are important because +* Seibo-EWSGI: + - to make the creation of RESPONSE simple, the decision was to remove any notion of output stream in EWSGI. + - Then the RESPONSE has to be built before sending it. But the proposal also provides a way to get the response message by block during the transmission. A kind of delay RESPONSE filling which is for now using a read_block pattern. And this mimics the send immediately the message parts to the client. + - This design allows the developer to use its own descendant of RESPONSE and then implement the "read_block" pattern when needed. + - This also allows to control carefully that the status+headers are sent before the body. And if not using the read_block pattern, this allows to set/change the headers even after setting the message body, in fact you really build the Response as an object, and you set the various attributes whenever you want. + - The read_block pattern is meant to address the case where the message would be too big to stay in memory, and/or to send some part immediately to the client, but for that you must stick to the read_block pattern. + - Seibo team find the Response-as-Result design more natural, and adopt the Request/Response model of HTTP protocol + +* EWF-EWSGI: + - the RESPONSE_BUFFER is passed by argument to the user, then he does not have to worry about how to create it. + - it is a buffer and could be implemented in various way by the implementation(s) to access the output stream if any + - For now, there is no easy way for the user, from the proposed specification, to have a customized RESPONSE_BUFFER + - It is easy to implement the Seibo-EWSGI design on top of EWF-EWSGI (the code is provided in EWF-ewsgi implementation) + - the buffer specification allows the user to send the message parts immediately to the client without complicated pattern. As today implementation, the only restriction imposed by the design is to pass the Status code, and the headers before starting sending the message parts; this is checked by the associated assertions. However this part might be thinked again, to be more flexible, and let this to the responsibility of the user, or of other frameworks built on top of EWSGI spec. + +## Notion of output stream ## +As you might noticed +In EWF-EWSGI, the output stream is kind of hidden and replaced by the output buffer, which is in fact quite similar. However the goal is to allow the user to send immediately the message parts to the client as simple as writing in the buffer. +The EWSGI implementation will handle this buffer to integrate nicely with the underlying web server techno. This will be done through the implementation of the various connectors. + +In Seibo-EWSGI there is NO notion of output stream, this is to handle the potential case of a HTTP server technologies following CGI specification but without any output stream. +The read_block pattern allows the user to send a big response part by part, and/or also send some message parts immediately to the client during the transmission, this looks like a delayed computation of the messages. + +--- +So for now, those are the main critical (blocking) differences I (Jocelyn) can see. + +## Personal point of views ## +(Feel free to add your own point of view) + +### Jocelyn ### +* I prefer the EWF-EWSGI solution (obviously this is my proposal) mainly because with it, you can also implement the Seibo-EWSGI design. And the "more you can do, the less you can also do". +* I find the read_block pattern really not natural to use in non trivial application (even in trivial application, this might not be that easy to handle, and thus a potential source of bugs) +* I admit the need to send immediately to the client might not be the vast majority of cases. But this is useful for big messages, and for long computing message where the client wants to follow the progression (concrete cases: drupal batch, wordpress online update, ...) +* I would prefer to include the connector part into the EWSGI specification, this way any EWSGI_connector implementation could work with any EWSGI implementation. Otherwise we might end up with many different EWSGI implementations, and according to the underlying connector(s) you want to use, you will need to use different EWSGI implementations. + +### Others ... ? ### +Please contribute ... correct also the wiki page if there are mistake or misunderstanding. + From 8a3b5a77e36910e84ced2a5cac332f5c9ea9484a Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 02:57:33 -0700 Subject: [PATCH 47/76] Updated EWSGI specification: difference in main proposals (markdown) --- EWSGI-specification:-difference-in-main-proposals.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/EWSGI-specification:-difference-in-main-proposals.md b/EWSGI-specification:-difference-in-main-proposals.md index a2c2f092..608c79bf 100644 --- a/EWSGI-specification:-difference-in-main-proposals.md +++ b/EWSGI-specification:-difference-in-main-proposals.md @@ -59,7 +59,9 @@ Seibo-EWSGI: the user has to create the RESPONSE object. EWF-EWSGI: the user has the RESPONSE_BUFFER object passed as argument, and it is ready to use. The consequences are important because + * Seibo-EWSGI: + - to make the creation of RESPONSE simple, the decision was to remove any notion of output stream in EWSGI. - Then the RESPONSE has to be built before sending it. But the proposal also provides a way to get the response message by block during the transmission. A kind of delay RESPONSE filling which is for now using a read_block pattern. And this mimics the send immediately the message parts to the client. - This design allows the developer to use its own descendant of RESPONSE and then implement the "read_block" pattern when needed. @@ -68,6 +70,7 @@ The consequences are important because - Seibo team find the Response-as-Result design more natural, and adopt the Request/Response model of HTTP protocol * EWF-EWSGI: + - the RESPONSE_BUFFER is passed by argument to the user, then he does not have to worry about how to create it. - it is a buffer and could be implemented in various way by the implementation(s) to access the output stream if any - For now, there is no easy way for the user, from the proposed specification, to have a customized RESPONSE_BUFFER @@ -75,6 +78,7 @@ The consequences are important because - the buffer specification allows the user to send the message parts immediately to the client without complicated pattern. As today implementation, the only restriction imposed by the design is to pass the Status code, and the headers before starting sending the message parts; this is checked by the associated assertions. However this part might be thinked again, to be more flexible, and let this to the responsibility of the user, or of other frameworks built on top of EWSGI spec. ## Notion of output stream ## + As you might noticed In EWF-EWSGI, the output stream is kind of hidden and replaced by the output buffer, which is in fact quite similar. However the goal is to allow the user to send immediately the message parts to the client as simple as writing in the buffer. The EWSGI implementation will handle this buffer to integrate nicely with the underlying web server techno. This will be done through the implementation of the various connectors. @@ -86,9 +90,11 @@ The read_block pattern allows the user to send a big response part by part, and/ So for now, those are the main critical (blocking) differences I (Jocelyn) can see. ## Personal point of views ## + (Feel free to add your own point of view) ### Jocelyn ### + * I prefer the EWF-EWSGI solution (obviously this is my proposal) mainly because with it, you can also implement the Seibo-EWSGI design. And the "more you can do, the less you can also do". * I find the read_block pattern really not natural to use in non trivial application (even in trivial application, this might not be that easy to handle, and thus a potential source of bugs) * I admit the need to send immediately to the client might not be the vast majority of cases. But this is useful for big messages, and for long computing message where the client wants to follow the progression (concrete cases: drupal batch, wordpress online update, ...) From 45db7eb53752cdd401b39971079d83d914db9018 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:30:35 -0700 Subject: [PATCH 48/76] Created EWSGI: open questions (markdown) --- EWSGI:-open-questions.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 EWSGI:-open-questions.md diff --git a/EWSGI:-open-questions.md b/EWSGI:-open-questions.md new file mode 100644 index 00000000..a494a915 --- /dev/null +++ b/EWSGI:-open-questions.md @@ -0,0 +1,24 @@ +## STRING_32, UTF-8, ... ? ## +Berend raised the point that using STRING_32 is consuming 4 times the space used for STRING_8. +And CPU is cheaper than memory, so we should try to use as less memory as possible. +And then for Berend, STRING_32 is not the solution. + +Most of the data are just STRING_8 in CGI +so let's list the various request data + +- parameters (from the query string ?foo=bar&extra=blabla ) + in this case, I think the name can be url-encoded, and obviously the value too + I guess it makes sense to url-decode them + but on the other hand, we could just keep them url-encoded (as they are), and it is up to the application to url-decode them if needed. + Of course, we should provide facilities to url-decode those strings. + +- form_fields (from the POST method) + quite often, it is same kind of content that `parameters' + but .. here this might depends on the encoding for multi-parts encoding. + +- input data ... + I think this is up to the application + +note: that form fields sent by GET method, will be in `parameters' ... so maybe we should rename the "form_fields" as "post_parameters". Anyway not critical for now + +... to be continued ... From 637909614b5d4726a5069ba94b85ca26ebbe0bf1 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:31:08 -0700 Subject: [PATCH 49/76] Updated EWSGI: open questions (markdown) --- EWSGI:-open-questions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EWSGI:-open-questions.md b/EWSGI:-open-questions.md index a494a915..eeb2d2ac 100644 --- a/EWSGI:-open-questions.md +++ b/EWSGI:-open-questions.md @@ -6,17 +6,17 @@ And then for Berend, STRING_32 is not the solution. Most of the data are just STRING_8 in CGI so let's list the various request data -- parameters (from the query string ?foo=bar&extra=blabla ) +- **parameters** (from the query string ?foo=bar&extra=blabla ) in this case, I think the name can be url-encoded, and obviously the value too I guess it makes sense to url-decode them but on the other hand, we could just keep them url-encoded (as they are), and it is up to the application to url-decode them if needed. Of course, we should provide facilities to url-decode those strings. -- form_fields (from the POST method) +- **form_fields** (from the POST method) quite often, it is same kind of content that `parameters' but .. here this might depends on the encoding for multi-parts encoding. -- input data ... +- **input data** ... I think this is up to the application note: that form fields sent by GET method, will be in `parameters' ... so maybe we should rename the "form_fields" as "post_parameters". Anyway not critical for now From 75dc9fac3e1a8717e770c7adbe9a2ae3723981b4 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:32:31 -0700 Subject: [PATCH 50/76] Updated Source structure (markdown) --- Source-structure.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source-structure.md b/Source-structure.md index b49988ff..477cd4b1 100644 --- a/Source-structure.md +++ b/Source-structure.md @@ -7,8 +7,8 @@ - examples/ - tests/ - library/ -- library/ewsgi/ewsgi.ecf -- library/ewsgi/ewsgi.ecf +- library/server/ewsgi/ewsgi.ecf +- library/server/ewsgi/ewsgi.ecf Any library/component (in development mode) should follow the following structure (when not needed, there is no need to create the associated folder(s) ): @@ -18,7 +18,7 @@ Any library/component (in development mode) should follow the following structur - **.ecf** and **-safe.ecf** : configuration file - **library/** : the place to put the source code of the related library/component - **doc/** : notes, documentations, ... -- **test/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ ) +- **tests/** : standard place to put your tests, if you are using Eiffel Software auto-tests, I would suggest to add a new target into the associated .ecf (instead of building a new .ecf under test/ ) - **examples/** : standard place to put the example - **build/** : a convenient place to compile your project, using this convention, you can setup utilities such as backup to ignore this folder. - **resources/** : contains pixmap files, .... From 077c140576ba9647aa2520e92dc6a4438273da20 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:33:41 -0700 Subject: [PATCH 51/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index db832b6a..835e82bb 100644 --- a/Home.md +++ b/Home.md @@ -9,6 +9,6 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] - [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - [[General source structure of this project| Source-structure]] - - [[EWSGI specification| EWSGI-specification]] + - [[Eiffel Web Server Gateway Interface| EWSGI]] - [[Overview of the server side architecture| Spec-Server-Architecture]] - This project is also a collection of [[Libraries]] related to the Web \ No newline at end of file From afb3890f5073e84d6b03e2ec13db4849752ba68d Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:34:52 -0700 Subject: [PATCH 52/76] Updated EWSGI: open questions (markdown) --- EWSGI:-open-questions.md => EWSGI-:-open-questions.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EWSGI:-open-questions.md => EWSGI-:-open-questions.md (100%) diff --git a/EWSGI:-open-questions.md b/EWSGI-:-open-questions.md similarity index 100% rename from EWSGI:-open-questions.md rename to EWSGI-:-open-questions.md From 59d4eb528c9ffc6de06e7de8302ecb8d3dead4cf Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:35:28 -0700 Subject: [PATCH 53/76] Created EWSGI (markdown) --- EWSGI.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 EWSGI.md diff --git a/EWSGI.md b/EWSGI.md new file mode 100644 index 00000000..c927f988 --- /dev/null +++ b/EWSGI.md @@ -0,0 +1,3 @@ + +- See proposed specifications: [[EWSGI specification| EWSGI-specification]] +- See [[EWSGI-:-open-questions| Open questions]] From e2fb4998536ebc834467266f32936c1b30bba4e4 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:35:47 -0700 Subject: [PATCH 54/76] Updated EWSGI (markdown) --- EWSGI.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index c927f988..e986d5b6 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -1,3 +1,2 @@ - - See proposed specifications: [[EWSGI specification| EWSGI-specification]] -- See [[EWSGI-:-open-questions| Open questions]] +- See [[Open questions| EWSGI-:-open-questions]] From ed587493e914ab0718d718b8be396ca5a22e8887 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:39:34 -0700 Subject: [PATCH 55/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 835e82bb..050901ce 100644 --- a/Home.md +++ b/Home.md @@ -9,6 +9,6 @@ The official documentation/wiki is located at https://github.com/Eiffel-World/Ei - You want to contribute or follow the progress/discussion, see the [[collaboration page| Community-collaboration]] - [[list of tasks, and a potential roadmap| Tasks-Roadmap]] - [[General source structure of this project| Source-structure]] - - [[Eiffel Web Server Gateway Interface| EWSGI]] + - EWSGI: [[Eiffel Web Server Gateway Interface| EWSGI]] - [[Overview of the server side architecture| Spec-Server-Architecture]] - This project is also a collection of [[Libraries]] related to the Web \ No newline at end of file From c337f49c87c255503ef1b223c908a065d4ff9f2f Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:39:45 -0700 Subject: [PATCH 56/76] Updated EWSGI (markdown) --- EWSGI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EWSGI.md b/EWSGI.md index e986d5b6..81db53bb 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -1,2 +1,2 @@ - See proposed specifications: [[EWSGI specification| EWSGI-specification]] -- See [[Open questions| EWSGI-:-open-questions]] +- See [[Open questions| EWSGI-open-questions]] From d14e65fdc09686fd15b37f6ce23eb7c8e92fb6bf Mon Sep 17 00:00:00 2001 From: jocelyn Date: Tue, 2 Aug 2011 07:40:10 -0700 Subject: [PATCH 57/76] Updated EWSGI : open questions (markdown) --- EWSGI-:-open-questions.md => EWSGI-Open-Questions.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename EWSGI-:-open-questions.md => EWSGI-Open-Questions.md (100%) diff --git a/EWSGI-:-open-questions.md b/EWSGI-Open-Questions.md similarity index 100% rename from EWSGI-:-open-questions.md rename to EWSGI-Open-Questions.md From 060f1482fe4d9bcb82b3207bd27b47fcff5a3073 Mon Sep 17 00:00:00 2001 From: Jocelyn Fiat Date: Tue, 30 Aug 2011 19:16:40 +0200 Subject: [PATCH 58/76] Changed prefix class name from EWSGI_ to WGI_ changes in interface for REQUEST and RESPONSE --- EWSGI-specification.md | 169 ++++++++++++++++++++--------------------- Libraries.md | 4 +- 2 files changed, 85 insertions(+), 88 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index e771ef4c..07d64fa1 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -14,14 +14,14 @@ Note the following is work in progress, and reflect a specification proposal, ra For now, the specification from EWF is done in Eiffel interface please see: https://github.com/Eiffel-World/Eiffel-Web-Framework/tree/master/library/server/ewsgi/specification -EWSGI_APPLICATION +WGI_APPLICATION deferred class - EWSGI_APPLICATION + WGI_APPLICATION feature {NONE} -- Execution - execute (req: EWSGI_REQUEST; res: EWSGI_RESPONSE_BUFFER) + execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) -- Execute the request -- See `req.input' for input stream -- `req.environment' for the Gateway environment @@ -36,114 +36,100 @@ EWSGI_APPLICATION end -EWSGI_REQUEST +WGI_REQUEST deferred class - EWSGI_REQUEST + WGI_REQUEST feature -- Access: Input - input: EWSGI_INPUT_STREAM + input: WGI_INPUT_STREAM -- Server input channel deferred end - feature -- Access: extra values + feature -- Access: extra values request_time: detachable DATE_TIME -- Request time (UTC) deferred end - feature -- Access: environment variables + feature -- Access: CGI meta variables - environment: EWSGI_ENVIRONMENT - -- Environment variables - deferred - end - - environment_variable (a_name: STRING_8): detachable STRING_8 + meta_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 -- Environment variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - - feature -- Access: execution variables - execution_variables: EWSGI_VARIABLES [STRING_32] - -- Execution variables set by the application + meta_variables: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] deferred end - execution_variable (a_name: STRING_8): detachable STRING_32 - -- Execution variable related to `a_name' - require - a_name_valid: a_name /= Void and then not a_name.is_empty - deferred - end - - feature -- URL Parameters + feature -- Query string Parameters - parameters: EWSGI_VARIABLES [STRING_32] + query_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] -- Variables extracted from QUERY_STRING deferred end - parameter (a_name: STRING_8): detachable STRING_32 + query_parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 -- Parameter for name `n'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - + feature -- Form fields and related - form_fields: EWSGI_VARIABLES [STRING_32] + form_data_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] -- Variables sent by POST request deferred end - form_field (a_name: STRING_8): detachable STRING_32 + form_data_parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 -- Field for name `a_name'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - uploaded_files: HASH_TABLE [EWSGI_UPLOADED_FILE_DATA, STRING_8] + uploaded_files: HASH_TABLE [WGI_UPLOADED_FILE_DATA, READABLE_STRING_GENERAL] -- Table of uploaded files information + --| name: original path from the user + --| type: content type + --| tmp_name: path to temp file that resides on server + --| tmp_base_name: basename of `tmp_name' + --| error: if /= 0 , there was an error : TODO ... + --| size: size of the file given by the http request deferred end - + feature -- Cookies - cookies_variables: HASH_TABLE [STRING_8, STRING_8] + cookies: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] -- Expanded cookies variable deferred end - cookies_variable (a_name: STRING_8): detachable STRING_8 + cookie (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 -- Field for name `a_name'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - cookies: HASH_TABLE [EWSGI_COOKIE, STRING_8] - -- Cookies Information - deferred - end - feature -- Access: global variable - variables: HASH_TABLE [STRING_32, STRING_32] + parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] -- Table containing all the various variables -- Warning: this is computed each time, if you change the content of other containers -- this won't update this Result's content, unless you query it again deferred end - variable (a_name: STRING_8): detachable STRING_32 + parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 -- Variable named `a_name' from any of the variables container -- and following a specific order -- execution, environment, get, post, cookies @@ -151,7 +137,7 @@ EWSGI_REQUEST a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - + feature -- Uploaded File Handling is_uploaded_file (a_filename: STRING_8): BOOLEAN @@ -175,12 +161,12 @@ EWSGI_REQUEST end -EWSGI_RESPONSE_BUFFER +WGI_RESPONSE_BUFFER - deferred class - EWSGI_RESPONSE_BUFFER + deferred class + WGI_RESPONSE_BUFFER - feature {EWSGI_APPLICATION} -- Commit + feature {WGI_APPLICATION} -- Commit commit -- Commit the current response @@ -190,7 +176,7 @@ EWSGI_RESPONSE_BUFFER header_committed: header_committed message_committed: message_committed end - + feature -- Status report header_committed: BOOLEAN @@ -207,15 +193,15 @@ EWSGI_RESPONSE_BUFFER -- Can message be written? deferred end - - feature {NONE} -- Core output operation - write (s: STRING_8) + feature {WGI_RESPONSE_BUFFER} -- Core output operation + + write (s: STRING) -- Send the string `s' -- this can be used for header and body deferred end - + feature -- Status setting status_is_set: BOOLEAN @@ -223,7 +209,7 @@ EWSGI_RESPONSE_BUFFER deferred end - set_status_code (a_code: INTEGER_32) + set_status_code (a_code: INTEGER) -- Set response status code -- Should be done before sending any data back to the client require @@ -235,42 +221,25 @@ EWSGI_RESPONSE_BUFFER status_set: status_is_set end - status_code: INTEGER_32 + status_code: INTEGER -- Response status deferred end - - feature -- Output operation - flush - -- Flush if it makes sense - deferred - end - - write_string (s: STRING_8) - -- Send the string `s' - require - message_writable: message_writable - deferred - end - - write_substring (s: STRING_8; a_begin_index, a_end_index: INTEGER_32) - -- Send the substring `s[a_begin_index:a_end_index]' - require - message_writable: message_writable - deferred - end - - write_file_content (fn: STRING_8) - -- Send the content of file `fn' - require - message_writable: message_writable - deferred - end - feature -- Header output operation - write_header (a_status_code: INTEGER_32; a_headers: detachable ARRAY [TUPLE [key: STRING_8; value: STRING_8]]) + write_headers_string (a_headers: STRING) + require + status_set: status_is_set + header_not_committed: not header_committed + deferred + ensure + status_set: status_is_set + header_committed: header_committed + message_writable: message_writable + end + + write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) -- Send headers with status `a_status', and headers from `a_headers' require status_not_set: not status_is_set @@ -279,12 +248,40 @@ EWSGI_RESPONSE_BUFFER ensure header_committed: header_committed status_set: status_is_set + message_writable: message_writable end - + + feature -- Output operation + + write_string (s: STRING) + -- Send the string `s' + require + message_writable: message_writable + deferred + end + + write_substring (s: STRING; a_begin_index, a_end_index: INTEGER) + -- Send the substring `s[a_begin_index:a_end_index]' + require + message_writable: message_writable + deferred + end + + write_file_content (fn: STRING) + -- Send the content of file `fn' + require + message_writable: message_writable + deferred + end + + flush + -- Flush if it makes sense + deferred + end + end - ## Proof-of-concept and reference implementation # Specification overview @@ -303,4 +300,4 @@ EWSGI_RESPONSE_BUFFER ## Acknowledgements -## References \ No newline at end of file +## References diff --git a/Libraries.md b/Libraries.md index 6b72e4d0..d456efcd 100644 --- a/Libraries.md +++ b/Libraries.md @@ -1,5 +1,5 @@ ## libraries currently part of the Eiffel Web Framework ## -* [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix GW_ for **G**ate**W**ay) +* [[Library-EWSGI]]: Eiffel Web Server Gateway Interface (prefix WGI_ for **W**eb**G**ateway**I**nterface) * [[Library-libFCGI]]: Eiffel wrapper of libfcgi SDK (http://www.fastcgi.com/devkit/libfcgi/) * [[Library-encoder]]: simple encoder for base64, url-encode, xml entities * [[Library-error]]: simple framework to handle error in EWSGI and related @@ -12,4 +12,4 @@ ## libraries that could be included, or or used ## * eMIME: MIME parser, and encoder ... -* ... \ No newline at end of file +* ... From d0576c6829c8284ec9bd4cfc4c7a41c029bba4b9 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:03:33 -0700 Subject: [PATCH 59/76] Updated EWSGI Open Questions (markdown) --- EWSGI-Open-Questions.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/EWSGI-Open-Questions.md b/EWSGI-Open-Questions.md index eeb2d2ac..d6958f94 100644 --- a/EWSGI-Open-Questions.md +++ b/EWSGI-Open-Questions.md @@ -6,19 +6,20 @@ And then for Berend, STRING_32 is not the solution. Most of the data are just STRING_8 in CGI so let's list the various request data -- **parameters** (from the query string ?foo=bar&extra=blabla ) +- **query_parameter** (from the query string ?foo=bar&extra=blabla ) in this case, I think the name can be url-encoded, and obviously the value too I guess it makes sense to url-decode them but on the other hand, we could just keep them url-encoded (as they are), and it is up to the application to url-decode them if needed. Of course, we should provide facilities to url-decode those strings. -- **form_fields** (from the POST method) +- **form_data_parameter** (from the POST method) quite often, it is same kind of content that `parameters' but .. here this might depends on the encoding for multi-parts encoding. +- **meta_variable** (from the request itself ... CGI meta variables..) + I am wondering about unicode domain name ... + - **input data** ... I think this is up to the application -note: that form fields sent by GET method, will be in `parameters' ... so maybe we should rename the "form_fields" as "post_parameters". Anyway not critical for now - ... to be continued ... From 4744bb15c3610ecc0ec1c2dc53ff7cb3ca58af74 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:15:07 -0700 Subject: [PATCH 60/76] Updated EWSGI (markdown) --- EWSGI.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/EWSGI.md b/EWSGI.md index 81db53bb..95dc00d7 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -1,2 +1,16 @@ - See proposed specifications: [[EWSGI specification| EWSGI-specification]] - See [[Open questions| EWSGI-open-questions]] +- And below the various calls for decisions, and effective decisions + + +### return type of parameter (and similar) should be deferred WGI_VALUE +* Description: Instead of returning READABLE_STRING_32 , it would be better to use **WGI_VALUE** . + This allows to have various types such as WGI_STRING_VALUE, WGI_LIST_VALUE, WGI_TABLE_VALUE, WGI_FILE_VALUE . + +* Status: proposed on 2011-09-05. +* **WAITING FOR APPROVAL** + +### change prefix from EWSGI_ to WGI_ +* Description: shorter and pronouncable prefix +* Status: **adopted** +* Decision: **WGI_** From 597f4bc6bdc259ae973fce0c3f1d0bd08c87630a Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:54:29 -0700 Subject: [PATCH 61/76] Updated EWSGI (markdown) --- EWSGI.md | 56 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index 95dc00d7..ae0d35da 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -3,14 +3,52 @@ - And below the various calls for decisions, and effective decisions -### return type of parameter (and similar) should be deferred WGI_VALUE -* Description: Instead of returning READABLE_STRING_32 , it would be better to use **WGI_VALUE** . - This allows to have various types such as WGI_STRING_VALUE, WGI_LIST_VALUE, WGI_TABLE_VALUE, WGI_FILE_VALUE . +# In progress -* Status: proposed on 2011-09-05. -* **WAITING FOR APPROVAL** +### Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE +- Code: **P-2011-09-05-WGI_VALUE** +- Status: proposed on 2011-09-05. +- **WAITING FOR APPROVAL** -### change prefix from EWSGI_ to WGI_ -* Description: shorter and pronouncable prefix -* Status: **adopted** -* Decision: **WGI_** +> Instead of returning just `READABLE_STRING_32` , it would be better to use **WGI_VALUE** . +> Mainly to address the multiple value for the same param name, but also for uploaded files. +> This allows to have various types such as WGI_STRING_VALUE, WGI_LIST_VALUE, WGI_TABLE_VALUE, WGI_FILE_VALUE . +> +> Thus we would have: parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE + +### Include the parameter's name in WGI_VALUE interface +- Code: **P-2011-09-05-WGI_VALUE_interface** +- Dependence: adoption of P-2011-09-05-WGI_VALUE , may impact on P-2011-09-05-parameters_ITERABLE +- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** + +> include the corresponding parameter's name in WGI_VALUE interface. +> Such as `{WGI_VALUE}.name: READABLE_STRING_GENERAL` (or READABLE_STRING_32). +> +> This would also allow a nicer signature for `parameters: ITERABLE [WGI__VALUE]' +> instead of `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI__VALUE]]' + +### Signature of parameters (and similar) using ITERABLE [...] +- Code: **P-2011-09-05-parameters_ITERABLE** +- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** + +> Description: Instead of forcing the implementation to use HASH_TABLE, DS_HASH_TABLE, DS_HASH_SET, ... or similar +> we should use `ITERABLE` +> +> `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI_VALUE]]` +> +> Or, if `P-2011-09-05-WGI_VALUE_interface` is adopted (WGI_VALUE.name holds the related parameter's name) +> +> `parameters: ITERABLE [WGI_VALUE]` + +# Adopted entries + +### Change prefix from EWSGI_ to WGI_ +- Code: **P-2011-08-29-WGI_prefix** +- Status: **adopted** +- Decision: **WGI_** + +> shorter and pronouncable prefix for EWSGI class names + +# Rejected entries + +... From d0bb906a96e4b3ea4508dd06676e4c8621182865 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:56:42 -0700 Subject: [PATCH 62/76] Updated EWSGI (markdown) --- EWSGI.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index ae0d35da..c5d0ad12 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -1,11 +1,11 @@ - See proposed specifications: [[EWSGI specification| EWSGI-specification]] - See [[Open questions| EWSGI-open-questions]] -- And below the various calls for decisions, and effective decisions +- And below the various proposals and associated decision +---- +# Waiting for decision -# In progress - -### Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE +## Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE - Code: **P-2011-09-05-WGI_VALUE** - Status: proposed on 2011-09-05. - **WAITING FOR APPROVAL** @@ -16,7 +16,7 @@ > > Thus we would have: parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -### Include the parameter's name in WGI_VALUE interface +## Include the parameter's name in WGI_VALUE interface - Code: **P-2011-09-05-WGI_VALUE_interface** - Dependence: adoption of P-2011-09-05-WGI_VALUE , may impact on P-2011-09-05-parameters_ITERABLE - Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** @@ -27,7 +27,7 @@ > This would also allow a nicer signature for `parameters: ITERABLE [WGI__VALUE]' > instead of `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI__VALUE]]' -### Signature of parameters (and similar) using ITERABLE [...] +## Signature of parameters (and similar) using ITERABLE [...] - Code: **P-2011-09-05-parameters_ITERABLE** - Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** @@ -40,15 +40,17 @@ > > `parameters: ITERABLE [WGI_VALUE]` +---- # Adopted entries -### Change prefix from EWSGI_ to WGI_ +## Change prefix from EWSGI_ to WGI_ - Code: **P-2011-08-29-WGI_prefix** - Status: **adopted** - Decision: **WGI_** > shorter and pronouncable prefix for EWSGI class names +---- # Rejected entries ... From 28d03ddf2ae774cbea7b93eea77569347789dd35 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:58:15 -0700 Subject: [PATCH 63/76] Updated EWSGI (markdown) --- EWSGI.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index c5d0ad12..a8d76ae5 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -24,8 +24,9 @@ > include the corresponding parameter's name in WGI_VALUE interface. > Such as `{WGI_VALUE}.name: READABLE_STRING_GENERAL` (or READABLE_STRING_32). > -> This would also allow a nicer signature for `parameters: ITERABLE [WGI__VALUE]' -> instead of `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI__VALUE]]' +> This would also allow to replace +> signature `parameters: ITERABLE [TUPLE [name: READABLE_STRING_GENERAL; value: WGI__VALUE]]' +> by a nicer signature `parameters: ITERABLE [WGI__VALUE]` ## Signature of parameters (and similar) using ITERABLE [...] - Code: **P-2011-09-05-parameters_ITERABLE** From 4a7b6ecc9451091de38030a820b310567de48c6f Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 5 Sep 2011 06:59:49 -0700 Subject: [PATCH 64/76] Updated EWSGI (markdown) --- EWSGI.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index a8d76ae5..ac8c5fb6 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -7,8 +7,7 @@ ## Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE - Code: **P-2011-09-05-WGI_VALUE** -- Status: proposed on 2011-09-05. -- **WAITING FOR APPROVAL** +- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** > Instead of returning just `READABLE_STRING_32` , it would be better to use **WGI_VALUE** . > Mainly to address the multiple value for the same param name, but also for uploaded files. From 820bd7bd6fab591691ff5c0560452c2783c8549e Mon Sep 17 00:00:00 2001 From: jocelyn Date: Wed, 7 Sep 2011 05:30:29 -0700 Subject: [PATCH 65/76] Updated EWSGI (markdown) --- EWSGI.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/EWSGI.md b/EWSGI.md index ac8c5fb6..e33c0cdc 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -5,6 +5,14 @@ ---- # Waiting for decision +## Rename `parameter' into `item' +- Code: **P-2011-09-07-renaming_REQUEST_item** +- Status: proposed on 2011-09-07 **WAITING FOR APPROVAL** + +> rename `{REQUEST}.parameter (n: READABLE_STRING_GENERAL): detachable WGI_VALUE` +> into `{REQUEST}.item (n: READABLE_STRING_GENERAL): detachable WGI_VALUE` +> and similar for `parameters` -> `items` + ## Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE - Code: **P-2011-09-05-WGI_VALUE** - Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** From 2407cb6a63a8eced8baa53aaf3327a1ceae2cd8f Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 26 Sep 2011 07:50:17 -0700 Subject: [PATCH 66/76] Updated EWSGI (markdown) --- EWSGI.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/EWSGI.md b/EWSGI.md index e33c0cdc..d27e78e4 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -5,7 +5,14 @@ ---- # Waiting for decision -## Rename `parameter' into `item' +## Include EWF_HEADER into EWSGI as WGI_HEADER +- Code: **P-2011-09-26-include-wgi-header** +- Status: proposed on 2011-09-26 **WAITING FOR APPROVAL** + +> Include WGI_HEADER to help the user to build HTTP Header. +> So that he doesn't have to know the HTTP specification for usual needs + +## Rename `parameter` into `item` - Code: **P-2011-09-07-renaming_REQUEST_item** - Status: proposed on 2011-09-07 **WAITING FOR APPROVAL** From beb721ed4bdb891a18aa7a1472375f419fb40fe7 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 26 Sep 2011 07:51:08 -0700 Subject: [PATCH 67/76] Updated EWSGI (markdown) --- EWSGI.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index d27e78e4..18cf1419 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -5,11 +5,11 @@ ---- # Waiting for decision -## Include EWF_HEADER into EWSGI as WGI_HEADER -- Code: **P-2011-09-26-include-wgi-header** +## Include EWF_HEADER into EWSGI as WGI_HEADERS +- Code: **P-2011-09-26-include-wgi-headers** - Status: proposed on 2011-09-26 **WAITING FOR APPROVAL** -> Include WGI_HEADER to help the user to build HTTP Header. +> Include WGI_HEADERS to help the user to build HTTP Header. > So that he doesn't have to know the HTTP specification for usual needs ## Rename `parameter` into `item` From aaf8af85fd94a49f6b3417d269293aa0122c9908 Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 26 Sep 2011 07:53:32 -0700 Subject: [PATCH 68/76] Updated EWSGI (markdown) --- EWSGI.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/EWSGI.md b/EWSGI.md index 18cf1419..7258848c 100644 --- a/EWSGI.md +++ b/EWSGI.md @@ -12,9 +12,13 @@ > Include WGI_HEADERS to help the user to build HTTP Header. > So that he doesn't have to know the HTTP specification for usual needs + +---- +# Adopted entries + ## Rename `parameter` into `item` - Code: **P-2011-09-07-renaming_REQUEST_item** -- Status: proposed on 2011-09-07 **WAITING FOR APPROVAL** +- Status: proposed on 2011-09-07 **ADOPTED-by-default** > rename `{REQUEST}.parameter (n: READABLE_STRING_GENERAL): detachable WGI_VALUE` > into `{REQUEST}.item (n: READABLE_STRING_GENERAL): detachable WGI_VALUE` @@ -22,7 +26,7 @@ ## Return type of `parameter' (and similar query_, form_data_ ...) should be deferred WGI_VALUE - Code: **P-2011-09-05-WGI_VALUE** -- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** +- Status: proposed on 2011-09-05 **ADOPTED-by-default** > Instead of returning just `READABLE_STRING_32` , it would be better to use **WGI_VALUE** . > Mainly to address the multiple value for the same param name, but also for uploaded files. @@ -33,7 +37,7 @@ ## Include the parameter's name in WGI_VALUE interface - Code: **P-2011-09-05-WGI_VALUE_interface** - Dependence: adoption of P-2011-09-05-WGI_VALUE , may impact on P-2011-09-05-parameters_ITERABLE -- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** +- Status: proposed on 2011-09-05 **ADOPTED-by-default** > include the corresponding parameter's name in WGI_VALUE interface. > Such as `{WGI_VALUE}.name: READABLE_STRING_GENERAL` (or READABLE_STRING_32). @@ -44,7 +48,7 @@ ## Signature of parameters (and similar) using ITERABLE [...] - Code: **P-2011-09-05-parameters_ITERABLE** -- Status: proposed on 2011-09-05 **WAITING FOR APPROVAL** +- Status: proposed on 2011-09-05 **ADOPTED-by-default** > Description: Instead of forcing the implementation to use HASH_TABLE, DS_HASH_TABLE, DS_HASH_SET, ... or similar > we should use `ITERABLE` @@ -55,9 +59,6 @@ > > `parameters: ITERABLE [WGI_VALUE]` ----- -# Adopted entries - ## Change prefix from EWSGI_ to WGI_ - Code: **P-2011-08-29-WGI_prefix** - Status: **adopted** From 2b112da43de3cbd7e659e2768460998ebd260b7f Mon Sep 17 00:00:00 2001 From: jocelyn Date: Mon, 26 Sep 2011 08:18:15 -0700 Subject: [PATCH 69/76] Updated EWSGI specification (markdown) --- EWSGI-specification.md | 623 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 554 insertions(+), 69 deletions(-) diff --git a/EWSGI-specification.md b/EWSGI-specification.md index 07d64fa1..750c1874 100644 --- a/EWSGI-specification.md +++ b/EWSGI-specification.md @@ -24,15 +24,14 @@ WGI_APPLICATION execute (req: WGI_REQUEST; res: WGI_RESPONSE_BUFFER) -- Execute the request -- See `req.input' for input stream - -- `req.environment' for the Gateway environment + -- `req.meta_variables' for the CGI meta variable -- and `res' for the output buffer require res_status_unset: not res.status_is_set deferred ensure res_status_set: res.status_is_set - res_committed: res.message_committed - end + end end @@ -40,96 +39,573 @@ WGI_REQUEST deferred class WGI_REQUEST + feature -- Access: Input - + input: WGI_INPUT_STREAM -- Server input channel deferred end feature -- Access: extra values - + request_time: detachable DATE_TIME -- Request time (UTC) deferred end feature -- Access: CGI meta variables - - meta_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + + meta_variable (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -- Environment variable related to `a_name' require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - - meta_variables: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] + + meta_string_variable (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + -- Environment variable related to `a_name' + require + a_name_valid: a_name /= Void and then not a_name.is_empty + do + if attached meta_variable (a_name) as val then + Result := val.as_string + end + end + + meta_variables: ITERABLE [WGI_VALUE] + -- These variables are specific to requests made with HTTP. + -- Interpretation of these variables may depend on the value of + -- SERVER_PROTOCOL. + -- + -- Environment variables with names beginning with "HTTP_" contain + -- header data read from the client, if the protocol used was HTTP. + -- The HTTP header name is converted to upper case, has all + -- occurrences of "-" replaced with "_" and has "HTTP_" prepended to + -- give the environment variable name. The header data may be + -- presented as sent by the client, or may be rewritten in ways which + -- do not change its semantics. If multiple headers with the same + -- field-name are received then they must be rewritten as a single + -- header having the same semantics. Similarly, a header that is + -- received on more than one line must be merged onto a single line. + -- The server must, if necessary, change the representation of the + -- data (for example, the character set) to be appropriate for a CGI + -- environment variable. + -- + -- The server is not required to create environment variables for all + -- the headers that it receives. In particular, it may remove any + -- headers carrying authentication information, such as + -- "Authorization"; it may remove headers whose value is available to + -- the script via other variables, such as "Content-Length" and + -- "Content-Type". + -- + -- For convenience it might also include the following CGI entries deferred end - + + feature -- Common Gateway Interface - 1.1 8 January 1996 + + auth_type: detachable READABLE_STRING_32 + -- This variable is specific to requests made via the "http" + -- scheme. + -- + -- If the Script-URI required access authentication for external + -- access, then the server MUST set the value of this variable + -- from the 'auth-scheme' token in the request's "Authorization" + -- header field. Otherwise it is set to NULL. + -- + -- AUTH_TYPE = "" | auth-scheme + -- auth-scheme = "Basic" | "Digest" | token + -- + -- HTTP access authentication schemes are described in section 11 + -- of the HTTP/1.1 specification [8]. The auth-scheme is not + -- case-sensitive. + -- + -- Servers MUST provide this metavariable to scripts if the + -- request header included an "Authorization" field that was + -- authenticated. + deferred + end + + content_length: detachable READABLE_STRING_32 + -- This metavariable is set to the size of the message-body + -- entity attached to the request, if any, in decimal number of + -- octets. If no data are attached, then this metavariable is + -- either NULL or not defined. The syntax is the same as for the + -- HTTP "Content-Length" header field (section 14.14, HTTP/1.1 + -- specification [8]). + -- + -- CONTENT_LENGTH = "" | 1*digit + -- + -- Servers MUST provide this metavariable to scripts if the + -- request was accompanied by a message-body entity. + deferred + end + + content_length_value: NATURAL_64 + -- Integer value related to `content_length" + deferred + end + + content_type: detachable READABLE_STRING_32 + -- If the request includes a message-body, CONTENT_TYPE is set to + -- the Internet Media Type [9] of the attached entity if the type + -- was provided via a "Content-type" field in the request header, + -- or if the server can determine it in the absence of a supplied + -- "Content-type" field. The syntax is the same as for the HTTP + -- "Content-Type" header field. + -- + -- CONTENT_TYPE = "" | media-type + -- media-type = type "/" subtype *( ";" parameter) + -- type = token + -- subtype = token + -- parameter = attribute "=" value + -- attribute = token + -- value = token | quoted-string + -- + -- The type, subtype, and parameter attribute names are not + -- case-sensitive. Parameter values MAY be case sensitive. Media + -- types and their use in HTTP are described in section 3.7 of + -- the HTTP/1.1 specification [8]. + -- + -- Example: + -- + -- application/x-www-form-urlencoded + -- + -- There is no default value for this variable. If and only if it + -- is unset, then the script MAY attempt to determine the media + -- type from the data received. If the type remains unknown, then + -- the script MAY choose to either assume a content-type of + -- application/octet-stream or reject the request with a 415 + -- ("Unsupported Media Type") error. See section 7.2.1.3 for more + -- information about returning error status values. + -- + -- Servers MUST provide this metavariable to scripts if a + -- "Content-Type" field was present in the original request + -- header. If the server receives a request with an attached + -- entity but no "Content-Type" header field, it MAY attempt to + -- determine the correct datatype, or it MAY omit this + -- metavariable when communicating the request information to the + -- script. + deferred + end + + gateway_interface: READABLE_STRING_32 + -- This metavariable is set to the dialect of CGI being used by + -- the server to communicate with the script. Syntax: + -- + -- GATEWAY_INTERFACE = "CGI" "/" major "." minor + -- major = 1*digit + -- minor = 1*digit + -- + -- Note that the major and minor numbers are treated as separate + -- integers and hence each may be more than a single digit. Thus + -- CGI/2.4 is a lower version than CGI/2.13 which in turn is + -- lower than CGI/12.3. Leading zeros in either the major or the + -- minor number MUST be ignored by scripts and SHOULD NOT be + -- generated by servers. + -- + -- This document defines the 1.1 version of the CGI interface + -- ("CGI/1.1"). + -- + -- Servers MUST provide this metavariable to scripts. + -- + -- The version of the CGI specification to which this server + -- complies. Syntax: + -- + -- GATEWAY_INTERFACE = "CGI" "/" 1*digit "." 1*digit + -- + -- Note that the major and minor numbers are treated as separate + -- integers and that each may be incremented higher than a single + -- digit. Thus CGI/2.4 is a lower version than CGI/2.13 which in + -- turn is lower than CGI/12.3. Leading zeros must be ignored by + -- scripts and should never be generated by servers. + deferred + end + + path_info: READABLE_STRING_32 + -- The PATH_INFO metavariable specifies a path to be interpreted + -- by the CGI script. It identifies the resource or sub-resource + -- to be returned by the CGI script, and it is derived from the + -- portion of the URI path following the script name but + -- preceding any query data. The syntax and semantics are similar + -- to a decoded HTTP URL 'path' token (defined in RFC 2396 [4]), + -- with the exception that a PATH_INFO of "/" represents a single + -- void path segment. + -- + -- PATH_INFO = "" | ( "/" path ) + -- path = segment *( "/" segment ) + -- segment = *pchar + -- pchar = + -- + -- The PATH_INFO string is the trailing part of the + -- component of the Script-URI (see section 3.2) that follows the + -- SCRIPT_NAME portion of the path. + -- + -- Servers MAY impose their own restrictions and limitations on + -- what values they will accept for PATH_INFO, and MAY reject or + -- edit any values they consider objectionable before passing + -- them to the script. + -- + -- Servers MUST make this URI component available to CGI scripts. + -- The PATH_INFO value is case-sensitive, and the server MUST + -- preserve the case of the PATH_INFO element of the URI when + -- making it available to scripts. + deferred + end + + path_translated: detachable READABLE_STRING_32 + -- PATH_TRANSLATED is derived by taking any path-info component + -- of the request URI (see section 6.1.6), decoding it (see + -- section 3.1), parsing it as a URI in its own right, and + -- performing any virtual-to-physical translation appropriate to + -- map it onto the server's document repository structure. If the + -- request URI includes no path-info component, the + -- PATH_TRANSLATED metavariable SHOULD NOT be defined. + -- + -- + -- PATH_TRANSLATED = *CHAR + -- + -- For a request such as the following: + -- + -- http://somehost.com/cgi-bin/somescript/this%2eis%2epath%2einfo + -- + -- the PATH_INFO component would be decoded, and the result + -- parsed as though it were a request for the following: + -- + -- http://somehost.com/this.is.the.path.info + -- + -- This would then be translated to a location in the server's + -- document repository, perhaps a filesystem path something like + -- this: + -- + -- /usr/local/www/htdocs/this.is.the.path.info + -- + -- The result of the translation is the value of PATH_TRANSLATED. + -- + -- The value of PATH_TRANSLATED may or may not map to a valid + -- repository location. Servers MUST preserve the case of the + -- path-info segment if and only if the underlying repository + -- supports case-sensitive names. If the repository is only + -- case-aware, case-preserving, or case-blind with regard to + -- document names, servers are not required to preserve the case + -- of the original segment through the translation. + -- + -- The translation algorithm the server uses to derive + -- PATH_TRANSLATED is implementation defined; CGI scripts which + -- use this variable may suffer limited portability. + -- + -- Servers SHOULD provide this metavariable to scripts if and + -- only if the request URI includes a path-info component. + deferred + end + + query_string: READABLE_STRING_32 + -- A URL-encoded string; the part of the Script-URI. (See + -- section 3.2.) + -- + -- QUERY_STRING = query-string + -- query-string = *uric + -- The URL syntax for a query string is described in section 3 of + -- RFC 2396 [4]. + -- + -- Servers MUST supply this value to scripts. The QUERY_STRING + -- value is case-sensitive. If the Script-URI does not include a + -- query component, the QUERY_STRING metavariable MUST be defined + -- as an empty string (""). + deferred + end + + remote_addr: READABLE_STRING_32 + -- The IP address of the client sending the request to the + -- server. This is not necessarily that of the user agent (such + -- as if the request came through a proxy). + -- + -- REMOTE_ADDR = hostnumber + -- hostnumber = ipv4-address | ipv6-address + -- The definitions of ipv4-address and ipv6-address are provided + -- in Appendix B of RFC 2373 [13]. + -- + -- Servers MUST supply this value to scripts. + deferred + end + + remote_host: detachable READABLE_STRING_32 + -- The fully qualified domain name of the client sending the + -- request to the server, if available, otherwise NULL. (See + -- section 6.1.9.) Fully qualified domain names take the form as + -- described in section 3.5 of RFC 1034 [10] and section 2.1 of + -- RFC 1123 [5]. Domain names are not case sensitive. + -- + -- Servers SHOULD provide this information to scripts. + deferred + end + + remote_ident: detachable READABLE_STRING_32 + -- The identity information reported about the connection by a + -- RFC 1413 [11] request to the remote agent, if available. + -- Servers MAY choose not to support this feature, or not to + -- request the data for efficiency reasons. + -- + -- REMOTE_IDENT = *CHAR + -- + -- The data returned may be used for authentication purposes, but + -- the level of trust reposed in them should be minimal. + -- + -- Servers MAY supply this information to scripts if the RFC1413 + -- [11] lookup is performed. + deferred + end + + remote_user: detachable READABLE_STRING_32 + -- If the request required authentication using the "Basic" + -- mechanism (i.e., the AUTH_TYPE metavariable is set to + -- "Basic"), then the value of the REMOTE_USER metavariable is + -- set to the user-ID supplied. In all other cases the value of + -- this metavariable is undefined. + -- + -- REMOTE_USER = *OCTET + -- + -- This variable is specific to requests made via the HTTP + -- protocol. + -- + -- Servers SHOULD provide this metavariable to scripts. + deferred + end + + request_method: READABLE_STRING_32 + -- The REQUEST_METHOD metavariable is set to the method with + -- which the request was made, as described in section 5.1.1 of + -- the HTTP/1.0 specification [3] and section 5.1.1 of the + -- HTTP/1.1 specification [8]. + -- + -- REQUEST_METHOD = http-method + -- http-method = "GET" | "HEAD" | "POST" | "PUT" | "DELETE" + -- | "OPTIONS" | "TRACE" | extension-method + -- extension-method = token + -- + -- The method is case sensitive. CGI/1.1 servers MAY choose to + -- process some methods directly rather than passing them to + -- scripts. + -- + -- This variable is specific to requests made with HTTP. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + script_name: READABLE_STRING_32 + -- The SCRIPT_NAME metavariable is set to a URL path that could + -- identify the CGI script (rather than the script's output). The + -- syntax and semantics are identical to a decoded HTTP URL + -- 'path' token (see RFC 2396 [4]). + -- + -- SCRIPT_NAME = "" | ( "/" [ path ] ) + -- + -- The SCRIPT_NAME string is some leading part of the + -- component of the Script-URI derived in some implementation + -- defined manner. No PATH_INFO or QUERY_STRING segments (see + -- sections 6.1.6 and 6.1.8) are included in the SCRIPT_NAME + -- value. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_name: READABLE_STRING_32 + -- The SERVER_NAME metavariable is set to the name of the server, + -- as derived from the part of the Script-URI (see section + -- 3.2). + -- + -- SERVER_NAME = hostname | hostnumber + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_port: INTEGER_32 + -- The SERVER_PORT metavariable is set to the port on which the + -- request was received, as used in the part of the + -- Script-URI. + -- + -- SERVER_PORT = 1*digit + -- + -- If the portion of the script-URI is blank, the actual + -- port number upon which the request was received MUST be + -- supplied. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_protocol: READABLE_STRING_32 + -- The SERVER_PROTOCOL metavariable is set to the name and + -- revision of the information protocol with which the request + -- arrived. This is not necessarily the same as the protocol + -- version used by the server in its response to the client. + -- + -- SERVER_PROTOCOL = HTTP-Version | extension-version + -- | extension-token + -- HTTP-Version = "HTTP" "/" 1*digit "." 1*digit + -- extension-version = protocol "/" 1*digit "." 1*digit + -- protocol = 1*( alpha | digit | "+" | "-" | "." ) + -- extension-token = token + -- + -- 'protocol' is a version of the part of the + -- Script-URI, but is not identical to it. For example, the + -- scheme of a request may be "https" while the protocol remains + -- "http". The protocol is not case sensitive, but by convention, + -- 'protocol' is in upper case. + -- + -- A well-known extension token value is "INCLUDED", which + -- signals that the current document is being included as part of + -- a composite document, rather than being the direct target of + -- the client request. + -- + -- Servers MUST provide this metavariable to scripts. + deferred + end + + server_software: READABLE_STRING_32 + -- The SERVER_SOFTWARE metavariable is set to the name and + -- version of the information server software answering the + -- request (and running the gateway). + -- + -- SERVER_SOFTWARE = 1*product + -- product = token [ "/" product-version ] + -- product-version = token + -- Servers MUST provide this metavariable to scripts. + deferred + end + + feature -- HTTP_* + + http_accept: detachable READABLE_STRING_32 + -- Contents of the Accept: header from the current request, if there is one. + -- Example: 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' + deferred + end + + http_accept_charset: detachable READABLE_STRING_32 + -- Contents of the Accept-Charset: header from the current request, if there is one. + -- Example: 'iso-8859-1,*,utf-8'. + deferred + end + + http_accept_encoding: detachable READABLE_STRING_32 + -- Contents of the Accept-Encoding: header from the current request, if there is one. + -- Example: 'gzip'. + deferred + end + + http_accept_language: detachable READABLE_STRING_32 + -- Contents of the Accept-Language: header from the current request, if there is one. + -- Example: 'en'. + deferred + end + + http_connection: detachable READABLE_STRING_32 + -- Contents of the Connection: header from the current request, if there is one. + -- Example: 'Keep-Alive'. + deferred + end + + http_host: detachable READABLE_STRING_32 + -- Contents of the Host: header from the current request, if there is one. + deferred + end + + http_referer: detachable READABLE_STRING_32 + -- The address of the page (if any) which referred the user agent to the current page. + -- This is set by the user agent. + -- Not all user agents will set this, and some provide the ability to modify HTTP_REFERER as a feature. + -- In short, it cannot really be trusted. + deferred + end + + http_user_agent: detachable READABLE_STRING_32 + -- Contents of the User-Agent: header from the current request, if there is one. + -- This is a string denoting the user agent being which is accessing the page. + -- A typical example is: Mozilla/4.5 [en] (X11; U; Linux 2.2.9 i586). + -- Among other things, you can use this value to tailor your page's + -- output to the capabilities of the user agent. + deferred + end + + http_authorization: detachable READABLE_STRING_32 + -- Contents of the Authorization: header from the current request, if there is one. + deferred + end + + feature -- Extra CGI environment variables + + request_uri: READABLE_STRING_32 + -- The URI which was given in order to access this page; for instance, '/index.html'. + deferred + end + + orig_path_info: detachable READABLE_STRING_32 + -- Original version of path_info before processed by Current environment + deferred + end + feature -- Query string Parameters - - query_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] + + query_parameters: ITERABLE [WGI_VALUE] -- Variables extracted from QUERY_STRING deferred end - - query_parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + + query_parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -- Parameter for name `n'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - + feature -- Form fields and related - - form_data_parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] + + form_data_parameters: ITERABLE [WGI_VALUE] -- Variables sent by POST request deferred end - - form_data_parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + + form_data_parameter (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -- Field for name `a_name'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - + uploaded_files: HASH_TABLE [WGI_UPLOADED_FILE_DATA, READABLE_STRING_GENERAL] -- Table of uploaded files information - --| name: original path from the user - --| type: content type - --| tmp_name: path to temp file that resides on server - --| tmp_base_name: basename of `tmp_name' - --| error: if /= 0 , there was an error : TODO ... - --| size: size of the file given by the http request deferred end - + feature -- Cookies - - cookies: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] + + cookies: ITERABLE [WGI_VALUE] -- Expanded cookies variable deferred end - - cookie (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + + cookie (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -- Field for name `a_name'. require a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - - feature -- Access: global variable - - parameters: HASH_TABLE [READABLE_STRING_32, READABLE_STRING_GENERAL] + + feature -- Access: all variables + + items: ITERABLE [WGI_VALUE] -- Table containing all the various variables -- Warning: this is computed each time, if you change the content of other containers -- this won't update this Result's content, unless you query it again deferred end - - parameter (a_name: READABLE_STRING_GENERAL): detachable READABLE_STRING_32 + + item (a_name: READABLE_STRING_GENERAL): detachable WGI_VALUE -- Variable named `a_name' from any of the variables container -- and following a specific order -- execution, environment, get, post, cookies @@ -137,21 +613,21 @@ WGI_REQUEST a_name_valid: a_name /= Void and then not a_name.is_empty deferred end - + feature -- Uploaded File Handling - - is_uploaded_file (a_filename: STRING_8): BOOLEAN + + is_uploaded_file (a_filename: READABLE_STRING_GENERAL): BOOLEAN -- Is `a_filename' a file uploaded via HTTP POST deferred end feature -- URL Utility - + absolute_script_url (a_path: STRING_8): STRING_8 -- Absolute Url for the script if any, extended by `a_path' deferred end - + script_url (a_path: STRING_8): STRING_8 -- Url relative to script name if any, extended by `a_path' require @@ -159,15 +635,25 @@ WGI_REQUEST deferred end + invariant + server_name_not_empty: not server_name.is_empty + server_port_set: server_port /= 0 + request_method_attached: request_method /= Void + path_info_attached: path_info /= Void + query_string_attached: query_string /= Void + remote_addr_attached: remote_addr /= Void + same_orig_path_info: orig_path_info ~ meta_string_variable ({WGI_META_NAMES}.orig_path_info) + same_path_info: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) + path_info_identical: path_info ~ meta_string_variable ({WGI_META_NAMES}.path_info) end WGI_RESPONSE_BUFFER deferred class WGI_RESPONSE_BUFFER - + feature {WGI_APPLICATION} -- Commit - + commit -- Commit the current response deferred @@ -176,39 +662,39 @@ WGI_RESPONSE_BUFFER header_committed: header_committed message_committed: message_committed end - + feature -- Status report - + header_committed: BOOLEAN -- Header committed? deferred end - + message_committed: BOOLEAN -- Message committed? deferred end - + message_writable: BOOLEAN -- Can message be written? deferred end - + feature {WGI_RESPONSE_BUFFER} -- Core output operation - - write (s: STRING) + + write (s: READABLE_STRING_8) -- Send the string `s' -- this can be used for header and body deferred end - + feature -- Status setting - + status_is_set: BOOLEAN -- Is status set? deferred end - + set_status_code (a_code: INTEGER) -- Set response status code -- Should be done before sending any data back to the client @@ -220,15 +706,15 @@ WGI_RESPONSE_BUFFER status_code_set: status_code = a_code status_set: status_is_set end - + status_code: INTEGER -- Response status deferred end - + feature -- Header output operation - - write_headers_string (a_headers: STRING) + + write_headers_string (a_headers: READABLE_STRING_8) require status_set: status_is_set header_not_committed: not header_committed @@ -238,8 +724,8 @@ WGI_RESPONSE_BUFFER header_committed: header_committed message_writable: message_writable end - - write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: STRING; value: STRING]]) + + write_header (a_status_code: INTEGER; a_headers: detachable ARRAY [TUPLE [key: READABLE_STRING_8; value: READABLE_STRING_8]]) -- Send headers with status `a_status', and headers from `a_headers' require status_not_set: not status_is_set @@ -250,35 +736,34 @@ WGI_RESPONSE_BUFFER status_set: status_is_set message_writable: message_writable end - + feature -- Output operation - - write_string (s: STRING) + + write_string (s: READABLE_STRING_8) -- Send the string `s' require message_writable: message_writable deferred end - - write_substring (s: STRING; a_begin_index, a_end_index: INTEGER) + + write_substring (s: READABLE_STRING_8; a_begin_index, a_end_index: INTEGER) -- Send the substring `s[a_begin_index:a_end_index]' require message_writable: message_writable deferred end - - write_file_content (fn: STRING) + + write_file_content (fn: READABLE_STRING_8) -- Send the content of file `fn' require message_writable: message_writable deferred end - + flush -- Flush if it makes sense deferred end - end From 40ec2f6419efaf0d1d84d2a7b2c77f9fdbccfb8b Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:22:12 -0800 Subject: [PATCH 70/76] Created HTTP client library (markdown) --- HTTP-client-library.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 HTTP-client-library.md diff --git a/HTTP-client-library.md b/HTTP-client-library.md new file mode 100644 index 00000000..f8e8380d --- /dev/null +++ b/HTTP-client-library.md @@ -0,0 +1,11 @@ +# HTTP Library Features +* HTTPS +* HTTP verbs G +* Custom data +* Custom headers +* Proxies +* Compression +* Caching +* Auth methods +* Cookies +* Redirects \ No newline at end of file From fdef9b0c8ff83b484321f785c9baa2c63f3ce73b Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:23:42 -0800 Subject: [PATCH 71/76] Updated HTTP Library Features (markdown) --- HTTP-client-library.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/HTTP-client-library.md b/HTTP-client-library.md index f8e8380d..bcf61954 100644 --- a/HTTP-client-library.md +++ b/HTTP-client-library.md @@ -1,5 +1,8 @@ # HTTP Library Features +The following list of features are taken form the book RESTful Web Services + * HTTPS +> It must support HTTPS and SSL certificate validation. * HTTP verbs G * Custom data * Custom headers From dc0c90ab462f3d521c878c09778755b44e275847 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:32:07 -0800 Subject: [PATCH 72/76] Updated HTTP Library Features (markdown) --- HTTP-client-library.md | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/HTTP-client-library.md b/HTTP-client-library.md index bcf61954..9d0bc436 100644 --- a/HTTP-client-library.md +++ b/HTTP-client-library.md @@ -1,14 +1,18 @@ # HTTP Library Features The following list of features are taken form the book RESTful Web Services -* HTTPS -> It must support HTTPS and SSL certificate validation. -* HTTP verbs G -* Custom data -* Custom headers -* Proxies -* Compression -* Caching -* Auth methods -* Cookies -* Redirects \ No newline at end of file +* **HTTPS**: _It must support HTTPS and SSL certificate validation_ + +* **HTTP methods**: _It must support at least the five main HTTP methods: GET, HEAD, POST, PUT, and DELETE. Optional methods + OPTIONS and TRACE, and WebDAV extensions like MOVE, ._ +* **Custom data** : _It must allow the programmer to customize the data sent as the entity-body of a +PUT or POST request._ +* **Custom headers** : _It must allow the programmer to customize a request’s HTTP headers_ +* **Response Codes** : _It must give the programmer access to the response code and headers of an HTTP +response; not just access to the entity-body._ +* **Proxies**: +* **Compression**: +* **Caching**: +* **Auth methods** : +* **Cookies** : +* **Redirects**: \ No newline at end of file From f4b621ba08498b669ec7dd37b19b501f6cad6fa2 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:34:35 -0800 Subject: [PATCH 73/76] Updated HTTP Library Features (markdown) --- HTTP-client-library.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HTTP-client-library.md b/HTTP-client-library.md index 9d0bc436..02ef8653 100644 --- a/HTTP-client-library.md +++ b/HTTP-client-library.md @@ -1,5 +1,5 @@ # HTTP Library Features -The following list of features are taken form the book RESTful Web Services +The following list of features are taken form the book [RESTful Web Services](http://www.amazon.com/Restful-Web-Services-Leonard-Richardson/dp/0596529260/ref=sr_1_1?ie=UTF8&qid=1322155984&sr=8-1) * **HTTPS**: _It must support HTTPS and SSL certificate validation_ From 92c58bbbe036c01a2c3a165039c8955ca3829542 Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:37:45 -0800 Subject: [PATCH 74/76] Updated HTTP Library Features (markdown) --- HTTP-client-library.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/HTTP-client-library.md b/HTTP-client-library.md index 02ef8653..6e2b49b2 100644 --- a/HTTP-client-library.md +++ b/HTTP-client-library.md @@ -10,9 +10,12 @@ PUT or POST request._ * **Custom headers** : _It must allow the programmer to customize a request’s HTTP headers_ * **Response Codes** : _It must give the programmer access to the response code and headers of an HTTP response; not just access to the entity-body._ -* **Proxies**: -* **Compression**: -* **Caching**: -* **Auth methods** : +* **Proxies**: _It must be able to communicate through an HTTP proxy_ + +* **Compression**:_it should automatically request data in compressed form to save +bandwidth, and transparently decompress the data it receives._ +* **Caching**:_It should automatically cache the responses to your requests._ +* **Auth methods** : _It should transparently support the most common forms of HTTP authentication: +Basic, Digest, and WSSE._ * **Cookies** : * **Redirects**: \ No newline at end of file From 7dd9a307400b83759422308fcf4f8345048e13ca Mon Sep 17 00:00:00 2001 From: jvelilla Date: Thu, 24 Nov 2011 09:38:40 -0800 Subject: [PATCH 75/76] Updated HTTP Library Features (markdown) --- HTTP-client-library.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HTTP-client-library.md b/HTTP-client-library.md index 6e2b49b2..20a63d4f 100644 --- a/HTTP-client-library.md +++ b/HTTP-client-library.md @@ -17,5 +17,5 @@ bandwidth, and transparently decompress the data it receives._ * **Caching**:_It should automatically cache the responses to your requests._ * **Auth methods** : _It should transparently support the most common forms of HTTP authentication: Basic, Digest, and WSSE._ -* **Cookies** : -* **Redirects**: \ No newline at end of file +* **Cookies** :_It should be able to parse and create HTTP cookie strings_ +* **Redirects**:_It should be able to transparently follow HTTP redirects_ \ No newline at end of file From 8870e19991e44205e5da07b0c02c2101d4f7051d Mon Sep 17 00:00:00 2001 From: jocelyn Date: Fri, 6 Apr 2012 06:25:05 -0700 Subject: [PATCH 76/76] Updated Home (markdown) --- Home.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Home.md b/Home.md index 050901ce..c229facb 100644 --- a/Home.md +++ b/Home.md @@ -1,6 +1,6 @@ ## Eiffel-Web-Framework ## -The official documentation/wiki is located at https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/Eiffel-World/Eiffel-Web-Framework/wiki]]. +The official documentation/wiki is located at https://github.com/EiffelWebFramework/EWF/wiki , if you are visiting a "clone/fork", please always check the [[official wiki|https://github.com/EiffelWebFramework/EWF/wiki]]. - Mailing list: please visit and subscribe to the mailing list page [[http://groups.google.com/group/eiffel-web-framework]] ![logo](http://groups.google.com/intl/en/images/logos/groups_logo_sm.gif)