pax_global_header00006660000000000000000000000064126171620360014516gustar00rootroot0000000000000052 comment=ecba378f5c3603fecf677fb47aeb88a0a7b20935 jetty-9.2.14.v20151106/000077500000000000000000000000001261716203600140365ustar00rootroot00000000000000jetty-9.2.14.v20151106/.gitattributes000066400000000000000000000000661261716203600167330ustar00rootroot00000000000000*.sh eol=lf *.bat eol=crlf *.txt eol=lf *.js eol=lfjetty-9.2.14.v20151106/.gitignore000066400000000000000000000005741261716203600160340ustar00rootroot00000000000000# eclipse .classpath .project .settings # maven target/ */src/main/java/META-INF/ *.versionsBackup *.releaseBackup bin/ # common junk *.log *.diff *.patch *.sw[a-p] *.bak *.backup *.debug *.dump # vim .*.sw[a-p] *~ ~* # intellij / android studio *.iml *.ipr *.iws .idea/ # Mac filesystem dust .DS_Store # pmd .pmdruleset .pmd # netbeans /nbproject # merge tooling *.orig jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/000077500000000000000000000000001261716203600167305ustar00rootroot00000000000000jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/CDDLv1.0.txt000066400000000000000000000406161261716203600206130ustar00rootroot00000000000000COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 1. Definitions. 1.1. Contributor means each individual or entity that creates or contributes to the creation of Modifications. 1.2. Contributor Version means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor. 1.3. Covered Software means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof. 1.4. Executable means the Covered Software in any form other than Source Code. 1.5. Initial Developer means the individual or entity that first makes Original Software available under this License. 1.6. Larger Work means a work which combines Covered Software or portions thereof with code not governed by the terms of this License. 1.7. License means this document. 1.8. Licensable means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein. 1.9. Modifications means the Source Code and Executable form of any of the following: A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications; B. Any new file that contains any part of the Original Software or previous Modification; or C. Any new file that is contributed or otherwise made available under the terms of this License. 1.10. Original Software means the Source Code and Executable form of computer software code that is originally released under this License. 1.11. Patent Claims means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor. 1.12. Source Code means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code. 1.13. You (or Your) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, You includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, control means (a)the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b)ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants. 2.1. The Initial Developer Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof). (c) The licenses granted in Sections2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License. (d) Notwithstanding Section2.1(b) above, no patent license is granted: (1)for code that You delete from the Original Software, or (2)for infringements caused by: (i)the modification of the Original Software, or (ii)the combination of the Original Software with other software or devices. 2.2. Contributor Grant. Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1)Modifications made by that Contributor (or portions thereof); and (2)the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination). (c) The licenses granted in Sections2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party. (d) Notwithstanding Section2.2(b) above, no patent license is granted: (1)for any code that Contributor has deleted from the Contributor Version; (2)for infringements caused by: (i)third party modifications of Contributor Version, or (ii)the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3)under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor. 3. Distribution Obligations. 3.1. Availability of Source Code. Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange. 3.2. Modifications. The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License. 3.3. Required Notices. You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer. 3.4. Application of Additional Terms. You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer. 3.5. Distribution of Executable Versions. You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipients rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer. 3.6. Larger Works. You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software. 4. Versions of the License. 4.1. New Versions. Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License. 4.2. Effect of New Versions. You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward. 4.3. Modified Versions. When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a)rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b)otherwise make it clear that the license contains terms which differ from this License. 5. DISCLAIMER OF WARRANTY. COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN AS IS BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. 6. TERMINATION. 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive. 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as Participant) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant. 6.3. In the event of termination under Sections6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination. 7. LIMITATION OF LIABILITY. UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTYS NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. 8. U.S. GOVERNMENT END USERS. The Covered Software is a commercial item, as that term is defined in 48C.F.R.2.101 (Oct. 1995), consisting of commercial computer software (as that term is defined at 48 C.F.R. 252.227-7014(a)(1)) and commercial computer software documentation as such terms are used in 48C.F.R.12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License. 9. MISCELLANEOUS. This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdictions conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software. 10. RESPONSIBILITY FOR CLAIMS. As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability. NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) The GlassFish code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California. jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/ccla-exist.pdf000066400000000000000000015134771261716203600215010ustar00rootroot00000000000000%PDF-1.3 % 4 0 obj << /Length 5 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{*u endstream endobj 5 0 obj 62 endobj 2 0 obj << /Type /Page /Parent 3 0 R /Resources 6 0 R /Contents 4 0 R /MediaBox [0 0 612 792] >> endobj 6 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im1 7 0 R >> >> endobj 7 0 obj << /Length 8 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream x]z絶3ǸeD<#Q.rE|tOrBBGHM|.U;;Jq84DspЀkOtx@|CL\y˵=G_v~Z~<7-iCg]x4"v6.\x믿zOe~ߗOG?+ǏPͫ\p.o$wGdF>lr!|ї'3Y8%~q?nD?\[*Wx`:ut}os.i﹯63o šc9".Z[W۟xo}_ft˘ GyM\Ez[Z[\3yU})g,/-PJ@ (Hy,+C`dDJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%N}#dkn͞hP_b.&P|m14ue˫ Ԗ76i)/8ly#_!iB{[>e9oZE[rAkf6[Vܹ[iMlXws׀>?Sͺ!Nzny =i5Y=o;7ӁpͿsxm&B ޼5BTzGq4#q ʚimr,zAfj1Dw5SzG h{!y-R3\GP90:Q֮zb{M:o@o9g̕신qu>(XKl/`{B/(#QU7eۻQ5QuH+m/=Zj{Gku7xniky=?7mz2Ћ-M[/VsYEO _;g;zLTzGs{mO~!=I?7 KÎ;] v V(▖=mҧnk-K;FSm: d5Vyl[kW֠uUlk ~BB]o;pQM-g`>3I(ewj Jo=qrm%z9r+Oʠ&M>_di_^~Uew_I(^SöVŽ MRZCd*eVU':ysCރ`{8ç= aәZ3őIh[ Vv^,y˔⎫1|^޸d*,Ͷl{Gz/2熢=: ZP/"cƔ[&cK:IzѓnJ967뒷,}Oy{\mf5pa܎+)_e0]KFVeVeU:pnܥ !N!AO( ~Z7)~ PF2J˛IU0wMʈu&Mb2/*R4h">3E)fk( Fa~MA,Fz66>6s7I&oJ@4wט*/MRg5,i.=R1" ֘ۗ'+n$aP&Oz~)қI}|fH`{B)aF":t @s/)(ev&<ơ&\lj ˀ^^A"$Ll[ޖ^襑^0za*(dg  zd[r^:d-oq\=Hr5j$K_;`4ˠizIW:9E=2qE,Emd~=.ƀ\|GG+S |];lFz;!00\l :h{^ =[oYI a4%BYiG@t?!$3<tt*dOnzzs%b6mJd*k _dSǟU>@b؜hzH/Cuz3l D蹓m/sB/y>1ayG/pӐ'lS̹\CjU;A衷ۆ`{h0^ކ`9{Ak{GOC. z QO6Ÿ8#yb{У(Ezd@X4)အސ:5*] T7R8PAp2dy! 3Rk=0ʵĽ&Ε2J?@o% Tvq1_}, FK).mzڛѽ3A/ qFۋuF&eĶ@ H~6 GEeK >:7`bZCg)%z4 ;z"7+F =)ZKO=IJG@C#T C[zWm?y=_6s3O{[Z ?{coŋT6g^co եtՍџ]LCMeKzb^ X@ӛRXyP}Ix=QȥCE&me.[&%1]FUdƽ_nmqS|\h=\Y n׹e#e5*eIлΠz 1yQ2F~Ň?Ln]4]AK-:f# ՏUZ\hU>Л/B+sWo͏ G-ؚKb{cxQ5*uReEN5#^u-ijIOн%=!9$\bU5Wp|lz?lі;B*?D!Q-?Bޠj"=Έ7Ƴ>9GYŕ'͡?{vQO~дv^R -4)>Ҝ;OG[&lTLMJK,Ç_f>C8+m@_Pp8k=k땕PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (3N/WZzy.D{ޢ`4L4zIo?csK7+AG7m> _,iq]/r@Ȩ,R`Ĕ|ow12>8 o e23ޅ/)ܸ^cGUr+K?)e-@܋Myz"b=fays]Z&^_gbsl~tCoeqeGr3gt}z;+J|& |4c&X‹8iuXZeZ}H5=;di&,桾Q7c)u:͘|Hz $?XfGFK jEG3կ碿Zz:[錂wk t6q#  IRG/|6&"vFG]p2c:Jtʲk2w)yJr:^T!SMv-j)w5o+&>kdPהxӝ& ,.m^ M6yDgϩ|s0E[Ptl|D%M봽.6 Ec%cPG2IUB/= HDf V֝{tאA v( =Q-Ȫž=(^U ۭ[%C4(9m uL2G"7d$ˬuw|OOX PYϽr;SuAzX艠^h:ѱCM C3FNBPsvz[;DmO5`y, HUWGe;j>j3I8'lwklP\Lt䭊5ҩ8Lß$ z ɞDZ@3&q"D&"cSu6BmS4jTC!Ūc5JP,У:B李BoO-*p6GpI$}Nzh߁lagҦgވ0=Mۖ8WlA jۍ.# 3&&-of{X{c>#! sm(譸P6~mLj3ٔȣN']5ѻqaJfךM *ȸЛxx yJR9Ju9#:W4JzGiC']^޸JiM\GT%7""vϕK-Rꔴ;S*ܪMb{'3z}=CrV(:wLdu=z tӂ g$bA4Wc MloGiwߥdad(̈́Q-Ғ(@E/EAͳHga\ =YlУ-ca3} (=aӂh{8.~ݘd ?#i&t^2Ԇ$K$np<*s@Ezw胉Zuh6sz@Ş!,C3=fD=.z.st63u5ZF&+Z`ÃYF9*:Ը3h:qS5wX֘.9ǙKOCPҺ{͛zgs=sqT'ea9i.;;)Z! to e`*rsPy;ГWz6P8OQ^֚i5ꄅ  BI=Trie7??c ph\ 5c0n2 =b8VDȪ<r.R;?_yxZ3dX֭&I?MEkfhֽt6w'EۣoEboUz%yٞ]Ohɬ,zz5=н+ݵlfR,~2~"a_k魮~V d_᭮ѣ_Q|uj젛4~N2]l-=3jh0:n^]ckOs^s_]y]7(%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ+am٢.Qx8{m`@.1vN@Fؕٳ"-z㕥oٕIvR/؞PٝV]=w(ܽ,V#Nͦ: wW^EYݤ9&ݭ=*czzOwû#Ntv3r ƅCAU$ĤTzcK;o_1W@.o\?gxV^UlrFg4kW٥:?Y-/ךjg\c6 vPeoCHYh{zz8t^"{8NU8Gܛ-eVYhpԣ`5/?)leb8Eh;~ӆvg0>2xSb,ܶu5M^FaoU+32PZs'&!қ0 /Ɇ#D lzR)Ma1a' UHۤ}'^M$6Ԇ~p& FЫzI01h3lhw'\8͌ދ!l-u{skx84SD/iʥmwB'UbV&ml} |>!3/dmYɴ5ٌLR$I@1&ԩ 7)VڦM6wp=KnMΖcwڴ<2<)Cӓ%y@f*({.&mNcBoT rQO2x lҶnB YNpUO m77^)лI8$ͬ~L x&EK0YYRB1ӧSe\mkӫC@IeZ:=ea[pM'ߙ*=[jUnaM$C=&g1foΉu'"-b 7apXG_υ^ףXBI(62#̧uhۜKdր^G[^kcM+3X @pB/aûy}}.V39ry=NBjJؼqSB4w|Z#D6~/m捝GzSD +nB95# 'zz5orˡLC-k,kP3i^Tn,GS ix$Mz'ނED=^ܯ4D"ҫ7aJ*%ROF"=@6% dwT+*U5H27lϫ#"֛7%86'ފYoǂv'q)ΰw}yƭGE$"L\ IyDZ#o`޶3zR%zuc~Hen=o@/cюk[ x>+[[Kܫݾx: 6]:Çf͎4R)Dϕv+x1wiM]@&&K+CٞӾrU9Wc ߛ[,JԬ\g{TX Gz>ϫiCņW:>^};5p%Q56moFm+j1ԧo#j&U@e]aL;@Gz72a{8 8Ap=wc3#Pnz抛g|bg|`Ql3ɻ+M^20$7M9h\`Tq< Qkc'-)UJ6|Ʉhw Ui0],qTP\!X=6t\?&e"lg|㟃^|#)ˈ;H/#Kdm,hMUgۀǾտ$ZzƇыqG[@!4lİГWY%3l*B6׹TxOo_z<^GOx囀0 Ŵj˨b㊟g@ \^%eSibS9Gzc7dI˲=9JV͓^]͟F*eF+^}QX^l̐=ÙHaWl㞮y!(uZ^}S„+] @ z?՞dWΪY^¯}_ L,{H[hU$ a#*bthĸퟮ:B(ڛ\XՋ(z|PB(A[-gx aCaܨ\11ƭsT~;D_{,9AfTԴPmHB P-."=`C)n]mae'zԤY슼wʒtXMȅuka sՒzW'^LMUF~iSТs⋌EZ_2 ғU"ceD>zD)R撡jt'~;N91 J5򥵥'6U:UmǪ骱t3v?CIK_.m fNvP]0[{m:4ު&J\[9z[qH~[>rqݬB.7\zUo3w8գ#|?mVJ+\x0|4̥|jؤY Ez>yRњ/.\ʗV@>؃Q;m0mUsaW;pjٗ燎ьšo47-'PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%NKSQso]^MOJҴD@9d  z}vWe=^mbbuYnßm_???М3$jy+Im:ܶ"~rO(\~AvL!3.jBib9s񊙎۫VDS&>stK_|6i|Oa.ɼBN‡;WC2(o{7WY)YJr1ao'aӚ6;zpd:ߘ!o7'ן2۽Ldeφ=f{U`ҩ|^ ;}2`9jqz~)_wmU[fLJԄSLs֊PW..&(ӫ߭ۙL~9oDٞNr-i}BDZ΋ђ4*R3f 10a!G&?$M3l?U ~לڌR̻nti%?͌># {}^ Z\u8 }z3٢@ivv|%tZLLޱy 53Mlߟo\>]xוsj7ky{|`Bo8ga zݱ'ĝ%3|zɚQGmB@ {pO϶\wvكuk-ĬCΞĽ:TDlqCGo6[z'o-9W c 1|eܷ?hA3?geVkH*gqĠә7?}n-K]Z繧^4_G? V qeIS^hhL-Ƨ0^("M[CA^Jˑ^iq:L]^h&٩iTNxr:3Psɦ҈-#M< -6K!ֵnf/JH"=QԘCx(ͅ4x)SE؈e +n#O*"/C {M;ۛ@ff{zKHwkfC)ғM=N43EU#/RsHR ^(D_HT;=Y/!݁BP9vcLS4MkIXB%)\,zO舂(VyMP\*h{<56s-8.dOXf;B:@6l]k7w:z K==*cD"Jb{wpw\~a"1XF[ 艋L[twŗdOt< D$BW@o-1bAOE +)e!J==. >K)N(ɭa?={%.8:* Pߞzx.h z< \g{ZQkDzĠSk;zpؐc̵ǩN!zކcj,+/ *L"T^uq/Ds؞8=CgOQVC^6*%%&ϝ'ѧ%q{r?6]BGQŦ1^$اsdL~@tFsh{BqảGVHW4 }ܓiIg~3k<$(+Fٹ{Deߐ,M]OkdBg{Лt:ۮ/q"l&Dž\_^ATУVz6{ ጸ-Sh; D*\vUV |⼃  (BqVcO/lɤ zmNL[&6MC4pIi }j}>-Il}&Z}bd&rs;mXw- C;r(HES9rr~f{^GOl/^5b=A :rSzzR90y/E5ތBb8E"B vly=15_MxZ,nǸY"P⪧% =_VG8wc,6!&$Yb{'U4n3zyP율$"@/@<=krvs&sm$E-yFJQ#c%Uϕ=N1((z O le; = uZT]ܫmh@Fg'˟<*.w\ʅ=Lne0 7MzHBgĴHO0) ^57f@F51GMy [l Q oGMj aZ.屢Ry"1ѣx k(pq"jSlǛM*4mǀo$KG0˥TS蓤3Soezb[ޜpt\豅ݰⲕ=wŽXo'_$7*3pkaK(6xe.4)VT) mښk,5e(we)wP^B2.@% ]H1|<#zB1ۍԮѳtӢEdB&-`ZC ,gQ)EzY8+[(Sv BXlӈ;PM'n̚Xz롇ݬ5ޅ!=&295'rOlFh~6 Pq0ۃN =׊qj=Q MR6 腩 ^?r =@(q<|+Z(W.(6)qx7*N0M7R%*DO0y<z $K$ֱ4U/AE rّ^@Uz(JXmlZB2pm4j74XYa?G');AvLb{wogǡ=δ2:@p6@`ʘ^G٣s{z]>7΁^)1s:z;7҃k)-BG K!cXF"lR;#ς[IEzw.Ę=ySoC. _Ӕs 3GE-"={ =hHܳ<<bcK"=׫BxQNLOCvW?4\{V;M %T4ӒEO!0DB؆1I#:yG%(uGo$58IHw#=#9vtN2u7y$J 6w~-|Br,FhNxɥۣf:'MFє>G8ޏ$u9ɣ^I==omz) 8`:yFG^vt^>'I,@oJ BC/}$4B` lo7{zd=*j5*"|c{1R8=lY} i)HwRb{=酋U\'J#&`^=.[֥@%9ߪGF4PLbBO`{ЋMh{EmD:>BUrtF@/?yzT ܠ44Dzz 3't7} ѻ='4/T=RFZb{ވ@o:<&{}pR߹-ћ\)@%'pz7ٞhGϭ]C_EzmO BsE=zn!z.knr#~֫:ir¿14Ke:,ۣ}m#B\Phb"q={ 2&ǿ8\lsi$ ҤG\KqdGFzc z#W_FUeɊh# x@bzǸVRKmmSjG/cc^!aUԹb3zy.;j.rmzVԽDcL6i.RɊJ:\Z7,ޑ l7 R# ayO-FՁD)VōF|쮸} jt~E<ӖwcͬU$#T 1ғ񪎞p1A7wa}#bz>louO} Eol+Bky#.U&3ٯ˻I-Zw?a@`1B:Ah*PxHJ~)xf `wOѣ'N;NQ eԎ_fgp;?/ݏ)[DS$CuL<5Bɧ^cX 6G[^COV{.E_hD[騬g_l9GHI4YNŭAdYϵk6}l}WJ7B5VUlQ6s;tgShX:]Dc^=Tܢ>:)oN^|&ސ|X:M#lݦ{#֦Rh4!= sztɆ]]:U|?G[&-eʏ:k/lpٖSAX/戏ds96=޻_Ďp_Krwѧtqyew܃~q8e|#57x> _}xr:6O#?Żxקz>PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%' iZA(aŖyV>%ϼ12mlX<},zur𰳽.V3zެA9'2NbmWcTUG\;#xƪͫۋ :=wVrQ1^I@w+>NUvag硷zB*u=[2MM&+]Ez%i¤CbX8ezP5 IsqrFx>;aYX21щ9f;%20_pѿ lwU/.<373^PҴ2 iMiMFʧz ݶYM]ο+=-ѓcÕWZz1ǷIn9>a"g^ ?HeۉTuT9=s77/(YȣwYz[ޜ"Bܛ_SuinŻ𸿓aRk j7KKwWK+r&IJ10+2k,^'d~4et_x ~iޛ*>#VFG ko73ۭ_oYtXX 󼏳wl0(; =F! Ԟt'ϘʟP WU4.b8/zewKlzqHP!r6H1,#-aE60q B==SΦlA*erߊDt>Rt}&j3^]Ү'W솗ޡUOwvǝzImJ_YKK昽aI! Lxvܨ+WL@j͇f;g =c4_ƯUbn/*, z|Nz#%\eZw,z:- >cqm"#`)n:zE!#}ȑSDq_''!'yJyݍ1(󣉷8\cwmIRλh#$^bS%o{i H S` FzVΑ ag78d}m' ӗ؞(=YghTг2E.Ez'ah7L /Gc3l-N])P({BcJ[l(2KNeqn3+K:=q1 hDb>a3z# qJ`)=Ȉ2tLOn,9YoQiﴼbje &<^snS:oÚ R!s)=Zd@٨'ګ(y =RlϹL؎h'ыQb{e>(W HMQS*Â3iGsCA1=K< ؇.7HsU^9.7Co"_lPއ1E=&Ff2z9"}o9QzRk`8M#!bL{Bo֨6YY}W4MRJ"~޲B=iIWuMmsK)䐹<^HH**AP >Fڮ{;lEzM'}߉xf{[<7[h{@9;w%X6-ڞC ^P0|`c'1ͮ1ǽ2zc3yb®(+=w X$ƽ|#]'L'HM =H4)\ k.켻@O\@o18^'0۳Rk!n;`;z)\#".-(!ɇf=t((E{WXk@o)CwkYlOv` K=TxȾ-BOJ/*z]#Dz/ /B3Ɣ ez-|T= a/å  +8i#z;IB^(~D`9yWxSJ$^Ѝ@rbVT/)v`{Uvmf<1U1A(H{5>\pLE&z,ѾgA)A^sJ\c'm1-%nꀸ=.<=pۋ`F#=wK0ё@bYrd!c_rzWRp'CROG/|6%ùSli*cضט2л#Nn?ƽB:sW^b{tQ =$izP BP},ٔ[i?O 9A#=CrZ,btbHdV64=Z1Cm;xGKZ2.L]ؘaPXGo-CuFν:IN Wp,YJ]ř HcT0;&J1"q^1 =2IOBo#lXkl`OG ϩ'?vV|BZ{yyܞtjPth;eNl7-彆4yFڰC>:MF:%ԨxBOFGFEt1xT=yd'plŔJ1NjB]2&LbwIs!%2;zE-M?sN"-< =@Ez2Mz|N5ݱwLK8;++=%@N^0tz7!U@ ! ^B 9o>r{OP:6CGΠfUv[z<ڨrkGQlluFjV=^KqOVu*_ v!!ף+' ܼ=Lc-~ ,hQUA/3RAxKe\8ߩ\rZ}m1 ESڞ$|\] סG~ͥ#nXs-&aO!/H#Wa{<4u![,(VkWtQ =UvQ4ًDAW% p}Z ϗV n@+I`>!MK<_U}<}(z2x0I4_K+FJNN>A7э-mnRđ5C|(fTuzC=Xx!þa/C!J@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%ށ;;w]|CS]zvaJz7%\H#{r 벸^s۟-eV{)PYb*+m]Sez z]d[W3j$ӧal~/{.uxQ<_ cna5.s:iqyO?zu,{pU1$Jbn|e˽SY\oQẒzO+7W)efdc>4LP#Vxy决|"B=w,ӛh]˼bqKUKwnZz ޜ^?*񏉃8jy85Nᶵˌ@/Y]K9BA]}x꛵Ge6|vw\z\&1|Oq⍅L)}(e@=xccc{o+G?VZGZ±> ysoFPV#{5ɊȠϢqPEܒV<~ nzz[Dׅ15FwS2tҀ6xW17rGeiK[Oë ?}2гŦh _Po9fcG. 得 {iT)ˤ b5QHٛzkaˆGqC鿪5_"b+7So""W11A=M"`8OԼ\G@6_F X;ǿ*v^E9QcIBVh|!.Ҥ,-VFEeymAVZ-GmC4dHz(&J"a2vJ!%T$R[߁޸p-u"[c"SL_9K6I-;X"衔6:Ԅ^^ H[UzeGةre=D/p8}hl(\?"iS"|)Df!*IR(io)=ۃB^ܵI|ȈPm#7(d"%J}QIW"{x'UDq=BoIJ {JN[z6?iÈ.-r3PF<" Q@Q0Y-UT>{&U,{ }NPv^5vPlC8ȋ߲%cww#zQxp3d&\}!er. SHK*tF$$ڒWd{F 5zG-`^ zd^Q2G-u"ۮ"y>"&jc| 9VܯqKgz|QۄGVgwfGYqA/CpYǎ5dk@BOr ]umGzAPj|{EحYd'X=HSI VPݵM6 qzѐ56C4BycNT`Qnժl)6 18!2hྫJf.=bJw vǥ訷Dc[<܊CHs vl=stjO%8+(I+-8~AN@g{JѫM:lOY!>k]/a6ceC#y 6;υ0> X[c F,ηMJcbz#iqjտat'Sz=ҁMפs G߳%k$NT'=y,V!ೕ虎 C6 R}COW{eaJ϶GP9=4MË[B9V4/Z.i!wÔ\{˴AnK`6]{)ed{jˍev6 szlS!+'z|ϒzRix̖_K"O < ni{F=na7=ƴj=%p8yݱRz\^GԤ !!c:d{e"]3d캴EAy=\r5ftTZܡfP o9#P KhY(;Y s7۞d!A 4?]֨j2Szy8' FI탖cDkIZt>r Gn~ԙN)Rs3u zb&*5zX~g{|=:ӺWt9I &zH92Kx"dg^b[aP1"3 E36orA/DI5޵|ACԃU£/l!+j /ڔBp:edZW*ٛ2/QyhԿbtgxX:I@e-sL㖌1B?rX0"?(L!u<7d `x66e˹GǼU(/9`_0/JԸ3u-G8P5OkWd2LLG=7Sܤ(&8S-FI U k<~Tշ%McMMu}Y2QCO}p^I?w #~6} JnIu*ldKk #!߼e4rJR"O-!&DWq7 -<)'SrOToУ;ke{==(N{VfI|Mſ~*_MvHŰGIsW#68,~~!?%e ixDg0ZjKp^ Oqܡ|iӹc2o_3?waq/~vT'lN{D>^Z2E2KE/OGn1 Cbglϭ؂9.F0"ƳK%D]0-o׈81 :hwD7&#zEyzžy=~V.9smy,65-#%8AըLЭjfu"KoAG=I;z#5J+ׯmgDعt ґo{|JYW+]ݢ'>Ǟo9nr/]Ϳʬtq݇P/{(.I,I@?Q_.Js\ףG"H "H "H "H "H "H "H "H "H "H "H "H "H ׮µ^k,Dg{x.^QG/6|u!S\/2}pձ\k[޲~d c\Wrd6&CF!dYzlRg^5~w=r? Yu5nʄw.L]vR]p 4١4Ci*Aʴwпt2' {5wѻdyO˳ i"L{vvyy tvk5zi7~.vL[tv75zxކ6|zP)_X>]~8;7Yci3bW&}X&b:ݕ]d{/azL0w ѻ9mi]o?^383#8 Ksz0}vcsj^˥l9kv.~e—a-Ich\[99j>cVƙ3Ki/ٵ[q%I^aj^f0L'+Ǖf0Kd/ =4"n3w?3/0]g{ 9nuKŦbBH8&>r#?=?e}:2$c5A2w֥S&BoS&˝ϗ;uᮃ70]?N2 붸 Ǥښ)8T+UCpИpE,L C>63t˜ SdL&gg\sR4T93WGܳ$C5S#a^z ݼ #9e2o1nj?u; ;SU_ϙ3f;ʹc`vגzA2cvr<):$3Hc4ɌP"}q tiuo Tb(ffQb gnjD<<(ڳ/|DSϣ!W Y#_Y2 DPزy.vѣߛ΂ҡq! J%65iN Ygjn^Cn>6Z"$Ž[#p\r5{]м\kڱ@= rAg="B,ڡizlOC %zH?(DSe'.@w3@c5O0ׄYTF7mT~iz{aB٣G#ƴN׏퉞0dѽB/Oi,Qir@ >ӂ]N7 ??##$ B0 ҽ5^DŽy3~먆=:~5Ut"ˊ3%*F`=}3 )Rpz9^[`΍&(Al:fWuRH&D/=j?R1 FxK'=:&W窷0 &E2l]RQjϥ7Q+z nI f܅z}r`pt[-WܛGŨX ic%=1UA<_QL4tDzx. Pk2kO m"^2L.v9*W=Oz |G+\PPf0 XHAp! F0}D/H JmBy숝jD԰,ѳp\iOQ=\C^z{mSBܓ{z3)Q 2.M%ɣ ܒ=Bktn{D {;M" qoB?RS d~ ޷^h&՜޴W',$Nc_~zۛĔ^?;.KLIKEā^vH=ru:zi=fvy.gߦ `{? / ?'8S_G=+ ^%zeOOkղ57Le nN82 ?I"{I63ڴ"5[/K[4# ReE^=\t燸l +8hc4;R>O8 ӛz006#c>d!VNO="&9=ܜ-7\t@zIsce{nIu̐s1ѴMٜ^Er[/;=RZ^^Y~e "CLcUFꠢG c*j R9 \܇9؞UΥ=3=‹ךyr@ύ!N/3 ?ad/ s%֍hԾhYk\g!7l@f'MzjE{v+u "tʤ]M8isCP#g&OJWʔ3" __662۷Xx'ۃ?b`C݂@S(~OK„*z7f4]kYc ?{=֜[cl&z]O %ana8rܢ!H szXY#sm0~tA'}NX*p^fJ)8.zx7g4#A]e(3Ă{K^M}u˟ *xsA(yL\lW&yPfk=\?b\sot܎yDZ`ڴ4YȰYV>!\cdS̘D+U]7J6S0*HBƎ[O#]lTaEgP$l@=Ds[+Yz*6٩%ͣ^_7ݼQbQ69!ʤ4c&-8iЏ8'eks:Eֿb*k)˵o*?;pNy8`qUHB_C>z4Ыܡyy$_b@7I=QO@C@C5Q׷S2R\ |jYC冞K^yIGa%>)MR~S_Sʴ¼+GbBQz+g47mz_\oʷ]j5ѶX*\~?H}h}#H "H "H "H "H "H "H "H "H "H "H "H "H "HXǵ_,awWb޾;/^O>u-oFBN~;mmPǏS`y^ K9!71L!1BU .Hta  s.[<ґ~2գ.&zNEswWPgȳLَKnXW\Lwl[cIźCV|.,V6c](B ފXR^}7fG{vݦ5L.>H 1hWWusw>r~rt~S_PuK$[apW7|i+IϥB3r᎝5`*BJj l=s73\zA%f3/ŜSwJ`IΌލ SVU,le#MwBj_t[w)ېɴ7hơ0ZSʱ'+J3"?K 7/s_v MC N%[S[7zg)s]!$CY lMhCI?(#< 5<'Y d=Sc=eװaEv 9 hP@zTۘ4?6tli-WcBw Δo LK5;Q =z/x']-^+­b!Vzjt\Ɩmpӯ@l2$9El<zX#[B)MCQiƠV&{3#c?U[+z-znU_Ân5hտj?xlZOX`{f=(Rs4G(^<"VG#Dô,M#:sDdl`@?ƤHui^"G=(|P~V7mȑ^8K_5h8DTD˴*W(cŽ%ϵy'c:akE!\fy mzܤ"K"UeAMGݖo 3Y#3gΤPߧ:HD6K ǚ,,JZzEyKjXx=VFsVFmR'TM#Y-lP&X*uU|:za{Q,*( kA:y]S(q'D'm ]^$|Ve!pa 9oemaNH←]?hSu҆VG#$УsDJ7V5x{ U{p/tO]Y>#_'>C)cqڎ~۰Oȏ3Qt J1A؆׏=0 jCXSfBw@py,m ^W`ȗ㑆' M<1:2o5and^ogI⧥̖| 5+;d.XC=ۃƋ+Ao傞h @Tr' '/U08U>!)éղ9b'?~^>gԳ}\SPsJ`y2ԙ2c /3fIVI3j0|}؃ `aD5&*qh\էcju^׹~XВp[";h3d4>Mn96{=#MѣD s'ݟoW-O.vvk}*vўJiI)PgNnun87w?=2yz).gu/G*\oݍaTm?3tr jKDj\Zt:+_S{CO.[Tf~A9u7;-dT\#$Q~Ųs#MӁGɗq=|ubUx?z<.y%ښm/6_hx"=[N'+?l|k= w>'z7/zډ.Ţ3s Ӓ-ފWp~ܲ3s(4٥[ڕUz>~s1rbϭٸ6`.+\Lx^<+Y>nuisKC [Lc< Ƈixmz+ZYZ,TpQQm_>3}+瘭,-, %Ӣ6+՚~݊텩|v~ =ە=fN^d D`nJߘt|3Uu^heΥy.RT2gT݄^7Uœzzɿ=[c1$m0g{<KDjڼ)&+t=D}^KYs\7*Bo`/)+{dG[ИWhr᧏g߼3Nbiѻ%3w± SpN9s_},gMQ@QښuCE=fd]MT*>a~ziY)ahrokyf@G9s0ym\wvKg@vʫ3a#"ѣo^0qH7|V5CaT7@bcs07`oܿf)&q+V ۃ^g^@/d'^lm%R*zH|/bAw҆I{k2c74]طR.HP|^Uz!'\l/WR~t@zz@q@f= ~lK`ttCtcDo6Y{yM޹R$HZvɾqIA!ˬP:sFf ⹶޻A:rU{ lWWpe@ -_7̮'KqR^A1W#%DV*'N8qIPh =x!#==ٞei}6MpY.k ^^/9co{AKb0$'\ec$rI-%ҦP43ֳŽMv:Ee`{UX܋MTÎ`Єn>aSsa0؞9➤sEe^ )$Kpuʜ^cl;ϽgT2e`,ɳ1prZ,jZg{3J9S_BoWQ7kiw3i{=#yz.C vwSڹVYֲ4 i$laq{zCc{q9wb)3&[>+T ߡvҧf  {fNk@Fub?W<&ec~Aۃ|O Fj&ۓ 3!k7klJbƒNua7X:݁q{'qjFv^ 8˹H={\)W w!i1N]^0u[Zb\Cę@OBd'zZy.#|CbVaW|rSd%v )K%}=ǸQ+D&4v8**+^h(ݰOo{Ⱥ SGQ|YJ ;#[|\ـtimCgKiXgBDъFs~*ztdǒ˕{lu=) 9*} ` Q,yXm<Չap^e)0Lx"6MhNt{~Whh%C:k;^b$JUCuR;)\vR: ՜b)pv!%I..dŒHry/k<9ǁ#`賣SQɭx5U'kGֵdYáݲg3l=ms?q )y3%>Z&!PI -L"CaaOrPxDgW> u/Wњ5NWVŅ@5:U} HD| z=9[c\=e3p-ʯk{/Jrь g]z!^~Ƶ/')e{ʬzn;~հSo/}.N 7o?e:mL%z_j)& zYUglw\Mlŷg7Xa}B6\ԲwƬs,Sz^~"z̯I%=~1_s_&Δwڞ3ڍowƇW&k/7foqG/eޙ/+$Qz?[~U=y+kzsU^囼sg8fm g6l*We#߬^ZyZטvYЛ֗wA$yrז?S}a]ZiK9/6WP{1۫92=3vM-tMP[wGz.1D!1$ȍ!=/p1z@e1m~hƓwy8z/ ךoSe3NqZ@E/i)U$ɝLAqnu}񮜻DxZ;&˂P?.j͸qL@&m"z} ]z6%.Mb{U"1m_ʽZ*|쮷*=z99*MYoYT>9Q6y3J$ӒL|saqqgIzubEBKbʎЌC/ȵCWnX/ =0tvO- by%pt2LH0#,3~#_WNULimD1HW%yqw7v,c6ɱߩW<1E{Nrd.Op7VO c͊)Ѹ2pKgʜM?-my'FroxBLorwIeJ5d}y|yͼ= OC+zzʜmW JVMRMqOmMJ'BiuZ ?j6''c%ωbPnEqMѓxG_Z2*Ya*/}=|(=i2+qX6=5ZӎIL܍ؤ,5Q0~o5hC$vB#c{\l,ӌglH\8l{;z?zgzzVwE{_M،z_=lދN 4-wG-䶁^V AAOh[aIlO8n0F`}*q1^[,{qD`:zg=Ц%JG8"ݳDfob<LCn^uLmOHmMӾӣ޵ف抳VK\.4dṫg&w1~ٴ=K ݻpLI]E/̃:3cXMˎ^*zF辡:KHORa{>o8A5ڻ1j0oȆkZsL'Cj=}9[ۿ/̕U z[Vx{~QyloW6@3 Vfbq-uAIK }P{V9 ̴wƜ^y(zRm:z?СZ,[M! SsΦ{<7У0?':=@T⠛%l7oыьz!sdd1]xn"%lco_n"ͤ)k> =\^Z5Mzvlu{4su=9= ?^^ЗUeRmx_2֞>zzKH3ϳF{}NE i>kaζ}z *OΠ'&\ 3,8=O7玊@/2us驅g-{lHQ+Y~^߻&Bgh,#TxB-KG< yerg\\' T7dWlIGb0gda#nRv% }wi2tW>hQ3k_3pn nW1x+bkǜaܝ-C~+82=/zuٞݵM/:sWWiu>~q%o{sqNr9;:/$oEP6Wm訝S^>h>?Y D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$u~$'/Vj+{}W/'`X~BT9;R}m*;_ympw0Do{ ^;l{a.וڇ\E6cXw*ڗMHpgNnք熅K>\ Cg3U4{aѰw;K1|X?^47]0|] ^ 17,i.VE}rP~o l`o3|n^?[m,1"6TINk >,f̳9?fXޗlOpUbT -tӈ\k_=&tv{>*\ z {>,^kӳ^k4e8vȺ\s{&Pfֺucu8/jTL°?;\lf 4ѳsGL)mW#49oyՃqO ,c pt7iCAen \T.DSsvK -ӛR3AwXJ^$aPlFM&D}li&63mbZT_m3ٹP_3#u+b{g7hIyt߮-r,?Έ !hvΌד|ZXK~>C n5.lxG;!@l{Bo#ao&* l͇O^1K>jٴ'szDGIWaqMq2:G4fL! ΒŐoBdf= BladdTܿ5jtXQauI řzQwv>PʟrA%!̀^^y_"Yee +< a~5SCtI8V4"뵌3&jhҷ'5y_O qT'NjK87X*.K@@JO+D0嵒sFp1Iʷ٩,Yȹ `v'E s=5pjt)%# Ԁ2 JT**sw7z|\@c43 1D[!+a8#ziGOaF6ޤ\HLdat/q&7 0^NG|U^ > m)/~Z r3*8XIʖ\Pb$\8 2l;zC3$KO^xHRq> z2@C//OX>nfe#m앬=bdcZeO~I&֨A#+?\BH2j;Ֆ I. zBH,UVq1,0֐5liu_A I᫧) =5.kLJiJyI=4`OgO؀Nއ-S=r:eLOqWCQO؅ZyG︲+kI}jA~A0CWYL8bgI*-1У-%1F*b V:W!73HY=F![%Ֆk*LGϠ̈́-@e뽜dϩEC՛\bY^ũ*ɪڭGSDfkKv=k3H_$ ī%'52O+z%XBdWKo@/m<b7;waL$sz4Jإ =e]0p5FJ==n}Wl=sٞ+&zトƹab>v[LhK&Xcw F`)wTQhEAoa%oI~P-Z s\hOo=3G@=`]%z7ޤ\$d{zMKX Fw#mɃwyMG/H'5RY44Δ A;z楌 uM͸$ʶse{SНw׀Z\[3r͉59Z=)E׎Z4ᖖ;2Ny{tjehw-t[ 7\4tZ#a==:Ÿ|X"a枛 ^ڀxKYAg΄we{ :e[pxn*pC+f#1mr#Hя'$s#Za}73AjF# aGJ,DŽBoZ&$DC{O9HQεo,}+hxtkVf^FI+LQg5&qH$~i _1P_gr} gnG zcm/l $ԷDVk38[gB{P.] N =O]rnJt.am\&44y2zշWat)xOT?"CRQ=e ,>s=I-{}{WnjbxX-Ƞ <̒M \,:FeZ t^cz`0 f7+ICSjU?BR!M) =#}>Ҵ=cUKK'mr+!GAa3v"#EKIBZiՆxb{g7JϏLoCsGIƏM{zYqshh_6$iK]K`M^8Ɩ1Kz.; ӏD2-JZnPMpL3GF&dQǺ/RF~83&ICct\T/>elc8m]ժtq&Kќ̮?2BɧGSqo4GG| koѭOMQz_pfkȕR-l lG,j3 HMUߞ`nY_X]}ue݅zIWVS 3Y.NOyf(])YȰސg1ϛzSW][D?R`K;9o~\tOlgV֗W{gf%ݶc$i97>^6MZ__>$%Nh~$5bg?eC~Y=s7U&ڸ^6ѳS c.6[JUwD46d?'Ƚ!Xir_Oc{'x|(Mwy=*|0G/\m%\_mVʙd(ڛ* i>I]gV)YkϙU+Se0ymEalmiK({(17QsB#`(wRTLoHcO&@ _ ԁoI IP7C5FM*sI6{,eLgP|ݎ2j@ixT]^W=̔BIO7SY3TOB C@r&Hs>лlo u6vLtмQ!1dlA5RNfO/&.KzRZ`ildGŴoP?mx[.=|{a* zy'&GI uب gVWX'f<51iow҅SkL٪/ݎGpGEG@U9*CQ4UؑyE+"4XŽUX^EoƷIgv؂nR=w)i_ʟfze$ uY)@mvSc 8 ?Ϊ(@OzL0%0++Dxcۆ2U%ADbRX9%H"d;zALg>g=72p`{d䯩-HA8 ތѵ('1RHZܐdTe(y`nI[7 M21'YpS5E^@oufW9NUz;lǸACEE:/q:z) $B|*I1.DJ15E?5<9~!z`JFk=TE P"zDK0E,{ɑ()HKfEfDhHuEovFb+6&8~q-9|rcB~Eu=\3?QaMy.kN^<"^z>NǢ7~.`{=E:4ȥ:; JΥ\FXqFߖӐtD Hп-۷QteQR v^KCZ~j{z&zS<ש鑇kF~7"+?)9W& MFjt=d\]ܛ\.YEmMKۅ^N Cy9x.;a[qn $5k-wJ ѓ^g)qu)Ӌp?s& km턡͖(L C.9Л- C϶ e*:=M-W\!?[\(/ؙ>aM"?+pX#hn-#"rwI%)@nj1W/+]ʜ]OCtjcq}B:W2l4 `{Վ@/5l pm C[==gUgEr{Ϛw{wFroaE?P)r1`Z/ Q2FRν纁Paצ }<&fkٟ-Ě^:W-hr"'w+aysëbQvhGA겔:\{vg׿g~!vb8ySPۥ _K¦O+o$K6:y {_[.f7}|k=io"ṕ^bR>򔌒Uݫ%wx~V Yalm]X<'?-;,M/MgҺbW^6L3g"LWfb>z.͒jVrfҭTO=Uauxe<\Oϙ zz{v`d.~b'U@_eӑWަJ\3%?m:PcC.P~ c{RAm\LaC RiPjPo|ʗ+{~Mv, qk̺""H "H "H "H "H "H "H "H "H "H "H "Hh=UkRܼ5 l3xnLoDmIJ\z0wdej&X"XVWj ,.F/΢έ[6ssΌw޹+{odKCo-kĸlc}gmbH;(&Ť 9Z4 ؾB/k{5E`^ۂRkXu05Vm}0`߬B _dhafeS ؉y''ÿ'2*룋lrȮaG **#"ނed݂ҍ'.:W̜Dc,cFYl1Ḭ̿4%.yN VHO st:Gi E 3nմ+qm ` F#HKPU3qZOUTޤށlP@-49S5S۴ dv2ި%У>(2(p@O7%L/Fzz#16?x!b]KJV?GГ̊Ao-@{T84-=%-IBIP˻ ]R,=z'fˢEi)P*`h-7CEKؖ %T erE/R\5Zi)sK VypwB*!eBңXYs[%B/1^/jo9JMk%Gz 5(FLZYО[IG"zѐ*SдHr#5",'s|۹ =Ƀݘ'Ry )+@bR٧gzTT K{!bOmKjAoO{"{} ?Q8!Uq鋥Vg#w*sHi%`=eRx(2;T5V;RN9;YtoW-uBxT(irWԼ#yQBKMj[r[u/RT| 8= FitY*_R;.'@j[~~s =w˗1j\nÚD 7"H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H >@,»-AjeU|L~2/\Rb?뽾/g 51izQ7U @ϗvi/7Zͷ<_̟,^h»>wX?zN1mWV]fM_̏yu5zίjrz|5 [R{˷{79.Wk^9_(:]H+5ڛj;dݩrݡf^mD/v|GBzj;-ioBz nQ[ ^Iċutu4/lNbl8+Fw{ǾN‡qÏYWu2l[EZf2qo|zu ] :WuSW'Z̄^A VV)]+FB{e2vp3m&`;PmzoeI90#BR:V3w2s5e sp9u7acgM&rʧ6E}YgBo\ZF%j9^I3@2nYd/w׿?Yw= }¿(*{=~C&YT:ȆI|Qf-/ O4e+H{_@dC B֣֧wbn|m\FMZ8%Ǐ3hWC|Ύx5Dp& -~xU|!Fk0mȋKި֮4o|"W8Wh&xrٖ&!|!DU')Y'uut@Uz$UeީAYeGy~m[О^']ȕbS=RBk`AbTT -.$=h9iufYdC8s_'T zC%U^R4 >PO ^.޿ T$*,-ʞ3Kd襯1Zݧ4Df\ =ď*t@/X4 @OFt ñO%&^(Z'^ iOp*QO4f_\B">iͬSAk^@i7ڼ&Zm頧+ߤгa6 m8xSI+.FYg GI`KԎG@/kn-=g=RI7N fm) =}ާ'!}/ Yq=ՉUMP'IW{fH:]U*=I=ء{xwo蝰Ռ^,˶9xHBCBSz\z(p:@oWq%N k̝pTaFYϞ!KoGNsmƐkovm\UOk42zW6hD{do?l.û(X=~嚛x.uK,`9;,lY$cd}z!Jǁ=B,N/DJZ}.G@Vr.MYgۑ|,yQGOvvГҺxF~hs#TõBe=ĦrB:eG3zA{zddNO,wJ/W8ZvLsGAoG; P^ؙw[D dːĥBte4z'h/]רihұĚl{U{szr J=xGo=fq۾=5Wp9%8o\@ gZC zjzV7^%cʒ#i dQi8 z2E WJԁ^">KWB,wme.Hkd S"ỵq#zBOȓ i`KOTM%57 D,G2BOgt0򚾆iX$āDtLѓ &zlCú5f~CPq@XeОN^|NalٵV=1&GvGOzu9H~ NWiFmgؔDF.߃R!EaB_ș]ujER!* fB$>aiy$gOZ`9]ur˜hOFZz_xv\] s{CU{p=i} A6ize0MiY_C:y9. p^{/9z*pk/ B'A~Wu~o3R"Gy0:*MRc/PXsC[X^ =>{$; ?bK&d5i(A~z ) e~a'KC;Pxux R5wng9Q#tUK{! =퍄aa?Ӿ u[ޚd,=-yzs%qЫj%!ZTweس . +!ܜWiC)r䱨wG53zD8ߙӟ}}:1ʥ zN18yJ\Zbg_J'1P:IbFo"|ږ4^ۓ>'J Yoz(]2N8:rcCXu6|F2& *s& "cv.#֬o%X]/ B9[G~Q>)֞?a?uYUcs$`p-K>wFo<$;A ?>W?4@3-STZ$(v =`ӭht߫=l/:6o_HyPl<zHTJ6>*=NqPR_nГl=-sNj8{y7=+|5{}K /Y y% Hmjo놗 mhi}ntcxxS#シN._{lǷz6@gnV}o*Wo7цktZFͦqFfq߸2"H "H "H "H "H "H "H "H "H "H "H "H "&eT{Cu^^@yˤ$,-{g` )naa7Nm9/ow :_6\VތUu&-Z.i6Ppzʛa1.`өf k.5j]z`LF&l%_\r`^*sNoj_Ī曍~,FO]otk? .ӞO8gcfvUs lًGOI?˘S" o%PLK*g Ë]{CxOUzc*oSP^7C3n[7%~SC UO2c.UMeFP!²&;8Q"qR|VarP+7ůz=V=ΘPiKuBwP1W|Sq3#/e,`n-)3HnK:W (}o2y^j㚒ܔ˩:ڡ[= k_g@ YX-e(@'.+\HeW#"nCGuB dc)gIZz,ajeF`|¼ۆi˻:-4sI;J-j)P5s=zRa($d-Sl%%cqz}X+eRW&ݧ!+y^[1h BSz(M&Bis*W;`ÜzR9C 8E)Ua Ϻ%~7m'ҳ3텪iУZ(XR_DWg_COj=ZjN3zw+5;JW\)&"~4$$ Ռ0RNIYWB~ޭ%jңd=57-=*_نP'}<)en.j,7s6W(`Mvѣ7աңnxk*H15fғʜ!~R^5)Jh(=j= HoRgFPifQF./ѣ2z'-=4 l#8BϹQƼ'KLaד'Fh2P%Szd0; ߫i(#;}΍7XZAAzqSz@OLI*C{5U}EBQB,wN^J76Cǎ)j4.2gx~.Lrne1B#Y,zN8(3`&5h3iO Blzmo7l5w̍Ȗ {B=*N{%2E{CF2?6Sz5~6Tu}QYA< PQ9C? c՞z3=_']ˣQ:zs\[#J =5) _3d#cB؉3%4 ڃ35PUʔ=Tw5|U4uIPw3L19tȄc&~/=ay!NQY*c&лK)'H;9`QTs3U=̞4]Ams 4CN75-ڗz xʓЇg=zeb:iUsS'C6a6")='eS1pS7*b<=*=F%T4Tv͕y|^ o@OMJuɁnb1׮ =Z[Y7<:(Q VB!@$f^ =Q>u5_Y k*66~{_޳b b#gz.qьtEj2Rkn%yyʢG@m[G/߃=D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ \&~P.]37SK"OF.);z2cڿ/e2AmJWAoH~oz`"cNKZy(^هV{LU|Z!duK~?v̘"nҿ\."i5mf}B糪~_t@w?We2z.-e>y=/o p#hϼ.PHW'.*Fj6aV %5IΤ՛@e#8*SkHR$* /V7[ӢGF )yp`ظzbUw|*D'ď&+T=Ku;_ VuxKJ^%ޔ%$koԮ)j&&8#ɫ="+!|SS VB&A{buZl2{XV4 ID`%4+̡Ƭbj7%;g˯劄 %>I :zZE%jB[uproυK UhT#fBY@ fMAI]UԆXODŽCſ!E(]Ys$Гs[+z =,c =O1;=R)0S bZz$Ԃ:Π\z@n=c{ô!'2IH.p'EW|VlE̥cPBns1E :LP&3hoFoz=;'~.8YxDo o 􎩥-H1BI"i6I4RX;G"3[Lo|߭*񫢊3@*лYОTlnz ۷Mt=d˝~^# Ze$"]ϙoA3(w&m=Е[?$tV{h/<;\^-vfz~%ii=31M3BOdk0i"MHuU- w 5P\h-CtB˻ CcH2d~8TĮ&"q2=}G:"K'>tng9G[=*\;'VXXWBo %iƂPQwt-ړ-X.=WvL1,aViwA{E{3zWz^Sz<˥ڰ`sIJ蝑25s葠흊(؇.c!YiImI_Ak+gԒ,f`H1 z'DI@+nJ-= KGz]L n^ŝ){-8u|#8{@[,W #{ҍ = pg}N^_jwKJ>=e -G=L  VVi8Y efvY4΀ko=hY7#{(QUbd,r?Q;z%.+0[a*ܨlTk@qq'q"B8N,2%Zqq'h?]cW0%c =_jC(z|Y.pND^4ߒp=pwX.FJS,B %n M|A9{FJ@@% })"$9 ~˿JZ:vq_V;BSA zCK sč\Axn%ͅb&O`54&YgK4Myĕ =ydAbBV+?ץzd!lzpY 3zj5CXc‰ooh/_I(Im41 L:Tɠ-%1剦mAB-W0ROJ3GRt I< KSzt =g_rOVCxHa޾jOɢ *U 4jㄘrQ:A%2+3ؤ>c8F VB< aOɚ݄2r>wcmz'8٧0Jפ6gWtydJU$*K!9+J?`U_xD($h/'A{Mψ9=__2F _z qz@4BWiOXy@ 4yf(ʃ%jBo;zj\%zg Ն$:fi qy"{aW6Г܉Ŋ aY5Mk>-${m4}ݘM_Y'=__~#fWX)+2b3P!Y')#+}Mk>As895; _[0@6[JaH4= =hv]?m^k wi ڕ 9~m Fi; XωG4XpXd 1m+Ngn@Oֳbt= bēCOno\ z-{6~q e =uM^" A}Ijޑ"7[򫏵1Dz sz󋙚|ɺW54.wLo5_ڱ]K,kJesS]h϶-퍧zws?˳;k-rȧjWĿ{v&'*[~;ٿ_.优bS&[ӍoG -{ƯѥcY>]o"BYb"H "H "H "H "H "H "H "H "H "H "H "H  nb꛺+e*_v,o?t D1W jWSmQjJO3U̒q8^RPse\L:O}%l&.) e" d^zW9W>:OZ(EǦReNo39ΤF1B{nÛ_=nfW9< G;3[4(V)$p !87߶ȓoiܨƇ}L6UZGʼn̺02ePN.,ȌyѫB oRCa;zO5_i@#G\\K҂{sZJYYª` GS˕~G *)2BY[- _%/WǮޜS&= У, tҺ)U;i(eFAzP*ِﺩgD'Yhr[no;O/dBU{-޳)=n?O䌦t=?zԙx 6 $p)S+2RK#@ro'˽=xf+? ozLL;^2g(8,=d=C9!Uq_ql}N ~_qgVNdA{BONmS+< Ugi`]G#Q(7;(џ_?jI&27e9Yn˝$%q4Kod۴y7Tt2˱/0x&K=^ΕH7#d-=rac/B 2GO|-.ClNC@;S$(8E m簨(R XXs]H5 W4%?NwܷiBr~[鵜b{12C^z%(K:Iu^Z%-#/Uׄ^C,ۖj•KԘػk ǘ.;z|BОəˍNtBIl!sWB)kg*#@/3xQ؎̴R5N)ءKJ>@ (p'57Ϸkoi/k'B2ő!?C+s Ph\;z = D'-R1ŗd̴ə"RI⸮ ~j׋% 荰FqbD?BJi\ E{ EeK'y#DGRɞ?)3Ȝ0]e9w=r--CMO%6~SY.H QzpBϧquD\蕢g唞AOY.:={cd&L<=>ҼG;iDy$RnzMҨC**?dbC{?;zV2zjo-'>R!:V*؎ў#Hړ;Qvs~z؂gSzDY{ 53D?HgR!Z-4t8jmGfsOɄR WPeDK^=Ηqz'YЀ$=[ccl\q{|Jj=X(AUW܈T Ы4N<9۰EeV*9awKЫeSǝHL{At}InN&8,ã rV)=\F*=h'zF}1&#!I*r)B=M$J<'ri/FFQSGUr~e"c aFܦ$c\gXS9!^m!qcIv+D".\!Ezg!uJ{l@HxAƳ==1sJqz>\*3ɨĽPigo3zr1-=;=Y.eZdt"Yўp`2 ʞ;MGj6S4? ג7DzڼOx@ZW|e1P?SVq^;=.?d$kዏ^9jd@=;-e\\p@s[z:푱=:0H={Bo~핯r4+0z\gM#F_7d|B%R1֣,+ ք|7C听cJ]AF Yho,3{=zӘ+FwGB猻Є^׾ ,x=z1/sУ5g z%JoB=zRX25@ZȊj zdW![vA{IIԨ~B4+A/Sz'9J<|owߪQQk,d} =B-AWW]k| )ơl#ӛjSOo?0GBO)j?"Ut=:$[ڴNNeuD\'cQIK,l  !ul=ّ |wx ǟpyG+=l`p-EK]$|Q{:![Ƈ(^]ٕHYuZ';1!I [MrOքm?,mF _)ўD Ahbko^F)>6ݠD&}@AVYa ĚK~ 9!y7let"rgGBMK+uHϏ#Y?}dzׇ?QОB:tHL7[Fo49)PC8} G=;8hj o/?)4wpx\b$|]\޹H?XzI`\Ͱs`hG2zSJ2,ۻXqꓷGwk o}'WEW;FalʉzeG_ }}++l-rW6ѓ):?Vm^a[gT3| īum9NY^{{[m+iO&tsO7:ͣ}g٪V̲^=Jm(6lӽ7+/I {,IE2;z隓mƺAF7js9Z%~V߄oW,Wʛ\NoGui?h3Eznq>ɻF&O[Jw6[~#_H'Q x핽WiOɌONrNeJՎRn)%=+ӁI}p7Nor1x^akXsW܊/!Դ;Wn=g,7|uTK[EYA{&\-y~lWҳA|O65K@^2򅲅w*5XWȫ=;[܋^l7K>Nh|g(Sgp9$CU Z*RĸE_dޠL|Nv2>Rp6TITjp g=I^wï&l^o :zMFq^iTT $RxSItHp)X;U x(u=@[_BćГ*zې-Jtћx,էҿ2 HS-?Nld`SةJBg2e% ;j_ٚ>pH9][wJ\"Re6ܾjnAC-) %lzV QhʆN7b|1]enRzסW-8<3qRGw&9o=)&B,%eAe^pRX*boiQBpL*Q#_>Sq.}V4O+_~Q$U@ b.^%ڮ,\ږX.l=M'襡3K#4f=vz&ihֵx:rs[XVQ^GCgő:pӤ5sz֟Ổ2(z#􆜡h2$8{^jcKG8$FM3KJL>ziQ@p >fIEZ+[ 1Er tD 'i,ὖ^sKΨ!'RcR`9^c ^ Wc=%3iq>i$ƒh렣 Z'9hM [zg=HtB)WYi5=\h͡ƍ0_]߽f㕘Cu~IEJ4՜8x@qה!>*#ҥzmu3Oq2J:n6N +FiB fʪLav5H:2c5575˱EXNWj_J1זLq.{E*~OLY@*Ljs֠&+_6f))Zk~y$X!0B%^ 7mM'~?cUsrF$Cjܜ+w7DހQK1[{;:z{cϜ ӄ^HzJ(Do3o䅞|uR `N=1YTBOH=!9 ztC#s?6gW{ϵE y]s smƄZyA&Ɩ06 fkgAD zd,5m~S,jߟk]wZh ׇF腠4!}KDđ[y-ؓ ^)Zi|/%z$[$rak\("eCz-=\b3sO_{ Dy?kmf@H'$c =n2IIBO-=nF۞۴J K܋ BQhfK7'F@{.j=}%MƐmw83#z 'LʎJ®cV!v vS C\aqNs,#\~JGl?ʀ3._CV=6T9Pharsz D)9(#hZ&  }wˋk 3@ʪOKpGZ) 1_U8P,Ws%/KIN V gmh= i(=ƷD|$bd08SN $@Q1\@#I|uшz6l(D X\Ptm==|{'m8qg SS'½\s+%Xj*ww$FȧʲM4sz8ʟ[ e~WG[]aKCVj e.or_?8bM]ŭ֬t5~\ %Wk.ij+ݨSz7e.g.\ %ڽ{sRp P]ksa7#[5n6QZwmznӸl<,XӾZ,.Z!#,."H "H "H "H "H "H "H "H "H "H "H "H` }ľ h]g_2Kl2]֝~c9߄^|B^O7? Y*JCe%SuݟLɌq7uڛDp3֛@l-#usX`2owQwқf#j@o_-ȇ)\OR^-yX/idf.ϟ^w2ǝ^,~z"̶7=TWc)z:zsSrv%#&&6?|g'rGoKn=)w K\Y5 +.ޏgx>o -^{5jɶ,zٿuZ͇muݶѳr; HWa1k{ǑVZoFzNl f[5U8S̓w_&~Wr~ 1ʋumNOӼx)cY-=EHC^R/(*UzŠz II0aV7{LodQ cnuO{Ewwi鵽R-n;:@f<8|( e$Q/1((J5R|;]`oK]([ N˔RL :\0<upW)˔p^u Q0?{/uWڋ nmϕ2jzjs)IF9=\I xjSen,sR&CgBoA7NJK%kۥl^ص-Džn)#1+9wV8axʺpeZoV{/sLb &OD~19H/ڶemَ^Ihk2m>7NaZO#K)*'GSmAQ~cQ WD!\ɔ]LVgU@!cYyI2Y%'P&)4zc)0NSxת/+U^)A0~UP Śm&OD"M0!F񿻵/9׿՞2/Y 31{RhJKgNk.e@ !Gj7xP2A3)Q>C(slj`*(.NALo τ 3th,􎉹ReңZwF o(AK%P;DvG0hOz:3cp| Ӳ1kgT+m>&/HԆG 0#z'a?Dc- ]9s =py!"RqftAdezG[d)@Jkb1ՙ lcnW~L1*uYmieN!K }1C CwUuCg_s =:cWk'G(d#F2L!:A@ {h$A 2zHrh=ZJbZJzNheBϜyޘ/e"B8\sqRe#  uboM:;r!z=fڛҫ8Уvubn)¦״C{'p'cǗk荜v4!HL]׻byў)}6ZAK0وy=Z.L)(jBOj Q4;iN{'^ bCH0֍bRbx@:) rAOJz}@σWHVNgvsft{DJZ?80%D]~CڄKb=xU߉ߥ,ʡf'57-}'mk^e3=\b.ԓ{3RH`vGX4,%Am*BʾӞ 7#ЃvB˧u! 4-j^\vJ:'~psx$ =85#^PSb=D]yh/ : #CBOzZSGHgF[j91'5(̙ly =2zt:w @̵g}X$m`/{,+UR>~&HAI`n=ܷ=!=X'\^ޙo݃^9kK0ܣKy|$uIϽ[Go(-)7\g:QL7\$=aՔ <zt;z ~B3g52ጞN$A0ܮB D{V[z!Z9[XڃV2\8[zr.]]9=qhS^S)Kpm=(r㻏%.41eԸo$ھƄ *4}l 6f%t~rڗO-+-s+ #l U=';NbYO{V%!՚F#xG=)Α/Mh_aMBbSBJF{ateЃK`,@{NXUxJF N۷5 z[ *~e)MbrA0ڱ?&y"2G?sQ /5I 2$T't2E{F=>3Ctƌ%HH*z'8&Ͽ\FWWbt Lo+Q)៊} 3z5dkXMd;A{'P#GxdLh/? 1ǯhг36w/z.?Z/B߉wYU)tD}l?:%Oq1urO/H8y|%ygjQ {gG˛g nE֖/{^I^\&Wz1~d}1][Hu8 |<ɐ ɘ#"q֥eWE|hn=K061/mN E[nˋddk|-%3 D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$@PY ͧsd-7zpUFG07Tod?|?R&N?e“ٌ6KiYzOx :MW5}S>1{|=&(,YvFzogǜ!3E=;M(ZKzF=}B82ܨU_@wLDdRU6 j@/":fKrJ b^~䩺pa7{5Kx]]))&r|fұm"@}:.#Mrqw]fP@ދ;hG'Uk*Rh3,LTfE}]CL1{(~l7~Ԭb8 z+-5*zl r)(PG,Wټ_K=1OTLY T jQhzQ|ɸHo3<ғztsz.+ۍیoFz`NlH!]y62*=Q:މ6 d-S6J/wDѣnX܎d[ћP1JҫՉr:JKfXt-z5o]S^Q(ufPČWW; ۚ*GUJ ԒWTZz* q˥6T&XsziQUQ.ijO$)QZ(z[%6oniGZJAU1zEF|,S4= āvBCvODzOgg};{{7qxɸ_l@$ D@$ D@$ D@$ D@$ D@$ D@$ D{cDz:n_%E( ܐ2b2@9,X!f;H X,-'eb{b q6[}~xgoɞ?>[?J@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PGq_?<=Y&+E@_5 G,/Ok6ɓgiT simӲ]޸kbQVOk~H1d;8-UܞwY- [X< M:lڰuo?E(}WM9/zn~|Dmxy8l~Pkls^&ĴlA}Z/\-ЛU36bsy1t^^|E}Weoh$aM$l'],}Eh|Wl܁Wͭ]wVrbtKL_3VA/ܲZڝgl~fr?lxow|]}q=I6* `z4'MBw۵EZRN&^+B/.jIM䜷6Iȳ"R:$YsyFOkQa^/@v@o-m G|A(]? WKʤNˑ5>_ EZkGj+ޓֽҥJfiV!Ijtahmr+o'is]˃`s|ZIeQmZS>osᯇoP6.?|گIW o-jy~\XzmOj6n5u)k1³2m2G ]^8:y3ʤei+WwvްvxCY6C3Ы|R[JjWBoV}4=7AqFy͢yG/F!\4(|@j˟JH\ =b*68mmː׮8k[4Mb1 :5F!.rS,}'bҥ&o {]5E]X*j^? *pS(grK[wMWghBᩐ/n%!Bs/5hSfk{HM ==dS8D.77 PZj7瑯&ҫBO>ÚNd&|%mRg1qMBsSȈMC};EZv9B;AL 4-"E:MEx2HL==EU*ezu퐽ʣBYyF)w}=i+cIJ?Br|yǖG%g!moGm~ զ1h9r['U8 oU{CJt$/jsNʞzlx|U]Oc4zd0JEMB4ƴGi,JKoogL(A2aMZ'9"i=[3;K`sQI,L焞ѻOWh:zv{zc=#|وJGC:2Ϩ 0ԧAKhZ=Ɖ/g`\O41V9#:Se%WK o"p;z{_FKSW1ym{zhB"DdK(KhΐF?skc]zXzjtaL2=fy&vqHyZ^Dztd7\{\GJh Jzn׾D]?m,syq*+=c`>(?Sx[,N~iL:4\TlFc(;3eFW@-\9plܞv蹕!{c^)QQz\)2B _15j2cpr0AG-bw%M[ͬ=imOWQ=޸Ћj-e$vLjc1$tPWrB,' VE9[6:tf舊_X*B{zlq(ז-cȞ\Y==ޤ$8p2q?ڍ`,MFj\gIqC5SGq(2GjGQ؂9Tد#Ē$1QzOfv{D_h޸N^1/z^I=ēsߤWA):朞 :z^d QFG5n {I8zhl*&"NR'{=֒vj:\AW=HF'\l{sӐ>*&xıCX)Y B/T]o /,C! zp#Y' ًWZ}Epz57ce [8-Mz!ݑ~/zOd'5ң|]_ C0~@2s'댏;0^O-Spܺ`3:s$E(oh.c@YL oK~~jl;eϤӽwl{zL= ދޢqt8Z' =JDx{z \j 3 B(a%lDevL7WˆÒ[1H7B/Nƨq=g 0\ۉpe L er =ʣ:9ghzD򲅻EF~p &3 Ie,#rd!{: I.po3K8|uahē%*zyumOo*;zjg}i1f߆P!:E(XzlA/4Y9 f4UhH> {=}©josYG,ҋ#/RӱW)u揾4f7+L hRKQcm,w'(HX 3&Yϝ+«Pwȟr IzmDX.Ӆ\IHE@1hͻ=H62%[!fc(!rBoD7ln^ok~i鲷XNj+qIg Bأk֌gz3Qm}rz:lPfLf'{q[P[um6ڏށ`ZJ>QX7':q>+s/Zє]d%~KeM7CUG82(3TokKK'NoU;uZ=ը=dX 18Ыy\'s/ٖ$K7 \(/ҋHDظ<CT!Hc3EKm,lzooiÏ}YmԆ||yDעc3t>!|rr 3rb6sWH`>R+xf-Hm>U.hseUe]dXܥ$0>xgnͰL`.sYsW|GkflK W.QYwS|u1.SY{-Nvkf|}fƏ;Ft](-,_C5{R6?ᇹ"Qe>~Mx #||F_~WEe2kx.mr_2 ƅa˷^.^mz',KN*֔ҽ?{\b0Lk9{n Ri6K§~S q9{7O]H,xD/P+|ii_/Y!핳L7}B/o7-@oW\zX3M\}q?guZ){+l8P~r zsgjT`\=#=ߗD!Ze,n>s E;og؄9GQ[[CWɰ u|ݛk9Ȭ|&~gߗ]$kCVQDNA哵]'a]^GڤƖf${8rkbov~J"Wx-ެ݉{;˧ #13M؊ u>|L O?^>OSo]bޚ; MrG7cp1 <`~YO5i8layȾhܑ:i1,^2do]"_OZKgA|0+{&G{_w'Ja$|θ_ꥹeSXM-B^G[>,/eS|Gb.l=(Reb+f8|jƯ޾?2&k( ?o?ʻDx4B9ߴy 1sFm8gOY].^/YczE 8(|2VȠ|7DVc ICٸ"%7sJ'q@2uZr ML;zyc:R]^}ӓEjl;y(+"~Gh [?RBo_MEceR% ]Wo ׌dOVVa+lJ$X K0qB'IPl9tDTBmqeQ|:!P_?g]ZGp .'Lz\eٓSV ^S1Y?/9rs8{׍\sw}u*>J,qF%Ћ!2C؏[W|q' wH&gI=2Gim}qč~K RGWC/8Dޔ;l.Lm.V-qAGb;l!fI#=hDB|OVw j+<$T Gdn_eВ[m5z(QQH&$;-.f(ь3у1z9!Ę&|L}L11k'ӷ.^;6#RPđz=CXa¦ "1Qc%s×>Nf)OKC.3ҫ?ޛ9 A7AF'n&}n&G#;'ȱR]p%z!'tLI݋/BQqPb1ԃ `4#ɥɌ6-[T:8䁸/G'H=zDǓ"(vq[z8yEX^=V=5)f;iޏ+HjwSdO=s$œlYgZEi$|˜$֝9q3q pǼ^+ @,IgE[+'8% Ӌ&/[~STЃ4St?h';2DjK=c)uB H}?KPHo_ XtO l-JO>T\$:8~D!Q^ޗ3zd\+b曖5] (A9=qKgeX'8A$=/9r-!IГ"QI][&@fOo Ryl GJ|(ozѫh|N69~pHiST1#{!b(HIzL) l $}@B!n{z_"7\z.㔲V$1ձ"ә6Q1z>szO<6эPmJe&DFzM[BvOPf˞~mt@'QFO٣)G77؇z9޷TXjCJFTi'n'Lt#GEzXFpf'?zMgG0t[Z<z9emrsz)Hr)!W^!{Euםi&eXY5%* =3IbY\ae{|wY"=t`'S?^=>cͥEzuc_c%4F2ǕZ =%$YkÍCQ^~ӓ0o)!NqN[zoYNNϕıPzl90d%{,^}Vzcmpi4_SC9=ZϿvF<z "=#v*j+ =T'C\5˴'gѾ빱 G)Į>a Ȟ"cS~jT)ȉZ)M8dAz؞l֒Amu)0J31i,2ն ="brWC=Kc[+лyB/86`,Uh_#@UR(6sIzB (Qᤓ={7zH-ȼs!Ɩ\d"~kw XuDB$Sz(2eF]/'h+ٗ'{t7mG$/fGnG: !28N%7 6.1uq9e%_O&ǦhNpK.1$.e_N#ؿ na<;s*rT3kQܣ\ciԳޤ [#=.C\ eb-KTr@r”7`S{.CmY"${fyah9v5CNUIuxBn)#d:Sfmn$DD,hkGwiqF0"!˲! 2=K'>@1FwZ1'3E]J d  ʧ?ʷ@'%1]y ˔Z";Ћw$y^4N\77nk~^:v;"}rK+Ѓ#tM==L+{zNPyzд-~?>39\D6}$6k M@"bʝ.HDzL!úB4^ͻ{ŽEze!Z\GA={ =0ӛ<+@秔Dz,[?!*qIܑ>07M=TrOd;!&Z'}ao알U,> 7mӓ>ýO$i#9\65 (oP˞O`Z@B""!per\?BUsc_ o {dnjakͯJ[èK*,@Q[VnLaI&T=ExOwtb%$N8'j[|~ Î!$L3zIY%*,DdXᅞLЧsNY`^^m,Lm'tq]zhkɐl'ỡQ>z.]u\ <뾥 }2dӶF?L fS\ſ5j6}{6rì4Ś5)9KaZɣB2ewq+E_BzFx8O^T0sigm9HOfFJoް(-ha܎-sT4ދHzFo7+zWgۭTcZ𴣷=(v쵩ޏNOmUD3]şy36xQk@<>`{U3\&. ;Ai}>r|._=xzzmşKYa_ۭ] rs϶׊[lKWRî~ :.Q>lexVXOK5z 'Ho蹡 "8O)=+MD@Hz3zgg~[PwyzM/t79XGD6 0X ycuv͎ď7Jo#A}TwORPm+$CC,Jo#ztNf^<5|_Ywz8X{8p[Tڰa_*{^@)M> stream xROHQ6Axw )vuYm[Ңgߺ3ӛ5œ]`鲙}v*b{a[QÓ'a?dy֭S{=5ڊ^-CT#hsM9s1F9 1w7;aYf ]%{w;ћ9 \Ir< X}I<>Uw(gRVzWOelπ~v{|u׶>UEP>,l%KTn)=J+vp,ZSk9xw"zmMWzmʨ)(ͳDf[xf8:罊ZIE?9Z*UVPog~~\?A< =ѯ tIsQIi!3NTc)[d@f endstream endobj 11 0 obj 704 endobj 9 0 obj [ /ICCBased 10 0 R ] endobj 13 0 obj << /Length 14 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{)u endstream endobj 14 0 obj 62 endobj 12 0 obj << /Type /Page /Parent 3 0 R /Resources 15 0 R /Contents 13 0 R /MediaBox [0 0 612 792] >> endobj 15 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im2 16 0 R >> >> endobj 16 0 obj << /Length 17 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream x]vWMifZ.SSG<$4G  @ ,zPAFS!~`S@QC`2A@)@Ds)εYOW~{{>8<3gfٻcwV}tON?Sww_~ݞܗs~?S\麹ZĬw~}_儸(5?{#ooth\x?p{yh;/DurLݥxbpJsg߳Q}(;?9t}o>/NVyFzY$uuLu^voyc_x6>T!6ZWYfPUɞO{7‹w܏>]&!_<7R2"NHWO& d閹:Y./~1~>7 !]Zf/+_<uߧ?PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ \&/SaJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%D?~L-'ݲ]fT\fD+ aJ]Bc˅ߗWǨd풉h\){&1hzh+[wpޗ*g^ǦY+W;SGh/ҋlX!V-_jr̄_)pVJ^\JVNVBhظ܊__;Qqi1P lGWFoT9;.3e)ҋlY EDzc-Jod|6_\O2W蕋ˆ^YlY]: o8z˭l<"G zɆwrT1٢ΝvW)-x5лceB|Kl{ްݩrQ|8_.6.Q^3`9l(a||Lz@ ՂWBoGrDՃz- #C.d {\_,EC:=ٽ WbsO/Y\qDiյ0!XrGuzfH%YjlP@wrCrAhE]dm\cA|ggz;$&\:n+m}-z4\N!"Kpl{=|K.?lLe 7 h)n@dUZelmLh:s? Ĕ7 ))WcOBMyNv۸ǚ=vڙ*c}$gRB$tӞm&7'cOv^lyPUMjγK{7m'O>p.-_6WM>ozRy}'Y lڤ~6vJ%KIer>ъ,6qeBi֒r-[cLV"JVO м kUlBI! vm٦MRޤɚƔҹ*¬޲ڂ^le̸q^WCOkl <ۈPROIkq[5F-\m <_bY%T6k!ۤu>oږTeH'p^)"͋0y{=,${+%u@ nBl'Mpj vO2y۲ә%7ОzXX 9mmB{MCRX8ΠfFJx:͹AȠs0v y. ⁧M;I]oW`Bcګ^c9ؙ#w9P& UIu;IN4Tl%tӸsHЫ^BoJ DM„g Ϛz>+?[%%ڛʡWF У-"7[9W m-S(~gd%̈́޴ʣaU/a1tːUa*`7oֆZOz8UZ.kQk EUD큁#ۮ;hZOAވW,w'z3G쌔gE.` ~~XEғr!+*fp XGj zhzӮԱ\Ũ`E%MJKڍ^֦hATIHe :exA^GoRw 2TO7M&Gzyr+~3ҫ Ӝ9-w..H,wѳ\S5fgpQ԰Q1hUdz0vh@j ͡6H_@ ˕c?+SqGrQm2;b3_)ZRŅ ﭔkL ]*УJ-͡ÙB.It>bE xފE9г==lɑK/w/mwy a;D(k^"5Թ߳aW^տϧHoah}VocK9+hDa9#G{ I9t~ϋt;4 zYhSM)X鄭#=5Zwy){Ejn[9-Bj"=iHJ..TLVXpgoH- \E87\r)Π ?GRG{&f> v-*)"A{1 6ЫЛp)炞2{İ'šX*qh/Z.1:z34˙AjaEHGj//1sٳ\M^7!{9A&gОu܁-\ ,Zb*Kؐ[ZԹbgEk& oy+aډ ״G˩O촇I+Ei4DWB/RVd9=G],w.z#7 J_# (6Ew%"=tن9/d[ԞcZ#":zte(2"Pϖ3>ҳ\z`aZKz<@IO+4ޫ%}:aK9cv[iA{%/<2K{oA/TZ,zŖRgM4OAWV'bx«ޤS% pFaZ͊v;#z3\PRCotDo#p{Տ ={ٜd<})7Ҧ7'**#WO&=[4 LX |x&EA502w;%u.=Cnp3vdaAp s=gyxIfy4@̌-5(䥼TmXi4-䅛 ^5H;v~O?<)GzR-6`:iaG{z3H/jWr52?JzWT2/M)'2C2+@GY`cD!2v3h;批k<|`-sHG1˅FVd Wk,P^^_rF=szXϐhKĴI[鍧f=O-JqyFs =[VoHc1Մ,҃EhjXq -ku[rެr i&8`ioI zvĐ\oYEo9jI>|'M$Hnyy2M݅˭)[ej$htY?C{iGJ;dVw@z١.0S*-ȭ&+З~@*GNQl Ĥ+۠i1iѝ,,?Uz@oCP]F~UiP )6`F*F9E}rgQz/3/1u|71 ٧ODo"2IPzu.Fw^E~9=AP]Mߧ/howmx?ѣYu\o1a?#=ڻKhKmt4Jep4sFoLg̓C'># L7r) v~|q3`2#fްǑ_LIK^>>a_ͳaeE{2OƐ}#8#zo=wYODmJ K0VzŃې|==˶ =9d.@ۅwѶ$ww|\as9a^nfNp34\f@?2᷾8dy׋QzG !~o<\ҋcIemI@pI{9]yI}Gdcٸy H 3ݱypC7E\%2ɹRlE?cw̙CG`/s'{Kzކs5BG\5AxU5UktD*X[& le}[2z.EI8"KMf `)"%ZiWH,0tKOkqOr%#tfڋǶY\4'AH †2"(NGiϸIO"G앓/4 A$ƓG{ s.s-gu'+Q3\"fyԡM,b/SMܞzahiFz0jBmEw${"a87j#yH5H8C{\g$oeО17bX}lGD, Of/}g@}]brIɯt6U J˹'DnB/pclk@:sKWGD[bd_kH!s= BK}<ޤGBr+szzTi\UԞCr4{!4pC%" c|ы6 (=C,ڳ ӞEҰz27aP =4 =,7̽|grLl^jH_2>I ^ނ^, &M{V.%28|@4L#Wrܨ,o I7;Sli9XDKD#:Us9Hm Pr{opܤ\ֈz#B^!Zj9'^{aeyJ;zRrE{ z|EINJVu+K:!5lrw-UG'1\ WoczܩDz^ (l_봇5i{i8IU[ixϥh={kko$Zn[>o WmsK 7pOǨ*=1:z}ky¥AjYn^ޤ5msEo;%h_b?WqG=Ipv2om;zIb,{5Q*"g&-3\C_k@ܲMe}*?+%-_W"]R{zX3FzTRM$6 ޝ,<' J!CA6 xAÍZخLYnmNMA镽ߣ%`%,7be%%͡@d7 %8F吂%U6Yқ=UzX9 gI^HZE& a]չ+A4#%xKA==l^4|@@nύB6Z,82ie0^.80! hHwkWܹI). wlMJ Kxk1~ߕ[C {YƖ[πn'aٓvNAl8:gG\KmE?x‰}9[X[[[?T.>AwgϤf2 Gľ-KtzBG왇^3D; ^m×3|˦/~Q<[,-Bx;Ҧ8PW)_>c|Y2BB=苶S3|ZrZVnsZ3WSwtU0fJ:;vD4,~DG%:E+H1}Pc_P3.#S9_[}?Fy4)%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%Po 2]OLxE]Wq!{3哾^}yyQ?&lIoe(_ͼSm7hhLz4 & .i>5VJ+kIpx3 {^DWyƌcw_lhrLi\r^aemH!g+[ .(؟ۑ-R^{{^{[S<ʇ}zU'Qo}?R|v1jAXKNkJ';S NV'pS{#z츬xzN֗qY|czUC:l85zMl,'+n> |O^GohHz Tpqk k{ G͂^v~!zQr;'(O";.=uxpe}xsǢwR~^7dQI| A,cQRc ؉w᛫z)k ijlm?F{cO&; U4Tث2Orzd5>-]y?^z}{osw{:keȪ6C@i\讋0i[$·z$zZ2UJN+,mEz7k̦1u4G0Ia[cʍIkLmC/q.3O>y[o uUUΧ&Lۜ{]6ICi$kWB{~3=gzb%ho!\7aLcRmEii9Ay"`8CcꆑMV2,k7nZ{L 2JfGzM6l ?&-d9oA` 0eW7KIkykٴΧ*Xhqf`Im\2 ]" WɠN&/rDD,Nѳ5E,&Dz֝^*m i!CJ!S@A/Ez.7O'pGn&s›UySAҠn&/NU=AsB/Gq_Dyfc&^#%=ڋ@ of=WTUm:Mzz5CMMq/;om99:ˍd`xcnN՚hUqͼO޳+Ƕ/]/MGP<iؑ"ڃY6mKtO,oYwXn}6puQe %0~%=;v]ݶ zg^Г? aQPA/@ӫn3\>k,Ÿ\ ]6wh=^TndҦUVmw6za*=+T#rؙ%^1#DiOZ\\ڻ9УTB0=r3қLZbA,-6.h'/u= _~mgZ1WЋګ*\r=6ogf\C =i:Ku#C޳'#6pF*rÍ<ƥQF%?S\\Y&=>< czzY鹚0P:rR(~{w\ ו]0d?ГCB{r{X9'Л ==ygo\u/ KX؅r0t=y2$b`|~ ~oS[t^+6fMz#qmcTGO X5%zoRfǮ;"H{Ϥ(ȝKkMEe~&NgC{u.4o:L7i'qFߢf^u+.Osf+yɈL*4-GzVѶOBp(W]jw $^W[gšw&kBSy%##ml@hEF X 0/-~Jhn2 YWq{:)7)澁k=aTjBmYQ9 uE{O%J@YOFI؀PMCg"IsGI;eJ`0Ď̫{Ihu;[yVdehǐ!/SC!Yggww_|7z2%O&Jk?L=Τ{ᅹwtp0?7kYi9'E0PG?W毡Ȥx)e-qŀk}y*t8uxO*Y)rGgb31<{f1 6$^/Izxp9Y\wtw'92O%>P~N$7+ta=d8F'VRJ΅]9 %;?C)4]v_0/6t.vZ\mo e !Î=.W/Rk_':NBώ3.֡]K{(K3NM NwuUJoLJ1͢/L@}atz'MrSqGl22%^q܇7[L"yWݯk2qNf١mۊe*U\ͤM)nmb"ڂ˜).5C} !y[]%% iIeDytixNDBdmb ܹzj$l0An;T LKX[fo1vFZo;|]e)X$-Ca\= Wz3wJB#9ƞ Kb3?đtOc'}7U!zp"rг < GgAӱ*GjkL-VBFemԹQb'ފz#1;z:;I4`irYГ#Wt-n4yдFٱйDz1! #cr5I *4G ^B}x?x-we'#QZ,v~zlx2.9,}zbM{lD鸲q.A *RېעEzmz/;V'AU\צ"MaZ8Ąs)%9 kmH+HzKjm.FL䧒*b y:qDm;.@ zc"qyP@Qm^-OC+[>^=HgS/$ib=eڃ2&Wt|AwO@}+Rhܷr%?s|KonK_O p{ټqW!j?TTJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%m%ٷld;dӣWr|e54r퇗jLY&[]'답rmņ/1E8ޯO*GDwpi5 Xx"cdW;dMZ6,0T/JvTqo,{z{NMܥvlq\?K^+^vN:{"[ΡcZc%oA/\0znYQzvhZX>Doi1=|,˯IqN,!zz)| zu!&!h\o0* uE7 awDmX|?aW[ҲfXJ E U0-H{R{5ƕ7n҆"iL [&Ё0[h6`wZnRKen{^[^j,Ɣ&U*#`IPOhIfM0k'whEz^!g{nvMڃj+MJMPIenr,o IWܝI('co7=onCMn-0IV71D{Er:ͱ\U9wt6mKT^޻_EH()hBmɲ)0+BMOD{-YN$M^>7^cAp Մ0 | 9HmpI|mvEzpifΓVژzb#= m꼾m7lHE5 {zM&0Sv = &rSmi0Woi#+LcY,,axe5_aw+&jUB#$RBi^iP.%m:C>bcz)?0n|iuKE{ze!ΒB<<QALC{j{Y,,(~ɼE,Bba==Z,6Xh.[,3Xb X5 :XxF|3^\{>Hr+ طXzz.Xf>X.B/D˕Z|>}ZSѳܠbT^Z@Ie{XƖ޴ T9lћ?CE˼y \,\CvQn&{,1T#-qɈ]a셣WaWF A#%sCO~n,$r€Mbڀ=pC_fL cݕ6kˑָ0C4tqk0r-F :ziIY7o@o'DEzj+D{@B ᴩ7ZcEHuJͼA=e|o^OOqģ8 q9D{RzhmA{qG GfIQ12IQ{&s&Mi`X-_=^)O@8K&*RG1[Xy[Mҷet|-:çCwlѲq6yvv^;NFJWE'yHB0ΒYey"C`Y9IF<(͓x`KxQS/O`]nҤv壭MȞGC t߽ &KQrX>BFzƤ"ogg>O 3؁M=FPq8y*ȘImjWfx [,/IU9_ wBQG4NC#/,e޸!x#bàE%*_ zNX [ 87).ڼ{ j"B}{K6+8~1/G7:.'w %ߺx/EM*>vҎ#٣yqysF}Խwq 3*PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%P[YK,_c̙{}fcH5{L̒7`DzL|L.Eb<&0`21F$kӋ+32l>\+7mYqV~jAejq&E ġVrLh<ENYLA1EE ?('pk\蘳:Nffι&(C90Ȅ|[7ȗFVVm?p(zќ?޴06׳r;N.3ƛ=J|b5~VEȟu3~7NbQ$gH0{-&KWjH)*&n|GU/ w#8a?R9V?y˰G@ vTDgeir7l0'{^z[Œyp akc $œHz$W퓰 |="HH847i-LO2nyaV1!&"i2?I/aYRcQN䓠KL=I[,eO| H@<+>0ܺwy@qAO(bDNKscM/&|K6KoiZB/nl$wDV"ҖrO>z'uke%zzz!J \^)3MfEW5ě\$-d lIiEꜞpϨk*Y "EMK˻B?KO$+..+Xn).ڢP oc!v5sDiG!\ *;abn2n7vXRޏaE0/}+?M#=,x ft$<}XeOdvcChd{==]B{)n3=ثNOnk^Ô[ bqɤDeDx)z-zrbp Bh8xAKz,$ (Vx$Ng XTh0$D㲀b;zWF)tF8iD6W-A~="lg,|T77rH1N{Nk4* X)Yi 1PiMv^0^=CO&p.蹁,7{%"=:CKK&uڋ|eçHhnP+#=dGЫt;{E"=M:z"94\Tzj".@zzc%[h@ U8&B^tڃ^!fDA^E-IQ F^_kT)"Ř6JEzWwY[У)гk#zjXW&M"ka :]FI[x \8_J_~a!&U䑐Duvzis~czziCYX|УNkȈjp wH/d{\N! m;/:BVѣV): G,{-(dDMT$޼\,dG,W KIԉ3~u'Qa= M˭z0:wO >Phʋ#=xk&u1#k\+zsPޕc҉ۢk9¦wX5pR[q"@?&'5hOLk9HkYM =11{D y4$ocK%cQnBoU;v7r*l􏹬[Z,vSK>3= "| -\gaݛ&\HCk9VJcXhОÐ<^IO%zX_!(BpC\O^%%^c ϫ(N\8x]wo+_/4^yK/135[qD-W6>3>z`TnB`fȸHDP/8 zы+c˥gUG/ԓ-2(4DE;)=DMn8Nű'+Wgd';汩&e/] 2K:CfIffK)&OW{}DFB(`'V(2ءHÎD}&9P,$.7:[< |UL ة"xS;G2g)fw'/ғ!'0II_Wf!" ͟~߽Dܪ75$BHWѓr V<0#3GDb8?84flx!#\kzk JH>?sO^~kic3y'Jm7E5+RҮl豅d܆ qו 1{S%y~/1yV<Hŗz płI-E`h+o&1|T-?E^K\ZjWN땍_r3{.;ǠC^Ț ogGdC;$~[ !Zz4ӎ^L;xX*)bnLj\XzBv\bnp5{>'OONNxE*ڶkRG;Ѻ*0Vo(oJ-#IR4$be8ϧ]?x{cNCOz/<3+c~{J=zc;偩[.]nSғ-Kf#З-zQxnbL^jpIC<xc[.U~0,\$ 1^‘ЃL#zwDIz%'bwk^,ӊWm‡ KY⥹L?;ͼZhyUnҒң=REt(0lm ۉ4'cVz:\弒y=қQ1N$%p1 ;x+o<;|y%$^Y?Y =wT철7 /ʛC[H$)Z^z_/Q޽3tP4<2#}/qJe cP UBWE>B{m=<`[1pGעNO/6Ko-[2"UBOP^$hW8:5,@Ijބ<5H//]jrhy&d`jYbն+-]-C]B!8@FQDm2Cwa."!Ile5$oDG˽'8GñI=" LClysnl F#Ӌ.]{YX򧜍==d ]MjM_ꄪ~i@Ofsk^#L~mVT!)缈E?}$=ZoBl^w+wbԹU~L Ix\Ez4WW8L{7GbOKq2 Jzk( >)vŽ>d::Q-811y`J oW9`2 qZ{ SBZ4vž&̂;Uee~]Rl}ejbB`ad4T:U͔^L6Sz!) =3f"dBS[5y.>rٵǩS,L=c/tv >-cNJ_x"blzeW؊y'3 (y#e޺g_?;;c^FKzCh+mvyNnbeܤKB+Vx4 U"'Г$JlP2}BK\=Vh62%Y*@6iik:@@x妕|]Z߰8Fؤ G-qȽ4S (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%> w+66]9L`e^?ANLrWfLG@B>+)̏X+2?*7+{^͈J#=_hi"6 3֡} /&ػ*I{ui' =Y$kcCE,kfm =@-:z :m.[t}\OQzeB:z5[U{hZ!@ z2J@U$Noˮ.Jmv)q`BM*2:쵡n,!ҳh!tɼ 3{,$S38?ܣ5OO>%s${a dOz: ]CjgrXk;)=hK!;:Qi\iO*:h#`$-,Suz =iQz+;O5ړK>B+!|<KrK =ǐ./~w^VFhuK@2܂߃7QD^]?W"\e@ӳ)kViBj&5DPhm)|ZۢMTVб{,3خv.B+(Rƒ'mSד/ĔZ&/0]x =1xAde'6.Ic(H69O$s zIVMX-G@Z+d/?X4ש|>_?!uW%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ;@0(6;g, n tGV18}QF8gYeQ7].Vqb2j ":oY=b8JG}qArGHV}g+yxv2Sщ\lj ǫ|{z={1v f)׮%D6*cNzL閵jM-vvu\}97fWuZ$HBÜwRo%F+$Aetr܂^K>KeGX1?{cGb(ljYT ^4(x]Ûl* ]D@Ҏu}H'EE$Uf/$#JXV ,X'頻{v7&93sou>9_WUCqֽ6̠NH9:*+M 3Qu=So{9ǭU.Uz˰<;meߊTykMܵC77<=Hzz BI 4 ^tխ[I3kՒ|3n~͍N~ӣ@g*5q*C i}E/g?:QZq,İCp% WɼL zc7+DnAk5DŶ 1q  zWdHesz􄫐^=`{Gs̕l7=\Uzdk•^Yn]%87{Š{EcG$ft.DVÕeV&EgWB4ڕy]Yn]E+!lwϺ37U{<L]37gLԙnc{Qv'B%:5-;Ƚ.YP[} f$ICrYF{ЛқdyuOsJ\32p6ȖX|Uɕ-{M]j55ʇ ;=2IzzN°~n YVﹶIcup;z*.^-jaz)Cs6]u3lI A3נ=93cNۿh.j5fHwm_uzunZ]K?7BԖЋZ3 OBR.ZcQ .00(zt\7q F0.X@o"ٲr9,]k_%Z C+G.@ @ @ @ @ @ @ @ @ @ @ @ @ @ M&0pj쫹UjpӛY=LK?aMS\^ +&G=a"`_ީkotG^Oᢚ;|+7 cG#_v85A2e9qXL8٦;j~֩zhSM_a0a+_-vA ٷř#܆=uuܳ$hhxj.cjۻTN8/X{,zi _YSzOg[ WhWs[檬MMKR1jBDb\i [?j^yDIqwRF<`nJ&w[ƩlU]́*}"+ k"cI CGt@&A5m}a"?bn<NjM$;vv~ӣO=zdޕls{ANmRH6FVDN@g1B*D }2iUC=OۻgK: }_cIZlov2 CL0v%[tu-pe37t*uynmf~ԓV:"ګYheYIsH&D4F`l WIoiT"t5ras]XqVɽI$WfBYʩۻvZWгdRCEDri@֤h}(ϭ2oyW!?BBL;A$7ElIJvd1\a۫#Z"G&;k+櫓U9joT-zUGj{^Gךh1Kp΂يl)L^Yg;#u^iPy ֶ#o{%!aWs=@@iISOoe%~]+RD S_ыk=i||㣹x1,_+Mq2{evcl ![ӫ؞5@Ã2<ԜBU{0WxzgN9 |,}7 mσWm b齬-{l <^amyvpJij ],X<4\)z vI,$xh-MI]+7!U>pݎdL$빌{7{z%#E*ho{оE{̮u *jL`+AeF) 1zEٍ_i3 .c X&( I[_zc,CoʼnL}yK~[YVPVˬ o!Ӛ4;cwS;wSoPۅ.gTJܹBXw?, &t<7O=#@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ pY.zbgkWZ$JǮסq ']aouXs-wbu&_Ы;wW$mzfq{.w.F]hRnZ/goYxNKm];]m[,;[+#Uv^\\hQN֔^S j-st̬[%=m G6C;zK䣨Dm_6\Uoqsvۖ:yҮxBaW;Sm&FLu j 5Yz6':oFsW]kzpu9Y?>^clōn׾7g5jٲjWVq [gpᰅ!l:3te?;yѱTG:˲"}K^W' ~ǹF5Vism{]#e[4e Awu*=tiMcZ7Rc$w.5soɍBoWg|YڗSjtY^gLX0budxh RrmƤ&Nc\Ygs5{/\pƹj{F^JI3-x*-3Oo8񧸶3ZȨq ]9\tØp\m v/O .nZd9h ~c<̎qɎ ۨ5Effr 6׳fBB/6&aKe2#{ͪ7s5гE 8]\eNK 4XWqqrk|z^ìnyR_Oz$zi^@>*Oo/楗P6QUzS+3y5+!wV;r8g&=h#&ƄM`1-Ipc0tMr %^:<"}G/>4Ɍ$[M_yzsыA'zULaܧul<=H* gyWw=5~5=Wh2Ӧ$ pѬxzx<ۤ6w#z nݵ %};&L~sި%6ჇU0frEȉ^`f£2u5wѫLhjz!Q/B[r8bο@Ol)9)'OR͈%< /|{EQ|'dRnN*=r*3fd2;_bWJgԽO'X){z~02آw>rRCSKE D5%mOCRP+*c Gwm@u AD"*nҍ>zc}0jxA asWGB`|ۼx :K:?f3:ω{)􎱡.  1񦫜ӛu`+>y-yF[(75dZ=*&CŸ1fXX (1 D@LRqzt5 vc1p4[1mOrw"Pby?v̷S©4^vT5OVuZcW`f81ghӄxzeslQۡO<e&|7k5d9(f&i+YKs3u5-#ݲ| [B]f~`3dRFM\-zɂ^՚Kϫ.9yض^C!FUk/vuA~|9zgn]( TD'==wlއ; 9Ecl~:`K\so~?O S) .`'}'~ =ι7zӋb%@ @ @ @ @ @ @ @ @ @ @ @ @ @ wן?=ioVJG+4[K= N1y;Һ>;gfBlMSgv\iM'8'{Io9P7otZs#mcdۯ2虒[tXZ|cjr|֎6zuM9rb8}IO3`AxzZ?m.~_z7+#ڥŖ+4vsfg2_^7H-/ɶe|Dd:Ǘyh8/@/_怞ϩ=]F^?B`u[-q3-N ГIoM笜yoEo7#|E#k!zz^ M;crD>P*a.ω{v_e3vȔqoxkF)u8{l|GFȟ:г #z؞Ywlo9WAe້rշ2,1Or_( ﹚}Ƚo 2%WVl9CNϺ!Bo / )irˬiqE%RcffB#;Mٙ4+4i67(A]= :@K ο1oKFu緒f\m"+&6crdڸgLU7bЫ|i^lD5z"LݚҺWx?-Q/Fj =ĠQĐ{g҆meݭEA?;t=>͎{Pee+9;L~ I]:z&4wn,sXM͝=&OF;+E"cb_22[ ˼%ANwMpG4DH} FTjvqཎԶbc%X|& ,ͨ_7rG} ^Ih==vĥoTO\)0sNMYr;ߤGyCI:oF5w^K.6AIU9Bk&!Em4P\6l˔IYsOй0 z,q )pP^AEOO52j=J.$Ja*.lmEϺ )/%x N\ETSLìv`%˥. wW4u[q5)RK&Ig`AJCӓ@6^=k;J6 jGi $[#zt T*z/ˬ{4&r9\8LZȲo-\x#*2#ƣPse {zz=Xv"]KKz^om&&mGQhBxf#UQYQE bKV}AWN\f{LD/}ӳ^'vkbz>;oqGG2rdƃ,-:k=JGϐ @s"\[`=Dj#2 KF @+IRuv0f`{DQ fmAoIfIb3wLsYC).RLZ yV7-๤c{<#U! =&ЮC WY4{ʞkf ^ATtB$C(BP3;PS+p!y:kk^P宻|]=ySZ F)ϥU^У-zS>'3U 5DZE}6=Vނo5&4\/L{|ӓ%#ztf+*<=Gг'Xڞtפ0D wzpZdSft@A]<61/ }|PxcDǽl)qK|,>xlOeIONP}5aJ&B#M)Vق*UORܵr7j۫=qg:4hC?C'=Mݫ!`9k= ڶ.A(]h0CJE^[j@JZCO|Td= chĪs2jRh*ck\Mς{=ϼ4yPl0|'G)"{UމnŦVtv ^EM@ʝSg<]c,GsNیG~IZ> (,s5Ⱥ,ٱX!c-ع?T~[f>RW%yirV I#j{Gg=pc={ p~&-Z&omKA$Mo4A =;s&"Aݡm̞n^ x//N 6ט瘨y{rB=Q녂o.z(8oܺwjiwj =홨V, vL_sf9oϤЫ㒘g`"ʉ(ѿ@ѩl+s`̯?xTy˼uϓ?-lZBH[$]^Sz~h5+htU>BA]}tdױgP7#?.vLK轋N]{C kR2w1{+ݻ=^q>>@ @ @ @ @ @ @ @ @ @ @ @ @ @ ~YK9 S}ly'~ Ȓۏ<85鞲fom֯i> /ҕ&fh ߢW/iegdZե7bNe\fSoBgK7>zE/}xX滧秫m^.V臍O`ʷ˽j` 辖1Iֿ[Hu+'>&'͓9:AbUǮ})'(twovYα pBob{FY):.lO3e& L]|3W湬o/} Y_}ƲN@;.S&<>xdt*юcǀ1kȜE0s[SӔ+i'e!4ֳ~pmROOx.gO-' ty/ٞ]f LH;Xbs\zzҋݿ5~kMq.wļu&w+|Y^Kz/_0{/#oVCs2e~.z|ǹ̂DRKo+Aʟnw4 82t1^wwg>ɜm{ZS_\Hy㳂YwnnvD[&9FIvdzLHRm5~!q@3*c~ bӭ~r^ש1}_3`J-™c4#H}[5$bi蠏3SC@7Lͳ'j3)Y0q/9EPt^d.z?ǭqn#!0{tԜ{ >"ü07#o&ԩ+™_A#XN*BB'5)k8Axm๗Ev+PK$!77#[ ߢXigЛ mE CTJ=m"!oa9|a,> "q`*iI0j Q;!O-L=[lS{jb{aVDƢIHJ C/k4P~ ݵe9I6ԙ;A( @McQ#'"<עS-5SmL>~ U,{;@Pdvx\AuFJW+\kԯ'͡@ =ÄH3;ށM"lp= 2g9)-gADTʽ==GS;.PE95W􈶜qăI^ҀUjN&İl|_U쇛 <ՂI﹢imUwkL6FDpCo5`RƢWI z<'OL}88p3l&nN&zӲd&X"zDj!)6BZ ۈ^g{ A~ib{24)2lٞZMOJ_-X0R17l!-+}&9?^xU!=(`GAͤ^K6=͞R᝭@k՘C,嚓c}JO*jDN1c~s/-x}lnb{eJЯl47I}_$k=/A[Tۋ==x,=T6lp&gs9!3~ !ú(УF֓b֢1:d*#1roByLjϜ H;ŭO#ݦr%W4ɹ ١F,y@UJykQ^5@z%8d%z%a,;3*P(ȧzjijB1*M XGԙONknm!n;zsi0*.syۣ EyIV׵GU#obVjvh ؖ,;z]U~#OJD- yB= 'U<,zD"F Oo?sQ<=Y3e{ x."=r$u/ݟ@Ϸ|j,JjAď=tFF,6 U̹z-Sw4_D =9-a3Uml/w{t+zŽ\'DTGLIZ,6j1}c89oL'zm+i2zo^G"K<큶G7X' z-Ye%esz3P,+[.VnQx]֤T :'RZ S^aRZj,5PMrmSQ9τX w]Q9K==6n:ϵ ,:|7wٽ; ^APR~{zYGiyz,z+R70ju0Tsܡe,>bpNmWlv<1";v@ Z]jaȇ襌q;G#I9+/!WÖ(7b.hZc, EvnqODBʖ˳C[3^fkW3:B/EOJuT&zD 2AsK c:ub.lO:4OjG6'zl/֡[6nb2YgkQ9f9#lv+pNxVhϩrm&5=:Xc$S-ިx(Kz'Lxvޤr5o{ӞGK/{ ҿo͎ox2lMgὑwP&i,mto-h|d{qiW@#1. 2 (j7)ܒ{< W{SLKs :Do򂂍~$2@|xK1lJsNʅse c߷nDHQKb>zLV}NVXapx.) =n5OW7 k39wܛWoX9U3 Mꧽ0⠊YTXnaGN+98Y'trge̎z'ՙS&ON#m~ncuvhnYLrâKXuwqČ+erfIW.W&4ww-ߋ 򸤜ӗԇ| ,3jvjӨ=G3?LS::oؼBegnYsveI\'0@8XYw|>BpcﮖOMz3и y ĽӞ+bGkp12ӫ"y=v\~ַ=*K/ZݚWX#;ϔеӻ+CV~?y.QM9;ʒ\ӳ}p5+NU=f^m@/rEǿ\۵s]u7aM My:d{U45-zIҿIAvH^g%Vv=Χt{jDom 7 4K[;&㋍% C:<óM,`sJc$Y}_*ӵU"zz#{NjV2*^PW"]2b~lDZ- 1͔K:R*)0%Ȅ84;7N?Z h0QX#`"QD-`ZI}-<?#wpԹd߁+ҡȟ}<,j\)D/ͱa^0l0ü76&Cg`,q#G}{ = ٠3|yCZ.#jb{e'0["#ѳ< ()|GKeIWK;9\l(Os .JE[xbXqNK[ڎ fvy+,ٰ]Lƺ<W9 `-|M6זR3WzJ|J2r__ )7aǴb&+qS]A ێ//zK#zs504ؾ{+/FbɯFo47/Ӧsrկ!czc{]?߅E[@/ќC"Z8w͵Ce}/.s4=M:f˾ڵ]ށ,1ܴĒrui_,~mow/<űnQwN;~pkKf˥ae}#ٚ_En*zS~.en<3Ro{/cgǧ}։ޓl!zLpR&6YδS&@OJi9r_Lje|P`Ol7.[V{˓V֔3{q}97C+<}挏 :Iz{Ffښ 33۴YnKN#~1b M&r1|OW](q/J@y`[a6y{'KJ6Ш+ljkL}֖СgnvGC{KΤS=z ܽgt٘3Ƕ?P{iq:y+%ʌPհRw;vI٬JwmcR!ɺk'%u~koN؍pL=[%TݒA midR~'M:r_a4KXa )03tKYM"q GV:z>A(jkv5F zӛFlA†5so;^f4tfnvSbpKNVkI C#_Ɋװ| =z|ⷖcP=^@ǸE@/* /~s/x?^-?"/ڕ+tƆ}KxķF/g_q/u+ÇW>.k佶zfB'~o9vN)Y cK^.ͭ&#}k/cZ=et.C'SVm;^ڷtZuW <G>?͗99@ @ @ @ @ @ @ @ @ @ @ @ @ 58SyzNL=*-O1z3}^.&Mmڋ{@D{=t%Wfʾ.f+==w3C[r:Žng\斚ɳΔ&a8W,qLl)/Ƨsbb#4r@y!%rŨe} zwm\K2v6"Qķ+IK=gb=4b4@z"\B_d<5;=8Q>GFZH3IvN ʐk722-Ob#4$ffHye,ݶHh|cQَ v㱉%bȼԖ)d;28aSe؞cm7s6I][I rΞRbdF{c)h z5x4P[Іqn )H*AVYuH5R@yQj;kw'M0D&yZo<*F;VzxN[% >c"w]H6ӚQi :zmWPT4/uQ{!g#WOc*z6z"Q0o{0 {#Zpũ%)̮0f`1ؘpF踕tKܻht =u'ю0t6ة! jzz [[&+RcjL򀸶6^2 vO#xv7Z)&lĤX7'z$Iqyzu}g0#ޡ1Гxja{VI)Cwm=j暻Ot"kÉmCksħ^Q=*v۵=/$CAʇ<{ǯM,rrVMGol4y'˔qC`nYyz3厞{==G \B 䇝X r3r Eޤܲ 9Wo֒^U Q{A{s,SMYAzٱ;1>%MF: P<7C1"Az'9ʔf/ee}Jջ=T$۞%'(PY(fW<=}={m#lU!c#8 g~X>I 0,F _7QI\<EW=J( 2||6A %aEWTL8)4ꊴăߛKȞ9Cc!zpKs/EzV"Jfhߓko{*^ֽ*:2S+()Qu=*& zp ~1Ucf ULn-\:߳}#C{֫B|9|y;S,&sѨBЅ=.'\7zMriCCoiɾ 2=O1<;ozݟ]~Hifi)}Dx[h58P&#"d!:|=)%:^X\s -'fާcsD`eoqG8$˻?g];_Uwzn=nJz\3Y~\у@mwev3}_כ:LUgn@/-J|NѶB.^}0:bL 3-ȍ;lw3(lCg #eV(jάmn5E8E0*1瘞\|TIYH35oAk Ě[z8AzAI8osOO UJͼdih5vJA@Hqb% rC+У:̩1 J^҂-Ϳ2iۓ 4W_p0@==50imoI=fqqh=im@* >J2o=g )5ҜJU5,iXzzDSbK/5HP578@OJ^"Wu'zbGZ;l@.H1Y36^K "t=>,{IzwT\[z#:(4-=)Q[j.vc"LaJ"miw>CG=ÛCm/+VmVqsDoLMU%1GR8==ZCSDu=Qyj`m=[^+zMkure{@@@avj^jd}Tochޛ~[Ki$}۪z2"]A\M%0]_&pszzV j,  z4 :ZK6-snlK}*p=Iי~= Iƹa@o؟c Fmb{aj]E2x>=|l[ `{N3 3T.;cpz˱F-e=?;Rxg)Lsܥ9O{q 7V$-SLf%JI57ej@0(1O (Љ$p30ݘ8Cx4{1C_ĭb{5=L Ϝ1'\HN0}[v$M6JQVy5=ޯw3ϻ wdVfmkGEy^3/dmuL/DNɂDI=)^5梱9lИmK`ۦaϩ 8|_1O(%r jeaeҘл. WRZo|f-jp k ׯS_?~Fwzbzõ@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$pN LibGOݤʼ_ "fDeqƘ 6fbw"].F#Pd&SnWEp2kM-Rb82uݽOqRL(ʆzH-lʚ.{ hJP;Ӈ\.KwzrJ='ہ AکmaLRNFk5S_ۙodpIwd6iqH8޲Mf2.}4Yely";(j;A[Im՝Gʬ'ΘtNAϥs?i&ͨJ+6lz\uzٟhs Ԓ&qAf~EzǘȒ^ ~*zh{G[*+=%~^[&Mvٟe{?+{/[SR$vyve-mW^a?sDj@9PsCڞK0r%>Cz3sBlőڑ WMj1gfFck.3W;#c"`i^cRg} ,{I6YD/Ӳkfzݬ ܙ7Nme(Dory%DvjuzGwR[i܏'#%ld6k5_Ңg-&u޷q::7w AERrRd_2EzG؈1w&g'eJSDoL>lw=~v~/=~sR%f>mozyr;!I< D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ 1r;qXm;8LٙY~'F= _MI{w= ЌEe}[md6[/|pQwxeW>1|ܖå鑈/~iiUI'uW[wG?Uc׶+C3L zU3QMH'i㏠RH6ˋ˖\yLIead\-=yE;..IkL68WY_R376,ױ\,aHϥ}}-:ry_.r̨]h$u H6gW+Qeܮr{cܶ-볳Jo"zcӃiY.lo!%(zKXsA5ͤ =he}_zMʥ*ֽrkpt/n$z'W}>td ]䉇.l/>V?G.]M{I fmxH}@l/LSx|MZKzOk'uR gה#?|dzKϱloDzD{;,*؞5=Cl5e u{ptKg&}ev̶ 9Y5JcO}<}椳;TO&~Z2kK5NcBT7չ CrIfR,>O onMQuqT7Ƶ{y+eos)nim\ئ&~M$dsGsΖ-1sX'~eckߙ/:zf"\Aόr3پTm=-8|Ց# otӹxQ\J`Iٞ~V8Y%I;˦#*f*벤l'E+Nz%)-G1@6u|etR\&y1G,|90d| -cڿ ԼOqk{:)zL'kgt2[zE3rdwHItaL-|Zڶ޹_uz~dCθw&n-/ș<%paX_zz2]oEVE/4‰KGjۖ~R'UU|zfJo$f@T/3*Z?=*3Se=Gayǔ AMG2SEoRLF =QUֽo1%yِ7:z͡KI1\㫙~#0Ăj[@jF$L2: 4yj8jʌz)p(eaf"kZS =|d (`MΌ6-!VFe~ #[bOr[zr3 5w/޽ښw96Ω҆CnGʃJWe ,B!H+':;|㱰LfѓJh50z/:z(4F*lj= ǥ^-U+\e̥/Ha0E Z˔kjVe2ѻjh[zhAO5wAon驱A]"7+8ẕKEFzm,=TAj{z$Y M/SSU^\[Ȉ_ Y7w\ +imOcbBAPL$|nEPCb=JA &z).7jm/\Cyz)ݰYXneʵ›u7UK zـ^Rn@la]T mOR,y\@_sK=^WswXo5BO=Հޣ XL5W=NZx*{`{s?\kk7Cۡ}G~B~oU6ksLۡ)FdjRB 7wX¦B@zn^_sSäYh5rKG5#.a!I?'zm/tfzYGo:IDzޏsƺ?l連hHFtБ04e\ƨ& <43?g ۣ3`,lIQw.~yaL =1jh=I RyKqlڤmxsƢLB0H{FzC s =" /;=zgE/t' zBK2YȄ29!c଻q3UNP1-qÄQKM ˨L}TLWr~0Pu(-X К!% <? e{YlGN7-8ÔIחMC6Ļě_.*"УjK\P'E^/v&vl.kJ|50*u CX0`{c4Ci^0,Ql3qƆl2asqE\7#ܙ7-'()tGUAc="eN1B?q=BFKRRJbg{&dsaBxق^{Ưmŝi6xS^5Fnj)M8 EA B &-ccstOGۓ.ΌJJYXnʱM A1!a7oŝ}Z%EKnn zni9& |wc^|^]Ȉ.o%EZBo\Zm0MtR*庀~/޼YM&6^#ި_Z9KD8N\l;2eQqcAkp_p%WzC."+y.z Ve= Oo^΢3Տ7.Ǟi9az?5AdGot2a3lA+s_pJ [[5Eču0 I$[ t_pVNm K?ݰY^yanVKt]乎ЖpJͥ8"=0@$QfNuyJKΐJN]7Ӝ|@ٶD6ݣ?ֽ9sy+3kS:g uZ&"H "H "H "H "H "H "H "H "H "H "H "H "H 6@X h9ZkrϑY]ei3q6(z9ߏ@^=pԹiS ~=-&ŕZ^?hٔ6,9Z+aAգ Sn>հ02W/C2TE<vv!zC#Gm5N(Kz~xzrC/ "9 O(&B@O0 Nk{kK=E5Yo,rHeסG~ਹG,rjՊ+f~p&~݁o>N:zF{BZ8]A RjPO^1}aXvi{mj'7bz5 g,[wև@¦۞(Ŭ|Q-=^oRsEevj2,yÁ#]= d vgNyjЙP]V0懨S[0p6hǒh ߸wU #\5}'+.[22B J)lVX+ ..&,sXEma4-Vz3Y~ V[k[/lE$% @`_nݽ˛?Gɦvt_ ZcܯB{ u)xG=.d%DogWa鏢t Cq #'%{7Y%٤lhBIGF8Am1b(ٟ$/iě+$,$ƿZ{Ps`{HP]Lr U朶nG87WI/ z >!<4g wmkIXo.II=o@si{#ZA⏼z. +IQ,њ)H".X [_ےBH619m@@KI ӆzŸQ>0H8km"ζ3vdO&oTjsn猾zzA2?e=Ζ^[K/d}ͅO=͕;e{' 7yjH@ǐ *mV-GDOm..NAwt 4+zC} مԉ^a k.edtI*LT Oϑux\ۚ%ྻC6=nKʮ^tw}Pڒ!zk6A'FL .Mh1200i7iT^Pc 6UٞQSEoF=MW2&=Bih>ˌ(E^TK5Nʿ]"gL c3B-0ll1NH%=Ĝ}#;^}/w:{_4*;[znT#^O2{aB>fr@ZsiN @խ SR*S6q_ TQ@zT2D0l+z2)b;)2 n™U)pgʠy:Лy Q)*s}KSq%s0;5yi,jqkJ#Ke~w7pcCCR>evR~*rRx&NW+jY^w\%e)[ zO$any&ąy Q78ZhWnagMk2Hc% -lZH_rV?xW yܤ44M&ɯ_ +e3.o<±ncn˻,nYOdGa ۰3jfN;~9؜3%pF#sQva\䨮K$XQė;-+߽&l<]a!ۦl/ :V:D459^}- hץXsQ_COO?0؋S߁~'%^{#yu| C$ݎC*zɡgE~&w]5"}B,jVc޷f4=>l284u0.#Pu1bxWŗzf68J8l{5 > Q3"=x qtGKJуo:85o/W ./ޅ5+\Іл676mdzC/xv%7PVk1 P᳧wzZX̵"O@ .{c Լ_O$e"ᇏ}1U[m{AZXYZKO_)K\{ X Qٛjl5L^j}>4 S^6w[^w?3r$X)h*{+ɣ\7uEԚSPTx') U 6v- n &)/2Тl/=MG/2,~^Z_D25#BM#Ɩ3Y/`3B~/0L߁=lx3ھ8$AS]CorD؀uYI?e]sD/UXy36۾fM'{`d7ѩGbĜ 7 V˲Y4%pɾʲQ:4p~:AaH"ғX"#A? =C8('A=՗уA fr0{YfY~ bCeP6^av])6" ?;j; #*>mѸ&I=~~'QY:"rט7؎mL;Uf?gPwΊ{Tye@f!$!aƕXf^cL KAfzAGi{zCӹ`**ehV"DjHD^ƥU cwmY=- 0.7$YB5cQ'CFK\K4h|,~ u1 ȃ b[~c`{(@A2ЛK6乡]Is ==rH'a_@nO+d:>TQMΈRa{VJ Zx1=i׏L[m"إq6%,:Z/LCcj+\x@"Tٓ%=:M@'8i|B>=ٿǣ@=`{|2y,^ $u2HyږކC$1{7An4MIԗ'[ mI!tԂVO%oku~/؞ {ݝ= %7^CP(U"#/X|fdl@]IZ;gj .'k(&| X).{GzQ\BA9hMT&Pl"_ ӣ:RIx?2aMkNzsgC%[{⯱77zLddYg{^i3FB/cS4dYb#5O)]ң-3қVgã"KmHv&9,xbKsnCj火M+ 57ø> RC/\,L'[r֧]U'[N:F!qG/q=PXRJEOwD3L7eAd{xژA/ I3M<&ј荨-zG i5bf,ܴ ̈́^-m"8۠_T[_FSx\l&,Y\ZCQd2Hf^Hȳ s1I u[-̎#φXhb=dEw%͹5j#{r>'#1qG Lz @of2?9RvV,L%*R2b <Z=XnPC$=mXGgm1e'JUK6Fq2yuZ"hhLLF*3+;->TWP=}`9̿ڞ hdDuĺZ Wp5b&MУTL `Р $7)F\n7)SCIr'AjDldL[F*)zJ失#u4q@U@9F 좳7#و{-i(%Ǜ7#USq169MfarޙLQ^UmSuP!I1(B/LK8qۤm^dt#)_-sמmB`C;toFi~` Qgw_g4 }{ˠ|l_Gפ o:Jl3õ}?/F9@k[wCCZ>7qAl7=;nZKK/wGE7WyOkҾNraa"_3ʙky!M6K\pL?x5O\u#CZ>3J ;*p&{43#aRۼsK:@n,SAc!vf8vg, 'rvշhlucLQ%S>TzZe^{9ƅvy[uh{Zu'^Ib}d]|wĥ70\[\LeΎy.c]r>Mx)!.O?q{@_wM`6gMl:H`=%~u0a?5?cC$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ DAɟj0 a]~w`lXTglo5{|ġsf5_ᑁ4ΰTٚ$ '.ULrԧu>w8lװ8{zo9Kve+|J +5p ,fv| )X̎OC?9٦@`gYlt/v"=΀o>1z'qf <<^h|[Vc=A_#67 1Ք=7hw==V\qa{ k NיCZVuRsOg7?}5w=e.P:-C} 5QsmjwA{w3 ͷddW8ِ*ޓYhP/I*'Hlw}r$ËYEoo[ưKÑsL1 ecЌ[ћлt$+H"4S\HXhW< !Jv]lY9g5Dr 3z#$CX2]bvjmY$1Yv z5 _AFnVL1KC%i-^!Rܰ{R\/'jH"y#VΚьU o>JtiE `@ڻbOj݅AzzP #KV"QClf>@Z龜\Jg Jt`Fz2G4 Q.3)d&%sӖuXKB+!lDh!9]r~} &bMGW3e%}rWN) *7X{4EbL I&r┈Nx'U籽5_RPz xU9rPAHAqD{LL02=!lƹH,+,E)DzTmK( 쌧B =nHyLIu7ɤ\"?dy!i#Kq$HP s_d1XjF@RS]}aQ0)KrDk{I4LToheBN}ӟB"KgZ0m{7st#a HzUقu4ѓfgu(CMYb)j@N5w HqQSGƴX=(М:sr"Zc'Ф8iN߀TKJJзŝ$V<.J/GI^uT'm@3ZSILiݠK^%xL;zz&V$\IH*%~#%ۣ{5Rӂ^M?om{k1?\_f{.ZzgA2=rlZ乺Rast~#*[23 pѝz;==&Nb2/\:,, A?tƢgavzazS>PaTTg=M7G<p,X$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D{?||WC"{ǹ_ l:iS3;w٭?Jpe۾IZ$"w%P?z}ۜTo JiYޑ@; G8}e|>Xgr޴XǨfw oͻsΦsXz1PXL-W%ZCjpe>>ݭsV#kc'= =p?CzL}=)v"q4l0$WkAz8Uҟa)<罓ƚDSzIeY>B¯=A \byc9Oe_f=$neTb yLS(soS\Hy_$("WVoj{֤j&xRny 5bx7TqD 2d`nh\PAp,CԝZ,w 'Htg'xB+f/GuR;WIC¨R*ۃ[?SwV$*ۢ')Ph e#C&؞AQK/kuSXOdz%>?Gg{I{KzO=:~A3cf  ;)^lH-CI(*\uiQ=p-=[H~=Oqjw٘2-=b}QѡSϑzxOwɡ#kƻʺ"jV9@oz=UƎLI QikGenk[h+x t Je{)j3bcvk;NzjѲ:lrKeωXs6GwA/؞>a;zzS3Kԇ M =h[zAq?9Ȗ}"^f-mK}njJC!zAzpθLGOf*eݧ|o{\ns➪=V1Xjkny\u' ݾ+Pd|}L9T{˪M5,=4Lw9lo zDoi{Л\FK_=1x7ba{jm/YnE荟<1%=Fjr-bT>*XGgIN{Z ՠMT8 oA,0 D k.SDOC0*YI`_oc҃CgKC/X赆Vr=l%.l/ H`'Հgg{z:7A/%IUDW_s鍓S?)0qRw<^J0Kc"9i ~oG 7 e5zh@p[Z4RhDŽɮk5^?J}e_57TAHdw57QKOǛXk%^&z_)e{a{f^柏12ٞpSj,sJ" ;sDϒ:zY~`{4Ўuxj]e#=;Uk)plƶW,[Ϊ"!$MS=I }9|ڶn'ѹS  T[[zaM.)`jnk{=ӵ0ɫ8I=ܦ㏤mAQW6-W|2vy\ =a~Ny4{c)%^kf=Py+)7gX-x|302 57I [l|v8a'|y>;rf9GHP^5U| zN/ [ڇјx5G6=އ8ŝb)u2bavc[TvXԘI^)!s )1uˏ4׌=J=ŎJ 3n]6@ J/&x\ZdJwJz+345#%af7n = vCߥ[dva.zL4ǜ"ȳax{f$&&_Tꇊpן^FB¼ Qu1N2L>E/9_yǵ+q?TsG]dWT%i'b< Y㙯+LY {U<pi [E싶U< mSCG5c2Vs \&$-3(}3zcAtɸ\mRm[8 w3x 5g+ͅӥ;ť>l.&2Q2kԲg IY '&$;#'&9釺k!}~2Z ~qy<鳰'8tn?P\ۅNyy}L )/>etK/,B))n&Nnw:4aޤ\8=h} h\hE~m;rq>yCuB?Z$|}wwxb>^.7&65w?nz77_ݦ}W7=?֫ޑo?K;E;_>zq3@+ޝGxL}gfKO#˯S|Fjm~ڵGb?:9aL_Ԭ9NC?;MpD|> Ҟ&UL{q4L~vxxG%7=3J? |9NTn7-}'?ɟ{p_ɟ}/u~ssS(GS$TşkٟTc9?7N/ |BO\g?x^_/?: xqߞRp(bxpӶ?¯OKO y`i5c>pduWz2g} !z Yl/`Jx'[hGnt xNDt:ק PV ӬAOPsKe<^bJH$_;X5W'aUڽb6ypZlyu3O+\`ڤ_MϢ.VMHf߀vKUqxb`||Ԛ~pƎ-}7Ԙ,]]̲KzZ,%37xxJ-%篠]*<1H[cX@cˡ(Գgg]?S{8 }[p][Cm!-31[[h#-3{z|zʢ嬹7-]9¶Ž(+MN{C'[\577'/3=-K{4hpP0|?H0_5 E*|ox>Ѐ尕0u5">Xf!W!)l3#J-]`wm[uzL$($0]a>c~3{H.6gŤ@xW/@o'\->>;R4C~aE-ßy/$EO=A-+gItu2]t{BW".OIh}qm?sId{80b)Awyy0rF2W8\GK|mdrXJAf s Yk{gGDғRRm6aBu >FSyPF퉻Ebu=:1PCXڛ3UMoFɲ ,dZa>+!r<t1 "|车|)ݨ3^bm*%rșyEύ!ZN WCGbis1Ί2V[zʱ = +[XBzqGFj!OH'jBЉ"2$&arjJ1IF֭Cͻ]kGz/a@qzЁ+H~=rP)uU? UL7Vw}z FZ҃lֿDOaܤP 6o=7AwX"tTA4^֔ SZ:(2gQǂJsEEB eʫ\0mhIK%$Jܞ#-=T,wH%Ф :kGcr\)NsD iõHAd%D^*pAovr.krR(!$풙$6U^IӦeO@Ĭ2葨jY/z%1Bwٛpk_UCڅl+dߑ $Iiǐ"iDeOv q/alЋ045zeb*)= 轣V/tkW%ӂ& zwI_mzJLsM1+#1ʘ[Yp{L:ڽ 0J7H^j6|.#z y #N]s? lJnSKckɚE˹5z##/Y&5=PQ{As B,7Q7Cܡ#+k lYYb=}Q8^ u@GAV,h.JFN5xz(+%D{=Rg*/we&y;޸_rMhː1"Y㴹J1V& G,m0ttXZ%bjޛ9c/v]~PtFzFڈ̍yd>XjT5>_AsɷE^cYbhaFtC_9(QsC^@lA/uDo[EL֙`OO⽼r`k0% 0{s7TQs}*5G0 (lM'aްDc;]~\SͥřKzsz99\#50݈6|za!  /'*=Hd-}`pl!{l0OWʸ|JP ҭPQ"J iRu!8w/y'Y>uvr *Wl]eՒMnCpH$yɐZǓOl@蜭ɜs:k񋭠@O&^Cvs{}*Egx)FūVyB=q?~͡82DIb,>J%xݧwS:#;.?@zdh 9vlww;٫Ѯweϛ-ë][MF&?^S]vE@KH{e޺b/+ͫaۓv+ J?cTn7]F;  _m޵@ϯ]AoiVϛv'ֹ z3&mO??dF6O6W۵F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`@W9*G endstream endobj 17 0 obj 76067 endobj 19 0 obj << /Length 20 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{+u endstream endobj 20 0 obj 62 endobj 18 0 obj << /Type /Page /Parent 3 0 R /Resources 21 0 R /Contents 19 0 R /MediaBox [0 0 612 792] >> endobj 21 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im3 22 0 R >> >> endobj 22 0 obj << /Length 23 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream x_K~W6& MGF5@6 DTLSH!ȃ<Fټ Y>:F@S H PF7-<#(;J=Ky\]6Uwgp3OWLB|>;07 q_7ͯvI^dO ^K7>O`); ;._g=ė_TPwDv6?xY` r??~?ե5\}s\;cs̕???/_G|` Z2!B lO~_(Fg- M(Wx3+e7,<^ hUzKCSvU{N=D6kSbƒ^]mז*ktUu\Ezbwuc>^={uv55׏7~>o>@rCj;l5㋃hrIZgekiFf#/81"‰(t{[gtdͰih/CoG Fgi΃u^̋;I( =PHϛIc3+Ӛ-sP%0D c 959邹1ꜯ2f1uwOwRɩ~SiM*;iS\) A%cZ%JsfD2i*JT̶Ы7 |/QYy&cRj+j]eueg7 (j"=Wy_;pB/]UUHjT2LۉVl_BLCY0p^"W ~=a|n_u$x`^_s~N=lGz"l7k@jA4f^DyL pJ/a#=5پG#hjB/=~y`5Q .CTTe#d&Ba#jnGY2UPJ AoƧhoHh/*mc.PsgxD{ zȟ:ѓF |-=آ!zBNo)3.'koIK\qV7tV"=Dby-͐Ou2W$Nwy^Wssy؄e-0O+EarlBwD{W G\p4@nyR{tB04_Zjq5 RYѓ'rXU(^^Ws ^{\JYa#1OkPJgIzP$ȟik5娶:iOz2|zbz5v#OC AQ۸\-\B9{Q{B N{!Dyq=5wܛГУ_oMŸӍ1wc\Ս5X#܇XG^G@Or Q zt0^ d_NÍT2RB/P( A§eTf F#ԌZJuC@{uv1NU zQ{n$}d"N1O̱t H-캚[c a9K ZT_6{co%#5,SZ iꎞ_;zRo耸c]\sGO,FW n=AK-$(liO z=[C2H³H#z,w]%8+B6-g~˙oq2ʯ$2sd6xV: ѹq'}Rbѓ\ћ/jeR: c{ztv&Kz,T%CbE-?b*ݒȉI8kn^M„('5g]u糝S rb$i5 k8QPs =Q}zd5 ɫB`$2O^_4]X5 ռS^N{ɤm7k cI.bf35"]ZҰ+&% nA:uL"j, 8y_bozL G١y].B }n҆B$Ɯy\3Ya٪Nbx\Ui> ipEn :n@X%ezߤ%m{/a d@_e=59(E#|"KfIZk %z WcȋeeSrgZ>.7Yk.*>/hBu i~Z8guslWx}AÄCЛxiF0{k ly~dZX,}^Vy(_DŽJ/2)C>J ;$r(Þ+z&|$yW)2 {""C&Mr.P"Wy5m=S^ڇ:"qޚ!U_ _}gtav ޳g vɟ<뽇gT9pf|^PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%P@\@In._Դ ۗcp?'K>tR>*}q{3/*@Ly'7z4mzHjt8*=W mG"_9lYIs+'z˺nk-Fd+,5OCs^,oOoLo,[ގT+{ 相}CvpƄ1֎wOO^'âzDYD 2Gho&M\\~oؖZ ;!\NC‘nEeWt]떆=cb=8kgaѱQ\{8$Y-z@HY/"F_x\=䋌Uz4Yc zGNAXhoeq%ñSrJ8f<,`.!ta‚kk2ް0o vݽumtF;@D7.s@Cq8%-i}K#o矸h5 Ɵ'г㸞)Xl}|x6ګoM {,ۀe~\l(otbn^5k"6* o~=R>< rhvfA{i[7 kE߆]ܾ~C =/ Ȼ:˟O{,PnC[\uˠ+,M5ʥ,VuWYҕcG!؞^ Fۦ-̂p7*.q/ (_ ͦk1kc qɀWY5ݢ⪉a|s/[Sqe|(C7G]G$.yH9ވ#=5껹)nC&![⋒W镗*O*}:3w O.Y<қz =Ze\$ ##%s}]Nw+ S?5g %#w/]H=8F%CJ8ws&gCZO0}9UdzzhN|xa$ޙO7HG9@rշo " zQtxzrx!uu뚛y 顐\7 g'x5*]oDlGO :'cs:J|9}{zZ2C% yW7OezT~2/ٕ;brŭn#hw}'fIo9eV87 WmB!/$!ym kxz}By<ܼͥ9*)Sz뭏pަޅlK3q@8o1-$z##ɳȗ1jsíYѽ#kO=-r.IƮog^(T7(^Χ߂^> (>o ' j^G$T!ڛEz8ηmuLx2lz>#FśhO$£mNH6ӎoi.19oBLƖDDɝ0e֊bJ/u7}4k.E dOFzTKsx[̓hItEVRLT:pңGzb#fOS{)@xI\.Bo6\M5Z} r}74{P`;{FɝHF.JI)#c\=[1=PMv,pl T/3?=Q@OڌycvxeTW>>ޠ2vڣBO!/b'q!{ݍo4f&J5{>ٲ!n^ʕgobM^}-E٠2WYGO4kJQ{K#)hЛ>Jꙍ-9Lu@MA$Bt4#zwIiDƚAK)|WscLo5%FE^> QCqJZ V`D z;ߛ\}:fv/-\_PH!<['iӛ =aD/G@*90[Pse,y3ԫie͕*blq:I,犳Ё}PFyOomk8'T!|&9tRc Cil="R)Ћ6y2+:@Oܠ/:@!u걈Ki( ϑ/s ]=FsOo:4J*`k8f)t@D|XDz{!jGi/ ы:!#dmmf\4G9q#6u&׈e0*iR1smod}a?z*L-"̅^|+e{nړ׮YBO6whWT\7 V&I2:ہAg 0vOOFjBWbpiWsV9?;\$OGAk^=3@˖AY昙`^ᨋSG(pp2[B_ baD?/,1Z'rk׌(>2f%5]ኵdFik9xfJAy[.ӬT6WMГa5Wp@e+#R\hA!b2bg&)H{^ꉸ x5Iyyj^2`o>\|U̽8o=35YAO d &J a"cu<&/xOyXP\=g=*6]yi͌M#e4kn =+H5$ 2Ykwi +y=FŢГR`C/v˔҃.>vt܃3~ 8i?3^n9k!_17 +eN}F4c#1׸x- o9wWUekۤ䤴 #Y# Iӯi'I5!čs8v[ o*ghOx]s]Gci^ګ^{>[JI[> [3?p[q1|ov4[ c޲U:> G5)dx O[eJ3pګe{8y8zXدb}e8(z?BQ2v'gG)ycŒr4򀗉A& 5D`p`0u9-WPJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (_v>:|4봍>:-kQ\rZv_%\YdyqՙXVz==V+ŝ w* UXHzq]$wE|:,V@?U w4,eyXdp@)WXtKE*R&kq܁k$0c589^JQ&xtl RslBo\z{r^B~_!+X=ٗզV '$qЫp[a%^;h_^q? mbT[HZsrUr1G\Fz)/޶Q{.B))߀sptfNs=+.a+[f/jO?irWV sB|LoTW|rUv'%<\4ZYIMcrX3«~pc~xΊ\j쩿&܃W8WA);؀y- .=+ZszYx۪t1Uh>z'U:z8`\MqJp'-l1nܲM4]#UB蔸.OSeH??Sjh3h5;jw"=FA2K6)0dOb Y5C!n].C| ^Ro{p ΦH/)[z8BGa0 H08(Ml?Ֆ$f{ICoW. C =qo}'#ZqI\sC0P'S3q Uў~!^JHWn/]ߋO0s,QZMqќTw|~I/s gRKjHO#uf=8¿:XYa1@~<(< =rN֓2Tϔ(76.-WO F^ .Hc>CWsE{&{@*/m{zIrl&`+⇉{oR֛=N YBIIf8q+%iG2v)I O%8MlxaОNw +=:ְ ;8;8F7p AHnLt͒l[m}+%#z V(^L 02pZ.v[[e %i=4K;̞8]× =jOGrTN)N/ګGM3_H 6[+cрZq3IA%=+:GzkЛ{z#A{;ע^l5Bw3T<򨽅 &p;z5lbРӶ/qNZ ƚ+`qRs29_HӀhkp|&={ P{$ޮ nG2IS eJLi|ek.X8YBO:uVc~9:#=./z$@qQs#=i{z+V_m:0 b O"V4a',I͵#zBC,KGet6mb#En`/x[kiำ'HЁTN{FG48¬X$+LK_[7)â,GjC9Ų_#`ڜiR^7Rizq)v0XY^aTH]1U6ӓLҊBoN';4zLc,03=f5)rl Hߣ'6^0hAO:zXX{ zpqRHC̑%鱼oEzȜ^4C(4tudٺГn ʡJZGWqK㽔s푁be̶[wf=A/rB8X>$!*fa{|Sf{e8R}ʁUD3T{(Ɏ^1qQ.ܮӗBO(z OfyG)H3ul{zo!V x'.m}%cz\[2dΑhMg-ȋA|7--] C.= ՛>E=QDĔ L\eKz]:zhevDAOG,z.ŽomNJX(r̐+fkLLip5+>xW\tŽbFBBo#(\J2wdh'BO %Q6# ~䎑*E.BϛF\]2Mkcxjql.'M ~mZ s ~}tF/e2'F`r[$ Ơ Y1eKԞ45Fz/c<ڠL/)ݯ҄ȩ\2̊ LSd,O/H MNcE2H_UWs~M7oDz4+ЯHS{݇Ys_G&|Rg/!eM"ֵ*j[icStk6Be4w.s8faQ-+'~C Ң>R ظ{q+cЍzL\$xs{R_d0Awsř|mEjgz|vzJQlt7_8v'O zz|zz=*)88͟hY$n𶶲;8^4]ڃ+w{ )G3i z{[J|d{i_흞dq/vzsDS}A{([hG[G-i6~iRq>'cLruytkS,Q ݴCg?rϞZ]Yw/2PxQ P̏3gL샻>2kPJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%xg^?|QʸbO7NAEcM=nC"q2]׍c4"*JoI昭 3њ{ zu5@G!Yn֎VWrJX}u|~gF0VώZ^8ov|eU5D\ѝjuXE8Y͜Eȉ~p{^VSq~9в4KG˓,IOjfqb,Z%kw̻҇x+Wq6)B߇z?.%CЇ2/YŁZ,C:I}UKA^F+zؽ'c.QΧBeDo|{6A=DEWtݚǥ!zRsE{;̇IkHkz;a_s.gОx[ ꧏ?8tQsz,UzNjD\ƹOJ{:zW?k A'4i55k8mtfރKãc =aWƹ]8]˨"лc^.rm었#i3C8pڌF-IGzc.!3&[9QX"Vqdb / 5Hg/ğX@D_k[{?kub-$-./G|?Kh83=3:.d@PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (wI`]&EJ҃yU'ávqwi-N|=^AP[Ы̼vlIܭ f}Rbo9OƹazevSҋ.-Z[|vSWW0?QDz)knf_<IrA5EE !<>[͕$EYbiNK/'X{]o*e،]疴i3%ZjoN(;eW=݋[ЛGw#fKoXZPBxzXnEQ7a֘{Tzjn_ydaPs66j\0ۙ Ѵi xdUy U(@ޯs&BH<'OvBPk!-5I^&>M7 }H-(6yzKqlZu 6sڭ,5aʕ+mGM\479vh$a% tO@;zC=k=s蹁2}ͅ 55sBm){*ë6JmΞ.i T:\YNOqpWЫ0 QZ^lD'(tLt ގTWBL\霈ˮBkb(뱠Z{bGky"=֕yўO^I%]͝`_Jg Np =.sTO&2D 0B =Q-<Gyor կo5h$勛]Iu a('Q(S$L>^jcz:Th|6hZf92- ,s<-lu9#C)E)=J }bL6'EnQ{S.j G:(B{B/Έ9^k7ě@ms)Lm-`eF?:zOw˭izz9AgBvDy"kj.iv'ʕ)=Jۂju(I;5g_65VhR$lk6{T^@2٘YA&.+L} Ÿ IHBf8GoBnEhͺӃE'?.>rdiw{R]3n:&sR4w&<9{s%<e^n^dd+vVP_&%I H-k;_m!iЩB~*]V/+}u{I[HBgeo6xBp27w*-[' ?,;S%'hBޏ>N6Jn6J+{:ʄ]4)/3/~tTDuHpMg߮,_я]a{ C·3}KVvez{,.o5CWxcg [=&> {C`gO vRF'N x>ZḵU{ݟ&^7/ "ZJK&JJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%8+cI1[?N??,;z,B ˟k'Vts"Z)X4Xpt(Y#䲰hy|@[ٕv{mYpl{u\Cu\|V&FYީ[HPo7ھ57nYp;U= `aUÚsk 9z͈^s$Ͳ 艣Ca 냬h k5߀x IUJ~3>Q, XuXHXW{G#Յk}|-tsMxY{cqiwvHz45.vGo8:=Dz`%dzrJn<{C <C z'^bzIu&1;S[Օވ޲~B{J{TWIw%X9[MhswEe].vz#,)(RLuLOogF5Jˆ϶)ko [6,TW^a) |RB83Y( !DI{r/ghnLύkS\]WkXnK61ۦE=+_bi |b2Yp҄$TB/2TMǛѸ%n_;bNAa 5n5p`E!)]!dA%rTÖϒP)aifL W]UwX%?BF`$:ƛBcXڟж߹Gwsn Ǐ|dgpk赩vprRSYn75.p __wȪv7vҚ;4$X=W0:k_jMIq5iqnXןǀ쀇NgC Uo.)}៦'n2x@KY0h8V9FzG'.Fs%uR4~{]%ɤ]R1࡯rBע2'~]+%+nk+|_g,*ҥD:Ϫm~VT⸉'%Z0sw;JXGiѴbK^pM'DO\A/渧Wɂ3ǔf':no8H8)c-G& ZmK[\ b|&"OMwū1K nq;%Ss]yA|.! GA !X<8[CRm].Da(^Q{,9 Jу_4zxэDB{K} ש낎|D%-Wf]mxDE_g۩7=MiJjL- (x'ўSI݁{qAGOiHpegI-RL#t)Ykђؚs,?k.n ƫ5zU4Ĉ87cGȠ'=<h۟ 9Ao.$MZ 2D_ /\~ Y,c7,I:y}tզ=@e}|=BHnuD5ϋ* Xu.mBD֊^BoYsCm}l5PXBZ QBOcc`ݳcmPN{J fzH0Y]I豄`mԞ4;zbhʛqb\GyLtVО:rUVIgh.y6)}I7XJ$"^^'Kz=G۵3=-F'Vniڃ8 =&znTs KOsk5*qDx1aI}@kr=\2{{V#>hOͯ8z}0uV!FWA{ E:/($ 힛˱^S']-Dkh5:ztpNw GoWIO(t bu} (/؄TP^%nhI/ghBoJF$Dzޔ&"2n!h(`vb5\؉ ^ګ8͌@ %fIH{ Q$WT5l;ԓ؉A{46m~Bzh݋Qe"yuE "za2ui.͕}C맽B7Ss9gq! 1,l.HC~"=5Dۨ=.׀2upٽ<_$RsIs<ңͽɁHX9K={^kܘ^{2I+ 6A61D2C͕]'$aC2; b]5׵eNgeZ3Ĝ15W&Q0o3C^{EĈ^asJ[{1N=2BSjGzT[瑞 voDz'5~n &I+ć=u>[KX˟ĢyA^Vr8v&SnUX*m[lE_~ . M'VLb=5W㇙/lyϫ,qx- kأˡ*mȫ 6%LZDY}cg擎'>奅+i] | cH} r֭ev)e,?RKx(myaY(nnٺXYfNj+/p<ЋoFׅ^̇-_/' w{&ff6yOWD=!RolTׅbF;|y}Id&wDEZ 7;kEkOӳ+=3mj؋mpzv֍||1>] a?𮿚#~3] gAo`ev: fVv>Ɵ=Z8tSaC`Eй 岮,8~N='arA) L~~̱h8#gz^ )3o+%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PSE] _Cˇc=E07/[Nϡ ,6{9*bE A-O--`i)Y 6 \`L묡Ho&rxYˊ|(C2zk¡\^[5z!* n51VV._EUzUVbJk2 V΄D yAEĝm+_vA ,u6defVG,_؇ZMy1q C9<ِ)΀MBrzqx)4zKcB]wIEX4 1a=`ʋvoʇ\ӳ16T=6dS.A^:@o<)Nb@zְ~$+m:mL"@oy2^Epe|= zL]ҳ,\_jHu@/X2|{GC99݉y r^\>^@{?@^l1KnO*/~G1s|ؽ>!P1Y ވ!2.f7ɣkKSۻccEgr]^NC <I0+pv eX.99Xo|NYYR:/o$9<'S O[!5cr>1Xs/l;_ګRV4^ydC ͽCgYݬpfiIED#<gCЛ`38,\;^.>1/=~'xϜ|jly/ɪZZ_e} חqKp|GUS%== ^L-h*Ldkp4y~ZʊCa~ AL+ny"x,xiE|T?_oW} cv`$oⶡ1\`N{M[_z&)}zq⻥E3F 24I~kmY{Ӂӟ!u t -4|"7X>, .Y]ܛM_zv">o1O6|#;/&-a,k*6L Ĥh57'58-,2X@2lOeB{Dŕǹm7zzBSⲋl_\+Oc2Fٴ6-8}/gS,ۤioN^N󲌐 `IJ.W}%g*OЛ BAf#=G WaTl M H"=LAh5D$ q}WsԎq$1b Z1_9[zq $NXwZsEͥk =& e\>WB 8RG<ҳ=P=<41==C{ cJ?Ѓc' +#…[G,VԞ^ݫKm3梽rU7F{|gWjC*C#zӀoICmnŃGp8XsI}O!hj0*ޓ՚[ &ራ:W;Zd܊aJ#vͤ0Cx"]ފ蹁lߓgIDWWe#z=׍^z`xՂ>HL`i2;%54gP-Ge+5'7W!kBmngHzؽuGK͵R=yؕ|5W^]^)+@ = BǴ!1SL*jǽ=5s^LI!5f&S5Ԓ3VAXuTQ :b/d2ނ̓P&_i؛*w~,A%!O>*z Tţh*qtqZ,e]Kze7,6j-7MKUn%,`8$ 2A#3.`VuّG"ݕ3XI[ mnWCϕ5v3CT2QpnګMi,S5nkVVIe0&(j4/5 ^t-9T i!Iy+^1I7ȣ7p*T!Xs,Y浹Tj.kX[.M81q#ZO&9V%nE#=5&F*)R5sZ<%] ̝8j5)4[+~EMy P`D{dW@7yJhHҤ[Wi0/"CI/'qˆEqٶ7 'Dj\`; }si"䭐7۹፜7s4ގi W1APm!]R9kd9f ''U^x~ٗ`⽥}ŋބO|AO~nn} zȔl6M;Z { RgTneno `i'"Zq'"j|aOamA3?f`Ro x0]PEkL0 E7 03 E=9>6Xo҅1_g?J ?Y'ӯ6wA֫wndJ+r7)XOKSKX^'7wzړn4{I=l9vˬ ӧn1=}C'YĈ󭋽75=zm'MLzx3.\M99S=M[ot6Ʉ{qgT } ~Qs3<-PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%q=?ѓe|\C4VVZ[Dt ]';KN꨷R,nԳ dyZŵ//:b?8-doxWqNޗ5e)ǃሸ(KW+V.vb3#FsӋ덾ƜbShe˸/[m5y="3ɾ>YyTk)qC8ؒ, rs__l 5拈mťy, N~QŖć((zQGKz#zo5>z٢-DiF[,⼣e}t+o7Lj dx5xr%3'\I7X"yKM{Ι >WmSĀ巫7X_X9:l>4mj_!ynmg/ wYˌž¡{t KbJϾ?zIiYΪ 6Iq1[XdzK) Fz8nƇD9ph%X=l]eX/z#OC$}!|q+8Da%pal~w;B{tޗZUtx]s):tUso;]ce|Wd㇎^R<Tɒ^sհ ha)X1rFٙ6/p AfX_c3v np֨F.[ u'[pTWT\>o'޲p#rؔzyqZBD1ۧX~'nOpK#z-7/SǒdE/#N-JB2K\p5+"Q&ШgX%LDk b'&ZNݾAǤ%,, }R\d<nt"dW(RUq)z5Ja "8XM|.B_244WZ*¨⑫z؄GȪ֐{s2^];~-d= #w) aoaS=4:}x5\"-\2M* AS*9*K(3퉞u5gs4D܍R=v(qI鈥4HWF_yG4Ilu#Oonv=F,TNca7A/\]OEO"j'!/Pl =qzz^{zy(GI[VDɉäTۥ}c∮٩. ٞ"O5*I]CO ~ϰIQ)YAL+Ka{IתFA}.jj+xۃ"j"ZFّ=jԍS+ZS˸(;,Z^?Ql ,|mZOVC 1'GsB"ʈ^sqBru&a(RU1esyAna>uԲ2= vY<8nԚS!zsS˕߃FcEo{ U/B˝Z:5VjMbL6uvAFz zx[W>* t>jCKDs&L~kN{*z -RujOO /+] 7[zz;}G}NA׭Kxa>*;S>M!ehB& >wp=3\|{mp58)kF&-~:9"[%5gٞ@of{MSש2r=~=D/$t"lz4@14Ǻ8H3w"+ztI]ov,OomFރI>n.&tlzil`\;b }2F,lqO`{\DzQ5-9_O 2ky߃}Ƒw)`{ }dJIjg/*~\1ScW DO:s ,KO1Z?koM7N{=(جtբǼ~osG># {;cgg7wLBrP^֏XLU59-I<5=Ӻ[D!0;&AXrN=%KVnFane&ݮJ-wRYz ͌"LABGa"kJAEmQ*@/4N06G[CELS5S8&\ǡl ;zf"ӫy[== iT,5RF\bmi7+`Яuγ$%zZsʷ6g9@*+(a|˭!X)P<=M} z4iw̴#&h]r+In#V[C{Iex 'fgt=WxV*'^܇K-4b'J5ΧmЈ-JPauZ38 =y<_rz;\0>b|]1&i'tu赒eh jtIR)x}%׹\`Ȇ{z`2n_Ċ^Nb5IY Vz ?5rR/xK.g ϟ8WB{| XR zHYdG)o&cRpz<9-_-:uZY/ycԦx'`&#kȓ6ZE|U6<}/l/,3P9KsTfO2U[K0s %cAZ|Wnjq 䀛%ߧZ. vkM ϊK>2ztζ6d kғaFs_hA*O԰6Wxcl\y z=s@JWķYJl{L+?mx2d}?m]dΘ[q>zuOZ!CɛW=nxu%U{}t`/lk3+9;zOÛVtN=M{292;/y>?=&&EtqfokzEo-mz4szAJ+@}Mޯ彳I?`H{8vWɥzmMKq#Yw~r9{Iވm л*r(=)ru5EjOf{m<^Sg؟̗wXc+ߩmiCq2~lw{xC=>*`Ycvnx/ZI7{zlz>dx$e8%`:e؈ =͐D1 hA5>szs>Г1*M×bgPg;7mVݮJB D>$wo x=ICSv7wԢ3 !M KRվ.7~ bh:Mѧ#eT:_Kͷ.'29C1~HLԼօ!/̗B odƧC"9Л#2u2S@o'+[E^C|?z5L(~OC=vQzjK| V7X7(\0șu"x;p>A:sy(8tWmo=lM>^W<&.tJ 䏃/ w=\HOæ8-^Ɓ-aq<$M|:rfm|NσoQ, z̈&5зB+?H"&ѱɅ^yLM~of{3oƟӴ++ 8(/s{H<Ƅٴ&PSRªX(n/]d5$P(>xo,'F"E|w @Øa7QXQޭvis#M')W)4S ʃ=N{qy0Mr-aJ thtP)=KlGy3ey-OU܁W]5NaHezZ3@+*o@J:!)IoGERp@k YЛJE#q'wՎh,J5=JՎ9r37T\1%蝙N-_e z4^LZ&KFxM<zaw\N2G~IlZW\&?dS=JgT@!d }<|iH^#E G9D>)_iS<j6<Ia.eu)AoeN'!:JD5͵l 6;(m O/WN&R,uRoQ==ilb =v:Pө5 ,:_=E]j;sO+Yd;~z~7W%Rk\gVW:s~xۣ=I&Fk5hл8vD{ WBE9==1725OM{; R?oPze0".1AQ7MRg}AQ TQ"lX.Coy-9loaq:qI2I HC/$8EM:d1PBߡe:v]wC1&^k,+h$Aム3@Bksl/Y U=yJ݁kz=4HTؚiMgCGZsAxʐ=jnˤ^y*zHW |%˜nL~1AϗB f_ B!ݒʐE*8.z>ǥYO}L蠟Ǹ^V1g\lj!r@֬9zȿagC}huCة/./(oMRo{tjzWϽ9ۼMC/C-CI}5jպ(^WEzDgmB*s- (MmT"l/\{QcIJPXj>Wޟ4 ߷Wc*==1צ=cBF2t^3T4|$IQR@4X=M+-iն~=DŽ۞'nw *b #䃖U]o{><ABa{^o{욍X5lo-㴾rݟ,{o{9OoJcwh\\z_OEo zk@oGm#4^O3zfh6glݗa8*aq5[cc{9#F,~=G\z -wcAHU+I6^Dz#30ѻ/zYSEzIxO#2PsNjugafio2o]f%.?&=uR&mГ#>e;c_{a 0A(nNhdodcZ.E[F,LkԊoB85.ZӑB(IzឞQ{Kk%zZ@UaGg:ddapӗ_sZmޜkȤEL =Bc hDBI?W8B81!N`?zmF37 l=nu>75Rb—1]{-Vs\Y z25(ГpֿpqvbYY MmÂ*fQ#JLl>WDr-wꎇUo{{}V5($jQ1B*7ųjz~i}R@oTV=*tig3M=&A=]nzk˃ JK?:UzPS݄179"$J\A c͈9N{G,@! mYXoGm؎Dʑ o僤M Zo^3NnB llH¸8Г®u&|WC&WG#Y-z 쌴EI4?4 xW3~sbs_uBDOQRXUYm@dpр,4Icvz睹`Essϰ'|^3o%z2FuQM lyfE)? =.譯;jи?Xî܆,̯op 'Þ涞nf_z.pyޘec_Fؑn_BzC2dyӷ)ێ={[ < pHy{!Wʳ_K=b-#~ijK9JLx/3M˼{߻\oi>?O4K D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@g.ɘ33|Vx7~G'"ڬzjuPߜg (ƹBeP@M/Iz:]-~(g1ΤXޥ褤*_KoƴoOf =KL:[Uz=akޞlh26ΚX_PSH25|U+&&b!._?טi^z1T_PH{%\3|If YOꑵ) N >r>۳q^cȿL{wo6^v!#R+ᶓqCerjf!y$*WY%}ڇ]\k['zw b}G1OB-D#iMjEzwBsԬmO,x ]l2{ExEoڜCUT= WSKW̽Ft\'pܽ=a>FDVoۓ׾ w~$ 5Y TPWQcNt>uf %]S>\ۛEa*.yj\aDdpr0E'&Mqw<*W$M5 '#* ՛@aep!"qIM!t-N~AқMSI<2nOHj)B(Xv`FZBV'7FnZIb e{Xt($}XPR*O0#ev`V-FC#7NSȏvKTDMuc4T؉XWPKL-=P˙uJkgĥ@K{˧_ЪzzA==A4<=pjKOzNTZܹ;K7b^<[c{rq==IVp7 3ܒQ)8r4neC~Dlzg/F8Z#\10/&G,.8={W+zM UɊ) /3:mFO?VIL(鵢JOdTl@sqsO=4 Qi]p"\*MO&b{4G! eE7hzۛrg}>}mOh1\X0l/:ѹUWP$=TM^Jzj>gV_0}:EdZq%IOo'@f.~Lj]2}.,Ld .Уg4 r%gei< cK7%,5}YbgA%MzYUhN}:y)1ցƾ F,>:1C7WMyۛ,hce-aBvz*;UUBof98Wg2yLa*H5Q :s87},Ӵ۷\VɓO$7؞-CKG= q^EzX>L[ PG?f=Jώb{\$9f^%ŴFTD3ܖjyaOtyzTAC2lrz5b7ֈZOyV?G,TOy4v#E rl9mK55mncfgFvr>$Bu==.\zzq}Fp@7b6q==߫)1W;`e=㆗0(ՔA:XzXT x]z++tBO v\^]1VF}to{xOJ/s=죢%QOtw!F*#] cAj&j~Z2 Ig>ph_Z.3'WjQeq6YC)Y+a8Kmvlo2cqE~ӡǁ!((=~zz<ŒV>'VyaB"WUnKf݃IjO{4 6/M kQ{z'wK66`2)S#qnwx?#z ?aFd'_L'ae< GаPLY61LD*KPo+~ QVAlՂ@ef߿uH-o$AB'o.<\dsR*VUg}o}o0]]h6-ΚpmnbmGCOsR[7sy%$)PJ[#b My_*ͼeXN+hG׌sng?HOpZ:]S7>9n՞{k;HP'Hhubk kn8>^!Ӑ4+ԹVS_W0?3xnT.v>^s˨?+֔ew)]#j?=>-hGgHΕf{6M{.ߣjk_@?{ ǩg{\O}~)~y RtW?Ѕ~#*}q9Y/$]Y)"H "H "H "H "H "H "H "H "H "H "H "H "H 6{\9U.)qܟ[eJrgC|-b fBiI:2~4C Wv4)_^X˲ѷ\9|k7sɑ"y&4[>vS/ ^oogoWŗ:[el(a_>߻ყk›tk %Aޔqo>"zz5}zz)7/\c>BE}zz8[(O3zP9rzF%yvn~g|7nDWYUGYmCRz.73԰ҿKBtWhl5,]em83FFZO~<\s rAgSOo2=N߰ԍnT-Ҋ.-G4t;"mPvDsnC~ *BsIݑssՔݱF*iS_Ϝ{IGxrloJ ==T#ed+!*zH#+eӄXF9" f6 TN W[|xBa55m^r$rxƘ-~X()CgVT](e#3B߁*!~#A B0)W.ץ_o=mD=ECC$Fb&_ *6^ɔ^R_qM$sdJB`wr\C[b'I"qgz\K ׋EۢO1`ވd l j2lw CdݾN'eM?;sDe4m<1`āSs%B;pET 4xv2I&w_AQ!8x])&u]yD]=чՐvDnXMQPУ.л'`{=SQyˑ, L8@O7`C[H{t^.'F;PTkFez#i7#IQQE Z:m3ƚ@ko؞zй4c2hBʓäKuzcy;pEO:Rt5pFE)[hD-$h*IlUpVHODXSѓd50zavЏXW rs-.Xy)-ɶlOϒoPJPmћNKZ&]S۞5Cu{zfaĂFF,'szG8Zҧ~/\JZy+mD@O3m`,eNr[L^[. Wu9"[Wd`>Z6҃{qU/9 ag*'Qp8#l(ФfZjǰk42 bK7qwWF5/e{ M-I 23Xx|&4s@r/zӛC޳ZB,<+#J&j{̚djz.b&[cL(\uNf[^I+gr?amE*9t^K<`^v~ZrdKL=X=YeU|\]㍄+1r6pc{d{n [щ{wIj.8^cz+^^b7e_  5x/\l)"C˕}osZ?J-L&I^8GXѸ;}2&.nsJqEMR 1/,u;VG-/@Q}4w$x\=rQz`Ɋ zt9y$;ls{77]o PWLε}hR^L8,k4dIY4Zwjl3GUܔ`um^r^j_z#_Oİ1;|}\_8Gs3ql\䵑6NYa~ ܺ c睳v?]sOGIf,>n^fߔjћ{nz[y>n/BOkO"ZD&ObYi~-tj7lqk|Ko3?y%9^2U1U c"H "H "H "H "H "H "H "H "H "H "H "H "H "H W%pho_{[k0>(ռmR}ӺXn _o.ϡgo::^7ZB6GHHsřKQ1Cao{=ŏ|kj[_R]M%o:zoE].BrA[{&-: &)-iHLW L"CeFxp0!~L3 T=;z7*U))IF͞1n¤Eo$ESHHa:>x,ɔF)M)6Jh[` % RFn3 `RA'3FhQog+vL}Yp7C%f;n :oԢe[I\\~"z5DgBRk:ҡS% . p{8@u5R)"C _2UNCY2CsWջt)iTTfz%-pʑ@D`xD+0L"s} ,s-WSJQMY1Td޸oĽQ@`,h[I#[z<͏xf~/}o˩G\{z- 3kP5¶)`{;00Bdn@*=fGm[ u#NZU@o{Ø߳GQ=/NlRԣ8gC܏X&sE4m圉#IzÐ2& =ZkF&؞5zcKrmqrV-lOkW}`{,WʡMI*WSV NC==^Ho=ә`zzff˂F徠q0b{5OX}DrmOͣGKkw,0$Qܬ<8{䗈(Ca36owX5lo^FVpbmC-&EsxpzqWm+>X77Ldb:f% z#L@!,>s7hM2螠 AG14J8.ZtlI+/Bo$&ImZTY9.R#^tQgQ]pQi5cmGclxXS]=8zrrU.Dp9쏉M"ṙ1Ę-yzzzT}Q =LZT^C{ԡdAfE$?(%+ VbW%#&ްٿFZü񽢧(r4o9tsJr֗T:k_S)>pvzz͇d`k8'\E/Ʈyz<'Aa%89oX.ʎeg%e_@vԬ)'tJApħ3|r&͹/^U8H0LfsVMx\0yKRNL'oÅ?^_y/`o3wy[3o0{ޟ[0Ʈ9 |}^mW噗k}1`sxa} ) V7XIFX|rIE[{yu>=&/1~oOwz_qhe^Z&ɞ=]_=,[~Ggw}}a\Ϟ^md,g}grt'Y4ޯ2T]ty=W%xj$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ | ׿FSsm9*DoͿ^H\ 'Bí[oKB\z^ߑx_ &|]ZiuUy"lPb^".HpsҒAOc]Ir[Cdٯaˇ-AΆZ'n~$k [[D7|to`I-PzK /@pQh>`q~ 룻϶/}cЛ뷧lz9%<* zֹ' soJz+=k:Gs$zf;GzQ锚}>9kNJR[̬v97sKÞIoft+ G=ϪbQ5C ߯律x[ыCLE ҈eMZGhdk} 8ogz z BqOOg<*=O/f(pP(KJnq^VL\%;@+z5ָ>]%ɣVQ) xNd-$r\+'ul"fD2kYO &Ј5b]-9H{&7a$IkGD'bzYY;7EkĢ/j%[&^MSy>?J0/#l]D[, dr $_in+/˶g0=ܤ:QQ^ " ΍ƴ䏓aI~z |#T$ۊUD 2jR""E* vk 2S}X@խ΍d M\+~]'FG( ^#CBԫ"z^dnc"6fv =g5m8a> RSUT0zZ>L -lP0^E`tk#2/Ў^1-zU/KDSYlGwVb[觉N57"ɡ6߲=0h^SGՋjlhx8$95P?!POj 襒ekafw ;-oanA Nnb,zϦن^"QᲗ$P Aгvzz)9"wjM+=yDN!HOywE/=1I ?!-Wg,o[9 `eǀBGG'@k!YSIdr'9Lyz\ȀS{Uz8 }:-^*_==,Yf\2h+Z ïϤ3uFJ xzmKFj1# lMn[r vvazztPb7Ob:fڽ@6K EcN0*;Pqz⚈EKzSʑp< Q=i`Mkm{/5*л&RicF}˝,&TdąA},=Li .'zb ;:9{z^ڌi]T6o-{9#T@pʇ.=5hrKk4P4p== sz:ę0$&zOE|n## ~dbDHf g =4Y*H=2$0Z&',1Uz {cFO>;Wƥ8%M"TzzX؞ӳK3\XG/G嫁^{+d#f!=5M<|B]G. z b{<"hb{|˕MDI4Ȋm =\@>2\ Cß#rA@πB+ ~*F㋝yT?Ə;᪵oB¢4*49`,{lS\f|Az5d_-YS#I% Y-Cfq!I^=nt>ap,r#HNcR«lO^^C`nL;ma y}cXuIsuxO~z@=z:׸]y==q }n]f6o}.dCӓm50XVU@/+v@O~uҞm>+RT|D)##OC{,G<6A7r-W{r~GnfNEuЙ[2Pz2Skӎw,Ezm+}FtqozȎX 1.`j;u^厄O/c4& Ğ*uZpr=) ^>W˅͚@,Ğ5!nFB$7<=š#u <ʨJ${ <04416,ł ^[2t9#FyVq8d'Z?Zp'7zyE'DCB% ޡ1vyހVIOt's#[vt#NeZYF9.0_=>BF&Q4j^`LA; SYx UͷJX_?@e 1ԟ=wZk]sxq+b6O`C'>{fK|=#5VhڼV7>ГN\VDwqww8~ܑ^=]NXY8S6a[6ϵ8ryOOGN=y 's~H*Ʒ|[D +b<8/y6iޝjϯQU,s8'X6lfʎH48{)./~e\u9\1oo_`+?'OhZP%{!\}׻zv@$ D@$ D7@Mk F@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ DD^^B~\߬LGBTkY`.T StJ3o\//T}m"5djΆ[OZs ^f >{kmήлA&"F8- %3v`{U'l/-:q;Qe:{ptg):z™r)\z9E]=r~8oukX:}zb˚]aNp?M?aP-56޵$רˎo;[WIb N ޴M%qM(Qs'ԗ:vߑT_ K/)/#[l DD!IƵJe~3dꇹ3մԯ mdwpDbԯGE#7~(cCtnTc?wg" &!c^eG{D= =fj.TϬAIԀ&"JG DGwbov ˬhw !U$!FS2\d{6ނ#U6%( @#Ü+wnRyg%DDƳz\ݔhbQ5:*{< UlHxOh聠C/EmqJ#(ҍ;)um|NJ[Yevr=43QY 3D8y?I @R1;WWfB=ITJE(h)z@OWSg謈!2htt1yT{zT%@T?3HcMӄًۈޡr{;tƟ(-\`0(+ n#ieF{ Uz0ĉ7Rvz[==dr=G;٩#RT(J2;UiO@Y7!&=JKDo*z61AGM=GF 1h^w,g$օpu%do0qsܯW֚C+Ȼ; @TI(n ˠzoo}Qg`{ZN@"A_E$\*gl0@X9 􄙧4AΥO*z(lzxx*AMaǸ^"1dc=$ h􌚍Lfg7eQG͏I'QHXn=|pFO`:_Y';\ƦMAz; lctLQ3n{3f.nhq? =@O )='sẕ@#go@F,snJ+{ۣsB˞XO/k Nsٟg }9zI=Z <=p[./MH  =J]˟\aO/{e4,z5pg F,\o՞g_Ӄo}=\ ~{RW{65 zng!oT G^ΑoA%yo{;^! ;^ǂMOԏXXޜlOkgjZ r``8I~чydΎ2lq 0υ9=2Ϝ;z#Aᶳ fZ-Ӟ^n9x@!qP~Rk?C. hc4A 7dxRgM혿/?bKƧcBwx6& E-WGJI/=uV혺T<J8icX)C9-wҙrߔ- WGk-U9=z< \X:xޡ祅*<=a B٩.BâY*ۜ%h]gлzoM^a~˫;YNF5K[`3g^EsXJM ȢGler==)7&%SlXK]b^j@~^AT9]l:)Q V{Aߑ%WY ^c"5K5!l2 B4\lyW:́b-yƂm5%ߘ_|+j~+Fg`N%nRHO{,rza kK/X/3oȠ."/Û&yզse㎹삞Ɉ^XΚ;\U+$w $R'Iݫp<|siR=g[5ܾ(icyz}:ޯ^.zzT~ZUF#YwG6K=okJwٻFnݱ擥9?Kbq 1^@o;I+o$z^ @E]/]vC~TrfOla9-/7#vzYZJ`S;Ã̛UzdG\7-moNE2d[wzt':zK?/;8Y-ɛE;sVo~?W+Y;cxb[,ޘ{Wt9?59x{7z${ңk vޞ=`!^%`Aamu[iz2+;7լ̧fq;k>qv%~lF;wמb7Q/rW3Y'\_ r xñ!&oFwש1}e3O"H "H "H "H "H "H "H "H "H "H "H "H "H >MO>boF_M7^qE?zH]1l-}?YŏAm\k{qg@h@T/GΞ|E4>-Sha *cQUgGcWsz- Y-w-aWP xzU3[Y)0޺,"&!n57̾3ԍj 2iC,\.ByqWgí ȿ5Gg Υyހy4+lc-&;hV37Liq(˜i;E_bmnOo0p˼p5CƳ-W79^([f0_rem12 .[E/B>wܫf8]20Ë$w8)Z>M&u/ȱsO>n=BzkZn\n&>7^I̦fڭM"`rƈ=$Z! <ܗ/QXz~6GR.Bts0p)uEnemH脓'";~FKrTL9!4;bߐlfRH|B/y?Pf~U}9ChO*GIʃ#ziMV#'ƎecSy|<NQ0=Ed#&)(╀hx,* *LyG ee14$ ȏ}2ƀK Nd=bŦ(uv>fΐs7sSݧӿ߯L}s߀֭LQZώu.h;)J+$:ywE/I^!dUX+'p$1e W1o|uQp5䎎{=l H3h4 Xl`l~#N·H%3'ΔVGF1@u1q}3"+ 6y|/m8k5$y߻Ty -DF{%׽{?$]1E=XQ{Q! I y[Ѣri3ibA+:0V~O-UY=]5'ܱ%p`Ez8NݛxJJl /V Ir'-XLNX]4AI DZ咁ʈ荡 "RpIb(K mRU%.#s?@իQ0ybIBK5W5䶔g)'HѠ@|;^U˥!=z NGzz`mo@[@?lעw=:vaF y ET5m |Q}w/{κ >an>Huo%Nj{Q\NfҭlOVv0y)da^ʼ轠%}~U:Z˱n>8̋2gx\F n,(!CZ{Ljb"mg k79s)r PCl;`{D*Aԁj<_:ˤU!BzYO[[eG7)ɒM*oXu(MؒTyB^2n;s=#¹F'z!==p>VcǠpe{\ZkrBl`x._ si!2虉[lR|UKL=|j+B zK{uSuzhOHKn握T0A #0I~P)z-):u>8k%zS+#NzN#V8tzptu-Z>lEORz{:cKsӓ8G=P=.zʉzrJ\Jwrș1d ➴ cv&÷8S0;>)z]F䪟`mj.!ΌEo&G73KG'4;44{cC &,3䮪ږ(L[b ],31@[ 6" ܪ7r5~32y2I[)3QzK0 ~m4+A&zUrp!z#_~Kh\<QCC/Մ-[HZu38^ b/*Ĵ3{`\C|7>˼̷AG>h <3=vQW#?cLwƗ=~g9.F9σ, {طK F,4ybKiygIDt̊'?LSO_ &b 6m.xq?y;<6:&$8UIצ%zkw? WUO}&}Gƹ}]>d^Eͣdm^trNE9~ëjuqŞM:UAz/›*w#һrmV+m{ZLOld3Ҋ[}鍾4znhE x%N~Ku L D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ Dz_|MS3,dܻ{1d;9w?ֿ޹.KѰ ZXs?W" ~uQx\96LZV]C3O8- E2{ZK `ϴRvç_JEܻ|T]Cޛ9Xg4B|gC5l%[ޭ+d{}Ň}VG>vX K,!c~=xjY˼'Viœ˾kq6cpx`J@h8IO*,:oDF'>[~<,7C1T8h\g{{'ZdzA:Qc^+i1#V gVC};,|% Xd~9O>~ћ#T2g=P+IS1~O~v?`i)Y}}%vʿG- j Yۯ\JYŒ g4Hٓ]DGŃ67,[JۜW&QehE}Mohޢz!MBfaSo] bI@EaT$O:ʰ#Cz\Q}w<%T"(b"lz0DO4yS3"MmGC` Rr'ȼ|߫дpXCC1Z 騉d%~=!,K53ZNHxhlr+Jm!TP>iI/Xi{r3F *r;pAeQ2GI@D'u9^iN&9u_嘖$7<G +Sk-z( 5 Lo@Oqoe?=g|'={IuC-,ۃ M#Xu^EN@M(z"DO 0 HOz|=7Rv f_'Ⱥ;%t?x]Izϣ>q.z1 —r%$* 1Zf =ٮ;s;zy%=W8Z;& K艗eR-wเsA`{]O|+R[e =w%=XxfS[ O)~l{ } jiCP$Vv3`!c ^6w{%1)z,<*gCI?^J.Bs]ݱ.8[7ǂL-ÎώUd!NٞEo}y!]ͱF?K,^g&i%kfϬi,#2 (;ӎ^{^q.l+Imdf's4zv*p#:ޤfAǏz,  y6tN¢JݮNBvTx]:Ln3nl9F{ %l"ܹ=Ws,6Ы-:{+= t"f.ע\TEGw4Vp5&9'u&rw3 IyAc^#h^ƫ㧗hmb'Aw K1~!Q%Ab a\kXn+qaدJT{* ZOR3}KkK(soha4L_"˜4kM`pr7Eʧ.y3T^S˷@iR_@O~Wp{:V@{qUלދ\⹡~'GW?*?/ҷ-CzJWTJHv 7Kg4^ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ |Vm/?=ZֽYC"g/kn9bEz|FhpMVvҞ蹥aʆ$YKKJ9>Zziskk5[5 6nύYV/R23hΡgﵽS~ǫ_A{]|yW7ON2RZ$8^^z~rS7)o3Siݒ՛QTMJf!ywze& uʻGMWkXpelL|RC?{R p N_ziz]N>Q\laf~4Xlo饡}EQoBs zez7mqzRC=%7 7QO7F7]#rgqo&ա?9[L=u3G6z:m̞Wvbd_=bs[N.=3r~qg\mR+c:Ыr?aƕwƗ.ӳ''ݧ~rqPz! +Qi5}ȓqܺ0Ȫ*%OqkLk mLܣMK?Hǐ6J[$^\Gϵ)L{7%$C>&%Qa$k Fu:Rߌ]0[*B|}&G~碷d̳MIKzF6mE`٘nc6y%i2/l /fdݬ(x7$['2"KTۉmLc w+_,r.kDoTiwFV ,/s\e Uuj_t{-Vydn7weĚæyYՓ)6=͝U-H* X|'z(c-&?Uu4m/HnwuQz ))dv֘v촶e MO]>sSӎi¿m'w=$CuS'8-d\dS@|z*#$o: Llc=&]=mzo^+ y^nk =Pe^xLNł| cž63Ճ8vY?scwWofMݔ"moK6" F`qxD@vzH=g[#GXأA&6a3nx6|zGvF2.^;«awo{zqZy \W`(!9-G1LA׏5=^Xɂ^`z@ᖁc iVu/5MمUj=qO09FC(sSPsgV3=r͕c׬2=(#736$Iʗ9Ki$_׶t7Ӭog~=')Ž`{lޮ)vhƔg67low.#v]IɘaHx}骊bHGk;ɼo%z4zt]̸3;υ:zlfzXσ{MI6$w^VFՏ>" };e> c]hv1iiH̎xqߋ^8wW:r{]ys )z}jCaISlϥex%Ի)lA/+-ʤǮ.+E8d .39Z燵yAǍMVl 9ц/4O~Gmh1m{l/s1lzlHw:C +Ib2IPfMmZq7t?U|maG٣VANw}Q;/y !9MI5o|I6vL V^eˋsz|u?9ɍĔg?*:+UVzbA)&(yHe}'i"57I[>3ɔrGO7N°uR0 ٓaubҳn3O8T}f6KnA>Fxכ;dbFˑwrc LE-+OA/x3Ds˷21:oQ1`]~ETTV:qN[b#$4d >;/6%ҹi@:⌌ |%OR`fѨ@"uWG`cu[)ӦV=O-=/?lw[[\yzK6PnloXkt<6.R7bU3UcLJ7/gWr|aJPmz3˾~ g9W1&c"H "H "H "H "H "H "H "H "H "H "H "H "H "H "Jɮb~u KO? *,N*IggN5Wݓh`{TE/N5K>YLW㼟,Qƪp}Ѯ\Y9YcṴz-﹐bl]\ZC[, [, %XYk1| ؞3*6;˿s>,e̙CUΡ5w^<3PTiMQS].Vu=VB9FGlVw/?R?3=VܙEz]zsg5dqi,CkB[w?+ ej;Ui-=euYϑxHSOC|޳ŮesuhuP5-,r]:+ /DoB±N̦,PUv2Cz/=$#zHp[ylQЌ?niC/)W!K#5)%I_b?NBSI >t$UGE픈n뿌5llOal[$ w+cakl-7hr$,_ow}T[Gm"W&墧R$&=!&ҀBΞ9'Kc=!ykv/xo=a{Л}g/ Q@f)u*%zT%ɡ(kIȖjBB;s*=qNObÐ@Z׹|'L;z<:`c~amaDX܏֗jh@jxv =^O4^O{ ۲4sYg{.Г"iG/|Xsz=#;&*\y|:5z8Q!ʨlAA n==V̳LޮQ=:dAJ8sz׭9a6,i.!V&dQ:zLQ _@i%oڎ^|%NggcwJ`TVzz+zf"zB>sp-[/zE% Z]Ṙ# -nȄ[ ~Vȁ BĄ_g)0w#lvt%9Y%8Ю%8|i`c6A4Ce )ܿl7.i8xNgLlZMMu賭-݋5Gcϼe3ZC\LG+^Y=!x b&aשK'~5=6=8x,K٭O¡?_nM<4-sN) X,F¡ 譳zg~gexeoy}Eig/ah%~;.2Ns0d[g{̶8\yVkmO#9η5W]s&bؚ/߹6eoosf뎾7g{}kyṰ&G1k/^wuG}p%X.vkvnjw' ƺGբe+E/j7GEO]5 ss+WlhA+sEzF.{CDZwe>>ί:zZBfZ$^]R1+[Ñ<-w^XB6EZѺ#aYŜ ,q\2ontM1X`AoV װH58\ˇl'ÂgW %zXwk։7=\rn~Vs,ʞrnD[K/92,FKI5 U"v^ z'AoXT'9u_5d VS-n NӍ%ĺEZici}=vIJ{Fwr\z -o_%zZ430F\ǻz^KH*w~M4=V\s=\rz?`>x3DLC'A/@o=ו%$?ŬaU"=vk̪ӡհ*:zC|ۛnxl՝]9Ľգ%1Z͉Kfꌕ)J}c\w?y5wYmۍJ3Jcu?-Om=]X[l c^2jLya, mC~gUI<5r8! c,K/Y뢫W#ҪވC\z=JYm1.Z +QYٌuw퍵FERlNF+% ;TGIkie{VۑIO ۫d`46~N;TJyu4`sS {0l'C9 =Yk~4#z%zvW2M]x OD?k,7f݈ܷ|cS]\%\TDP EQd֪}\<0s78'`<7 &e;q,LJGzGi h!]`N HC' @ R#UWք'#)r^NK\Jd衤zc@Z@O^mP\z (<:.`d{\pN/͌% ϥ[ ̜@OԝDmC Kƴ6 L8f2@/xnOL҂qE$ ?a0h0{ϵ5LC@O=X3DAw̨l =K?T_^ #x(B;i *a{e {A?dzaVW`{}o-Mz(^߳n1Y=szrG'\o q= =? G@4//x\r 'xgc&YXwqoe=\Jܓp#I$=f*p=1W-lOcA%ӫ ô疈Ev`{D]՘ġIz1GNٞ腸7 ܏l깨/ε? {F@b{z4D&Ы)lj%Qbe U3œ*r^_xX*yn05/v?إ=z93piI/z QI˖1^<6NHXέN9p8CW ބ[YpE@OmqO6,z,o8myPCO~4R5-fف\b71P>b|d ^e z* bw^͢H2Uœìpa\Gm1S^`n}3)O5Rh^S_sou<I{KOwkz! 20=VHRV3Yqt`׉KNnfŮ~ɍyC=aϡ'{ b#BDAĩ+,=%lvClLfE0GyOE-N`fAp6CGIܽlAϛ6Lj9]q:CpŮP-~f4xS܅ ܣȚ+G#pS]D\ =D562Ĥ?ttnMfN8DxLE2m\DcХw;*9kGT4$/du=&Iqôz'i8n<Ŏ MΓ ˤF$.O^!V7Xk"Għ M+CR :Oh\^'OlKY4Bxe:UVkM݃ #0@m&C1ӚL(2 F/1b7vr?} ܢea S IhEaJF3t椤K#PJHw;#k YX hĞGim[Tvn{d-&DoMZrt]1,B"5W4_jBo:O6 v,År k͑G\$7Dgrz('ZWej5n 2'1#1ʙI$QQ*=̤/e{qkn{=Lx Y)zwz޳ng = z3/==zRjp=[c * rODI2] <7ARIEiF =:Js 9䬤zR|s1vڪyle\4e{wA'[/zr9sۜJuSL8=,ELvIK#S_KP5S?E^ @F Uuzu<1{g$@op݈B^eW"@0RsǥOXhq\ܸg{zz85vN7'. ksKmq$u2^zf(~K8X@H_[o#x80R L;=.8[Aۛޤ{`{mg{|T=49=“:L%*sٞF3~vzLOq^s,zcAxXc\s$z W731xw۱RbY}r3gjho%{x+2 ݊ge^/ɌѬ׷#bѫ>;f]ZZJ?I7O^onyA긣)VjS6 l]=tK,|dr>~9`3?KVHKf6mj,r3R>2Ӽp}ܴKQ:Ð^S s~s5> 5lL&w+W;5 wIx9 c%ۺ/ݔc==3gXlo8SgT}3|)!Fhr9=GCE-hoz8vK75QVCݕ>5Fyrj݌SZ(צgW|wp):#`vmӳӋac5-ά?'z0O\z_-.m!sgD/qv¾K<Rҁs7?隡N˚;hW_ϋcY6\%;Z-fח TT[:hXJ^љq_6Ϛػrroeov4XJ|/LP-&UHh腡y6q=qG 3ptpӠi=0s;\l IOvTĺ]w*;h/փ#ht&ßxa$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ |$V/wt[ZՔϯڏt^j5XW΍ŸR 'Ȱye\mvi+m/K+83 [!ٰp; J̰> NޒͼZ-wĒW2sV)˵nV>--<0z:6'aEULKȖ6{z@%zywp>Χg}@+y*,!u)]< Y,[o{.+U}(aANK .mz'!YzÑM: ɻto.z%_B,<Ϗ,*ϽN}[쇹3>J)h 2hN[r`{_?S~wcSusUhkQL/B@o*ZUfq_>8Mosܮe5uG7+ hD'5] ?a`f%Of$N~;lPEjeCnkXmi莴?珟}Quoe,|mOʫG&+Y z|8(FA|Jn* 2G4^.z>rɎU@AAaW6[I,sgqIC|tkNOGO+i1 *=lt 6oSg۔>})N֦G(]K'M]MM0 kcV _G( zfqeI `b j9-C,PW:=_L7VnȎqޅ.%J5B%vGRt^^vE;v+fdԨF!#EhFj-l JH:$%6`C2We>M fͨ(o%.$T<0uVaGy,4g= =?IvI99axVyj0yo/Qk6sI!J*$Q 2:?L 80%0=æ%$SziJK}ɦIwm4B#yn'fֻT5 ,uk %i&-ADSBauF;-Xq]BƔf p7ɣظN(KF腜Ms wr0@ʏ}%z;1Gzz,cTMBK鴈%̅aJdh0WZ0VQb;sq(Spʲ@-:NduKkȎO!;NV32"'q6Dl:b ˰AM Pt>tԠ'=1nq) GOD(>9'CS f5ޗL6zzuK;#=8"j;52ޞ.SD$Y%2L30uLØӳl5k]+>,*M[B΍܆6( tk0==3}-k<8OGmRBo_s) LzU 3kΡy.j&Ľɍ>?=V\vNa ,Q08Ħ ,<7^51qz@i7XHz}F㌨;r8&:ϥ&TXǁjTծAyRJ7۪P|@lOy&vD@ʆdS3?y,\ d9w"Guڎ= `C;pq2] y"mϝ$ja޿NJAzumnG'tPB@׃aJ:z;^osEq^e;z@H雔Л@k5rynWl-7#T!DOq/Bp&> Ylw$ߡ௩>(d+SZK[V DI?L=5>;%0dMqaиըx7ύ6,}xOo/mQGi?dyO},/M Maro/NRu;z`.zkEIžy3ϼ馥/zU_[\C uޡf-=!Z 8yc5:}Ґ%,i mYKO{K75V1_W| Ftׁ!o9~s?yK7ڌMdm+.̙[\KNAój!Z ta|T)} t4lXeSpeUG>KfΦeza?g,愑Z\{d,J2[Fc:72uUWU>Rt{;w|έ@Oe;e847/-t7~ @1Km`BO(ZFw5s163K++%QuQZ8Xqmp37?,xP9=sc3V>|u62h4X0c NШ l`a?Hn82VgAoXjw_ >ԯ?FRC㡗?bzu}|xxTZFoh5Ʋ+H iaVDnfc5`lR͙Ēr7N=%%:GE% ˘,:=+] X:tkx9|J(5O|'jJ&GZf"KxO|7vQ|}UY~>$@)KLطL7qwo%I7i&1a%z)Eo= 4t~Y:j }޾S ҫS)9FE$琛bd#mNma}f; bza\ =.BIyvr[ VA\*;@c58N)~vrPo%8^mvH1 ֑^~5(]i6m!/\*8!|1zI)ݛ"siKC!"zM^!5KaC}Z(J`<+H˚%qBamv31P ᄐzɟN@&@O-~L=N,zl7jiu'jx)\H#?F4j$aC@k D*CxJQI Ij0ڞrCDZ, +$v=DbK#t㋞ZTr@ 5V1llz< psH Lmlͪ"+D)ӭ;b1ZYJ/sHIH|r½szzJ5=zЌ!zԿE-@3y 2Ы'\䁉ѓHO:A70Ӷ59lbqzLy)ṑAM< 1lG_ѣ{~+&xmQ`8T23KzǥMNd:tAo{ У)$5jسRoz\T*Ћ9(.%'UzG"=NEİ1TcMF~^l0Fzj俏q46蹽1rl5=Ռ-0Ezn=AzsGC6þio@0n zXm| G;JΐV!&=榣v@67q=n{ܾFzzKwfjDzR #lW"ƾg\Jѓ#zx=9H{g[#q2s,rcng lmj p^ʨ!TvB DBU7,Qz,)q[N^d:#7GǢ[d{)zᑋl$jBeԺkYc NDo{urJqzQ-5ꭼ$o-SPFrߓߟRL1=F-lSbV:)P=lO)4*Y%+!Ru $ouaW4!##z=8W :?{;~͍y*0W^ӓ+MIË:=|sЛ]X>#~G!-0=[LzJ @/0<"@Omnw6zu./=4Z]Ӿhr#a{V1U%,/]WQ=i?šY׏sHۼfjsO=[TMۯLMϟefVWhNjt;'%`.fk2zg"IO= ?^{/@s0Gzd iێH/ Mp dL \jgǎyUW"OyQ5mpC^1qw73pI3ø>AҸ̝&٢V1[f P<_jZύ.Br=G29g_k& 2Zm U~01qkL7g. H" 9X3u?bطzF00{1ozs:0\fލ;Pء c~WV0լgRϤYլ#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`!ŷ9̎Z1pc-/G`8Kߚ8M(zk1/x1g1O}c"g%v:8"a\Y߻c%i_Lk^$_+iu#taG۪wV0 BP 'VtadOK$Ҥtq SMM/^P+IO(^Tyi\(DoD,*dke^^2ۂSteSq\=\)/;wqwQS^vdh{Kξ}T.N`;vj40d 6)jy.A'mjs^X+Z2-Fjm ={7tf#3TZ9K/[&sV;Z0ѫ76Eұ]Qgx;оL dCF6]MKDm2Ʋf̰o#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F tVM>۪\,33v;#88^;=]zȉȗughMBғ]xS*ydu.;v~*+W]߇)i,wA,,[祓_E2ѝ !4STEo.ޗ-gl-V"yw,5gƓcžPeQ k'~/Qd m5ޑ98Ug2ʹLp$,T}d#z(FL%|~S^7=l(5^x C8 Uo>k"Veio6Td'踿~>cBwκ3~7Ykmoݨg> rZNk?Eoղc> E/6DncKЇd{T30h#j(Nߟ7Je޸CӞO~c<N8^k n] /x𿇿HM CLwY\)%J[C=i;ґ粊ַin\gl !; ?SJ>G_dn;mj wݥME>q\LjDe4Ypyq/ ') /{e^N_#7/NT ]s4٥1}%]5t->{zs.FBh@/+* kv.K swv_!g!Ix^^ :, խ[R9so1׬#06nZܙ==C k==(GzeOv\dI<]YEnd`q^|'Ή.ml/TnP#VP^O@r iִNnZ V/v=mGz@"'ڤ:&ؓ&)|(]u ,P7.q9kQ\k5#=^ӎ64t[|&dĿnZ j_nH .lDVL"3kS;/96ۉ^D/wzzb@h+M z^.Y /f?s&p*卯q"_t"W9{iMkuoҽ.)['w둞kgJ[VZW&-t9GJ[ ]H/7}#f.kzz'GfZZnMA N \W3{Se{ whlx*)Xj^Kn=ve+^ve{{aO7wR^VoM_G OO÷-ᜲ=Y/רsmM3L8[?N=/PguOqЦUۊ| vERȜ$T[kzzmۢG<G"WRGz{z~Zb{x<%OG87)#q_ҩj_q^IhǴJ~/j5dp7 ;f:>2QЪc^V Z lJ+CKy^OጎFVnJG0$LmjR*w _\t{E^8hDO Vᤫԫ@s^ɮi0yc#ҫ"IO&X^,Hqh{5͙Dʱ:;ziOOz:bERTH{ๅZ lml5nonG~i@OKEzAcxnr8H/@o =Aq5a (&G^? l{c=55s3jp6Ӷ;+bR=X=S=dܲ$09]4DݯK L#=PÛō AZBÖsu+'崺UI*)ueJW>d/ C+~:km9a6ašm]L諷ë#L`4/ Aw2W$&^/uF'N\yZz6 ֯s?}_ٻWsų~m_@{3Ty+Ht?U~~5e#`0F#`0F#`0F#`0F#`0F#`0F#`0F|}'tR o %*[b⒪wSdGV-KZi"^U7Xy}bo`q Z]\)ڞasS|ՆEz|e]P(9>bg-x.kZXhK`Co[Gߥ?Bh˞KqҌ`l+d)Zްb9Ed"7eG?TS5_<۰Xb\O Ng5ON:s9uf bB_*d5v'eeqsx!XB%j>}k 247~8y{} zcdy=2_ps &2:uv]U1cRχeĢ,=h~Q9?l~=fu c۟z9z,;&g}'=VfP5*}!{g}~V?Fٗ뭫ʎz zùYzɊ/K1;:y'2gw,dx͞kAz v;1MO^_|3m/^#fC,w>ԁz7|9У3v+tY<-o9r@jvޱ4A ߭q2ثiT͊¶wazU$޲{ՙt. oi] n3 eiHB3ZD@ nt?kɳIɰfxƁ7^؏׮TCsg"Fc?vA _(Jf [4WAGJ F}'z{r.F:ܝudщq kɧ5=k4hTV 4V^rG#:ab::?^1oC=>(nUM[Cg%Z '4҅J Ds/sFT~fUyQwsH%rʉ^[$NZ,[rEWQ MBݗӤ"${؇:J/³DzzLP 98@U)9ЋszZ9"Nգ{Y9/a0i|jZ%TYͩ^)ș<74!RWA؝i%5䛉qW"XQbG\FjQm; …(k?#ZJ'y)b%Z_e =YhOO=q-z[[d {jzf<6{Z4ҜTBx h{_mErC=L)ҫz{o%G-TmNٔ7zC 4H,\4=i?adY١ 'H=[<"t~@Z口)!E.+:DleN.zq5Гf; GsHabOoѣsE'^wNO]#pz=q6S&lGzml| w>Х g=2f*D*t\L\6Z'^ha `M4s/_nWNhQ I(W=No#ң2S"Հ^^53M>/ۓJNE8˱sA 1UB=d$| z*}O43/UwUPsxI=izx.О+i6rOMZ^{AuqP8m+&ӄrI3AOM.$zR/Ux=\rOyz z1q"5ö؛6yVC$-ғcj};&Oa3l=7*6 Hz)šGV%x«O5BEM_9<@vCh{/02%^ȓ遠zEܳ2Ga,lEz5Ы&eğ,5nd r^Eb+lhD.zkoR(޼͍jи7-0p/,;m.IgTSs5=zD ᣦVߑa{tnPG/TJ{ {n;e{K0?4z.Bzx| ܲ"mK0i 6|OO2ge{t1ISiԐE(bf)-Ľ)ŏA Tm^.Ui>= +,w"=^ViurQ(v蒲Vcms}ߊj@2DzC;JZF=ɎȺW<5 49O).o{çKw~>~-nGzĄ=ur4uy;Ѻ(#1Z3+VxF%Ktr3 MDXWdgxVR Ž6m|_5; Cֽ"l`M\jŜaC&yx?碇MyU=DI)8&.qSWky*.U lAa)4W+d;Ϩ7ʼ /Q&dl@\ ^A=9Fs [éP;i 1DĥͮrBa3b{cFoMEW2MQmm=Ô2 _ުQxKaoPr"Q~ㅞZvcbZsIԚ,؄^lAラ}ewIak~F>]m,ۮer^-[lZ10 '2ˎݝ}Zfٗn~vbS/r;F?xX~/1\[rȊ/P6i5}~z}_ ]o{hL6\4E?>ƿMqŒ?>}ս>lxB.xn~7-Tz >b}<;|os?zvx^&cNBno]9R;0F#`0F#%I/=gіCsY0F#`0FC&΁c!pCzv/>l/daQk1qA<4KP-rYT2qzjKX4^+ٰ [rbl7'+ }ݽ}bk|\5_8'am"]UOq ;OOi!_,ƲeX3NpDs7篬G _ k z9yb_%\}v}^Y)~8DZN/VYXE7Dv 7ߊ:qN|>/{L/-zC2zڵ@oƅ:bFOx7w z=n^"9F_Z^Ӌ[yXAzT}ϱ=y$s:ءl//Ͻ9F#zZon*ު= z;m{"+7rן[:U+28{N~u7U{:ې7?RE"@X*xVzg]z{FңMd/7"}p'{vi`múFwT`}{>V*YҤ͊,n|3ܦh+WI_'%+ 3uzEE@o`Q8P5lr:1P}ej֮f3ve ";%pUgr9{Cݔ5Ics QiA~7ey e(YP&[PFkqvZ>b/^S^:>Ǹt2_~e HҔi/dLڍQ9}R~67 q3KQk,R~/ ;yB` 6Kb, YϝDqM봛T0䪺5B4g#=||&5θAEF`6n|ȠGf˚h#6[GNh7W!_!8%I;+$<!Y5-)6pQd%v]Я?4]U^&ɛ| %j|u <*#|z\%J Cb}rQj<+wif> m%"!qUSgʹ_G@*Cngl$ȴ+ufğ̀:1DXsyPyEյuof0b,XgҧUMFHz5w꣖Hr:J.G^H9ABw5OK֦rدICqwGUH c.[O%``.9fP&JfK1qnZYʽc{> 95~&ej^ңCG`Aml ѫ6'y1NDyŐAɅ[hftuY"bm캯zz Joz@Ĩ`7ң;o`aG\öçAw0jck^)z:AuO6 ]#$s[KVbP&[) !:pPC1 ӗ, ijѐSԬiS8zp#ʤTm*M ϭɺ䯀fp2WP+HO#6p =q9zDfJg<@i&)6!zeҕkmij0}F_^femONgj5\)5~I_t! w[ýi+Fz6%#vTd=vkDfK12i!geb}w< NqSIbU'z^s{^3૶ 起=߭ 'Y /dv_zqG< /}]{W6=l-SgpRzΌfO6]ozʭh{ܑ:6ؒG#5]歜{^@:遞; kD8R_/:l\Q$vDϞ^P`&{+]^<]-)l\pSTWǸVCEکs; ݌:l=W=^']HOwW?~.}In{t*M}M:޳5\==ibQ1z=^fCv(-=7݋{?|ߕ5z]XwzQa^8P= Ξ}]KD=ǼNA?8\C[R9ZzP=nmovt7_{,rJX>q#z\H /V Մ.Ք͋z/*8M=w&^+ "==Xܣ0N{4+ɝ1*c@p{gгi}62Ĝo޹' d{P\#2w)N@M7>Tz7f?Shy;!wM__gИ6#!/+&S%`ݺΈ$gFi*mf ^n\$hc.&l//>3C]yߏyxT6Afu]DݽkNte>}Ø_@<+ |)2Jk>/xHeZwSrR_ Diy)>H+~Cbr(kƅvҒ8|VT*x{/O~>$?pc_/(.A(=ʎ`|4ś W_pٙNKciq~Gg4v狞q;leU.,xɲgn_D3q+{ǟQ^>i17^/>Oz_eg]{{16|bN6!t,3}Kb*4Աy}"S]E>ל_*^/|#EK^qrd0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F3&G_ endstream endobj 23 0 obj 90139 endobj 25 0 obj << /Length 26 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{(u endstream endobj 26 0 obj 62 endobj 24 0 obj << /Type /Page /Parent 3 0 R /Resources 27 0 R /Contents 25 0 R /MediaBox [0 0 612 792] >> endobj 27 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im4 28 0 R >> >> endobj 28 0 obj << /Length 29 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream x_~Ws}0 `N"Jm^ S! 7yLE @ɫM Ak$e#qbA0{CADlE)^MOW>?ӳ;!y8{wS{\p4q7f첉1wv/>}|f._z2?>;$u9}kf˜]i?w6իuO~;?a9c ^C 2ؒG?_׶kq_lrTY{[6bGJ=N(?R!ہ@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ OY\ νhso}wި&-W]׆|jӅz}^(̓_6k8 + iv}! Tԍ}==_+u97d]~uETͺ GʸPyNzҺe ;ARY~u5~يߋbmo=  ?ޤyuo_?Lzm>eHF_Kzľ#A@Kt?޹ ׵՜o#z[[vs ܙC^o{JYK ˖{|g~WYD"zϻ_u?foGo^yKzt3}9*9,)1?.^FG.KOғDžu⒉*fNXr쏧fR}X4Ke/U3l3b:sqOOU';pS9>&n+]6Ic4W3uGų&Lf1􌙔LnjnZ\Kc&+qeL?raJz%w)_zfrL2 vW 3]IA<˷TEٿ[꧈3Nަ]y$՘lWҤ41 g3=P#iT۰6>)|9=|T/^"qbUfcLTVʒbO./)B\IyyOT6*l;Ea>5dFQ"!bf斉{;&g^LTJ2YɼHͭ)QpW޴x}2qTQ)`&}}\5&R 7Ie@6Ue,-%U&9wK[͚iDm2"ݽ(>.ܬyQp QM\EH:֨U Oty%.*xVDžNm}!I*tqmc_n~'́dŢabîW\VI\QXr?5-"( i̞.Ns5!4Өq=} uDg򭡗c䶴smp ٪5Tjzu)C4wf(y3U(Dւvm[ j{$TPWI!4 9K4 ,-dx^%z29 7}Y[p'%7q:Qfzp2K# !$4QGof =xAUO_ =`( MtD4)C/z-yzKj[CoBл |TdLQzO+^eRK.<=BG{N &E4IlЩw߀\O/om c2z7 1=J\B=#%VT2=^K4R CqcN囻򦹊i~Lq4*Y˙T+YUހ^e s5EjQ$-Zg$f."aI]CBoB@Gl(FK^1?嬷EO0gUIBM܃^1:V1y(^+ ch[.GJkôJlOFiw-+%SloL/k>1q#ۻ`ڔ|oi{D0 Y77ʶʎ^E$Tfc~--`]̎oLD,WfO5-S/=z:L8|/aE=n0 ӦN-z55- zjL*cgö-,m7GO2ir[ &r2ЬFau 2j͖{.~D z='eW/؜l۠}^\iiYZVk 7Mݷ·g3ǾScy:s`p< zͽ}%od[ezd,&ZF튢J ˖lȌ<=JwȿӨAYH5|eKY}pQ!GI4|;){)Smhwc|Lؚ,Jݒ b fыܔӘᷛX[iiv`_Yo{=UKm~8,i "}&zԏm=sFYDF%sAiN!'Kzn$ƚP9;&]¾)0aRG&$cZD |c7D>"Qɳ"ᷪd{Փ P.}S;;H&RV̯ Zci~czE\aXF#bյkʴ9&ޔu4f&l:aG%gwt:deGFx9>)h0ddZ"ML fУzS,x˞$3Kg25ސ3Ϟ¸4ᵜ==Sƙ#/3&hvyb0w ov =S2/',}\Ť{7VL(oQoY+#z{-IqRZXA;>oxc9kHf*(AAOs[܌w^,e&k̤[5b`)l7mܻ *ks]R]1Vxs%n7ѠbOr;ʹGUʠC5F{z~a´<$E1ܨa^a|xzms>_/USFn6.?wko+1ባ`{1V䖌{ w3\KL=V.uJN.t{wʢ/ =$#?b{uON}_ub]/G[ږwOFսΫPtDMmu~ŷ^iMzʎ#'zy:NtYɓ}e׸Mץ-/=%.Y8/n>5|07Gg.}t }o/ T-0ۜc}7F@ @ @ @ @ @ @ @ @ @ @ @ @ @ @u^wO8.`?/,yR:`+λD_~d%]eM )?/"]dUd YjqQޖwn1_CXgP#ɮ|Q6}vFl;7uT*Nuhc 7 ߀?aN?ҹ=A|0_Sq(WK#3U ۅ0˦1f5/|*;ǛэUsNZٚ{qkb0Q~ h{qL::$Fp=!LDa$WmɭpyV8.斞?=ď`tx2X i!xgDar6<rqZLm]Ēѣ:лjSNoLqgՓK{vR;\vs^] 15PzԇP#c˽ip\eZZDK{z Ѣk%) )N[zUDӸGi |d{jB@#Zdm&r>9@dLtLAo44S6v%AE~ =PΈ4cA̴ÀMԙ[zmЖdq)4{r]["=D/zfQz֞^Oa  C8mK/=:h*Į䢆_$͜"mov E\[Jc,jC;A, dbH\qeT~ԗ*oK K:xEb#E2%P F _^kMIT\UOEN0@mO줟Vw#C^B<[!i0-쳎j@\R_ @#ĂrHcՋ A$8G#JfhENh`rBF3UKRO P("K3S{zuZx Kzu驓-[LڷEN`.ZTGeyd==#鋎Ձl I<#WY8OJDRXV9 {)1RLؘ@ AO7`g'}'fl4۞ Ιa&EԤ=cC\lJ'*4czχ<zbC(@ =C'%]+ |Ljqx#H-DsW˅!A<Σ8pRE.g}އe+O|[~[GOGuEGo~QôXGWmO"}-ٸ|6L!^Coӈ~jIA?dk];-sy_zɗB56zк2;YO[F{]K@ZppαΆm<#zWk{iecq@{ Q{s-\2F,0K>a:z&#o4 1OcI H9ǒU439f]MH&(Op6G%3xyb/NK ]gӗNN66 >α2y.ВMB1ϼVѫq-zr_+_ e4T "HTL{6|]E$5} ]kce{<}抻Ivsr3˦'Y62J$~%I l<"..#OnSp`An˿*,@E@o!R 9riEvRqN LU,d&akpx>%T"=TO ?<[!t@HT=LC,5uR.%T9uDjёپ rCNCHGp啫pNTRE26i'#? 4jLR,B79-꠰ܻ$ + YAP;>PzʨV$m7ZhCB/J$Y.i`8 8WQ}F ɡ"9q3]iU'ES RB+*(.,*=j,B2sB˳Kb+˄Rz؆Iy z𬠶LP=]AtV'R`>Zz;fiDuüh!^UPZo>"N(̗PkjV D-Ro;ղD/m-|ΟK@$-Уeʌ=(16"|`Ϛ^9>>_})d!B(j ^Jd~dż֥R&k[YW$$H%BW=&^Dw4Ej=j7~'*VmQHaHiz~kwzЙ^,=€1^Zc.G;4zYߵ}1?N~sٞW[ Ew#[sc<(ܑHnuv,-~I%ݘtd\ Cb't 1%O4&1KsK1gУsIhe3 /^^Ҥndzҵ%2^U730/$j(2%MRӘ71EM؇gNR6u/2LT15wϪmbګFaƮ-dzS$L]ܼ.Ml=V X/\^OgEcjבF7M #–~oJrE+O"D .!z3ǺKU+nv`Leɬ 9JV5g,te51B:,A7*{cz͕*&2)zRͰY@܎; ~˷XEWL4cZ.#R'zQf׀k6KOi =:{?4&)(E2j-=!s F=X _> R㻧ǘ"v)фz5Gn*˝M;Z}m4tr`x|] =zHAHrpiX_brd]OϪCs/]B{^ zd~1cnmc zs+s%^ef`D: *Oh9j)Sʪ͏ra't\:7|oQYDžr%)4S𹶡XU e Kۚ/fUV_:ÆĹ*{\0ȼ\zzX`X<EI9~?>$oV8f^sX 1ϸ`}~ nQ;} N߾uE{5z~/vOӷKcu>^q~DQߥ?"oB_GR?YR|_Y+۩GzEz"=8|s_y2@ @ @ @ @ @ @ @ @ @ @ @ @ @ *WygssʛG׹w9Lzٌh]輠Rw—\Xx9ʼn[kit̎U/a'~m޽9ՎiDG1?zqG=D5rK9[ASj/vqVhhC4εӵ_F~,OscFu6Igqp^t%o›}+\{s L|~4=YCϗji6W7e;⭻!*,lZ{CzŞ('x\pJaz1;7x[rh]g{gZݚHxң_% G .ϣRQx(֗C2v2-Ϲ>{#?@Bol]{K{DeܻL~z[\!G^x]:\blo.v+>ZºK^$|џ?>.=iY̼(rLW8S5>RJ%f?,[W!ฑ#JBHpäq3M믻?ycyNMnrcnIܞ!gh ~Z0BN7܆^nn!"e'Q5|1`,9G/"P.q&:OHpZE9=ړ)SQcZsщ肹sPE9ySj]n,M5%&KȘmkWh禐]4! ~J9 Q"gc?D &R9 s)zY\+46ˍݝ뗠Ii! HL[y\}! 5$m$z~iz*C [-F E !g@)m#5ܖSKޱiщy +3JH[$=ۨlP0AYC2|z)8 u ( -,?Yyzhƕ-mIK[مw+Ya*OhCk\ #6Y(41|zOOiVOcL2%ەzZ z B=0Git Ȥ,z:bxa%xz5;1DFcdH* K Uaq}tq] CKB%^\3[>zb=rOOAMTP)Rntc6O !޲N"oE#m =j/LD~mou( zD$t L.=)4ǿ91>t0:y=C᥺!gnAqN-͸^JNm=rF:2Do7:zH KW|m;dUE@~(/펖މvȴnARۘk!wۤ1=e=ch~F/z=0fqxf)DA+l0c@#$yʍ-3:*zUӓX TAw]v4URKHMLj.@O&mMz#e@=&2:@zGW'GdlNr+kbqk{<;K۫nyzHn7hVj 8}HEw:ÐG.ZGh/Z[7Ya&w{^c5sk[Qp=%/`{* "4Ύ^ĸ7fl5@Fİ=Op~и%+4\= z|17'F ܞ^{ME$&:z̒ms'R,=:9TҮ=dz7XO54Bkŭ+MГzzI}5-VU E6ۤl/q#h%PȰ>rX[嶶N]4U*rm7[x<TSQ-s O6-x@WW=msn~hV?`{zZ1Wj={kcߣ(颧wе\MC09=@8lfn冀zz{czdzGDl̵Gs1쭐Hf ]C {5sE/nϖ#IG?QD^ѧnwdK0ߖi4ӓ}#궥mҚ^ )fySڿс^2si==Bϩ4Z܍iYmS`zzD~Abi ЛPexz5[jWpQsvK5L j#'7f'_M!uݸ>xa+؏rM%vԲrǖv\TWOqRހޤH1AE6{my@0tddfrR@M<)R۶Iun.;eefuUDOT :4o". @ygw%2WQži Ì㭎xcSdj2=}=q=sG0z+DxP\Vemn$hkެ𶅗xKF>6M/=ٗuYmS S'-21__rc`q`~WL8L&̤:$0$kscrG0*xvWl1~|lwzKr~hպ!?z_Qoi?Fc]MΫ"IB37WOf_=8(e6E/yBMQŽ}K5ɸowlܑ;]of'}ܚƗMv&© ^@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ |఩׸ڮrt@wch\3ҫN@'헌3$gt4`=G~|m˵om h/ߔ1AіiwKC,9p}s Wde#-L}~s?V G$)[}28糙.홳Z*^owƱٕ5ơE}auʜ_͡]Rtd82n>c_S/SE/j]{cz$,TGa9c 1W7hsn떩p47Џ#+Dkul{C!uCIGV^s^eVt-=ԙtK7`kK^opUo\Udhz?}8@wO|b<ǣcRyշͺ\9>*}C`]'C!-Cs)][1LNԟ߆y$ /1X[_^QZKdPos4b;z΁Ēu7*q;ܶ~V~?fdlK gm5i{63z5rCrZc[r>yKk:z^ۈ`Ó>E҃{Rxr0}h "m=sjx3pM_Boqf@ՑןR˿ i\EB mݳ/}%67 SP|4n~IQw{G @ 5T*=Gt? ;1q1ͯCOz3׹2[~=ͯN;(ާf |$9{&r?N'.h*8);ܘ1C8ԓjRK;ܛ>7|,6"9 0U&]^*G⚟Vkss݂i=_ %ٕZyd|?a1\}== wᴩ"һ+фKCpX.#m r.?&8W&.ʛIeYS7D fVGuę4AAqC)ER :1Tcq,0J9 %,Y;eD[FxB -$ }@AH ` !zkDn_"ɢe@7b.x*,;+IW]KzE($PT2=$Wz 6"Ӥ|*Lz9h =ah*˨0B)Vd/Ejvaj7aCR HsAZ0R9H詿9euNhS5ոېCD-:m=<}WY҆!dBb2#b[dsJkJ;Em(Qa{O]eF+zD@O-Wl}("G(p"ne{a`fsR>#/2`D#*=f-4^;$TZ.Szz%8<=㇯%{s:Н6JG6ؤ`( 6w{ŶZ\¡gROlea.ޝ0HԅWҳe9CQ$K@/bW;aIt&a|LFٙ5(¡4W)00˘@{zxa@@jeVci`&8=lQ=ǓrCH9*=Np"]WA/a" -IPfO)ŔIe@,DR}̞ҳD&s"AOЕen=CLY<=zAr*AѺŎZLҼGNRs4~|lKT9~F5аcWcn2"]zj鉂' l S}'f)j=U) Lz&†Tt5: 1g;i&ӌ)\in=/TiH8%dP^ 5bHW[=ⲳέߙ#5`h ~|e+`Q0c4N͹LØȐ&nvK+X"]p NФ{C rMěk|$S76Q&8yzWT04K|ɣ~} Xf!R29=IPW) ` xQmWz*l!f}, mUEzq$q ^ӥ%N>N0dMJI@ I#KXBM;l dzE|x*ul|c}AϥnUv/Q^svKW7Nwsݭ֋,vj7 nOTOWYgݿQ9Oo,ZvBGo$K7ׅy{G|@Q@6Q/ZW_}}w?=?Hoy!5!^)T9H_{sUSQ]d;T\raԢF3 P>"_&2CUm3| @ @ @ @ @ @ @ @ @ @ @ @ @ @ ktGE'p+߆//78'7PFsIZ̓$VsyW>z: 漑sZzqiJt֥q.@oM)JTX(Ӊkip6wi֠lDqJ֑xN ޽g[W~X} |zz0GSz+g[Nm,miss<"[d3uO}D{s?^S17gI˻/E/++:z:@#*{+~uWSmSfZ֚bpt3$:7؞jPW#zJ1M Ym6sVꙴ_|yFݙ=NJjOҟ|dttgS==QɣL_P^͓/O/{[#?"Vp[8>1Oi?7g9{BOS8\*x|m+ (]w.QPk.zҡ{sw`{|zɘޛt87q+~ [/Kzq;"/\V3֝%C&LBzG>|?t>>")1{(j=8$ cٔ%s&/ܓp"Ӥ956:,4oLʑޤ0,e#Ց|WCAY{h0۩H\L﹃>hӜ*%K$6<.ѱe&.mdO1GY 'Nͭ[h\tD)3REnXQsġ'&-9zAԛ$_+feHtnF)1zɸ2ձġIW 6C/wsoYѻP3S˱g"EI<vu zՍ41s\}3Q kt=ѫ(v?GξI 48bk!&^ZuRa@cDKqm},.A/q*%V(J#5|Ǣb.eo^.#5A!"-҉1s>C檞D'̂P+F$El9DEezjMmH?)*/JKf'L)}$Cn@/lk4+`O2hSHlz34-e@Q7J@BPvz?M&*G=".I,fKDHSGU ͂M;{׹97cm/|S, g FPR҈3@]26e?Wo)p)̖!=q򣆯 w6렗<䍄zXG͔1*ݡeDTDYrzjԐ$B#zz{_(n:"zXTr:GھDi ~J`IEcdAH('z1WyZmLV钞@LJ{5cѓ(YꞂ BCh i9`c:^jdSW?|[=^k{>}k?>==`{8Vi;4W,zLG?MYkR{z<~驋hZDoǠWb۞'QmxU2MDOsZ͟ƞ3PB)k [_G"= x=eRaXM\aVږk.ZnoOdhw Cޡ\fzG-=V0aNjx.mP-yK)5d&mBoqG -Oצ] 6z~x}.=^{=\4lkK."Br1Vl{Z^hMfQwerJ;G_>r:z{mEVmL/sZnGHJҵ\V¢?=0n.!ݠvF;cah.+^+qg^ҫW1kVg,èArK+DGr}$!yv)0<צTéXV؋VWlέ;x^>}!qjP/USrJ&Ju^䷷ᘄ< &8 ciFwOF}9ǁgNN^e2js_ֵ\ؽ-="*zV,*~opfr支^`o0^N\4g18}GqY׏$狏;Ks~6-J'{#z:AaG¯qa{kBu+cLhV'zHZU9g!^dđ}"+y)c{}@^umZu}k`7T} ={{c{zG]>[XKWv~0bgmlWs:z"-:u/_@[L̹j?[w\Vw{DׯMMfBuڿY*?r^]KPp)~~3Nw$$} 4Mr9Iƺɷ _1U7Z5C/_ɠL̨A\3Oج8\[H/K(UrYngSiv83% <ęS7k)|68>57'2UyedlenpR3MՆ(>4qq? D }Kw\JT NۤG-|~/-esZQj'.[WԜu۫GvN\0"($UDmLĶ.`H K]LWMD'&Ya6zvј{h^H`LPf88@A "R[ QeM04@OZ"lC$@sIˆsJ(ܚAR`3NVQ%q4'jh\C9BO=D/)o#}zN\^9L'nl,{z/=4ң 2BzU҉3"HG.%.6hH@/H7  U1䟠6E $@]qsi"3#imJXHEz{z(3 #MO\DE$^ѓJW}L'XJ6Pݐ b|yATk>LJ L+Kݪ![큥e@/1r{ndo2͔́f䪌7z]uzzM+yEJn 2z 9b{͘NV<XeX:%$v6w$)AP$ z=O7f`IEJWv-qKo04S˅^id{|Ia~*lO]M:S˝ZzW11sd2yƏu[cO#z3Eei)/WGDvRzi%^\da*G cG F=_ c/z-7c]Wr1&B|k[ne#hlٷv%u-pD/b4I4{y&>†?hGDt3zތATd2y5qxTi aoHHr8uڞ[G/-ͭV5~Z\l1*~.|6?J) c4-"z{$P-W9 dxm}F-WA[FX4/< "x}.<[@Z$wkEd{r2|#2ST#fN^"d|[5'e ΡǢ pjXYr7t*7Z%ǪX>6Cka:z,tj{zpkzb9==Gd%G#z\݊g#@QSdL+o> #8|=y.T4뢧-\?@:eǑbe{-k0\\}\};]گ}lX+ iz[>s#e{Uǐl_"khEs-zI!D=;ݞ`{мVv]G_c=Z=ORجur1%Z Cة]Wo)Q-}XA̲,WӓW'A]vMzz'=L*t{73,ikz(If ] V%ňyn8۹-ȋ/+jO+Oӂ`R:Kٙ] ,͢3N۫xL Ѭ8_\ixA"t˙eny_c19DI7|hW<ۼ7^א@37:"){fTYn]|@h[tv>jjsp\e{8-vL`^ыG'z?" A~{&.i9J*LG&Y5ogՌuK@'ފ>"/Y3)2E)9)mE]]H17ы)wT[,"DFǚՙ݊R3ɘ@8z TN~媯\ߎ`tV`[9+W?]eANׯퟘu.z83ӭp`Di A,X=QɆ~\7gov J?vk]n4~iޱeVv& Sv{Ne^c o>,Lo@oBEatޫez6VG<8nWzﮨ;k_ B[=xeY0KV'W\,Ow=M\=_ n p8⊧c|QH ~vpշkp}ݼn#y3~*Ƨ.:M<8Jz[=<Ǜg4(yw*sU_]M \5$i~!~ @ @ @ @ @ ?>iHe } x!i @ @ @ @ @ @ wC!.ş/~(׉\ 6_scѻfّ "6dVCHrJ6Цӫ\̕6!dW׆4=mevɋY A;cK8pץv%t[Grs#%OWR S:R yyq{ıUV~;.Ec+*{]znG2fd{ѱ]f&j_}HƸOwvׯ_\%uSZUnFA״Usu;2>d+׶_.5fsCq~2OT۶~RY[]jiޭs=C/#v17SO?&֩.*ĈiOoahpM|&nd{7]|uaFzk[<הN_j3675Q՞`9CKk{ k?V2jطn,,[!eO챲믓k|#$O`oco%HOuq^j|vT~#f;H>_W6d%AR=g!s~;Ft2om̒v0.flR~[y7Tpry$A 괛3#{ voCwȥc/H*+=y6 ]7^}BX^N,e o_66 S [GrxƁh:g%&?a?KPPeBEʰ߸'\jD_)lEN賱zq( k{\93lf˄njxS!z}.UZqd% f ufS Q柉^xbs?iSXT)deyJ92! =k!em߈g|B HKt^Rc{1`{үCz7zOA=ݑHy>d1 T{:C!#F&2$,!I&?bqGdX(B .>D$Zr&V٣ƳZb-_R;ۣ*jLJ,>.hOӤP )6^#{i2A 6W_E# XT^ ^Qclm  l01&Op%HaKTQ_'ctUʬ CIN/HvSFKq0<B1[ h%QI.|e "muT]=򖵾9󴢇>qPRX 7\lPt;Z+AOpr]qX1eTlpq)!CzDO"8o^OB萧SӦp8yMO/+$7EnLjLւ(c_͡l/s e*X[N}:ϥ%tِt9A)~gT Ej̶D1*e^A~ѶD NlO^!Ŭ& C@qW^FNQ K֔=cG/]LEBz)8th2UW^{B9 C_je{xQL@'oSp `b\ugz2(3š'`H$z.3q9G lz\=@yzhZ~Ni(幽I7wMpm{LYɢq|zd2f0/.GCYc1bOo!']ѓYң"DqSdF힭Do-1cUC`z=G㉝JG*ԫ⺧-e++ۣpgG@l =l[CO^cExm{hs[cz 'by=OCk rhR4sOo9 <۷{3u#\H3SWG{ `f'PP&= yNӛ9zLl{wGo=`*qfrk {³J<kg+ä [7lxx큇%;s ]5x![a+z8jOU6;ڽ#V.ɚ?o^=yz̜=im(;i2S쏠\?ZܘV{{3=t<lcl4̵4CJPualϴr]Pb]*"vE PLt cL-,Ƶ% La^Nq^m gG->\O'bݨp!{b%z(Z"k~\h5F`&م3ੇ%EII|7TPpreFP#[Pү7Zcu~iFnu41e 9)vjNiHԒ'H;RFX&u{( IK=zFmxF34nL>57oڝqk\/0 J[AtU%e&w&Dil _S{ f}Gyxp\5"1Lɘr?*N,+k&UD ^p ۬4>oȊ6s.n3Hs3^F6+Sjx"8p rѣԘiyQfi/^7J[6=1xi"7[}y40w.,JiF f3꧜E:DqHWMprī&.rgX˳Q )yթqP*&{qDG*o*Wb&oˤ,+۠zmbTBmFN?/H XRY\$qZҋ;_kwz}ogoWi׺&,iaۿ級LU}n`(s޹ߚ&*0%$=u&R=/wwmL x '!yiH ([ƌgm{!yzR aLoԣ#X>1B9=xȭҰ=.nEB=fQiQY^MvbHxn+n S[܂QD=wQ뭱Ty7d]Uֿ֥ͫh.Kerͭ=B﮶O9(A ) |ft-ڙ~uU33ds{>㹤YZrc5E=fL൩#tfdl3[ĠTD$˪ݿE#4B/WuL"ϚQB:boІ8_:mLvET<ˈ!$ja b?p)c ,BgE-\TQ˾nΐC+ة%9힔 zT19ԃNZS9,|Y5HA7kf(E/Q_RG{HoLFfAD;iht$VDo.zh"^D. D?6^cXߗEУr1޾p2±*=O pZe|_ Å*oK8)x)$@c0G ^#@ )8z*EFr6fIy5+%q\J꼹?O/s8àWN^rڗP` "*LnKZvkzu(=EEBlJ=d$r OϝY!$&@ ^Gы/mC}E4<L!FY;s N"hOHBRf+54D6^lsһXi䄢i;zΔ|}B%۫GjWGcP]yztd0&Q`QCQ4Y1Sy @jkHP0[@]J$qL =@ hLzmg6)GTUNd}'z_9kWG :emG+l1'|'F3Fӈol;]ydZUaUQlOlZ'bg5l1g=D$ G|]Q\ [( Ee8#)zH޻&ӈzbG%ZZb1Sw#zs=@5ҁ^ccswcBѻ^>`lMlV]T4! }.u[٘y!қ$(5ƶ9z eOV}./mg^vh8A 5tkƢ'U]gvmJT+G c%$)z}5N kQ,F#G,ys]̊gXWnԖ:hkpqCHMgG;YE/sj|5ߠחPLRN5VRV|RP'!htw~KS yG,nf%T ^{:C x-CALueV 25K9(){II\V8 J:2"gy_e茌~ed{ \2LwH;@q]'@j՘SHY?T:ѯ/xsG/cJLX3#Sad&%?&#Sj 4 5wdi㹬N^JB/=/TWsY= fE:i مlH)v%è |4s(nrf5F Z2o{eX,I@d$9UT Go7 ~UfiƱo\]gVwZon\]o;\Md7}M0W^#K;mBb>:u)ݝ9K_8<& F"_׷M׎ysz|\9?.of\v,'y&K+,k;H. UrIk̙Ҧg.Pפ3cO96(<R8}gvgem;R{h ]I1<}MccTpMz58s'oG(it8myEaKp/ fL#6ù:hLdvXd.?X1?9s4>4?PWqV l:+%ͧf{Wօ\} 8T9*cWl3:^]#8[s7=%Tz1L|XpϏK=WyS%}bN}A>sۘ^7<]ݻs]yJ:{F_}nbg%q{Y|o ѽ5u/\[cyDz.c1,[ͿPk}ll)+b6oZD(Bv'362GuoFE9;n#4NȊgQzpiGьxud9k4lrIsE-WуdT{#7zĵFGąe =.mAAOovQȽf? (;?<Ç=M'}CZ!+f5ёW ,[h7Y(c3jH#amRt67L pҾ(vzv0ؙއx@v W ѫ$;Yϴ^H4b84[m,;v'yWZh"]V{1u}˟OU g٤衽@om/hhyl#A,l.YD#[I1c.i1s &-T?ߙ iVTr=.BsLQ@*iG_3hآ ńf7CbS_&CEHx@֖z*76.Ees ̑l4kSm9GőFOI\"$n`1x|8 I Wb@\_FM 3=z^>)5Cz/?4X(>,I,g}=X8Ҟy`f==oP3;m ,!bgd2OA ~ ՞ʾb 28՟v=M8)eSh }W~E&ӥW3ϣ׷{o(voBwnKyzjiE^ˊD9gf{o4\AH{.C\zڽs$3۞~=465sK !)힆2|m';7y|E5=0b+z~4cr4UѢ˴&=Aƞ"7hޟw<=n+Rt:z!ݤ5=Jб<3\zۣ"0 C'qM+̛IfN2VZe c |adV=gS&tixMRUUϐCgjHBc鉣+z$W̑YMLx#,Gg /BF Ff"/vpWbV:Hι>b!‘j)*Ɋ|_g.bZ-RNua֖6>QEWBj5TZשI߉Q_yOg0Ӹ荤@ z<9eEri2m!7kLinL W4sۤͽ18[xzOz۟q qѣzD5yą|ÔLx˕]2wL.>d?|9JڜTה\zǤ;燉8!M|'O)db>ws k?KZZXk."rtэn+Dܜua;D? SLuV̙%:y@Wίvx|8~䟎ݻP;Ρ2y}Wba6wU̯&yLn%D @ @ @ @ @ @ @ @ @ @ @ @ @ @ >e?f[(vf&yk O_E*2kON?| Gؤ.{J6[Tm8qHw!i>TSޘcON2[] TņTl=p}zhOSuFtXj^v{gy3%r} Q|+:E>`'V ׯ`7lf4B<׏)[ $tFz/6vG0X^]`EO{Z}8?XMv>&ӻ*z;9\+Z\~/c}|W' i8v>5aM/4rnUT\^9>wM~2*UHuŜ(=m|3;S_s{h~*KMÞ-s2wF^%݊l/ی]ۜIAyeԷ'4ϭ)8{f#Fޢ|NզDjQ(Ls_$U/dZ|Z>bsr1z9㉣YTl"(a/.ZqFpV?س/gEF$)({mfVd|6^+\ ћ_Ҧ.ڗ:_|soJ` v(}/)'+ּJ(=~t4۝[? gPg(C4/QDAv!S]|dQ(X_CUU-gh2y)#W)zH/b:(:ڲB M [;&ӣg#8vR`BSSeprj2sL6إěզ VeU؊k9ɿFzzEh:0S,5?Boe#ŕh¤=5Kb*H^0oW|)m]VC7eciISb}]6uN"HK9i=:("4AaDu+[CRbmTxzt!bkN u7zD/s*?)Hۘhl^$l)z "!ҭ>$r:ѓloTȽ@O`JkzQP3sz" ܈xSƈNzs옞mrlS؅E6Y&r YS 36%]NgV4Mv=0uͽ<}i,=&o7b C5dTs QER;zvir~o:Cz7չ?Г>"3X0Ѳ#z\k%m9PF蹨4bt-{z{x kCTW4LdOS; k''U'ud2bl/;K|a]DF"]ns oG5ԜO\@A3퉪"DZ{ rdek<=M@}ZzX t#ĉs(`T==ib~3E㟁^O6WR&ac̮ƸexM0}.{#)`_{C]L%z@G Cp3NDOflM6Wx<#ZΦ5N"/Fa5I%tX%؞.^Dt`KsWAOm^z*{);m/(GSv|,Y1:w)z2"SPL̼{qZP5eyl܉=dchDȵtC{-1@I-͆w#:Ҹv\Y"I n&y좋߷ ǣʼ%ּ^]6ÏP\.CjǷe[OՔ8wޢ z&clu߾rQcX{R"x:KbϹ*sd We5,y#["wC^'0e/o=P(fڋmivg0IVŶL4IF2̖n7!q/mcI1jvLuR.5[6;`S&OddmydfY O)"qtG_(ճ𧃡]^WkO`˦ox!2^őDkLUˈwNTUG@T}ue$_l?UbO ͿtΏzUE/mK'3z#D䋯/o$݃hCl{Y^%tawG^FBN<uzT`=E:)L!8Zz"\O*q^C= ',G۞[Iy}Ây'4jf(wP]/[yquT~פ˕S ?Ыz\p-P]#ԑFO7ZefWtZمH@ @ @ @ @ @ @ @ @ @ @ @ @ @ ?2|Ee;חvo{dm1Zܖ.(zM!DTzQhHۭjWG [4|\۱?P(sE5F7 qΉuν~c|On6?ѭw@t>>utU>\=w#CX=p?N>z6ӳHi. }@hh2sDόqo4;c[COC;S:lK zʭbWttl\h/o4=&w1*z߹ Ƭ:rֻ>[vW._bϫ8?baz欂3g6e.W|8;!u{]iRɢf.S|#[?aԼcM̳\0ݙ6`o=MOY\@OZ}8vnjz siglOm؞4b -~P=?^[SzaE9rtvrЋV~{noxi.ذ=G"azOz^b{*4qq@.(#fWAkrHɊbo@;Am~퍦}.Q>B>4ZS^j{\g{ݨ|6O&_aq%7~qΘ|'3zU?߉=GxhV-L[9- zڵ;kѣ,Ol9=+{w(ĢA7fl)= '(Bd{Q8E{5v^!ֲ{l?2G=s?Α*@= |,eeI%#(;YE69D5GӴI~Q`;XNЛO#22Q?L8B }TwQG^id%HK+.cGCͧCx^Bu)9=?; tL#v1hBX ދ&sSY~| "/ 3C;=O ghHy JI0p%Vv(Zj~3]"}j 4%i=Jd4ϕ~DphYC3z W{kry`XG"8==rj Syz*dRchr˒D)m3LFE t0?@#K1Pԙp+(r"'&]Քll{-#ݦ>Xj]>nCOÒ=S}Ѳ6ˌ#5d>?8^>];Sg{ H)kށ-'m>i\jq9^M:8Ô5ޓY^CJx Y-^b{]oK]N;ЛxOv { cyn}?m)QRcҩ 8Xd!z9zK5]vO詹\/1Mz•>d2G{Fy9ӄ@?9$ΈE#f豨*z%1t{#znG3(nk1QS@dF,%d>^w pclC>wӶF' |IIDe{ [Yr5e EJ^e$c45d9WTH%Z[. UR%GLKܟI.e%ѝ Dգn-2tkCODhxRQxu5%Gt.IW|ltg.v-s2Y˯'hĜnT@lzIV1K]xHٽy.;hO_*t7$'IC?a,EiW3DPXړ/ PNݿdA-K1c_~~E :'ҢdXb^'ʔW慱˸%:lS=4NTm1z,Fwe+Hvi!yeýM mz\cg8Һ<~;&%f<Y@okD]lciWz#48\4r[FHvVQm7gEe"s7Ss'Q`{a@y=ɿ]haYf=)zW3[X{5.WA/x5,P08Ȅzk29"UI8 >tA6hK^ZT³q:koH8ډ#vsdmYo4 mMoE6> M}En?} }酈@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ O"YE(pLu=Ά |~8ڏ9<6U8:Zͽ2ѽ.\sc}*GI5&)7cw={6Zeֱn~l~zOdCɊ|8Y6kz]Ҙ ژ<:۸y2_[Gͣ:2NhLtN z?¥w\o6hh*shV(+<#z6iW]ޏ^۵Gnth>^ y~_KmtVfpZt^I;) Tn@43K;kzi6Y OLzVB:`T6^vixzGX=9ul2Z66Ȼ>_fjy@NPbZxu ˶(ԒO.M01fj!k[y|%j1\3 ^~ͫz..\ a@6bja'KĒӠg ߊ^E*,b)F K-T. )݌$"-lOvzz{窖S 4sm2, KXYp5v*F}Yh*+3YSXlʺ8{dh+.; 93KjLZE۴G-C5n*o&Hv'kۓQ,m)k(o8]vY8R%nGڒ{ lqPe{[=\wɷߑkN78BUan)N@4ƷN{z8ltJ=50)Y z- m=k'Qwb >Gei{e|#7zC}N mGW#z[P.ql(nmIWI^Y(iE%7ГaӛV팋xn+zMd3S#lү?g^D/?禜bV?R(KnF/Q\=5]<6;m.^FLg{mi6b?^P+z3sR =&zNCPKVzz,M;_T݆Q\eھZPCp+ J Ckj &38lc0t1N,_F;qF%Re{.7rq幚*O`Hfp+m@Jzឞ&f,zESY+GF2M- 4bU;.܀C~<FNsڛ u[2xˉrr3 `Y --ɅiAC =9͘7נ3hqpP4s5&l˔񞣧\A/4G,{ >f?SsƈD3U-GV'3`S;Yt2qh 5bI:ޙ Nixb5۔J\v?׶gU7'i8z"ϭG~M/V5z*Oo2KS ]",Fdg|3}ڽ=(gTӲ~YQXlcH-z}n|rb݅,d{~fhOO35-=͔,ͪ ]62z 9$>Y;VJ@c—ys0/Xa!Q8zD=g{xS ^A|5m^ba)޲٬E/ym헦ލ&QdYJ*آ`R#V˽*0m|44Mzܤ1? 9|W}?bk $:h)jJA(g^ǔ8S0mBiǀ Ë6Q".i>Yʧ9ꔩz8c3^IFToկJɐ};| *="0Zb,mV'5^0oT MzHV ]BfzO[UG%@/*>Y=ћ0e`k&zO]28$=UQ$*cGA /jwjlx;×͊Ņ".Fgq4&!Qsb- [{Av+W!%Ы <=^*㴀V3 \W/zu"zy#M.'1U$C,GE,z.dϒo>$::(pjo`_X~/:^ӟX#߿ e粠g.]۩kӻу檄W WqQEӇ+K^[f~i*.ǁ)DO]碴}=VG W@4 MU]>O4|?ptbpU޿^}fk6wEy=# ͷks⫢|j%&3w*0 !@ @ @ @ @ @ @ @ @ @ @ @ @ @ ~*qlaכ2ga'?cvξMJӧ.{~Rasw>*w);Wl>r7'A^soCj Ye{%\Mo:y.=&4VNtڅݸ=<}O_7lckU򜔫?>`ҿayK .-S^,:>~ r,m;/!9:FF ;=_\x:3VwW[n=N}Nlzn-n捍|0ذOwE/UF輧xTyϧH̆.|Gdk8a %o. vקdq-_Dgrh=#5?y׮|)|曩iGU!kŰG7i{v=oΏ,&#[wQ70v &>}֑lvhr |&L%/n[l;wl͖;msG;l&NDٛ-'d*Ra>@2a#7 =ur6SfVd;yl;_Z_-m^noRi6LkrV-@3lm~]sgXg?arFY°CmAm~橉L\̣Uw+?w2(W\ ziT_f܊ oNL[=IE_圱㲢` s@9=gJ$ދiZ>^BcTmZeu^Ȱi'EE^D9_r gU豛}74L HIt' U]AvT!H,z4kz &$+3GމoҰhs~==<.3=_)?t%E.A1葋0\K+y^BCQE"Л[D&!x OKx BI2(¿ Ps39cN0OD4xII1XqH?x.x)XJy;y7Kf=uIV%)S7"ȕ԰=)<=rYzzL4zzJ/GW{ksT;;kzzL~V\x.ݡ<\sCˈB3j^.[y~5)z)bEDkR׈=*'2[s==&/3Em $4k *s`t yC7s =<5Oy~ p58԰3uՃlEߢ=nF}z {71;E/<=y0ˡGJH;io폣C~yn򐙞 ESf_{z3IubWtđp*4us/܈}`Yoxncx*i0d{xg{У^;ƺsyҊsSÆ^ﹽMܘ(d`9z {2=x!3sbToD/=7=AǮILϥݫ=7-ciDo @@AGAB4kJ#BV}t &@ATl&@G0mDPm0nlvs̜ #g﫺s>}n}Y?Um {zOUu^yq{zG)r/:YW=?m-{#mSz`)dl/nuu!-LY!\ʠevPqs-S,ըuθ?O U+U[;Hsqs8 ijMB1ǥP}jjTe#8J3B9t+r!z,T{ZFᇧwښ!| z.=֌g[es%sդxIJbU{z澢:}>=z|:;z,8'Ȟ%ϟ}"z.~VqD/ٲ֮i0,Lj s\ 6{c[y2Wn *Sz>?BOv8>CĚ˖]o i0wkfzqtAQwlpjz7QrA6i6=~=6f&_;loVt+V}>>s5"]?lM OO蹓UҜrǫ:zcjجv퍃Uяw8:~05թ{l=cz>@NmχolE6aӽT o5\ۭh;K0N?v_W7奲'6rsǡWQ}G J$krMPxzCEg^ν-Jj'!K}ueˉ5LRc [ "Ѭ;Fg,J-\qHZnnQFŽ%pRA"څ$z$E̕=7p" iҫ==7gjz @y  QAҊ^o\ 'h9Se>w'CqnGpHM4%F>iO<,+WO;۫[2N7s6H(n=o{2B# z\[C@mecC讑zk](=]Oϐ)}UxtxTS3]5c@1>If hwcWAK^srDü7cѸ8g$ So{D}mb{FlȞ_| tIޔ=/J|+/5zqEo<~ys5pң(zEjTs;c:*ǀv Es#;z>i BWrZEHQ-j˄1cw:p 2n_?`;x[< =XŎaVFs [9!XQ%q#,}lTfUYA3CK!,<>kS?z=vwn\%:j5ΟQ7b:zSڍT5z\O/eĊ,qo4&tBR_s#7"R#6OE%Ni:¾"[zzj:޽0-Ƚ1i:xckW uV힢QQN%x17^H婢w4TaO4 чgsTI*NP،S^\[A x1wm\,jd>\ mԴi۞Zse"5sr#,j vˡYQAX1GRƆwBq/9H&P!Vsc1opzzУ``7{9ۦhl'mih~.֝|ˌ%T{jپwz[5W|=_KK/(kȑ3zOdn=OCvCPL9[{/eװ 䟘\Qif`zvd܍[j2(Ytk yှy^dxzP430;c3:a2(!ś[(,V$ruە!W],z𖞁 7SQoBK7#k8^we.a$W܇^dSnc{16/Ǽ9#¨6/@ڔD<<_nK_$77ksUі{==C%E{]ewUbѹҔ,f\h? p͖Z(O8c!W]-) sn[_s 4AQmYܷ5w,=sL!-5WS|lhw\&Z!V?c?zE5V\w~“@ @ @ @ @ @ @ @ @ @ @ @ @ @ MZs |D+M/6\ajթkM6ln<ǣc2i#;vH(>fhkns?Y o6Y_﮿Z[%M d۟!vIJ Fj#ny#͑w=\7\OzjYN>j}%ڄȼ^GG[o ysgewk:[k>ܯl /]IǨBI";R\:L >|p^] Pۯo1@*-@QciQܩ']|%$f=VU u:~!G7^)dKtFo/)Ͷכ3m t#MZ[R!) yjػ+ ֲS?NÝzw}X|y&'Лkj{^W"sv`Hob(=믮m-9٧;R]gu׻8#ӓvYAQzӚ\tQ%ic\W ˼߱#Fot]o{Ӛ{Rmovm n{lc;FOO.jz$Βꤧp!&8.Qz"}ݧ%uKgL>ETZ1ꇒmfSDqDs/l>ȱ#`{X@tm%>^lK1ۙgKr-]WZeHdmQfgtryzOlD{k~Ӡ22Gݟr.zZR<(Ķlw]G} =b.Cnצ v: [; =#f %aAOJU*[챎FNPxgb^PEߙr)%%T=d0=4ߠ1J`]|ڲ%̒ڎydrf^Vng{jC[;)|8q=$@B(zjG z {}))Do^ET,l#y=)bP2Ln;)RĚYTBa")2EkG"n"CӹLyeW>KJKwN4 ~Dۉ@r椿h.ܡ٢)2(D =4Z##ivj&(\E^f-=NjKf~?Ȫ] j4 50Ѽܵw %"7{xmxzjPY0"M~/l_m[ѓH衧_V}S~3YlH-dj 5;B$5yg{8sag. m4tC$ a.Qrjn.I7j42 uǎ=>ӒFRFẘGJ.mWGo&SV%1KZQ݈U{z!9Kl>;zXKI;>6F|22gRZ!}ȗyB'!2i|=z@y7wT= AWaq= -r!SܬM(>@EO^[vӛoy!C;!ڴ );̹͏Sk&ڞ/5}V5̤x ~ѳ=5K$(S_sA^@]%Dg{:z3@mO͖1LVg+zQg{%'v;D}o{aGAτ#RO}[Rsi䡧| jzz7{zӓ-*7?ce0Ǽ aN5zⓚJ:؞O#xk> ^}/umIa}jm)l/MFB7KAU:z5̪.1eMu5ӕ`@= 1 =ԲQ]ɧoH怪T#zsd7kˤzW8L z{lOjQ\O+zPbh$誋7z,TsYK(ӓxvs{CJz S=z-p==nEO/)lyۨ =%נ>n{~ b|]0#= =?j 㽎VӣF0{]˔ cF }qvO$j2TLMe#le{@׏cN'[jnm :8hY 1Hmz F&|݄J=}n?{ŔGŠgUw=zqRuNVL3NU+9ms16$G0ShNw-ZhlD. 4*T{$zI`{-1PUte3;djž߮>##rÝ/56=axjoa{s7WC5ډSᬠ5FT-Y 3\`_sfxѝzz@-m],@#6qCb߅Um Z#/wsux)/n2ehYr E\Ԓ/^Ô0 SQu>`c5 "CTqc&FaBJ{ LKF%"5(Jp )q߻A"z3 ^]GW =[~BA8+uXo!WNpf{ B5‹޲ՆbJj=e<e 0W3Q\37o ^rRmqV\5R TH쇄p-՘^ű̞A߇zĊ;{a(=qf0Ħj*f)"z)yaK0bRxy?0mjXxϜ{J:DLO!ܽ\Q3ֶ FU:}L̹KwKK%/Mq.~NSb.[)A}S K 0 .@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @lVg BM N/l1uxTkԺ>=;H4uc5֍!~=!}wzǙ>#KA`ul,Vo gXa;{3fkVj n~+ 7^cڃ 1l ~P^Їwt7g5#V++ӉAAp {3I۶&YTL,4=2c -zo2ZYw]?׀E2~ Bs 3M7hd0BgҵNT (Qb n_%@6uek2;A:PS==7%P@fMZj.3:YpCYu7珋"Yn81U:AcMJr% c0֫6Z˛ bn̗-}{_HXz@hX#:l4 қ k=`Tܤ7v{=WO8Ta5TD ΩS'@8^ NE{z);{ᇎs%3;-D~2@d6g@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ 1<9^g#cyWU&IO6RoIejE]\h5gu?^._^ѫ%J{7ޯWovK']8k/ԕƚߘ:=Ĕd M<޿No([y +;qfG.imoǎIRQCN׬j+o&z&=^>3{'$qǒuA6Y1-W"nk{LiޱhvlɍJx9+Az q$2>j>A׌GA%i:^ok>?ק>lfP?\5ǭ30:BNx<<ǜ||0V: ]ކF&kmjGI~jH5Xioy8~ב 7|b犿^+̎ѳA+ܝ#]0̺섏3[/ȵ>HNKƑ>н./Yɹ=;k-):̞ڽwDީxW\;Y7f檫ΟIn1Lر,DnQdeUbBÍ6#gƕbq}'*mψ/Uڶӻ|ϯxj:mg]azzl1g-o0(1͝+g^9Sra[ì72.yzNĄWĞ1qdG_Tt̃1r;.ܵӫ2|T^K\aHwojkN!3&MgaҤ N'ۻ +"/cs4U6žl-Lf*sɘ[1gf,O-z [g@%zLVc kNI a?d1*]A&q{qs)Z66]2(-4yhƮ:ߙ{^\qa{;q/QsCZ7+оx2c)3]6Jl]?Akm$^}ZRs5qhlR%es3KeCM][0!R]K5+7+Gq'];qmK/%EsL7/ݲ5UZ~qR5)x;=l%".Z͘Im)(eB ۫f$Ѣ4w9́Iˌ,D7}tu;u2L>" LAfC ==,J,}fw|uD,z/y,:53EhȶE WNB3G{Ԩ\5)ULͺE/* Ɖdz:^TeT+|N<_ڊ^:xzT OYaHM]@}jǎ-:_-K^|JN}ͭ2=z/Vm7bX)7( e) zeV)y\o{2ٵ{C '_ѣra{лiR:=?i=,tBÒe% O/zNb =H/\n?w Ϣ˒6^s1$ob{fdN7j.Wk !nݓuY#z8bS)T1#AJ5xc{'t(zdV8Nd:Qs6CJWno{p##55J*!c{CΓz]@ EӫhI͵==ʘ ~R*;Zi H)DF{ PEWT'Kߪ:SsaFuk;zV#fU8K *w}'Ke\PPsY۽9XjtJ5|9CD!%zEhT\Tͭ\NU5|Qj(=+鷺1Hj.|'!y_sɫ=ָϓ={~,z5Դtƅ{ǢÌz;,8+0Iz;v ot IYRM!"rBσް"ۣ QS7h(  !^uv}a`U:+45*wnFufj:+o>NT.v<-QB֖5B/\#˿Ա7&~N zez'=Z23FިXJcS@#|GBY[%K:Z1r:5YΫKp缂ϗ_dF& ̸T/8x{Fd,+yT˼17i XVm_,wLT*tN==?mg5MK>UmѾ|ٽXބAxi4[K8^>3{ yV2Yw *Hx(.^=,1yWDn(0pKq8DX.,.x3 zk6&oJXwPTT9K[yԌ\FLPr-%rmD 5Wd4+&63HB/b%)F7]2cެ{dA!-W0[◔Q76&xtA_(%s/wGcʄl.0q_=~>w_(?ඤRְKY{~_+6?6|+ ~ŔbOr~'|]mt[DipO^nN;ZzՍ=h8_:Ha]RElC qR l{RWkgkuzG5~vӖ̶$%gMF>:kON? n @ @ @ @ @ @ @ @ @ @ @ @ @ @ #F9Gu1G"fld#ywvfo?bm>GwMǸn őwcpy}vwzF~C0xYԛE;5MWPz~x}}ndTc[}FRvݭ'u;n;1N΋WgppV;G%߄^/J:һma;.ӝV @8>wvrᑚ9Mo5zmIOvkWS)ñ>ZGzeo]dP+x8'M׍_';} { tc@m#ߢfpݗ®뛆2;#Qt-7$\lFߩٌ=cȍ7m=cy?E6msm@)UD=Zh{pQs?]^ϻImϰۘ4rQg1/:;>Y>z(dݽܘ$FK1-GԸof4IKzISrVz (Qa.j怜} 7yIvo}M ӭ٨>lMc6ֲ7,CTo{vFV7kdPl_ڶ [Ƴ:;sE_$50[ ƯIEV]bblmu9S/f6G̐"`t [eW}]N轠lG*; P.VK:x,tVmzInNc Қ5׉k2^^m#_qsR[ERiy/.1B zl_[ћ"1oKm/z2R C/?O{Qs VݶՃ4^tB!P-K ԁJ`)B)"rwVM"kΠ:+J/zD ŏp۷Y[: 6ZIAiTCۅ:ps~3ECi\xz.W;3ZRTɥ Ҥh즯O#rC/E= 4G$&=/3r (6RMQO.BW7bH⊞z!B Ip4i!F  ] J^G^XB{:Л?bvƋe9,jGKGz >e{R,C8iK yz 2D(˷ʷzzL#B[H%D8^E_T6'B(qN e]Brd=}'e}J gk*={z)(Umz~_B=o8B"~)ɘUSE`h߷{'/ d68]EIU* ,XF!zz1|eq1SMO/X{E/RNGOda4ГVܻ2ePxc%. 0uRG4&g[h15l wB3O3AwߍР$zT̎jtD== +zVKf=+Iv8Ҫǀ47*ď=c.Ct &-Wk>#bB/%(]f4Z6\yъE.SRhӁqdws$Ml衲8bJYs=+հ 4*J7aLdQDo@8UhJmWfXWGA2UDvc^۪ʍwSuzb{ZB&]v\ѳ&LzRz\|krK|oz W𞵣WEބM=Lט=IA׬蹂Ϫרow4:T[ַ{#=!xB\ٞ= 5@|ӟnYޟF,O=xtW+Sz>&k;zF,t 尽uzCA*w`{TsGƣ5';z~ĒV5+aԦLh!L 舻v=Vd/Yz|F,j-'>p|ɟ- ?͡JYuuǨԡ=CEH[c{=9ܴ7{Go!z ?1%zßI'-{۫2ScKa{/t1"w`f٧I1ދݳzz܅ 6]w\5xָ{>(2-ջbrGC>j5Dj!ˌX=O:wE찣J at_u^=u]##oً*}\SUe4k&gjcHB`vs 6%51'5yHReEo\cHA/lوt˒UOT[U6Ke{9Eۭ4w,+TuJJ>jULnQN/dt?ޡV.-`Ij&Q璗A:^Rrf J5є ݔ\BڶuI5/ñ$%{+iʟfa:x4q-_?r +W/y !zIy~3Ycͤ_0=_lj)%)r' ݯS|>d5ihѝ'CS܋S'&Q #9΅^jÁS)?!;ё2zkoKAg#62Ov;\3 ^O .8y:V<ϵ{Xt;9L71$z$x/>1~+WʎAx C>hVej㿻fȃU^ҫN ]#1wmu_kdIfC,J.AJ.Wqc5w2гw?*#G\Сi2r7gN[.ڎy^`ܥy7Oly=>+y6n\..js\6;ޱ+xj{#tOl_i ң=$D|SzCg뀧nLu|j;h狭Vn(­ٱ&4~Xʹ54Z^&F=4DOR\ P&PԪ`͑o!xR?^cAMit'DϙѧXjHD.Rv(Cɮd10RR2S+RXEt{zVTY 诠\[5ײf`=lT2@E0c4YJΧCUlRdT O]TP], KK5ope@_6J :,LuOZ$U6+xrꋥΠ' h&]K;]UcĭtFӢCJ^q:"z)n ILQK>$+=EV=*5]1EYWmI𜯹.3\5,teҥSdqʘ VQ4t=pb=o{]%==IYӐDPkFYo=9)D fBN TTy$U33+˩ڰ;9rWAY 7bZ(z/kG*==o{jFrd]蕷2O =j"|mt){52@0T@O-2JD sѝ^'Q" 3wZȜ' P={dʯ%A OɖAϷ{x@Hd1*z2S`ݳ}A#xVsocVٙLL 4(QIoRCQaP'3[&+)elZH/a{3 O$vjS<{C&CP K}j!je< Jjvy/2Gc,hrt)QW`IT ׎s^sP1!pUSMZc{*PQͱCFuWXBi3]y |zGBG<uo9nbhȻVs<۞+zzױ*Gɇ)Ihvs) 5z}ͅ^Io4M{%)o{CU#~4wJzP= apz]=sRT=D_*zY7ތ[o|/[[zmgynQSZ䂩4yL]͍!'24n=/6a1FH-Ftb֪~Kp@@Ս-p%|]E\͸*FyfƤ0Z 5y,ܘZ3mmV[D+UѸ6(1@>Ŏ% 1Ygm?%:Z:l&sZ z~d\k}ODOҠt 9^ |܉KFZ6 "I]\-TZzOHކgT噗~`ʨͭjB ([ ˋKW"nnF֖aZЃMK -DKFDw>K&g z.?N:!|/I f}^5\go]Dvq[^6jV볚1/@;¨Eyb_d; U(GoJ=o2R{3>R_\q_ȁ$F&ezöJ]8bɽcv˧ ܿCN?MO.8'(tZOIjvF5xok_?3mfFe\`l?nte];{'}bbsw N3EǏOzBfGl(os 7-Mnu)LoIiswrfBbαg[Z?lu:|wý}ZW!.H-wjkωISߝd{6ar~z'hs=tu^ӳޯz^}ܙF˱xzۓ&xurM]br>R7n=;'*d\ Pg؟`(U4cv^OLoUFzl@o7z|zvz7K9ia)N &@w~7*L&>ks.~H`RW㫾vUelHm]M?NFvomf+r&LQyUKYaмd]6gtY;ڭН<~(r~mPBKk[7ɤ|3"m`4DZC3 'ޮKM>KEQ=pg x#8*Yf{بvW BLIv~-г”A̤ 7fT4w6OFKҖH;x:+&fFp̶,v3{z3+%,lmHn`м]á(pmzen"_lQy`+nR~a2OvC:z-c04[C(SnPt5bulOt[[{6bgټk,(6Ha4qzDFѫ똇m0!-reK1";޵ăcd RmS*<=q셎 zfMe{.Q+wU˿孴b_ +hBL-==?Gj""U?IJy*.QB$355X*#BtP V@\C/GV,[' B'm-S,Uwj-r{뗢@!YsqOͨl_&,Mc/=j.ͤC2=(3-~aQu=5k:/':Lwޣ-GHW :Qnk [LFa01W{rlY7Wh7c! 06)|^ޞaHIZk2i{*$ߪ_3@ 7=dU;OZ&b(jS\cɜn#.p^cfoV06ƖאV^4wv%Sk bkiGGlohӢ8,pژ9yǛzy!àH9"I -ޖ!%skUPFޜU3nJV y0.s䆋e"#}O񊍷rC'iҼ(x !Ԯs 1SVָ=翮96M.WW9D{CZ\?ڨE5p0JU wW6M+ P39 *3ʴ܁1Ħ5j7#:P٧^KٚYN#_bYA(u CsEJ TCi]߄6MV})[7~@;y'\w*O[|: =tL+\P3ׄra5D#?jcoBen8Ko+}1蠗lsG1ћ=K;G:xn[,SoLap'7BYIb>H'&&ØO;c.ߐ@ @ @ @ @ @ @ @ @ @ @ @ @ @ eؠq8<{}Av};O&;d3)y\ vS}اO_.k;w>bpKZ;w5|)>o|4[QM"oY]+z5d0ךOox^;~˦DC/5lO?ώ^zZ4>J|jˇ"G|ly]=x{rb@aŋ>lѫr&ŌmWvW5גϘ^J~Fݩ[ `w{zY=fz xt[Pn ԟӁޢU70@Mz\HϡzheLzj@yo{@vϻoYsXW74JWO4PGauIGi{CvT{N=#6ݘ` 3|DT[l{io}OfW[q2;;ٞܲet6yϿ;mM= %vާCv:soFU_ @ooGE WEln>dH:ʸ~ރ}9;qsC4Oj'u4on=hy˖Ϫx+M8$y>RA&R^XE 4MeyFG%Օ׶zs [CHѮvҞ^jG%؁{ҳ<0UφL˂ܛ $Q3+2bg{Z+E̿it( :P_(u{1OG*âQ< QK1`rS=$ Y(E`Dl5RloڢA.6yۓҮmu#@=qS:\66Gfqӻ=v7%Uk$}1'E?W1-QDZn`&ˣ d3C:|7!4yRgFxebv"]7CI4.Y9FNlȄ'E7xh^Th<4"r+ Dmw0rlyM)f߅1 \@p`i8+fQpP5lZDa`X p`2j1`tf0,ZݢXO gz~xsNyWhL~彚_2C~OQF& Л㈇[ѭP#&(Ԡ-'Aa5zڙsgeS&ɑ?Ru5u%=yJi0Ak+BAsHj$<7{ v3]}4@Odt:C& # H7ЛSѣdF/5愋9`];Zr>nTU!T(be#Ϲ q&mCibvTB fWء%-I zXoEutQ!A#؞콥whG\CɜJOn< 9:Tpw~؁_00-*iG ~DOB|彤tM<,`UZzl/Kv!<{Uqc.rX?@tN/pPu'ύQ$C q*Jl87d%DGpiIWy:?]_;z=}TBV)rCk|jѲTGBmA C^lfkKk0DR5{z4=r'$8r!UE6TSKsx.J>=Wn{ۣբ jsOU~A dfG7Уwld{Ӗ޶@GC=>LzZH.ús5 s+=F՜ ^3F!rjiF hl5 YYЭdTPƜ*fٙIe}"DM63,lsL_TԚ|3/]ѓnwȅV§ <zϯ ي^x11sX k e\?s(ЋS xOMʪ;) .ztB3j5bfb8[ ڴԗΐjA:㠉m5z݂1Tb8ћz4m=K@8q=^mv/Kvz:b'z(&Je%4 Jl0˅˳;D:CyR[4J7hm}tq`P[C2L'hTÔ%ceM O>+aFl/Ø5UzoDAwƸpO8 QzJJ-i=:u;1__ȁ2PkFO6c9HÁ%2IJ37`"r((AG$I""}n'_rŠ֫;<,"L4vC"f>Xx8z$ɞ~~鳧ƀ١/]lG8^4% J0#6Y`oŽsv}0zinz{ D7(A1F=Hy̨\~^Rvgp"v}: ^x4ē>GoÈGDCMluCKfyplpſxpZĿу}$?˭r%cflN*D}1qEF̕-H8dkl/()i]?`Kh9-e's?{ʽz+C>S/561.}yGOK-bGCqpPf\zZyfEop.m{QP=xX̵KӀee`}}IXIPq%!F~ o&ήFA 6|pxJzήtWEc~%x%K#+2@|IR^>^,Y84]t+Na#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`>& Pӵ~5 vfgĬzz_%My|ώa1zzP䍘g WmVaid:$C/}IuΒfL˪ƼK|o$/&Qu[hNwz()(fg*I'B2=WAOlv[]g"΋E/?5'"ћMx,AGC{YEX,Yٛ>^ ^cLOo D;^xW#yE|r^Su_E=pˋТl =:xbO+FD} 鵄|g gu^ 7cs wԕk]=rVx%=`vj鵻:垮To{mۗi7'U1 Jקxmta'7E-BV ݏ^g jmpmG4m; lO/_ȋ{%GhEOy;殳}8N:s8!5MMgv#lRRy?pY(n8E/GT_qukœ)S;F@/CC"+8vAun}e^B荳E=JÒC T\ \~;BBp3!pSQjlG(I7rd}c0!*I)TCs -tEF Yоe(x&ѽC/{c`z gGc+1: q P Mqz+# =~nTWnBʰ;D_`;`Qi0%^ϡ$f=:sÌ{s4#6^ʛ^es<}Ԓ!u5وz9]cTk(ʏzܺ)b5h-4̆EK,h"W+7ڇ+VPj"RZnJ,D;qRs $Xc5:qin 1 t g =,FTPnmK$__N=ŝB/MMŻԩ-Ѓ*.rFq# (RB\rvNPw6^<ʑrVfa)`9+kMs7:AQfy'a}"2,+"c\g=tZs}u1t śJS`{4U!hzB3YĻBL#U+1rLrz\ 6J4oll!ŃqCs9*T!N^0  1#ޞZB;wcv[z(T5M\]-JA/KT3 :fKA=԰d{.$웗N Þ^*c%w+`iRr xn==vXhADS * %_b_jZzOۣeod(.^Hpn{ۆd"=R^޷=Io/bjKǭ{#{v>)'⃮2%_>=ܓ׌X+#)Cg{sscK-DwzK-= [ۛiC/9Ac+CybTskkkF, }27.x.{7eIzG)}`*ڞfЫQCH"5Iq!ԠF%ra9.秈7wx3Fk-s%>7&/R\l/L=乌>WCzq G =7b }.C lG!sC9^0D:C@o6ЛfmO<^赞ހ `{aW51oprB n^3E=iB݁[z:syڤxntbPkla0glGLڙVy{݃ض{W 1`{ 68(O(bl$yE年>IKKK*#b-AFKo׏b GISĴ̌r4kQ{Fo9uVڽuX~fgM&њ۽4(MZA0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0FM$&GsOehR ^~҄'V[u8ÇݯT2eʊXytEūgձi[~Dzn_ފ^Lz=@ʢhKN3ֽKiPECړIj{Sc0'Xy-Z+[9D빌XVu7K5*.\ c{Biv{o~ C(u־/nN6Lf^+bȩ<,߸piž\1T0u>|~>Oayc7FGQѓa1n|\Gşފ_%ߍsnw*i4lB21Eխ@/gQTsL1ج=IGtG7o=Y9MqSM+Pѽ,uW'c;4ÆZlxL Ѯq3K( eOq'xVr=WnXQ]-7ժ@(Ox0"*zfI6;la CoXRQ{48=Gi, PYw$^:"O({E2,7rEt844xI=%5xQd0@x\T6[C•"a(J"[X|S|LtS?mQ,gwTC/Γᾟ HXVW#eJl9x^Mn;X5r@|˕}I%&P'αرϾz)jU^QG_WTx}~So;ܨI{7(YCO@0dzag^h0#n?`3Pj<þ|UhFyJ 0>#֚5s ,4j|zcnt;UL+@շY;Ao0@X$E &Bb4Jwc{2q: Svq'P^tdFyC/J ɾ4)ۣ#l=|̥"J^O[<<7kufFJk4*lODOo(υ$܊ބl1=ۖ푄i_+|-.5c&\+ivLG֥we@0khا i=}sX}.G氼s5k,#3spi,ۣ9ObmOqs֡"k=^7S<I3Suk#f,zqC}v=2{9 gQ\ =ްz zvU_:6*OapTߣ7fq.{8e>e7bQ~}m2wWm3]B鴈jG#=b6{':0}=-ZGFd<9G0#}xy{Jړ=GS%!w_EO;z(ӦV*t3'% K.ǎ.Wa "?Zh:rn> e3‡V9s!hSk.C o$=L;>%ч8!e;nF p>z+_* }I4>%U>鷾AC5ՙ,_fv<W2[xx sN2꽲s<3=&J+f\+4xNg&ѯk|V^&+O|ԿpN\I}Z~3imaW,jyLOlNb]?Q_3gsi<߶I X6.ǧ0-eK>6.ӧmK29}n٧X˖6In2nгEKpk$G2&Ic t6zAz &d3Y "_ ˅Бt.B@ !zğ|L a^]0O2YױpA>I-a0Nk A0慓ZBoKH/֒f18]<b(.@k{{j]YfpQ/7/ZL?{k:)7x $Z2G? EV*coWu67 -Z;Uǫ+m.dɚyxZVi;y6Zŋ%Y7-?s%GW&y);J\ﴛkPwOFiUӯVR~g 7ԋ Pfrxb]'&d}S^*ǻfi흒!鯁/mBmBO;06ߧt/.=<-K~Ѕd{`қ-}GEW%|q;$ttVŮ^Nv}wz /5췎mpT0d^.߮-xl^}qbAߣt KCHg;I껍ފvO ;뵾)U;{l]};n*jխWr[8jn\ƞ {#@2pLMM!HVk#W]bu}V;]qGM5bq>=N_Bޑ:f/k/l`k(N əlӈϨL[K Y=@Y3 ·eaJK䯛x-=JI@o "=ϟϾOCywbF/}B,e^W >U_ Tb0IzH1Ч 0M^_zIԕ=.B T"(Zn~\oF7~6S"D﩯IC*.uz{!7TC?JCT?)EAH kF} Io;1k;/<%lgxBDR. Xijj1/OH%+ ^_KK8O\7xqGKH?Ge^bEtJ(Vz*&ypd牼FCHqz%AvOEf23n )*|5MdM<^I2Fįoz@17;ᾆ\cYG_!35Hl|5]O٘xɣ8\i+ =\.@dJ: Xztn]i16"~ȉ@#zhc.#ָ)pBOU"$\z錤g BbR ʀU3Q^=Ҡ;žHF􆻒두5er1s:ЛBo94ޑS"@1Pʞʉ,zBR/]vzȒ1~ӝb\S !>[  \p!Tf,`q[D:t)=* %z"{R6A_$/V T% ۮ9.JRmAo_q1P(\&Sw[>8}B% ;# 0*I+#RJ\p~x xM -'z*07 ȴ1kl;/}mF, CzSqz(qLK*̦rrs'z+E`f$:5VmJ@zT:UzX\l/j<7^6kuݏvP.:z1ϥiR{ڽ^MKlbvωIWp"=u4V赞;u\\eKlOpCR]ad,9gcXz.&FY\ =rk<7^DΚB7ܦctTI:ۃOHgȱvOJw$$1ZNh(z8Fm1~OJ 6 {*Kfa}@7b!=1^r^&~^ Kk\qGCg$=i'iJ=,ܼO dY[= p7(ZYM`yh{_U4iG]ʂfj_A9n\-豳zzoZzBAY0 .2SVv/X \C*N{zS0@~!Ip`sHڨeV5c1q/1>=$zNz+RYS>`gI{D=`|h96^;z{dc)x8f5|Ȃa`~S2IDe +l/: \_U"J1k6q~֔$UKӄ6e2JO #Wa=1 "Ez5M{ۄ`eDMHU!,bJc/;k1)OeH4ڤ &qEϔ .4 D豚S:V B6PWN9yވl3e;Rn APEh}/{jl(" U:OuףTOvA׬;"L$Em_}{1qEggQ3)w<~+1& 1m*R6ɘE:f{7G{e}* V2yT(C9<"8mG(ݟek}z}smrWb{^ҕU-ڧa]sdqm; Wa{r}hzk5!6߫hniw~q{ent{8<&K.NoSҷ&m<2:;?|-2a:|JdG9D8V` ]/?BeYPſ;崧YE|Ei-OzZFp>{}~6%𼏨?p>6d9kTUrM`aΎm1=]N#wG=lc]}mK`ڐe]fxgQZвtǧkm $P}oƺün[I8ӵCgZrzl%x8T`:']\[H0xY^ulܑ8sHeQs]Lב8~&s| []>.j[CrF_К#|cg)0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#s2z`[0F#`69҂ endstream endobj 29 0 obj 89702 endobj 31 0 obj << /Length 32 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{*u endstream endobj 32 0 obj 62 endobj 30 0 obj << /Type /Page /Parent 3 0 R /Resources 33 0 R /Contents 31 0 R /MediaBox [0 0 612 792] >> endobj 33 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im5 34 0 R >> >> endobj 34 0 obj << /Length 35 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream x_~vWIo3)d\=FWa$!F#49U̜D9dP$8N&;*r]Cs+ ~k߬5cr7/{qa۵׸vC>6m&qIgL]?׎r- nіl5IP}< ^ yE^u o m+CHꤚI.\2o6 y}|}ҽN6& ܥ5e4\gy& Ϛ Mт@|hvN{ΫBFo9y2λi܍Iw%E- _%'M]XaUy\noliZtws /JNA¥^ЛFzkrR7)/Z5ReWT9żɦuӻ.H8KS+ 1[Lډ^j.Cۚ\^2hǵ6Y.B#z7MY+z\&yuuIi} UG޾vp%t$e^\BUV*sھn+TPr=}"z "07MCtٹrgX.AW]8zA@OV%=]xv.f~⨽f_x9:b't*=*L&h9':W(U<> ^ J\}tکd(1J;ִ?Znۡˆuڠd i|OQkSk3Ŵ1ّ^wmMs{w^_Ii0XfG{$ZnFy5ҫTjze9-7:p+*Tj.@n[sO{"kګ^jzgj'bh=OcEH/^G~z,?"=HOfp-#:tҢ2,xnob@Ձ^p~|;Q ,n*/{jzj\uyu}7`kha{kDzCcۚ {x(.fZc5Av'Cb W)=l(/*=<ћ=LzUbdY&n+;J]$E]=^o -zBD!(zhWۦqO%c_#`k=eM#0ryc qzX~d '2D|܊TY[)'|77`Anxkl1Noe%C6tL!z^"@\˚ %cOڇ^-z^exF*N[c,| X`f;oDZڵ35蕓Υ^2$WTyї␣˺Z"h+:FngУ^dk:GjFqѠf;ūozy.􂻓"ɜ;@Hĝ<^p:w5u7'8sۙT!"oIwy\qcQPkm+υTvE 1mM\]>nW/ hBr2qbie+ndP]G\Dᒖx4*c\GG/pOUþ7@&#KO^vU Z\w86}=vu%c[N/z"`E/_rQ^$/QHuG6z6HOTJ̓-A*%sŸݟ}c@#`0F#`0F#`0F#`0F#`0F#`0F#`x v_o5?y mրB>>Џ￸{GT,~Y0VN 㤜oҶ /v-3'fbV3̐!x}4Z||mj1_Խa)I[as4/W>Kg^{Ӎ'xI֯Z>qڀ8suH{Hv ⱟ~z<|6֛KzL!3[q==,'~J뮭`y4k|,&S^K A11 Q ]56n)6e&z!xz=1Q, ?n{b=Re^K[KHpmumި=F1ԑ^֗&=~t$j\T$˙b-(V11zo[/0ɖ\G!ac}}eIo= 0G[Nwrg۰eқľɥnd8z'dΉ2o0}riW{4f}8K6p)OuEyi1 hS8w5anyJ~sy]۷m챦OVZMK˯m}:m2  =5 .&lUyiC$% 27i;ٳtOdb^T3xbm/x ;!4QysMa;ެPs*mPWGz@sZh!Ε=R#0us#$uO!].GƟnDF{[DOaTo"6W$]$AIn0.i#5Drد޽ ki|}#jui&LpʴL^l0IOB!u)]$DoB*9$B)MH;NܮqCG\# k:; ZvO/i|FR}FȧX7_W`T!hou`'>SXü,)Wi@'x[ z4*#X!!!7RSX~==aBhesd(!\PU1T_'KO/rNoi!a礪㚫ݖ4o(fG&O E1tÀz î8Ѓ~J,_t1C<+jXu\"LMמ0xr/n*nPta8 ׀T^7CdfNlI3E$Tè$FYFzFDz|To1>Gi(qz0j 7/GJыEa=ڞ^*>#RG`HzZ*=ЙZEJ{D=;^V.=ܖx @\W y! /7=J.OFXt:I{=E{z#=pP@ѣG^^̃y ["?Nzz1͜R*xhMh+z!=ܢ**s0iDx-.qz9VS ul?+l&zk[AO 2Yu @[hi{KgO=N=T2JAwe:sd)uu,׋'T{o8_p[fWTvDF\ eGqXlXo>^lX]{}@+QM/==U2|Ny"=N0PmB$2չ$yyi+NXkD2R~E7NGmI%A/S͆b5@O1#|-ؤNFbRU&7{)0[ɂbC@݇zj-~WcU5fFPǟ{DT~,^S-=5n\% ծY%4[ֹ>ԹjZ!-)U"zIi,7Pfs˩$)>;èKn fkFz]}'z $FLɘڋA{!x^GtEoG'pdNCN_ܦ:s:0l_% -m0ViiSH/FV|=#ۥHzsUx-詯\*b%U^]w}\~"s" Sі+PS=U#KI\,=0.r]]rIsĥlOQT.Iƀ2`UK<9ho,pቌ_o"+$в6X_]s |!ޠ<@ȩ! [{nR91,M8 ?,+l:fZ{ջQzHn6eu.E8hJ}b={rl?/\oV7!>%R2DgCBc.&`k3:㘲I5cJw8_%NL-K̊ʽgh cԌ. -3w5( s-3f?I63ua[_)3,+'`LȮ}wS6S~&q!l[߾Y-S7ͯ zL}Uvu!LK ǚoyTfkxfNZ0t;@pBS*{1e{m勾: 4IģU!aGyz3|~X h-GzW8/W顿DLNDzЈ1BP/ 5dZ6-k%5K0‘sVImh!h_ .U͉T f]3iWG)S:z4. Cj,ܚU#DzIP v1ea\;Cn x č /&wL`kYyᲁ5}iW#{lԞyyI^ K!9=桗 ,wzj(6ho 5g0~EsQnMùV: 6ӳ/0zg'X/%2'.VJrL9_:OrsكGhQ=uzsz\?MƑعqh Eju k|EjE$O ?^NX+uOW@oDzC|nq$'KQjN=c,zVPTyˀesz7`S2. i9j)UH{|j詽L0cFz9$'&nf^IǕoCPhJę*>vـ'=ņCńqʒkE+Gz-=B_^:.B"rKF:2:EjFze7 w8= f'ev`Sـ:%'ЋU _az\~j܁D}Л}EA=hbEzKz3]i^@/3&:UC+/pX)Ң=RRы΂^azXCsT~>G3O7h[iʒVdܒh{KzhO蠐!:9m{\$Mܧ?އzfrN_ ^rt~VF'Ud};-Fs6R^>9J3[Z=HEICDzWE(%}%Yp1i*Stuev\?ap~OI`\#].>Uw{(g^H˝el  KYP;`5c̈rc+Zp· fK!F]bcGO^rXg[ |^yŏzgE=A'~3\zz!yOtn4p|^Gy  AC;wӽh{&M1G-hr}3٘%ڏ*6ܧpHodU}PAPRxZ6!9c}U^G՜)tzzS}[у@J~u5zhyJw^P1?=9+~Q9hoB6>_{z">~(U cˡ{6ڛע,]αxpNG핒L^VAYiWg⊋޲"IHxo0y#.#&Ǟ@оV!y~2$#F^&Ah{Q-%L#=,ߋ$DI^=U}ja>~xHЛ@/%la_ d栈:CbZ67`~ UԾoyEO?bE􀺢WUq9|#/&BcC>qza=U QztHOጋHoUP^r=A %GZ#ҫneL_~ObVx|uk~L}7 EFm[5?ObN|R3U#i8Fs%@'Ҽ8%=W^}=Ugj#׌= M]B_U7Deˢ:pg+Xʁ\zop=]YHcw9FIgg E B.q#~RB^=YÀ/Isuz%UB|\ B)A5cҞ8 ?8cׇ's8GGFz<*X7n 3hhWxOݏV]>葁#hmPw}|IY \bY`E?/rw< D,QlO)N;φ}aփ^N]Y^S'j20eH8R= (?dXRVZl7b޿B?z:x-9z sr9׹\S{1$7igq';re!0W׼=:}[9 NLM3F^m0h,>w9X6DGӺ)n[E^{)UI<;ڰ++T}^hK{*c"V/7Bo;Q6&ܑE-b+=>tZ\P[,et.+s=}:i&ˌv`u9=苾*SDz9J%wHֺ,+z2n-^6Leԓ\57+ f4/n2񌗓/,i,i={esb)ÿl^x !́kӞ hfh{FFy҈s@OH{[+)Պ t{]ўo|O+0Z:H/LN2jcNcЮҜ:LUe8r?Z } .Yt,BFJ{/OEO~KTΙ9Sis{yWcOm`^H 05';+zȥw 6ppe [ ~cV^oZž@$ ~#0Ϊg(0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`j?Pst-+Ll-E\dN!k7N>¼sJezl&GdݜY/ZJ .4(Nﵴ8J sfҽq%3k,!>(ן hJUlŸ&=ny| X}1{kf\Y f[\HǾ~s>,$z̐{^u>(vIog`Rq7+Lkޠ0!)G4v-w[ICTٻNMp?mgO5ُjs\q-B{IP[4tЖ;ޠw6^Κ^Ԯbfr.+'irҤmQW|FO> dشڠN- 5_fRҀYװ~*z\=i]iᙂn3a3 \J/\lxޟS۸yv}up7n2~M1j'aЈ Wǥv΄.m#=C 9O66. \$MR'뚤 p)6^Pgq<vZΓ~zmoyQy鿶JPܚQ*cuU[_{-Ш/Pp"/w.CkuA!}"m&)i;|)1EVS!Ld̈#z4 J9mjZe1WuկPm Z(^gaJ[nZ,+ zin$;V*&)jحQ~|p*ᒲ柤:uVj"i(t[͖=!,ЃnEVt!ΔED+-bX-w' C(n7T#kPP=aetvǪMM*TzNT奓kI=n8+#t7 @'͊bJ.aI,y%uV@ϩ0Ԓ^z^`N/ze7o:;-#"d'ƋBK7dS5WШhiN19&Un<.J~*ѣN(hރ0 %HQԞ ~@N^q1=ywk oMfo2_p&nמbYhDziw$GmЛá_gmƑTrvуcڌ7bAp9A9"Iw/Pݾ;$EEBB9x vT?&Ŕr<}k~/V# Eſ"@}>ءj-(Yd:KBlvDQgssU<)7퉞D zeT.V+z쟯ͫj ѣ\ܴwKsS99BXm=iOg^7zaxi>PN.e+/]pkOkXS'k/ءk2W+FsÛfлJx׹**z/.e4%E}H]Qni`F[ݝvt&j,|.x{a ߃^ @3E]<6?^_{x{ m=i`iZNs~ƺr@?=zSL`fo&HtPuۀT*~qnGo3ᎵFNJݒ Ok4Tn֡$QG?w]h\\!< ֘ͤ=N0RMoq6jIs̹Уߓ/ፂd}-ݤ9͵=%C*;e)ʈ1v8ghY z#=(r7V퀏.ّp 7ia[.c4F$wQGp餽I3 suHZ#PVYO!{z,Qu[Q{Q#Ƌ#'H3 B*߆`eIV9wpRqT#̹(TR(ݴuQ[/(1=2gus QxRܥ14PkOS,,)lNlZz{zjk=p][":UIre25wg~()W=]\c;:} DQhzy'KCyMI{'kG K~9խHt(CxR= =l-2@Kx:YdU{w]!6/H|.9uWɃ+]0L%7ȯOtWl<aڤ%jޤ^{,\-)|sRY@q|ix,OB'w Sa [W![9xzs?j=DW%*[u,2ޏ,@;c`ǵ(6QƏ@qF[lOt{68q9C.}%5;{jyH{q)u>kk m>dغ6ݱr,6S#ƍq!|1_W;/Nh=/U뻻촬z,.҉fHqn ]|m,]ml9٢l/תpk;3;eBw|7t>_nR \/u"W-E_ x Y2F#`0F#`0F#`0F#`0F#`0F#`0F#`0o 8;CC6ن#0c,?p (/teC99!}M8o^G?ݭ=˙qzp48M =?vlQh+!X13fÄ8YPiɬ^{o7h&OtjIkozO{M|f5ט0zRa%vK*bFBU:U1& ӯ__}a g>i=!27QWm*}ʡ͙v%{D(R >NfAܯyMzԹ[3:9B[= ѕBs )`ctZM`˚ߏeZ60/ۘMRfgE;hդ΄M˘M>i&Z\iz-SFzȱcA/=Iy7 AõsR[t^ǧZs-/"F#= ]<\hd>b&+7*B/.3{(Al zϙhLNOF.2H;]+ظzОrfWKvôTr$j#p3APx#AuTGn2Ld遙8-oMKiO3 + ~T=6A7Κ[0or5ތ`6W J )[O/E0$QgEkpqDK*׵0L_euЫEOnK#=d^[@FSqD/;#z^mB17v 5t B4\BY"0ts~d#=jޑ^cjj.-ʹ8kH)0qpn;}kHz*:Ixw{4=af;=6?Ч=XkayJb7-'zl8~u)\ wqA/Q0u٢$w{EQ~U.kNWb{/bE4 ʑv7О1h| j_s}' {O'cNۇs;UN#T'iAZ:O= Z+m-G.D{=;z4>(^ri'zUGz%z4c zϹ<:d9|Z ew8͗cON9~^HO hYuG"io=JL5z #N@HwGT--\Hl*ʥӥ}ũLY3SklˡFob@j'Yˠ@rQ`Uv'qbCχ>v}  zHo4~0S~şN=Pb(8DGh<Šu5-GV# U޾ϦH@3-۫ZOٚ::{7;4],(3i^8]LM=e{KcX˃p#^{2)Ŧ(冎n]ی"l]{ݻF T%{"=:rK6z>|I{4VH[w6")~GDoUkpSr5J-anw{zq?D1_a *#@O-^ci`%=^XЬ+BOf 鶈{8V<9=\Za b12ZI>i&nFkD82e6uh#kϑgg@ji9h1B= R{+zS tW­.6v3=㮨yu.h5Xn{]u۹DsIM=rDw5(ܴ*XrS)*A:juq w$[,"̾?U^As.*k[\k$' pJѣFCaUk^xtq'j}&Gq&ZiVt "Q]ի7%' pnTH10*⥔ȗ7>V l_‱ym (ѣ/A$k-Kz5{#hjE~zFOFt΀xϵ!gǰkD&*5ʕ_tZG7ע†;[A$x 6x}*=g OPf 'xŏxQFuy?U0Uqj3ԉ=%SťF8nĒ־U>㓲+Pk)jr}Ig eAo?r5.P} :c'v;XIEU6q(?u_v蟱=PS'%YnF*C] -ПγUwSTIx6f| yq߻HD ;=m4xI՗t[a E:Ep}C@k[}IGpY[wnt=}J'; x2~I'S]Qܲ_Z~|d_ u{z5 x?뜗xvZĿ== ϑ7=;<$I6.1AMlko+=ޔ/&4H7yxo;^{ CDwz,=୑HOړ7|ᡖ!4D<һ'z>ap^LrؑL2z/&m/=ֆw6M{g(zZ(mWzYiօ?MM+a Z(Q2ڃv,u4,yuYxY+zB\5 Q3\fkإ~NjjG#== mKW l] 4Іs= 3H~dI]aĭ ,zMMtI}+5PYm5:WX^2^V>"(i&EYnc]A&qsb!WSMgW^N! }/.jOth7O5AyұS^q@Έ6D 4zzT z%c;kɒ8p/':\ˬPQ_kDe?- Sc@)ڣWY44H?-l7:yHh/;E'kXW酣w]r(q)=pxW]VO\.^ko/@B{ziT,U|4 jrޑ8 mCvI} Q@HӃ.J[X[,eޥ Ӑ+nz1Z,eOM8BR÷AUy==2;5Fz B!X̙rU碗h< \C8af~| zXE~.Gԥ M:py@: x*af<Og=Gz,;  =8TRz<A#zS*6:/#=:,elT;#F/=GeLj=yx9ЋʛD}>[mw~M5 IeI|Z)rL&G}Ԫ^{@v{z5hhI!kǀ ˌV%zyȻ%gl% FyaQ,3J)jy[CJ̠%8~Rνp2ľ5vkIqF?|Ng0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0F#`0Fdxo nJCprus7mMBoBOlm|(iQfB'LE7_f  n|oҫwz!E5T&-qL]ޮ67Y2s_,_moZ4ֈ]RpEI*WkmumE{rPwyNqYz^[0ʯ_)=cf+!<^*< -7ޅ7Gac4"3-ne&/7^AV|p kGqMt>ZﲡjY7[11;^w&?ڼ>r]OZICOOd7C(Bi#EzCfs\8Xkzpi_L(y$->7{9$/@oӉ^@-lң6H9,/^o7=>|z%,=sWeܲ#a3빯9Qݤ`, gg}kV͖غ*ߑϣWTѨjҺƁTwB4:a !K{P;W|u_q-'5mghG,쓿dp(}\gO#N^8Rv9mw.*P9cJW͂4IOo MJZwYsyq#:)?n@~^%0<YNMԣg{Ͼ! tI"Ґnf]ZpIԅMh/A|p]%cI |W& Pݎ;)uׯP"~ C2'^_4(\(}NkZ!m<[}#4 .m F` u;-n{PM4-7mJW\92w zE4}҄;y y2E XpGFMWYZ!:f]iv2d0L%ʹN$ I ߟhd):57{9CP-E*P(` \HC@@d Zcݣq\8pl *D ∽ڐ9lvݙy>[u3oo꥗=I{Mr!zC[(U7%RP8 r0_\S^pDt(?#L=!JRα2Ic?QmTF.SeIM$"&lgyÏ d<-,yJg1nn;$psvcxW-bG%szl?AQͰ^* |Iք*WfeS3|ILOM:zZȀzCvgǙt@y<*x2* 00QK1Uy-=nE^Qi#,r}^H@L j׎i,z=;g(I7WY :L5d1%Ԏ Q?4/Ing҂+lB& z ГjWSFv=Qf$KRsx=Z[K/iPdGuz0piRU10EY|*!v#D^BQ,F/E=^kϥD 9SO-yGI qOa4z<+֩W9VڛiPKx_;+O=7q#-J.E̜ NgDsQY Tר䢣Prɛw'M%#ʡ,YW1I"|_ۅumO0B[z(ҳ_' U Tt\]~=J,|mKg0huB 9beuZGϥS=C4p\-bQ{^(/? rXO3zZh z k^l .%wN=J.X=_0k/ڣ̆SrY*{E[rڒ寠\=oPL{][ݿb{iEz^ f=:%+[z3;ZG)%@ڣB< ac[Rڹ[}ՠ=WC65;k7Ud#zu.}%&Tj<$5;z@/ zal5ތ/FN{&ŊBܩiok{l_C{-S:Bio⋿MtjswOKO%7z*Nӫ4k9w S~6ތެ;}hK.qH@iO?C{ - 3vݏ)@봣siaډ?jP5qO閴hy5Umk9M^{m{/=Pk𲨥Wn^R_{HsLJ  } yq[r7SU?WU>i): iOեK-B{)!#;A{G-qUc HNgg 2@@/TqLl Pji\R =*M { CPxFJ9Tth詵U6Q'lܵ4klWAd <^{H┒A>7T_m չȅ9%=d=vSJnVPkYwWz<šlPf ]4uBp6Z3B4  fn #ƎA30* j8ޑD}2zY/z(;\mq< %^L<‰eqiD,.7qoqif6z+h]0[7rT{#V., 63'*w0BYCCoezMM%zTBo]u/ˡYLysSٖcRyq BzC0,u3F޳by=Ap|9QLe>f_528!v͡7xH0,A+2 G  zz4W(HJ,IBD[&i{dd|ehz^1l^~<41>6!]{IΩΈfe(-2Zn TJ,PBo ~&ȟ|qsIwFx |Y"3=TD CC_Voj_Kŏ̞)MP %N 9o) P}3M^S.HGm@qWmDL.ۉ3׋eћpNHl}`\T1 %%%Qߧjnnݫ^޻Lc)?^WhmV=\Y@oƓ"WD{]t_׾Kj2[jwc.qo!WeyU}Gh1ʷ,{6`d)]; /1Ǒ@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$UY/{MW2QLK;%~ p4}]߂[{A>Ka0_hCxvT}k&2IBjC5fv=\O/u|c 3s^i7loujqoOgfk>9oNpB9=Mf<:(;DؘKѻV{|u$]z%t.Pi>1OdlμLvd4^K6҅3n `70AۚitV5Ejԏ 'ھ՟eQI9e<ȋ61q6,+;zIx d笻گXz' -k/3u$m 4qR[6cngi1}f^̿W{c_%Bu] |N/\1|JsvaX7:] ׵Fvfe5R< KR q~G}Y5i+xlQ9< -!z?MWi~ˡͳr-o8B֣t핇)uc3,^ 3C3[zaI9w4|p:o(]14++t&ޡ5rXEmiɧAo'kS x4[1sy'ǃ<9sK' zc=䰇Wvg U[gjy?ť8a"bkD:HF3[՜^fL' ;66U;.]Y0*26I})7QV+[zaM.zz6УӅ3 S39`f n ;~ǿR0}l)*%&4_²b@(xdazI0#0%xa8m90r*x㳁v8F [;leP6{G N<}MSZ{z4ZzDOn^5 |:{ ^$/ X6!N' W2G-U:u25ݐ=vi- 2Mzz{7̙^tԺ|tpGW@{l@P-;=YjmQ\5XR-WAozCV~I@x `㜛GTBF!97[X{nR"|M{rU N׾9d(zڱL]R=rI{z_Y@nBo% ot7ɣ9Ox"eU/ T3ɓ= ; ϴGj^R8h[.*<޾a$z<wgNb')dؿ@H^N y)sÎ^XQԥcc䊞v3]mwަiq-p-Ҡ{gGMLQrz[ 5nǽ2 5ZwDeǽ#z$7(^FFZzD6~2BP%mkٷ%w % n'ϣ^<,X;mk^EGoң? i[{aV iOq- )ڧD^yBʗ~!u;V-mSC{ }d%<GFֹ<^R>iO:[kt*n=RYX_G\ } bNwn{*qu8!#aqѣnzlcT84&cO4 [ %|ąt|FOړ\Eo]0;m3@@Z.%4Fp4mIDRC->5c5@1GOSJb9 2=ݢf M zz/0 hk dF7*G\>2t~mcB0!20z9RREf4eUzly@cJƢMO/Xr_*TdƌR17)mLbXwjfX%x3 }![F&Öd2RzIaDחF136aqh>KTj66$V8EJ =9XRP!i' *YnH1韫HOGĸf2M=ڌ9[kXͷfCyRj3c徭xyeH1 6W{ m5$zJ/ e#GJTuCzf?/ $~"͗FRf^GP)|f2y Uh-yG\zC-_-E$,2kĿGr&Ir ɻ> 7Yw}QIW,.EXV||߾Ά]/w'}6iJz@s[6^s| ڴyYS7;W3ܥU.{RuF'ٙ+Uݼ.zezlG& uin|eƽ~dѧkR{G/qR_AQ uٴ^O=vwmج\uM?-:6-_|Vf-qyy.h<2"H "H "H "H "H "H "H "H "H "H "H "H "H "@ ]zK\k&I/~a=g6t_#e@0=ͤSs{fV'MfV,}-+l:z͡𷉹-O{2>9ۯ-/ ΥDg B\>}[f>~ۛ,,6,=_)VMe,-y&Jș ?zzbAoLvISӄ.hIu[½7]8fA6K=R 2[/p+6sћ0h9휚+}~ ;7__Ҳ#vLa 0[# '.i*~}yjs8$ U.\'GOT/%࿴ae^xAri93-.[z=K/]zg3w*^vxٴ_=LȚ47frL̪0fbsvixlCM]G2<,8"IzE6ɧ'1ɣ p . 2>&rpD>&A0enxZI)(0ʽ-81>6uy  LO˸R1z3/Q{ᗏe>lpYAҢ5L1(pU.˘1Y2&9,BhwDù/'\;47ɤ֏,Hb!z|T^EƒJ#S=Mq&IYm$; C8 5TxWAoDS B5ÃHjVqog4fTc, OQii_>&<,,^HÐcZz WKoҠ[@;[sH% 6֠EJC= ]$3,bV&7yeA{CRU(5J˸>=2?ek)@Ks B`@IT=fCm. .Cؒ|㈝TM[zؿQcCh&SZB;UcQ%BDCR>ApyU-W'KNT=Y֨6Cm '<6΍#s{*SH^\A9V+5.`/MWb7WWƭ2¿̵̸޴Dڣ:iVEP6x0'rlmS! vEgAɭGzh/tK:$C@ ua8sQ\:b!ff'zYG\VAwT<jdZ#GUܫ~<ٜ*V _fΗJ.\T1%zb ؎;Xuh'Pup'WpI w~\Ҏc7GP͒@׃Zm&q(=!ytȩ^QΈtƜ˦Rgu%0zT^pCTwϨ;>h򋛒N{zc'#Lsz4=r7fǓhrZǙˎ=[c7^Pr Oy8(F[˂(o~Oas( iR ˤ=ho==Βs8s3z+i<+z]ҫͨGSS7%qs}٪/mܛX,%G@ֲg`v [G^15Bɥ\r?~%zXz'ic7QBOm_:ng[oWk5 s-Շ4rɶ)鬠Ô)A{c2Zs9÷PkZ,Ky3s?ƒ~@6,i`ODO2VN-nĿΛYh 荼sJRrSdKeі\94׀+ubGo*Թ\*zކ=g#iEȉAJLڳjyL9Ϊ*Nc ƅ<*mʬqչm_{6ƾx3K:v%wʧ6ȇ=CRՓ9-:+O'(̴!= tZKZPs[zfKNJ. I͠2vW\CYm2ESgC} `ckorlI Jɕ{īѳ==뜞z=J{s=؍64poh~CH6oSC `;RNr[fJ gTa܎DoU.aPUV(r,CiO=liz[ޔF 21 ޅ>M[ wO~”%IpXSMN٢nDtvy#̻̤"nzDRF?NN% &Sk\E0U:G*)u3\uݨrś쵖vT y_}ACzYtFN!`2l}^isGXc֜=di`{ =S:^+n 0KL];ٹEC?W#s|zB^=dI1,^R+EнEa|k|{=OhZNad&[W*=XyxZ~J!7p_=7nϜb ^ !gڛ̓]skEm_"d0 >{V!"r\ElzN!}+Sz6V޻[euknP+_g_ϵ_K,]]r\/nd3&:ٚvv͎h 駙BsCNc]nú;3zt(#S_g*_}WZlyhWט_ů_7곾wPͿͽ2{;q0"H "H "H "H "H "H "H "H "H "H "H "H "H h>:z h4i 7t$pMBbڴL0[/-gG5oj{3prm~pVəv[rۭl[Y7yzT/]0=a֮s<_#goI^ڿZݙ<Ɍ^LKyVc<T![N͟=l̖aڷB :s.Am8eFϵSr/lՕdG%9iFo!1oڊ\Bs g/\q ۴^0=Qw3z u?mw.n4"^֞υ7v-̊tIOio!A'VX|(nOϏ氣IPY/w=;qsW>o^Cljz,79|.3΄qVSЗapk;8| mocٿUM{xzsǘ q+51bcfQCTN'{Oo\Cz3//.L}zwWG I%#026Ct[` JlS~!l_~I'DZ+sbƖ\i')w1M=}Y[?,3(+LԵ)bԚ #-^Z}[38u1뛏K<ت`RN:u?Ț@AW fWqŨɸ1-1'EÛI 25z.JjK?`gD\abC'nTXEoo'-+y4#aʚܕШNƖ)#wq Y'zmD3l< x=>3.\kNoNCm0i(\* z#!#GU)Yh YJRf8Wu HSVӐQ_LMlw+HJpFᢀ$eLIS+8Qpf%6#sl/#l'$zGrJ hb*ѳF&S? .yZc2M2<  5Mf V~:h{.d8Y0M,j *Y!zb =8#Og^۱248~蝶[g衟~>?,9/v5osĶ+BqzG\G֏x7;F7\@cy+>УI<<Ө !-3gLPTGOx:C~O3_QP p-oRR ="$A{4CZzDY:jQYWHT[z* qRu'sK/u=NfӣXg[zmZޑћ`E;mV艭_w Zk31[-Ku&3N7,uڣZ)GUeR7ެ.҃yd3I2FGg,c)n=:zhjŽF{J==ZƽPv#-T-5qdh&h<=@:kIg 856.-Թ-Unurް{uAΜ(|=='Nܽ@M"(f4;z^a{ b=]s%oV{%ڷmYJ+f%w^ݖ܎` ѫV{ЫQڥl\{Q='4 B{+P?7X F #(K}OC֨bA=Z˳~~祸E۠nHMp53O{ziCdAE+Թ$c'#Nio2jk !,kO׆O7+m8m9ݍ񮵣''f\J47GV{=J.2 {gTpƥ00 F!z)*~$z8Wґzj3=Y_rs9zl[,[tK$ c eqx64\7Ih4TP|0ؐq*„,4J*NVnP V>c/@@=A ;VgM)0wTNFM&ң{UߠGXhnB/G):9~RJNO%E=mi!ZNf#gN I{=7dI5f%o 2M,2J$؁/y =Lo H7U >sf5uM rgy6i:n3jyo[:E|=NR)D.o=l 9|R mUNMzlw,748~B ޺}kNU~>]Fڤ-V&;Y ɻtҧOo?KaODtpW}XN!T}pB++6DdXqMx~J}Dd;3ه:9׵?W6 >i5~M=_s*cu5ƛsgD׸5Goc"H "H "H "H "H "H "H "H "H "H "H "H "H  sWQc/R|{!rFo̿Ӄo9g[:~9;ӊgYzLnIwHm g'R8?1ӛtsʅbsarV^RO+H1lw[\^!mgNNڼkr&>^mrR?#R76oWYhN953釥z3.s>&'N/6+~(|`%qPr?^iqY[ 鷆e#E{LӔ.=r!hwROe~dpQ襚"ǽMy gEUL~-Wxp1gVu?eJ޽C'7ܺ g|*j﫢7*p̤q%sh[^1l ,u.ʡ;0bGMwC3֜1Kk_~N7ܚy5˶Vss8bn@#ASĤPe#&7Z&fwV9o\p&.G,6hܱE:L6#$3v}GT&;L}qݚ'K=|)BvO7(y7m~WM5eRL+,qƟyc51.3цN#t90iG!01@TDox w\)_d]&Ra'"GzK&/-!cTXCIE rɵ]\}\u"BfYd!E=<: &\@o[~)ab Xg_A6%LeyWN&#(ER[Ds\>Ά:; ')MNa2; N@7lqlŎdrUX_,q'!VTӧ^Q>4:f.I;*w'3ayv Iy侠2{p zp]FN[7{۸ JOVrI*WkNB"%*J-{;+z@*;z޷Ԫ1>wGcg%q*jLn@0!1‹eZp!!T`[x%zc1J3$ۉ<qzR`6.&gh0h]`ToU#KrmЪ­la-l\ udlo#!ȥf?VF6Mh˞@+j L5Pa2IݑBC 6#hYV[upJ3ܱՄ4 `>Wp=y- QʹĜ^YOX} JnR442=OQ"doGfJ{9q^ aŊC*^n: 9_ǘ [-D8./M = ~|8)~V3' &Skw3R!)ÜKn x*ɛ.7ͱ b wAS zͭ\  #x=WTQ)?Wex szu[h}衘 ̤U]5&zDâGYW-^ j[]7%Y&N&JX]Gk|3v\J{D -|w@̈iS-1U--T:7h(Л/zA#i'A,ۗу~xIUMv)hQr;z@/cdB&CB{$5(aFgq%QWJ{>$)a-G5 =ށk长J{o亝HZxz[sICWrڿ) w\k>| jhBykfeGˢjN% Wj[r{-H=V;رq#l_m 9G6Lp*BuͰ4Vg?^zh酒 PrDKƏup@in=Pr']}n&C(_K{@KDjMFDe *u4>o-zj==ztjuţo\7$AKÌPj5DOFy*^-ï*cA@۹Xy AL X- OsJϩٌ^rԚUZz]&È}0h~= !;*aDԏCWT*s ݨ&U!: ~;e(ozmVR@U%0pxV<܂#}ݶ8zyV<Rz_"#IȨ; x 0ɤd%ϝVz)Nj3Z= =5uK߱(bAXѓd\a* zX{J?9D)VOv+9)Cob^k$b0^w9cIs?m`&KPiBǮU?:j!2`UPL_1M j ޱ ]F\@KIm3nwx3s]s`>znۼo)G= RPaq<o ^Fy=dك~QV^|FL^&_AGN ڃ^ ,5Ƃs*Q1DWWCrӒ쟄g?F1Gn?krLp+R^WIp^/0&1p3+ dڇ\2幝tq U˦~N/[A53KP-G:]ozr(zF1_l=%cSז# }hKۼ୲7qB+#쌷@キ{:UEX|)c%1F5/Yo XVή子Ӡ ds2ɤK驆&U/}S. mtDӁo`N0wO()OSSe}zjG4͛,Riѳ}6.\^HPtՅ=IշDōtӣ1餋u3DZ SGOMjtưmGd㒏Fne~~|n:_QI58?t3cRO65wU֗o#"TV/Wo}[*G#sުTڪl+?)ܧYs)umՆHo%]v\H D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ D@$ DהS~_Ӝ^I+k`‡]ߊ\~4r:OXf/yoŒ?Ϧ0F͂ȋYlZ7'(]٫lwͶ|&=X8?E98ŴiNoXa8I#GgqC^^n醙?)#穧3;lf4դ =jc*vFnAe-L&fb菜/ρ^1웕҅Aa9-ƾʵb>VO"@x)ϥ–1OezeQ{cڊpX]EYl/>gR4+-jorc%szR Y]38 GvqIoyֹn,V*՜e>ќl!]D=]l: J򌇓UqofDkr[\۸^VĽp%9 s!| Y5lFs"1!þ7-vܯYz+=fQ#%K?~nФ6-zc[ʘ:U"w۔֫FMhiy'9TNֻ>{㟽?mb=T;ه+XE|[>#K.Vi=sFZnimSL`Ӕ, GyR<8ֻ!Fc e_CPpF=Y*zA֮:D"zdy4mZ2~&g)WKՆ O5sOok{k+A%I&A|Ei z5x =ˤi>VYKOڌPa=G.CXsG정66Ľ)WNbBj[QƝJ襝)WIO&Sg(7DW|ZО;ĹO UE,EG guI{I8:o!Ug۲AǠ"13+1U|:l~͓B-iGz0 *0B[N۩~i/bJeIu:W;Prm!Aj⦚-b.<CҞZ`]=Jpt(Ash== G#Թ D[/wS|m@RC'$sPwGsd/={zYWBptB Mzz Uh-/vZŷTң1ƽXr}27wT1 ?rERqcWf=kPkʌCG/hd8kS5`JBkϺ@-fUrU׵ +{-=ZR(ifu!fir;Ȅ*Ou=b6STS,HY.=}I*Г|Վ^WktTׅNFtIFc,7.UJ=bCq!zZ?|BOf5%f qj0;UTrE/#1p@s.HZwga3z mܣ)U|)+5l*4+a8)Rr_dдjFǢW@:!,vv#vX@YBO:ݴyH\u%wa@'z]9iO5*9uk.θ<'w8PESE!QGRsn> X{ +R;z?niN'q/ y*cP׾yZ˒Go]i_6v ;{ {i#gzbV'5VIJ9 !qDJ %ϼ6͔\2Ժ=b~9m=nUK uIWí C}Y;.Ƙb~{"̝˿jYW"yƠ-)t!☄NOY \aӌ?eǓ۬8)="Ui26Cѣcj:?ۛ_n>9ષcmhp9aÏ3#ay f\^`2.[Eu@cYU ۖ\<|iKn'Neo=-,tHM|oS֗oohR^fkd[br;w0Dr9 C)y U;]Zdھ rU,R-gEDЛpf qYzBoej8=lʻ %OqcT2]fBS zz[ww.{b-~5#)"JMܦ.+x^$QA+o 6. Қ"rjyހ7 L{,U=Msz)}BhQ r <A<klFqE{5r4͏E֍,)}d8ܿyd;?.}ܴZY;L]J:'kmZW94!\n k~HЛ>]K"%9/;vfչ)yI궮H]Q#дo!(s W:kn6G* nMݘ"H "H "H "H "H "H "H "H "H "H "H "H "H "H "H X"kqyYSh7faqi'ZDpKZEl3^a=M,}bF)GCK0-Y>Υv YO+K[0PЪN%wm53ͮHlR[܌cصԞ "j;kJ/ڐL)CZmKړU:-qaX70Ϲ+Zc㯂-lڬ1ut_t{ϗ[͠wXr\]MkB%wfqϗ!3QrO,kT2גްlƽ^Wu5@/Bj f%wNςz%SrC%nˬY 9ǨVip e"uJnhR c~DHʱK7:a[gzj=0a 1_t+rI<Iy7 CJ<+ 733,A4>7zK:r0celfp׵I{kAp{9x7[8N{I28]8cQ;;8X1Aީ Vީq5Ӟ_Ak'G?C2AShΥTEDݓ^|uٻD*w-{jXrK Guں&0" zMZ""[W Lx&潕.pͳAoXt&]Z( ?~-.#H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "H "pQg|-y[W}^ o媽ݶ=7|a2 K|ЫIқڼBU?p3hL5&H/{E6ލ֜{g훟|@j>دP{ k%U{{z{&ʴފ;ēZ3?* *y{땁]D +x.Ŭ|&ggNszYz~~6ZrK3&;4Iw˭M@/u"ki1rfszN}a+sgכWo3zZR5'N.Eof(z9$A c!][z@Π[_*s8ce=5Z1 .*IPtW/v5ګ+Z,2/]:m&qS&ecܭ!LP6(ho <*(.0ЫW!-Wfw8RcDUr$EŶBo~\j;Yzx۳*Dg-^YGp/ 9ImGΛ:f?*Q凉zj'3GOo}jL+K~e)gbqfj/=S'\@qa⅀AQ(6 uxB|> La{ͧƄYg):#Q52K|᤾q3^6S?0sS_Z7yO8yfk24Ok%%ѿ4N{Ыoe2O\@ E󇖡zyhӣBpT>/*P{"z@Y \aتL'(X([p9OI zH`7*SZťGdQQ7uгQWB3JΡEo$?i@H S'^0~[ԝ^0BUfC1hRCU%dk)&~dSXn&< 37ʵ-2rj_*̞pSni3 ώl/=*pTR,T2l'/KZ -@o,JeÆRw6赲l('Rbθq;m rzRT~ H%+&*UmL86mqRŔ.*핞^ m_‡C"Z-Be338kӝԣzP?DX@ogKM9Aر@+HT]g2!.EΆ=b71ƒzC5mC›ԃ[z6=x^2mAHTLUNK9iZ.vyS"zd3"K@9Yi$.1S@ 8x!vLPNz/w=#\DNQKq˺U#zlkǴ7Eʏ%mO3s_ 9l[rL%WTEB#=*GiPux_ hϣݡ@<1S%\/۲szDث7s:гDg[zܲ%ByïDے{ʔ/DN{;z;eǛeiotq~~0h *[uDTB'Ng7:z.u`}7C{3z_O5P7=nf%w1)^yȈ%pA{T}̋^%%zpt[ ^YAf?У"9Q8Nc+v3_%! qoPp1V {*E{\c%7RV{U(zVP.$-d7=%严G^OГpEρ,=Z Jqu?a'Ruitƕң/zzS؎^JވFuJIjZܷPrxZz{k%vվq_s (uA"!H1¸/ c@yG߇q$;gx H<¸2f@`/Age_P)EVK]&&M޺e䱆Ѓwkr 6+֜yXi3$]nr1QkH/ҿQdOރY(qP~O#n'гG%}Z;^yxi ᅪW0ks ͡Uy0s盵 YŚ4XCdm=RoL {m E}I\FP`e;Az6't|{ Zt=͒ ase+ma%HV,Ɠw uK.m;f)+66ygt3;$^pVMh{&/-;l~SwPY?o>wMd]O"bC!;ڬ~m [`7aj%{ˤ~0?r3nx>=n| ݥwHoUhʖm]z\(c zr3+%<> zQsC{|lO-{}z˳@e%=َ9loϕ lwtuf7ˍ ʖ6j^cd9ܥ·_?Oߠ׌'V.&{lo}(=+A~O+{ͽ+6#[O|Ъu.W0"{]kWۣNSs^OX=g/vNRDdi@OFlӛ2 JW~6 {3u3G6^w{RFIi3[ ~u6mfEx _'Eh+?T#{NR4e8 @}@h=m!=DõЀt_T$R%T$H4>~dAOB$;BpImC D^gcQ Ȑ#jKhd=~X:q>*'8ՈZ(m%=zSAӥ փW͋Gr_I= Ы艗vY= 3giS 81/m.kyd1Ɉ85ΌԖ8tEE'BN>I3鹸̕c,%"C.e/3w(=LQ)b1KYa[cB"qNi%^ew-I, I? 3"D>45qwQ  ~"Kjz+zl]S*Єfg(_1{t:^4co[?5]FC~4-2^ַMF϶k؏l Kz޾CsCw?#+ ̭B:d'۽ z5e%Bi_D㥺s{g9nÀ^=V.W:Yz.{_õS2&a˸el (d&ݘmh[b죣f}<ˢ;d'Xy{0?XVٹ!=PTъ±pQ}#t{ȖM`>U4z/7&%o,=!ku;k5|ȾMpv^-|gD@tnlXΆzdQ6]Fǐu K^ie׆N.?mXc7#z8D+lQg5vBkrvNP{9B/^*7}q9׬ .Vqz- zA|ظ:*+٣ك#w."Нz)m%| Ùka[r4g433QJ;-[9n9cd[n ŋ!  & {ac@kKGjMhsK74< ;cv .g.ewٸzNigϝy +jz&?OݯI7e*J.HJ(ﺂ.>e|l}2n6?7w)U"{hQ}ag_3-h yKR%`߾ar,˙/qr⮥/ v3~+_^noJ '@|&ӂL|V⏸)o 4t /4_9%!qmu N3蕤޺fE,{ s ;D(NZ< =sM!ߩr-5%pnLУCD%xaS) k g-IR$t)G#GË +tC&HMDQp _67CIbMP6ލ慨ݐI//fh: %HՊU$46{ nso:.R Vh=rq҅-n[/{Bʼn_KO"{RJp@> |D6 pmyq[5T>Jq1"nF )x!\ gpC|-gFNQۗNOz %HY=';4[4=WBusę =3MZ^rwKHb8%0ſ$=Y*ᱻ'5z쥬O =q%K'{EҖ1zZ<]Rbrϥ勻) t5zztz Rݴ?3[J[c+l/gAꛞ |v=W\7QC/78+!MfmUcs A/id/Aq-֨y5=o| z0$2\ʉkhP"LߠWRYqHm啐`Q, M\Cp#{!q F[CKhR( ޣs1 Ez01iDA4 w9Hܰ%#rCk-˕K{_UrsK=+=W\{禴l^JB&yz,ApNYrmwse}kxGL-C i9aQű^3}ТgеKZ|ߣ' %F5X=$-=gVe!g԰;?󂫊$5De:235c;zζ^\ 7 T}bܰ(x3|QEl,W}$pGЍLwUz ] G=nbz(:8EX(q wԉ|I=D'MU-=@\Y̾2ֻ/fb0x5gi&xO1),E$!cƚJEO =DSi:/ DZs =q4.h2(E9or͍˗י:,7cSm(E0c;g e?ό5$Ы㕸xl]\7f2]F,Ysp_מ1G20R'DD}@O YjzCF=.r[?Ơ-$*ozH.H/Lv'nm1*x`2gY =x =f]hYlC,똸NDwD2Ȓ_=  Bێ"_ʒP:# w#|\]cD{,Cs?(i(`@. ACHxq f J7gL !-,( &>jӗݡGN~-SOŔ#D2~+o[;eY[^!S|}y^@ 唾`\q0|xoHJyENܻHxa=lzHw1gt~9K 5y`<+xv E#j̧!s^'@%&O u!G<2^tz_0bAhN4(@)JOl("q͡(ByFDH»čۨz%D&XFm Rc SֈNhĩ@ TUч 4rp^#փ J@ (%PJ@ (%PJ@ @VJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ Ll~<d wrRG*9pvB/(qO0B/Onlձ`;ϭ=[nZI3Zb=)k?SJD-=-鱑S ]#{B/K Bf5L`I/Qz'([4"{d}oMq3UR}9UN/ž_L{MSu8bUl!ޞWRV &lc'oٯ|Ipqʖz78`_qw=U8IbFG坯gYN\=JP_ ɮ*z$ {35zM_.m0FzX,HȕށX,f`gXUMmJ = =:xeVz[ w@ToZz>C0F >g 艏*\M!14h'{ V5 U+7JN(ѳ zmvREW"cd zY~(nfxj{\p%p{<)S^x{sNycۆ^+{pvJo<^znxl<狤s |.0ǽ @Go^6]V²x֣ 5ʨW ڴs]K/zO=ʓOdʘ۬tKw4@Y[7+^@tMC=z s[+ir-v~^)&fsKpy'x0""Wڈ'q={YQ;a #=[ ^Xkcq0##񗫲7n]=/~>߾4391]GAKUgc6RJk@9u;50?I0jTK]Zބr?:uQ-A@6!ai8/zJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%NEv<5چN]gr37'c*䜿s90FKPǎ4!ld`#^En0_pVyAϥ:58[G9NLk#ଟI8"^"mJ)O $g){CoA 0R 8$-)9߉Gbhp)' g̼?ռL+ygհFY_y̬2X) 7,g-=3_ܷk4"l=K $Ok#{Lr:a 8=t-:v !We(4fԣ9t|Q=9DU+E$nzu =mR =Yze2 ˠ ;cUs9tw.|\1K Y9_c0 DٖyġW$㿆ޝPBÊ^BE[y4]C"psS[Q=o_Qxy>Of$8=0岢F/RW$ r\${ws˨6 R:ࠗFzJk^G{OG! k`531e'zE(QzyZ˒+ֲ/%#5 8dje,zqK/fPS.\챰\$,J  Pm5 8T[G (0襬jtY瑰,(,ѐiX*bPSx1Ӝ}de^s!sѴަqT }wKL =FaBqfɧ{,!4\g#/v sVl=iX'5' zf?CܤϨX뵧^0)󶀼g:{!~3Kp<od`AXj-ӔkOQS jXpҫ5oKzMƀNOQS"}ցIp e܆S P]56]T#rҧ7JȓO\:S8'AUdrPJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ ( _ī.|+*>os旫h |:S;/<gM/|opgb=D`􋸤 kvF/8ۋIJ$D/;r?v%"y1w&|ݳNo'#?O?q&u&iݵeߜċ".w$-=lhN|myhj̫yi{ENW~ul =w{>/މ3Ejzq Ϊ읈2s=كXY dz.@^W7Vь%jV"+d17޲l'`1N%Π5L >`耞ӳ'W| S[- ɜ@WD^ZN=Mɗz=bQz . dN}) \d/:b4RB UwvtX_LӝaʥZ/"=Wޮ=2mC c^Lm#-BZc:A#VU:1inylΊybsssЖ9cliwCSFS}eg^ז,)|\_~|:jlqݩ5 g*3Z/|d=Ec':=:s8`=gUsl %PX4@iUZUJ@ (%PJ@ (%PJ@ (%PJ@ (%zӷ4ɧ+󍙙]|;7uѽM/K]U+:*s`8 &sWՊQ'!ώF<+ @ӌSwzl}U"?p~:䞟ԏڹQ=E$H=<з.BO Ы*Onu͓.BO7OËT|]5X.",zFpxEn'.5qފZ# sZ6 wքMsO_b kQ'; - [_[ k&;]@|{-[i 3\u?QsM2qhأм=fvrc~!g@L4a9wx/(o;Z-|clHD9viuW=dp]ҫ_JX?|o_N|p\o{5=zʂ')s˫eiL9ՙc*\L/IpL<VA{K5}*A"E_B } c5LU~W޿LChqk%1hc(VX"k)m|4dmz,WWY~ v&2Ο ObMrP8(rhEixMRkMN^%6mzVxo'^f%n$цEw 1UDl 0ڥ*僤)&pMǝl1UbaY"hNѓN^ٙM$,Ti$kEhǓZ1lSg5r5QRBC|q}Qt8 />a f[+f>'Sk8,VtGˢ^ެT7BNRFCk@/۔+v3ۯ<}1AiXš0џVWzCqهYif+sois[j&†!z{AŪpGg-=L}lRyzef",[{ۚ3L'a eIOVڴiX 4yg06u:kw8*q?Kz 1LEQ9?VdYIӿhnU'dzN{A+[zFV8UCԿTljM7W԰"hR켵DKXػ=5i{ݗgޢ;m hd2WG2H_E762u$uze+t&<6BgG9#\%zp@ C$BO34J[\߰0"{@UzĕPmҫ@)DL3o6cz="Ybk!n?4sVȦ i<.Y(NA2S;~ʓ#cjKzO2̢ ih lZ -?ǧUt+ͻ  bL-b=wԀ`r{T?ҫB'-!nZ+[Uϗ6^inRHCK`luDV=o PZ?m+f#X@oE?%KK4+HzG"Ã<L6"{[85kj+[kKzkT;Z"Ke1Yt6z_b}J9q+ҀR^qE|{ickzba]{t:!Yz]պH椒V:^luzھB~\ޱVɴQ׼{R[κ2P3̙B<гPWx ۾r EvP͵sM^a[8{ꌝ uEظnm@\'N"wGkvQZiSID#?=d^yf,C,9󳾆^PzZzpRoޑȞNm4ƚ+rJoˡxOvG@`Q& PF7>jsw'C>*L߿3+c̫d+8[Z43y?&SsMū*&%+ˤ{t?zI4/s/<1~K3/{SRa9~Wa۸lU=r-u ՗jm 7uR>zB)G%:R-?cQ|_%.7^;Boqg1̬Mћ͜yß?P"zG{~/r0w>h@xz6bt= J/Ņ'uP&B/M={%t^e?S֮Coaz>j3^u&O:.0Y lЃf ^ A1$hL/jnҦZca-;+c2YMX$ޱeށ{ Bt=wa! J/n 艵Y"=bAy$>:nle/Ӧ{n9[2j=1^4̣Kt^?=d͋|֧_g =&=^Z=|#Jϭ⢙BuRHT2ݫzjXba7X,9֦#˨>2;Ef6c~9:QWRhL}텞p^r[/9ss+6w=HX2sэco~C; ^CkW.|=B32qiU-SXV`X%^\M;i?dYÙ?͆^~;*H*VH-}.],xz]F ^űyvU|*=Rz<P{ӗA9Ds#ej ,B^d8Wl^+{S;6FW; m _3{xv,=Y,tH ezJ[ab>dEޤ2qq> zI^f۬~t#n+UM(6Ɨ/ldŶ롈M})k6JN}V6 #`L ؑFz.8z#':.Җxz=W͆efj_Ѵ!S֎r"X=3P'æccƆae|TrWAZ =4i7ve-c4z02KFÃ=ʞCh˸XyMJOgϓSOXѓ^;sdꌶdzS|"zγeTvٲ k#d?u [\#e =X7"{y2%=Y(=U{KV=zv^=ʓOy$ 7em,3e鲀l`.qDܚjN5WI=հUܮh[27 )&PƳ?WE\=867M;@ps?agk:mF7!9IS7Zk3Wxa5z㛐{ gc~sq8N8.kW{BONڋ ;"zt2V*TzUm#Pc\&n+^ֿY|&n.M\j8Ԍ_;'j"'c[ekڱKE~zl9#P%SxcpNLm 61WO,6 Ȼ,jl9MJgk)ek8QzgV\8cDZqwF`kuˤ5g(3[/nף;w&\:9ST נ9ST נ9ST נ9ST נ9SٙJkuJobJlKG5v&*{g5(7r;Aa7r;Aa7r;Aa7r{g5(7r;Aa7r;Aa7r;Aa7r;Aa@U;AᗃFB@靅ְ9K\靅ְ9K\靅ְv>5]s~Ksi>KwI5EƫTֺJ@ (%PJ@ (%PJ@ (%pj$ɯ_^zH^8AWz="ГPrÔ 3 nةLVznoz)Uz^}<57 Qt_f>-" l .px Ү}n\w~md؆,^N!{E:(4ஆ E"{7] n>.| WM;~TzIH &= ܲLc~P&opdV3_F-GS,xkyFrltȔ#\:\:;7?ѵ􊨓@/m1ID5F0H7z.=_Qy;FS.W>gad6+'z/Ы c/( cN]y:}+M,5Ju 0c^ݭ/Dlu k˿ŧ`'ԼCz.(>{3wLݝsKr Z#7s2@Uz{bsG ]50>Ickq̘}@pI^m' TM'S}ީJ]PKk*Ѳ"6/=W$2-Hmfjf);*?A͞5s RJ5h~=W|Az湩Km=g}.%KzL@kWm +TsK^襥+RLh{jz}̹Kz.|Ws]doI矀]. jTa Ɋk,=s娎J/BF _(\KݧNe[y0}:g/eTlb Ky_긾<gͿZ̃z8LTYq칔ݝP^u^#yAEz.'{vH*KW;E'EX޵vt׮kz'{[69l5nvu5sπ|eϧr?|wDN?тЩH >?˟{ڿ?_oӋ՟v/3PrkG5V)  ժ?S '_C2y T֪+{ϥ.v>K*ZwJdr\]i.pS*kU.@ O.PyU U|]V."&i|_ӭ[Nmy"u"J{ړLPJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%PJ@ (%/T endstream endobj 35 0 obj 69266 endobj 37 0 obj << /Length 38 0 R /Filter /FlateDecode >> stream x+TT(T0B3C#sK#Tp< H$anag`dY($*{)u endstream endobj 38 0 obj 62 endobj 36 0 obj << /Type /Page /Parent 3 0 R /Resources 39 0 R /Contents 37 0 R /MediaBox [0 0 612 792] >> endobj 39 0 obj << /ProcSet [ /PDF /ImageB /ImageC /ImageI ] /XObject << /Im6 40 0 R >> >> endobj 40 0 obj << /Length 41 0 R /Type /XObject /Subtype /Image /Width 2544 /Height 3280 /ColorSpace 9 0 R /BitsPerComponent 1 /Filter /FlateDecode >> stream xϏ$G(&1KLeSZ^]|1"َ-Am/2(|;`v llZ\#h|3z{f'3]UϬ']A{ۧ_9}w]\؄_.|ꇿ}៽88w&XAl~'o:|W8?|+;%r|o74ƒ0\LO]E?p>` K\ >oЧr>ZZ 0gͿ*|~J>o>UAzU?}_LRoW/_Lvi@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@wDໍvc͋|݆^mdjckzFFμ>7_}D8^U^Y>mc ]-ѵ^giaRŸ+zK\ gzvt@W^7R^XvVu5mz +k.XʿkXW+ZoH.qq*:6F, z 襷H6N{^WsaYTq;p?Ý洭+\\{:ӫm?}1OyPIϙ߱?kzWe\Mha_X7 kCDz|[ޢ zjᤧJC5mRcjGiц257^@zᨆ+JT />^#Py,Bڽvks?x i\/({mp4vqIbi ӫ |x 6i'={:Rgz ck7}cL/OzC$R zCm|K-zi}%Vs5jjgSz!]-`zZ X)ǚjJ}Mt@zoaV\-z') eŀcl2agE!&-<^9O{w_Ո%z=D:4!S-"vMpti՟Yo-лFY'iOzGY?;ߓ##5ϵg=~1/\S]MIUۖI}?/YO\5%ꇶG'_fv| @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[hTnNH+/lOmݥΞ*odoYR"Q2=$tϻMk>IRR.r7۵srNҋ~عNBvLT/}Wˇ& kRtw2 woQ/ ]{W^؛fMϥ?Y )wCs}j⊚;Q/;W6g]jPbVkLa֗NMY=qfHW~{jnPP'<M^,o͒VwzYl7.2:}MZs;U3u6g)J6ЋtlRѷ+WE7ҋtze\>NU + Bܯ^QImZ[4agyjw0k= _)֥׆E_y5M{ٷԛoy7yT=yV{,~۽٣Joizr"$nS_eǚ{~bo抽r镝W^N4iaW!EVR{PERHz=EeѐW+z@׎7Y0T>WǤ4JN[Şw.˦{RyV޾G|֋zGӸUUcRE_)vL8RCS_hOiqN6<0+򠗿zӌ:tG<=Whn9سQsz:S5sY+9\CQV*cOan D#j1O?Χ9bP|9rE^jk L7ҋkeazTU_ڣ^^Uw0Ӭͪ'*sy%h&4T^*)p<*I?۽>f>)8N]^lxR6$Z_d4>یi:GG=y|3}3WV b:%+ :kw3](OfoW&                                               va^-ЗsN+RuB/k]~z3פf+_wY3yg9.L/r]ޯM5gT^]])`6czro&T2]ީڏ &6,)ߗ^N]hEyy]H<&$5w] z/E?n-,5dlFX.uB~1,/lMmZ~{[<'vXq[թ۠3O0eVUϒ-١qzLA˿7ߧʐmԩtd5A7k[K<+W$tYL}a46)-*ݟPw2ҕ+4{ҋ^ƍzS-%X婞2z2AvW~iޔM Ijx^c&LfhhKCO{(ںx};-58ʛ/hiLC/Z]S/,ʝwٖ4.]y;0nЈ7_)^R+?SL{G]痃@gkE+fwoa 89@;6XCH].?kk:g@pzU#{Z ĩ]}K";eZi=L{.::>'ԣKOl4;k4Tbr@~{kԡ)5VKg'jboԓ0S{ ={y[P=)ꛠ?0S;XsbOmEuQzj7}V iM@wHc[ jVK[)NI#͉>*ӽrƽŞuӼ"~3e4$=a#0u(!ؚb)GEaRgz,XG2_fҫTɬfݓ̴Ԍ:l{Sk g0ʹԄ襧;ֳ;7X)4'+.(+=  ;yj\jfǀ4CkjLOH/^FvܼRUuPW8Wz3=9繪OwٵN39=nWLO]'hسP?kg$~R;-6}y׸obԏİ>S_M/RMfe;JO ՟tyঞ:]Qo]ʋ9pqk2;K6RQe[q}-Kۓ^Vz.{jg_*nw{Yo>nϔ^,KzmP`z^NPmpmhr'qثlm^[NTzE2W)rKm.^ '[/j~5YjˤSq_nbw'0ĥT)azJ>RJۍP9ѩy|w?oи`z>9,)LvzTIPc J/Xf"cEΒں ?J˶*u,Uxom9?52eCS2$iRm9wE:z>oòˣ[}TF_lF7Wtqc}SV]x\r1蹠rnn]k9G#Ss1FbS35>+z=Lح^^yԇ4"M1IOѦ:bbilT;Mkdԟ򵾝ax &Uӭ^jgNz[gus:L),iֻAuwEKN*[z~Lsվk! Ea]啦϶)tw:^U,K{R]5%e9X\{'lam63>۵kr'6^B[C-Wzص\(V+UilDj eg'0ӻXsٙYԝۂ+Z@s4=$=:oz dz'o6ae2n++U^~^c[(.vg5WzZ~ջAoVzVæ\&D=Vz xX z}>M=5DaFfSNd}ngz;^%PV]{ZqX^bS4h#ӫtEVGs!mӹ/Ycb~dfjjJՌz!+'J5֝RKvuG>r.oyes&[z<^ s tژ%z[Rꖲ^ i^*ih}Z4nE0EgwR;2ˤWz]OzX#t[yS޵Zeؓg©ejdwy[].;"3bR驐jiy%~׊ޟz\3읾w\ŞÞi&H Z((õdK-E?xmiGz_zۘVץw 6?u[ ؂.]F]{XKt\5HmL%y6;                                            \)+qJR^|]UZ_ՎޅK~MYGA}6Tz0q\B>nzz lb׆jLz恭h揺S;p+YWzL5ws;ԝiNw}+*Lo~w{s[^R]ޕNO:ɾЗ_םtlrCOܕۮBo׳t,v+R(ˮboг?˪쳿q_w/^&ܕW֝9NkL8}+[7]^Cz]ej.k&ởl|EP|TR}:tb/ ;Uef+LK1|nVM޸,jV=պƞLӼm}軼 SD_Je+FG7)c'loj|DO'^zQs,'}Fل7u ZϷEd:ϻMOT[?-cYObk! V~4d`zAzuy߄Z5Ym3@J{0miAhzQջhC@AބX F˴WHSun}b݆Ky|Q/zzC@zFz0ڲsG[WH:uI%k}(EYEo9zaŨw.)Y?ԩȖVzZԋ+V5WC0&7 KA:u2bOz sSus2ӻIE>W^SäyǨWo:v/=z=z1kɨ{"}i_Pvbz'-GXSN+AOcfjiq{VsU\OZK4kn@IT[5w"5wiZ1ƞfAFH)il N9co]sըۛ\o8knSC^;M֛a{a}B#ݻAQد964SMP"x삞j5ϽxwhwJ##ePT۽Zz < (kBchc^WX5mSؓ >Wz'khNmras5ϕt7-Egz4cU,ٶ9KS]iFleЋ4[ Jؤ>7ԊSub0fLmOҘ^TQ5P M[·ߎUnBeЩW5)- OuXjH/npg$-RKzaaSM1]bHeA)f^ S['-Ҕ BU>L-5NJ-TW~Eat~>k2:gM\+~n@X4C!<;j}/5WAs{"izQ\̅:zS\ámlhz C=rZi-S?==(Y*ҏr>ͺs9~>wzUreQS6C(Şp.hzE\/1^anW|}}.4?s>\݇ \ÇIfn}L]}'zGJWoBz~Ւ߂z*/k-л61~/2T-~=Q;W5WC'֮K'Z–o4{ٹ^yJMZH:u9yysTBJ-?n2 ~Ks?DSwK7q7KzSں('W-]pY8^]wΆF @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@MعW^X_헯֫pzש)@,6~sKuܬ)aw=;8~cu*5~{j'io& ںɦR,4^,[ÔN(vY4e+zrK|z;kB=ƿ'za{t;z,w}؎)ޅ.TRcO0t}zEW셡U8F^pCXoM{ϥ 4ѥ$㤎7XĝhЋ}. zUڷnֻRŸf#ڧ^wH# Y>%Úb]Bq ]3Wzq֥QtNF"Y]Jv_l}bXzڽbK]Q''4kvCy,5yӛsP8Scju^9ŲIAOڃhW㖨#-|QERO4`}j/g^꣚ՄϟwQz)?ޕݣNjjR_=+vW\KqwS*!SËY˪/U@ߢγ}\X4YMZyk7j؃uJp}"o}Z#k~XiOz?D2-wKn<:nгdߧחͣdF힤[ߏz*\MHl2^R ^_Ci9ZzsMD 5ךݤnIX칃@qtߢס%}{YbkW95;*, zGͤqnzGҫӏ`w)z pŞPjŞFH{|9_ zIzz \KoUt7iSFB]쑚Qog'I.}hvf𚧍z7,vzL/ z\oW'zr3c8klCz2ޡ=ykIc5Hz;;n.MrƦIjF(Us8bѺ1IЭQJƚ[,ZvUVs2%nWz=^dzcg^Nkv7^CA =[ M/İHYW0ثӋXT/h-l5zO)\SjqJOnkߴk}%jnc݇:qRkl+s؍o^D6̣?bIϛ[jx7Fz\&>-Pp~ak+kӺW6z  4,Iʊu6pE E:XsEC֖GjZX['4ez+T{]\Q;M{췐Y>wO\ָ\vTVGɎz&o{K߷+L36:U]$ff?ْX&G;cIZ]mvvlLYRgW m\=[?ݰ|O[j[Bz+˫R.g}8'tִe/XR:#K`=B@3f`\lUoH]XYxt@_ŕzOcNBӎ|ja]~3l {3ivh]Kz_ ro>t;lBVomMtLyFx}X-lU                                            pY+Raso֕U:6wnc"zWxDzsqU|YWqeޭ_NxK0^j;u)(;˦.0lVvw4dγ/}~վ3K¾gAx [n/вk{:lkq|cL`qAL_OwZFvA9]9ֿI',.3{^R$+۽m;%Kr~RzwoZWv *>E7KΥ6*ԟdzm){WTT6R#MuJ6ޠoseQ=)qNz3Wzl]Ȩn(KuC0wZu 2;=W}]]y,W5okgwiq޺:ŷ\v&^8O3/XͻN>o}]іzݣ"ch;y2aV }nz feηe)Kn}ɔ}1^+Є,AC-rNqw[^kw A/FKyo}mX$U:5bS`zym[l&>DTd:j zQ+ӔTz!7vwK)G90my[kHXK-rrߩټԁk9OCj;yK`'Yc+z ժԖ eS祩Hc}^1=VsՃZV=A='2=j}Eibן{{X_VB8QJ顐Yի.VG<0M]==25.l֜|i]ExUQ KS֍?[] ]y?Wu5.3=WYz_vo b)֮^Z馾^_/6J\-Pm8ϵ/lzZ'MR[Ițb0k]Q؋-Zm|Ӝi,rjn-ϵ.ЎzS~ۖVg+K%ԀvH7*&U=*Xaɖ[zQer* ʏuO/?^y銚^oXsp^g=dUѪDһq%-ĵ[ߦIROCuH"~0nG@rgK,տs֫Bzso?Yr}mO1FfN]~ҫͲory2Nz'lzu/t'lUoLg^@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@8~靺rc1.W۔gMƛXmlz7;Lζ׵yzGSffz]P^dݕpNvwS}w]C.8Oϳuv{Ôzt|>C.CL<ӿi,5E ^8jUX^HoZZ^ Go~t|TuzGzO !T^l|jӥ*MP6E&!aPw4O\BvNFqP0=Զ({\ysi.nR'8dwhz.diu+y=)E%=gjl]~I /lOChr-Gw}6e\gTNW lq&7:w|J/t+kݣ{fw.OULmgi54~ މuaukR~[-Of]jqY W[5zݿozJ[ouLqEMM}窵Au}$ZsJs^PIo=}ZђXjp_hCK/G&lZS;Wnju_fujNֹ[{1iVG^ٺ~ntyr=m{RW =V:Z~};r}`үggzW&VwșV\.ËBL?g+N_                                                                                                                                                                                                           G}x CQ@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@`?݂ endstream endobj 41 0 obj 17308 endobj 3 0 obj << /Type /Pages /MediaBox [0 0 612 792] /Count 6 /Kids [ 2 0 R 12 0 R 18 0 R 24 0 R 30 0 R 36 0 R ] >> endobj 42 0 obj << /Type /Catalog /Pages 3 0 R >> endobj 1 0 obj << /Producer (Mac OS X 10.5.6 Quartz PDFContext) /CreationDate (D:20090318020526Z00'00') /ModDate (D:20090318020526Z00'00') >> endobj xref 0 43 0000000000 65535 f 0000430774 00000 n 0000000176 00000 n 0000430606 00000 n 0000000022 00000 n 0000000158 00000 n 0000000280 00000 n 0000000369 00000 n 0000084504 00000 n 0000085353 00000 n 0000084525 00000 n 0000085333 00000 n 0000085546 00000 n 0000085389 00000 n 0000085527 00000 n 0000085653 00000 n 0000085744 00000 n 0000161981 00000 n 0000162160 00000 n 0000162003 00000 n 0000162141 00000 n 0000162267 00000 n 0000162358 00000 n 0000252667 00000 n 0000252846 00000 n 0000252689 00000 n 0000252827 00000 n 0000252953 00000 n 0000253044 00000 n 0000342916 00000 n 0000343095 00000 n 0000342938 00000 n 0000343076 00000 n 0000343202 00000 n 0000343293 00000 n 0000412729 00000 n 0000412908 00000 n 0000412751 00000 n 0000412889 00000 n 0000413015 00000 n 0000413106 00000 n 0000430584 00000 n 0000430724 00000 n trailer << /Size 43 /Root 42 0 R /Info 1 0 R /ID [ <744318228ddce7041f885e61f7a649d2> <744318228ddce7041f885e61f7a649d2> ] >> startxref 430916 %%EOF jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/ccla-simulalabs.txt000066400000000000000000000167641261716203600225430ustar00rootroot00000000000000Jetty Project Corporate Contributor License Agreement V1.1 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. This version of the Agreement allows an entity (the "Corporation") to submit Contributions to Mort Bay, to authorize Contributions submitted by its designated employees to Mort Bay, and to grant copyright and patent licenses thereto. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at LICENSES/ccla-CORPORATE-NAME.txt. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise send a signed Agreement to MortBay. Each developer covered by this agreement should have their name appended the Schedule A and the copy commited to LICENSES/ccla-CORPORATE-NAME.txt using their authenticated codehaus ssh login. If possible, digitally sign the committed file, otherwise send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Corporation name: Simula Labs, Inc. Mailing Address: 4676 Admiralty Way, Suite 520 Marina Del Rey, CA 90292 Point of Contact: Full name: Gordon King E-Mail: gordon.king@simulalabs.com Fax: +1 800 822 0471 You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) were submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. It is your responsibility to notify MortBay when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with MortBay. Date: Signature: Name: Gordon King Positions: Chief Operational Officer Schedule A Name Date added Simone Bordet 12 September 2006 ______________________________________ ________________ ______________________________________ ________________ jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/ccla-template.txt000066400000000000000000000176511261716203600222160ustar00rootroot00000000000000Jetty Project Corporate Contributor License Agreement V1.1 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. This version of the Agreement allows an entity (the "Corporation") to submit Contributions to Mort Bay, to authorize Contributions submitted by its designated employees to Mort Bay, and to grant copyright and patent licenses thereto. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at LICENSES/ccla-CORPORATE-NAME.txt. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise send a signed Agreement to MortBay. Each developer covered by this agreement should have their name appended the Schedule A and the copy commited to LICENSES/ccla-CORPORATE-NAME.txt using their authenticated codehaus ssh login. If possible, digitally sign the committed file, otherwise send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Corporation name: Mailing Address: Point of Contact: Full name: E-Mail: Fax: You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) were submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. It is your responsibility to notify MortBay when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with MortBay. Date: Signature: Name: Positions: Schedule A Name Date added ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ ______________________________________ ________________ jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-djencks.txt000066400000000000000000000156311261716203600216550ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: David Jencks E-Mail: david_jencks@yahoo.com Mailing Address: 2215 SE 39th Ave, Portland OR 97214 USA You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: 27 June 2008 Please sign: David Jencks -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.7 (Darwin) iD8DBQFIZT2ToF6+5lbz4BsRAs3wAJ9puXC26Nr8nhFvTZ9oNwxDFV/DVACgnC8O VFUWPZrfLOJesKa0/rYNJlM= =jC7I -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-gregw.txt000066400000000000000000000156051261716203600213500ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Gregory John Wilkins E-Mail: gregw@eclipse.com Mailing Address: 62 Church St. Balmain, NSW 2041, Australia You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (GNU/Linux) iD8DBQFEEaStXR9WPTAwnLARAjsNAJ4jBB6wCEqucFljGge7yrAMSrFv/gCgoMC+ 5hdry6ZjXRcUhQEyNz2F/T4= =I4Co -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-janb.txt000066400000000000000000000155361261716203600211520ustar00rootroot00000000000000Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Jan Bartel E-Mail: janb@eclipse.com Mailing Address: 62 Church St Balmain NSW 2041 Australia You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: 10 March 2006 Please sign: PGP -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (GNU/Linux) iD8DBQFDjMI6J97Uv2IW248RAmGNAJ9/krpkiYJRrJTMXVkL3cdnVvfU+QCfYFEh pN0h9U/xdFTRMFsXYFHQeN4= =24Hd -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-jesse.txt000066400000000000000000000156251261716203600213500ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Jesse C. McConnell E-Mail: jmcconnell@apache.org Mailing Address: 7717 S 167th Street, Omaha, Ne. 68136 You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: December 19 2007 Please sign: GPG -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFHacMO+jg6q+ULyBMRAky4AJ9CdNKsmg8n2aFcpQAvcEPXxEjIJACgrvjM C/W/GuQFfCJJykkL2jd9/Ag= =ufUh -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-jfarcand.txt000066400000000000000000000156641261716203600220120ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Jeanfrancois Arcand E-Mail: jfarcand@apache.org Mailing Address: 1800 McGill College Avenue, Suite 800, H3A 3J6 Montreal, Quebec (Canada) You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: /28/08/06 Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.0.7 (GNU/Linux) iD8DBQFE85cgaq9Frj/CIrIRAmuJAKCFgi4W0UOH8IUn+SV6PBHRF3BnLgCcDqqC Zokttk0bTHfwaa5TtxQbScw= =N/w/ -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-jstrachan.txt000066400000000000000000000156751261716203600222210ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: James Strachan E-Mail: jstrachan@apache.org Mailing Address: 1A Leigh Road, London, UK, N5 1ST You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the public benefit or inconsistent with its nonprofit status and bylaws in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: January 30th 2006 Please sign: GPG -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFD37/4dL6IZr4c+6kRAtsIAJ41tfd3lj4OM6sIMfJfTOdYdT1bxwCdGgWv 8sfMxEDZquIqhVbfZU2c76U= =8WW7 -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-jules.txt000066400000000000000000000156731261716203600213640ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Julian Anthony Fox Gosnell E-Mail: jules@coredevelopers.net Mailing Address: 2, Tannery Cottages, Tannery Lane, Bramley, Surrey, GU5 0AB, UK. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: 10th March 2006 Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.2.1 (GNU/Linux) iD8DBQFEEbh9SoT4b97cQk4RAnCMAKCuNGYLHa6n/Ot3GEdwCCLeQxsMPACdEhnE I/stizRWWZZkeLbcglzdQCE= =piHm -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-ngonzalez.txt000066400000000000000000000156411261716203600222440ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Nicanor Gonzalez E-Mail: ngonzalez@exist.com Mailing Address: 37 TwinHill St., New Manila Rolling Hills, Q.C., Philippines You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: July 14, 2006 Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.4 (MingW32) iD8DBQFEt1ZxHR/ESK2w6H8RApbOAJ9c1eooNr2oN59WZVitJExGJjUvKgCfaKji 6etDJ6AUj0jTuSl59hUsWMQ= =HmqH -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-sbordet.txt000066400000000000000000000155341261716203600217000ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Simone Bordet E-Mail: simone.bordet@gmail.com Mailing Address: You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: 8 January 2007 Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.3 (GNU/Linux) iD8DBQFFoniQJVhlFus9dGQRAmJmAJwL5y1loonhVQIICsparvjHMQuwqwCgiZFy LBDVaad1bJ1v1EHY901kPcg= =6rqm -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-tbecker.txt000066400000000000000000000162321261716203600216510ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Thomas Becker E-Mail: thomas.becker00@googlemail.com Mailing Address: You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: 2012-07-17 Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iQEcBAEBAgAGBQJQBb4tAAoJEMHhjBmtgF91HDcH/2nQDPuPztWFrBifnEoLF6Jl RUkfJzAPZaLDtDMfiDz7ucdRL1RDodmz4VIF2+fbKeBYQquZXfXIeEghz+tKriK3 0M12guFkNLDteQp9h2p3Zu9JU3K0y4m84IDWq72HRmh1nRyD6lzZFhDGZ/D+69fF tgYG0FwEit00MAq/lRbsXHLpBOY+Jyh/Xy+QRnQTcAQ+tAgOlxds3w+JSs2sGdes YLAJQQacLeGh7EzD3F+CKuiwT4c5ub64LdXSlAVj1u2OjZBfqLaJ3FA60Ti+I3kn FNWKpzaeX+SQgMak6hsuatXi6EsVk6sIaskwEgl6+Xk+HYWy23ZQ8BKQRLKOZTw= =gAqN -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-template.txt000066400000000000000000000151131261716203600220420ustar00rootroot00000000000000Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: E-Mail: Mailing Address: You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: Please sign: jetty-9.2.14.v20151106/LICENSE-CONTRIBUTOR/cla-tvernum.txt000066400000000000000000000156161261716203600217370ustar00rootroot00000000000000-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Jetty Project Contributor License Agreement V1.0 based on http://www.apache.org/licenses/ Thank you for your interest in the Jetty project by Mort Bay Consulting Pty. Ltd. Australia ("MortBay"). In order to clarify the intellectual property license granted with Contributions from any person or entity, MortBay must have a Contributor License Agreement ("CLA") that has been signed by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of MortBay and its users; it does not change your rights to use your own Contributions for any other purpose. If you have not already done so, please complete this agreement and commit it to the Jetty repository at svn+ssh://svn.jetty.codehaus.org/home/projects/jetty/scm/jetty at legal/cla-USERNAME.txt using your authenticated codehaus ssh login. If you do not have commit privilege to the repository, please email the file to eclipse@eclipse.com. If possible, digitally sign the committed file, otherwise also send a signed Agreement to MortBay. Please read this document carefully before signing and keep a copy for your records. Full name: Timothy Philip Vernum E-Mail: tim@adjective.org Mailing Address: 7/9-11 Cook St, Sutherland, NSW 2232, Australia You accept and agree to the following terms and conditions for Your present and future Contributions submitted to MortBay. In return, MortBay shall not use Your Contributions in a way that is contrary to the software license in effect at the time of the Contribution. Except for the license granted herein to MortBay and recipients of software distributed by MortBay, You reserve all right, title, and interest in and to Your Contributions. 1. Definitions. "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with MortBay. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to MortBay for inclusion in, or documentation of, any of the products owned or managed by MortBay (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to MortBay or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, MortBay for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to MortBay and to recipients of software distributed by MortBay a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to MortBay, or that your employer has executed a separate Corporate CLA with MortBay. 5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. 6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. 7. Should You wish to submit work that is not Your original creation, You may submit it to MortBay separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". 8. You agree to notify MortBay of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. Date: Please sign: -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.1 (Darwin) iD8DBQFEnjfA9nwdoZUd8/ERArwdAJ4lzyXEi4zSlIiJwEAxknGPhzMRswCfRsdI RUIoI0BYmYpaETSqxt2oLFU= =Tr57 -----END PGP SIGNATURE----- jetty-9.2.14.v20151106/LICENSE-eplv10-aslv20.html000066400000000000000000000724741261716203600202360ustar00rootroot00000000000000 Eclipse Public License - Version 1.0 / Apache License - Version 2.0

Eclipse Public License - v 1.0

THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.

1. DEFINITIONS

"Contribution" means:

a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:

i) changes to the Program, and

ii) additions to the Program;

where such changes and/or additions to the Program originate from and are distributed by that particular Contributor. A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include additions to the Program which: (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.

"Contributor" means any person or entity that distributes the Program.

"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.

"Program" means the Contributions distributed in accordance with this Agreement.

"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.

2. GRANT OF RIGHTS

a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.

b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder.

c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.

d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement.

3. REQUIREMENTS

A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:

a) it complies with the terms and conditions of this Agreement; and

b) its license agreement:

i) effectively disclaims on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose;

ii) effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits;

iii) states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and

iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.

When the Program is made available in source code form:

a) it must be made available under this Agreement; and

b) a copy of this Agreement must be included with each copy of the Program.

Contributors may not remove or alter any copyright notices contained within the Program.

Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.

4. COMMERCIAL DISTRIBUTION

Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense.

For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.

5. NO WARRANTY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement , including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations.

6. DISCLAIMER OF LIABILITY

EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

7. GENERAL

If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.

If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed.

All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.

Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved.

This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose. Each party waives its rights to a jury trial in any resulting litigation.

 

Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.

"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.

"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.

"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.

"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.

"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.

"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).

"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.

"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."

"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.

2. Grant of Copyright License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.

3. Grant of Patent License.

Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.

4. Redistribution.

You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:

  • (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and

  • (b) You must cause any modified files to carry prominent notices stating that You changed the files; and

  • (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and

  • (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.

You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.

5. Submission of Contributions.

Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.

6. Trademarks.

This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.

7. Disclaimer of Warranty.

Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.

8. Limitation of Liability.

In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.

9. Accepting Warranty or Additional Liability.

While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

jetty-9.2.14.v20151106/NOTICE.txt000066400000000000000000000042311261716203600155600ustar00rootroot00000000000000============================================================== Jetty Web Container Copyright 1995-2015 Mort Bay Consulting Pty Ltd. ============================================================== The Jetty Web Container is Copyright Mort Bay Consulting Pty Ltd unless otherwise noted. Jetty is dual licensed under both * The Apache 2.0 License http://www.apache.org/licenses/LICENSE-2.0.html and * The Eclipse Public 1.0 License http://www.eclipse.org/legal/epl-v10.html Jetty may be distributed under either license. ------ Oracle The following artifacts are CDDL + GPLv2 with classpath exception. https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html javax.servlet:javax.servlet-api javax.servlet.jsp:javax.servlet.jsp-api org.glassfish.web:javax.servlet.jsp org.glassfish.web:javax.servlet.jsp org.glassfish.web:javax.servlet.jsp.jstl org.eclipse.jetty.orbit.javax.servlet.jsp.jstl ------ Apache The following artifacts are ASL2 licensed. org.apache.taglibs:taglibs-standard-spec org.apache.taglibs:taglibs-standard-impl ------ MortBay The following artifacts are ASL2 licensed. Based on selected classes from following Apache Tomcat jars, all ASL2 licensed. org.mortbay.jasper:apache-jsp org.apache.tomcat:tomcat-jasper org.apache.tomcat:tomcat-juli org.apache.tomcat:tomcat-jsp-api org.apache.tomcat:tomcat-el-api org.apache.tomcat:tomcat-jasper-el org.apache.tomcat:tomcat-api org.apache.tomcat:tomcat-util-scan org.apache.tomcat:tomcat-util org.mortbay.jasper:apache-el org.apache.tomcat:tomcat-jasper-el org.apache.tomcat:tomcat-el-api ------ Mortbay The following artifacts are CDDL + GPLv2 with classpath exception. https://glassfish.dev.java.net/nonav/public/CDDL+GPL.html org.eclipse.jetty.toolchain:jetty-schemas ------ Assorted The UnixCrypt.java code implements the one way cryptography used by Unix systems for simple password protection. Copyright 1996 Aki Yoshida, modified April 2001 by Iris Van den Broeke, Daniel Deville. Permission to use, copy, modify and distribute UnixCrypt for non-commercial or commercial purposes and without fee is granted provided that the copyright notice appears in all copies. jetty-9.2.14.v20151106/README.TXT000066400000000000000000000010761261716203600154000ustar00rootroot00000000000000This is a source checkout of the Jetty webserver. To build, use: mvn clean install The jetty distribution will be built in jetty-distribution/target/distribution The first build may take a long time as Maven downloads all the dependencies. The tests do a lot of stress testing, and on some machines it is necessary to set the file descriptor limit to greater than 2048 for the tests to all pass successfully. Bypass tests by building with -Dmaven.test.skip=true but note that this will not produce some test jars that are leveraged in other places in the build. jetty-9.2.14.v20151106/README.md000066400000000000000000000016721261716203600153230ustar00rootroot00000000000000Project description ============ Jetty is a lightweight highly scalable java based web server and servlet engine. Our goal is to support web protocols like HTTP, SPDY and WebSocket in a high volume low latency way that provides maximum performance while retaining the ease of use and compatibility with years of servlet development. Jetty is a modern fully async web server that has a long history as a component oriented technology easily embedded into applications while still offering a solid traditional distribution for webapp deployment. - [https://projects.eclipse.org/projects/rt.jetty](https://projects.eclipse.org/projects/rt.jetty) Documentation ============ Project documentation is located on our Eclipse website. - [http://www.eclipse.org/jetty/documentation](http://www.eclipse.org/jetty/documentation) Professional Services ============ Expert advice and production support are available through [http://webtide.com](Webtide.com). jetty-9.2.14.v20151106/VERSION.txt000066400000000000000000013613231261716203600157350ustar00rootroot00000000000000jetty-9.2.14.v20151106 - 06 November 2015 + 428474 Expose batch mode in the Jetty WebSocket API + 471055 Restore legacy/experimental WebSocket extensions (deflate-frame) + 472082 isOpen returns true on CLOSING Connection + 474068 Update WebSocket Extension for permessage-deflate draft-22 + 474319 Reintroduce blocking connect(). + 474321 Allow synchronous address resolution. + 474453 Tiny buffers (under 7 bytes) fail to compress in permessage-deflate + 474454 Backport permessage-deflate from Jetty 9.3.x to 9.2.x + 474936 WebSocketSessions are not always cleaned out from openSessions + 476023 Incorrect trimming of WebSocket close reason + 476049 When using WebSocket Session.close() there should be no status code or reason sent + 477385 Problem in MANIFEST.MF with version 9.2.10 / 9.2.13. + 477817 Fixed memory leak in QueuedThreadPool + 481006 SSL requests intermittently fail with EOFException when SSL renegotiation is disallowed. + 481236 Make ShutdownMonitor java security manager friendly + 481437 Port ConnectHandler connect and context functionality from Jetty 8. jetty-9.2.13.v20150730 - 30 July 2015 + 472859 ConcatServlet may expose protected resources. + 473006 Encode addPath in URLResource + 473243 Delay resource close for async default content + 473266 Better handling of MultiException + 473322 GatherWrite limit handling + 473624 ProxyServlet.Transparent / TransparentDelegate add trailing slash before query when using prefix. + 473832 SslConnection flips back buffers on handshake exception jetty-9.2.12.v20150709 - 09 July 2015 + 469414 Proxied redirects expose upstream server name. + 469936 Remove usages of SpinLock. + 470184 Send the proxy-to-server request more lazily. jetty-9.2.11.v20150529 - 29 May 2015 + 461499 ConnectionPool may leak connections. + 463579 Add support for 308 status code. + 464292 Implement stream-based transformer for AsyncMiddleManServlet. + 464438 ClassFileTransformer support in org.eclipse.jetty.webapp.WebAppClassLoader broken + 464740 DosFilter whiteList check improvement + 464869 PathResource.addPath allows absolute resolution. + 464989 AbstractSessionManager.removeEventListener() should remove HttpSessionIdListener + 465053 Prevent gzip buffer overflow on complete + 465181 HttpParser parse full end chunk. + 465202 Forked Mojo does not extract war overlays/dependencies + 465359 Resource.newResource(String res, boolean useCache) does not use useCache argument + 465360 URLResource.addPath should use _useCaches setting to create new Resource + 465700 NullPointerException in ResourceHandler with welcome files + 465734 DosFilter whitelist bit pattern fix + 465747 Jetty is failing to process all HTTP OPTIONS requests. + 466329 Fixed local only TestFilter + 467276 NPE protection in SslContextFactory + 467603 Response 401 from server hangs client. + 467936 w Check HttpOutput aggregateSize is < bufferSize + 468008 Scanner ignores directory length + 468421 HttpClient#send fails with IllegalArgumentException on non-lowercase schemes. + 468714 SelectorManager updateKey race without submit + 468747 XSS vulnerability in HttpSpiContextHandler jetty-9.2.11.M0 - 25 March 2015 + 454934 WebSocketClient / connectToServer can block indefinitely during upgrade failure + 459273 Redundant license notices + 461499 ConnectionPool may leak connections. + 461919 Use osgi-friendly serviceloader mechanism for WebSocketServletFactory + 461941 JMX Remote host:port set from start properties + 462546 ShutdownMonitor should bind to jetty.host + 462616 Race between finishing a connect and timing it out. jetty-9.2.10.v20150310 - 10 March 2015 + 445518 Provide different error callbacks to ProxyServlet. + 456521 ShutdownHandler should shut down more gracefully + 458140 Added DispatcherType support to RewriteHandler + 460769 ClientUpgradeRequest sends cookies in the wrong format + 460905 Make sure TimeoutCompleteListener is cancelled if the request cannot be sent. + 461070 Handle setReadListener on request with no content + 461133 allow stop port to reuse address + 461452 Double release of buffer by HttpReceiverOverHTTP + 461499 ConnectionPool may leak connections. + 461623 BufferUtil.writeTo does not update position consistently + 461643 HttpContent.advance() race. jetty-9.2.9.v20150224 - 24 February 2015 + 459273 Redundant license notices + 460176 When checking for precompiled jsp, ensure classname is present + 460180 Jaas demo has wrong doco in html + 460291 AsyncGzipFilter Mappings + 460371 AsyncMiddleManServlet.GZipContentTransformer fails if last transform has no output + 460372 if web.xml does not contain jspc maven plugin insertionMarker behavior is wrong + 460443 Race condition releasing the response buffer. + 460642 HttpParser error 400 can expose previous buffer contents in HTTP status reason message jetty-9.2.8.v20150217 - 17 February 2015 + 451092 Connector will fail if HeaderListener return false. + 455436 ProxyServlet sends two User-Agent values. + 457893 Close temp jar resource + 458101 added test for maxFormContentSize + 458174 Example Jar Server + 458175 multipart annotation on lazily loaded servlet does not work + 458209 Length check for HttpMethod MOVE lookahead + 458354 ALPNServerConnection.select negotiation. + 458495 CompletableCallback may not notify failures. + 458527 Implement an async proxy servlet that can perform content transformations. + 458568 JDBCLoginService javadoc incorrectly references HashLoginService + 458849 org.eclipse.jetty.util.Uptime.DefaultImpl() not available on GAE + 459006 master branch does not build on norwegian locale + 459125 GzipHandler default mimeType behavior incorrect + 459352 AsyncMiddleManServlet should set "Host:" header correctly in proxy to remote request headers. + 459490 Defining a duplicate error page in webdefault.xml and web.xml results in an error + 459542 AsyncMiddleManServlet race condition on first download content. + 459560 jetty.sh handles start.d and no start.ini + 459769 AsyncMiddleManServlet race condition on last download content. + 459845 Support upgrade + 459963 Failure writing content of a committed request leaks connections. jetty-9.2.7.v20150116 - 16 January 2015 + 420944 Hot Deployment of WAR when Context XML exists doesn't trigger redeploy + 448944 Provide m2e lifecycle mapping metadata for jetty-jspc-maven-plugin + 452201 Set the container classloader for osgi during webbundle undeploy + 454291 Added busy threads JMX attribute to QueuedThreadPool + 454773 SSLConnection use on Android client results in loop + 454954 Jetty osgi should skip fragment and required bundles that are in the uninstalled state + 454955 OSGi AnnotationParser should skip resources that are not in the classpath and close the class inputstream when done scanning it + 454983 Source bundles should not be singleton + 455047 Update JASPI + 455174 jetty-plus JNDI tests should use unique JNDI paths + 455330 Multiple Jetty-ContextFilePath entries separated by commas doesn't work + 455476 Persist updated session expiry time for MongoSessionManager + 455655 ensure multipart form-data parsing exception thrown to servlet + 455863 Fixed jetty.sh handling of multiple JETTY_ARGS + 456426 Exception on context undeploy from EnvConfiguration + 456486 Jar containing ServiceContainerInitializer impl not found in TCCL in osgi + 456956 Reduce ThreadLocal.remove() weak reference garbage + 457017 Reflective call to websocket methods that fail have ambiguous exceptions + 457032 Request sent from a failed CompleteListener due to connect timeout is failed immediately. + 457130 HTTPS request with IP host and HTTP proxy throws IllegalArgumentException. + 457696 JMX implementation should not be overridden by WebApp classes jetty-9.2.6.v20141205 - 05 December 2014 + 383207 Use BundleFileLocatorHelperFactory to obtain BundleFileLocatorHelper + 443652 Remove dependency on java.lang.management classes + 447472 Clear async context timeout on async static content + 451529 Change sentinel class for finding jstl on classpath to org.apache.taglibs.standard.tag.rt.core.WhenTag + 451634 DefaultServlet: useFileMappedBuffer javadoc is misleading + 452188 Delay dispatch until content optimisation. + 452201 EnvConfiguration.destroy() should set the classloader + 452246 Fixed SSL hang on last chunk + 452261 Multiple servlets map to path *.jsp when using jsp-property-group + 452424 Do not add Date header if already set + 452516 Make HttpOutput aggregation size configurable. + 453386 Jetty not working when configuring QueuedThreadPool with minThreads=0. + 453629 Fixed big write test + 453793 _maxHeaderBytes>0 is not verified in parseNext() when in State.CLOSED. + 453801 Jetty does not check for already registered services when bootstrapping + 454157 HttpInput.consumeAll spins if input is in async mode. jetty-9.2.5.v20141112 - 12 November 2014 + 448446 org.eclipse.jetty.start.Main create classloader duplicate + 449594 Handle ArrayTrie overflow with false return + 449811 handle unquoted etags when gzipping + 450467 Integer overflow in Session expiry calculation in MongoSessionManager + 450483 Missing parameterization of etc/jetty-deploy.xml. + 450484 Missing parameterization of etc/jetty-http[s].xml. + 450855 GzipFilter MIGHT_COMPRESS exception + 450873 Disable tests that downcaste wrapped GzipFilterResponses + 450894 jetty.sh does not delete JETTY_STATE at start jetty-9.2.4.v20141103 - 03 November 2014 + 376365 "jetty.sh start" returns 0 on failure + 396569 'bin/jetty.sh stop' reports 'OK' even when jetty was not running + 396572 Starting jetty from cygwin is not working properly + 438387 NullPointerException after ServletUpgradeResponse.sendForbidden is called during WebSocketCreator.createWebSocket + 440729 SSL requests often fail with EOFException or IllegalStateException. + 440925 NPE when using relative paths for --start-log-file + 442419 CrossOriginFilter javadoc says "exposeHeaders", but should be "exposedHeaders" + 442495 Bad Context ClassLoader in JSR356 WebSocket onOpen + 442942 Content sent with status 204 (No Content) + 443529 CrossOriginFilter does not accept wildcard for allowedHeaders + 443530 CrossOriginFilter does not set the Vary header + 443550 improved FileResource encoded alias checking + 444031 Ensure exceptions do not reduce threadpool below minimum + 444124 JSP include with can cause infinite recursion + 444214 Socks4Proxy fails when reading less than 8 bytes. + 444222 replace CRLF in header values with whitespace rather than ? + 444415 iterative WriteFlusher + 444416 AsyncProxyServlet recursion. + 444517 Ensure WebSocketUpgradeFilter is always first in filter chain + 444547 Format exception in ResourceCache.Content.toString() + 444595 nosql/mongodb - Cleanup process/Refreshing does not respect encoding of attribute keys + 444617 Expose local and remote socket address to applications + 444676 Goal jetty:deploy-war produces errors with version 9.2.3 + 444722 Fixed order of setReuseAddress call + 444748 WebSocketClient.stop() does not unregister from ShutdownThread + 444764 HttpClient notifies callbacks for last chunk of content twice. + 444771 JSR356 / EndPointConfig.userProperties are not unique per endpoint upgrade + 444863 ProxyServlet does not filter headers listed by the Connection header. + 444896 Overriding of web-default servlet mapping in web.xml not working with quickstart + 445157 First redeployed servlet leaks WebAppContext + 445239 Rename weld.mod to cdi.mod to be consistent with past module namings + 445258 STOP.WAIT is not really respected + 445374 Reevaluate org.eclipse.jetty.websocket.jsr356 enablement concepts + 445495 Improve Exception message when no jndi resource to bind for a name in web.xml + 445542 Add SecuredRedirectHandler for embedded jetty use to redirect to secure port/scheme + 445821 Error 400 should be logged with RequestLog + 445823 RequestLogHandler at end of HandlerCollection doesn't work + 445830 Support setting environment variables on forked jetty with jetty:run-forked + 445979 jetty.sh fails to start when start-stop-daemon does not exist and the user is not root + 446033 org.eclipse.jetty.websocket.server.WebSocketServerFactory not available in OSGi + 446063 ALPN Fail SSL Handshake if no supported Application Protocols. + 446107 NullPointerException in ProxyServlet when extended by Servlet without a package + 446425 Oracle Sql error on JettySessions table when this table do not exist already + 446506 getAsyncContext ISE before startAsync on async dispatches + 446563 Null HttpChannel.getCurrentHttpChannel() in ServletHandler.doFilter(). + 446672 NPN Specification issue in the case no protocols are selected. + 446923 SharedBlockingCallback does not handle connector max idle time of Long.MAX_VALUE; BlockerTimeoutException not serializable + 447381 Disable SSLv3 by default. + 447472 test harness for slow large writes + 447627 MultiPart file always created when "filename" set in Content-Disposition + 447629 getPart()/getParts() fails on Multipart request if getParameter is called in a filter first + 447746 HttpClient is always going to send User-Agent header even though I do not want it to. + 447979 Refactor to make MetaData responsible for progressively ordering web-inf jars + 448156 Fixed INACTIVE race in IteratingCallback + 448225 Removed unnecessary synchronize on initParser + 448841 Clarified selectors==0 javadoc 448840 Clarified ServerConnector javadoc 448839 Fixed javadoc typo in ServerConnector + 449001 Remove start.d directory from JETTY_HOME + 449003 WARNING: Cannot enable requested module [protonego-impl]: not a valid module name + 449038 WebSocketUpgradeFilter must support async. + 449175 Removed extra space in NCSA log + 449291 create-files downloads without license + 449372 Make jvmArgs of jetty:run-forked configurable from command line + 449603 OutputStreamContentProvider hangs when host is not available. jetty-9.2.3.v20140905 - 05 September 2014 + 347110 renamed class transformer methods + 411163 Add embedded jetty code example with JSP enabled + 435322 Added a idleTimeout to the SharedBlockerCallback + 435533 Handle 0 sized async gzip + 435988 ContainerLifeCycle: beans never stopped on remove + 436862 Update jetty-osgi to asm-5 and spifly-1.0.1 + 438500 Odd NoClassDef errors when shutting down the jetty-maven-plugin via the stop goal + 440255 ensure 500 is logged on thrown Errors + 441073 isEarlyEOF on HttpInput + 441475 org.eclipse.jetty.server.ResourceCache exceptions under high load + 441479 Jetty hangs due to deadlocks in session manager + 441649 Update to jsp and el Apache Jasper 8.0.9 + 441756 Ssl Stackoverflow on renegotiate + 441897 Fixed etag handling in gzipfilter + 442048 fixed sendRedirect %2F encoding + 442383 Improved insufficient threads message + 442628 Update example xml file for second server instance to extract wars + 442642 Quickstart generates valid XML + 442759 Allow specific ServletContainerInitializers to be excluded + 442950 Embedded Jetty client requests to localhost hangs with high cpu usage (NIO OP_CONNECT Solaris/Sparc). + 443049 Improved HttpParser illegal character messages + 443158 Fixed HttpOutput spin + 443172 web-fragment.xml wrongly parsed for applications running in serlvet 2.4 mode + 443231 java.lang.NullPointerException on scavenge scheduling when session id manager declared before shared scheduler + 443262 Distinguish situation where jetty looks for tlds in META-INF but finds none vs does not look jetty-8.1.16.v20140903 - 03 September 2014 + 409788 Large POST body causes java.lang.IllegalStateException: SENDING => HEADERS. + 433689 Evict idle HttpDestinations from client + 433802 check EOF in send1xx + 438996 Scavenger-Timer in HashSessionManager can die because of IllegalStateException from getMaxInactiveInterval + 442048 fixed sendRedirect %2F encoding + 442839 highly fragmented websocket messages can result in corrupt binary messages jetty-7.6.16.v20140903 - 03 September 2014 + 409788 Large POST body causes java.lang.IllegalStateException: SENDING => HEADERS. + 433802 check EOF in send1xx + 442839 highly fragmented websocket messages can result in corrupt binary messages jetty-9.2.2.v20140723 - 23 July 2014 + 411323 DosFilter/QoSFilter should use AsyncContext rather than Continuations. + 432815 Fixed selector stop race + 434536 Improved Customizer javadoc + 435322 Fixed Iterating Callback close + 435653 encode async dispatched requestURI + 435895 jetty spring module is not in distribution + 436874 WebSocket client throwing a NullPointer when handling a pong + 436894 GzipFilter code cleanup + 436916 CGI: "Search docroot for a matching execCmd" logic is wrong + 436987 limited range of default acceptors and selectors + 437051 Refactor Filter chain handling of Request.isAsyncSupported + 437395 Start / Properties in template sections should be default applied for enabled modules + 437419 Allow scanning of META-INF for resources,fragments,tlds for unpacked jars + 437430 jettyXml not consistent between jetty:run and jetty:run-forked + 437462 consistent test failure in jetty-start under windows + 437706 ServletTester calls LocalConnector method with hardcoded timeout + 437800 URLs with single quote and spaces return 404 + 437996 avoid async status race by not setting 200 on handled + 438079 Review garbage creation in 9.2.x series. + 438190 findbug improvements + 438204 leave IPv6 addresses [] wrapped in getServerName + 438327 Remove hard coded Allow from OPTIONS * + 438331 AbstractLogger.debug(String,long) infinite loop + 438434 ResourceHandler checks aliases + 438895 Add mvn jetty:effective-web-xml goal + 439066 javadoc setStopAtShutdown + 439067 Improved graceful stop timeout handling + 439194 Do not configure fake server for jetty:run-forked + 439201 GzipFilter and AsyncGzipFilter should strip charset from Content-Type before making exclusion comparison in doFilter + 439369 Deprecate CrossContextPseudoSession + 439387 Ensure empty servlet-class never generated for quickstart + 439390 Ensure jsp scratchdir is created same way for quickstart and non-quickstart + 439394 load-on-startup with value 0 not preserved for quickstart + 439399 Scan tlds for apache jasper standard taglib with jetty-maven-plugin + 439438 DataSourceLoginService does not refresh passwords when changed in database + 439507 Possible timing side-channel when comparing MD5-Credentials + 439540 setReuseAddress() in ServerConnector.java is not coded properly + 439652 GzipHandler super.doStart + 439663 Allow mappings to be declared before servlet/filter + 439672 support using Apache commons daemon for managing Jetty + 439753 ConstraintSecurityHandler has dead code for processing constraints + 439788 CORS filter headers gone between 9.2.0.M0 and 9.2.1 .v20140609 for ProxyServlet requests. + 439809 mvn jetty:jspc cannot find taglibs in dependency jars + 439895 No event callback should be invoked after the "failure" callback. + 440020 Abort bad proxy responses with sendError(-1) + 440038 Content decoding may fail. + 440114 ContextHandlerCollection does not skip context wrappers + 440122 Remove usages of ForkInvoker. jetty-9.2.1.v20140609 - 09 June 2014 + 347110 Supprt ClassFileTransormers in WebAppClassLoader + 432192 jetty-start / Allow JETTY_LOGS use for start-log-file + 432321 jetty-start / Allow defining extra start directories for common configurations + 435322 Improved debug + 436029 GzipFilter errors on asynchronous methods with message to AsyncGzipFilter + 436345 Refactor AbstractSession to minimize burden on subclasses to implement behaviour + 436388 Allow case-insensitive STOP.KEY and STOP.PORT use + 436405 ${jetty.base}/resources not on classpath with default configuration + 436520 Start / Allow https and file urls in jetty-start's module download mechanism + 436524 Start / Downloadable [files] references in modules cannot use ":" themselves jetty-9.2.0.v20140526 - 26 May 2014 + 429390 Decoders and Encoders are not registered for non-annotated ClientEndpoint + 434810 better handling of bad messages + 435086 ${jetty.base}/resources not on classpath when using --module=resources + 435088 lib/npn packaging of jetty-distribution is off + 435206 Can't add Cookie header on websocket ClientUpgradeRequest + 435217 Remove deprecated TagLibConfiguration + 435223 High cpu usage in FCGIHttpParser.parseContent(ResponseContentParser.java:314). + 435338 Incorrect handling of asynchronous content. + 435412 Make AbstractSession.access() more amenable to customization jetty-9.2.0.RC0 - 15 May 2014 + 419972 Support sending forms (application/x-www-form-urlencoded). + 420368 Default content types for ContentProviders. + 428966 Per-request cookie support. + 430418 Jetty 9.1.3 and Chrome 33 permessage-deflate do not work together + 431333 NPE In logging of WebSocket ExtensionConfig + 432321 jetty-start / Allow defining extra start directories for common configurations + 432939 Jetty Client ContentResponse should have methods such as getContentType() and getMediaType(). + 433089 Client should provide Request.accept() method, like JAX-RS 2.0 Invocation.Builder.accept(). + 433405 Websocket Session.setMaxIdleTimeout fails with zero + 433689 Evict old HttpDestinations from HttpClient. + 434386 Request Dispatcher extracts args and prevents asyncIO. + 434395 WebSocket / memory leak, WebSocketSession not cleaned up in abnormal closure cases + 434447 Able to create a session after a response.sendRedirect + 434505 Allow property files on start.jar command line Signed-off-by: Tom Zeller + 434578 Complete listener not called if redirected to an invalid URI. + 434679 Log static initialization via jetty-logging.properties fails sometimes + 434685 WebSocket read/parse does not discard remaining network buffer after unrecoverable error case + 434715 Avoid call to ServletHolder.getServlet() during handle() iff servlet is available and instantiated jetty-9.2.0.M1 - 08 May 2014 + 367680 jsp-file with load-on-startup not precompiled + 404511 removed deprecated StringMap + 409105 Upgrade jetty-osgi build/test to use more recent pax junit test framework + 424982 improved PID check in jetty.sh + 425421 ContainerLifeCycle does not start added beans in started state + 428904 Add logging of which webapp has path with uncovered http methods + 431094 Consistent handling of utf8 decoding errors + 431459 Jetty WebSocket compression extensions fails to handle big messages properly + 431519 Fixed NetworkTrafficListener + 431642 Implement ProxyServlet using Servlet 3.1 async I/O. + 432145 Pending request is not failed when HttpClient is stopped. + 432270 Slow requests with response content delimited by EOF fail. + 432321 jetty-start / Allow defining extra start directories for common configurations + 432468 Improve command CGI path handling + 432473 web.xml declaration order of filters not preserved on calls to init() + 432483 make osgi.serviceloader support for javax.servlet.ServletContainerInitializer optional (cherry picked from commit 31043d25708edbea9ef31948093f4eaf2247919b) + 432528 IllegalStateException when using DeferredContentProvider. + 432777 Async Write Loses Data with HTTPS Server. + 432901 ensure a single onError callback only in pending and unready states + 432993 Improve handling of ProxyTo and Prefix parameters in ProxyServlet.Transparent. + 433244 Security manager lifecycle cleanup + 433262 WebSocket / Advanced close use cases + 433365 No such servlet: __org.eclipse.jetty.servlet.JspPropertyGroupServlet__ + 433370 PATCH method does not work with ProxyServlet. + 433431 Support ServletHandler fall through + 433479 Improved resource javadoc + 433483 sync log initialize + 433512 Jetty throws RuntimeException when webapp compiled with jdk8 -parameters + 433563 Jetty fails to startup on windows - InvalidPathException + 433572 default to sending date header + 433656 Change to Opcode.ASM5 breaks jetty-osgi + 433692 improved buffer resizing + 433708 Improve WebAppClassLoader.addClassPath() IllegalStateException message + 433793 WebSocket / empty protocol list in ServerEndpointConfig.Configurator when using non-exact header name + 433841 Resource.newResource() declares an exception it does not throw + 433849 FileResource string compare fix + 433916 HttpChannelOverHttp handles HTTP 1.0 connection reuse incorrectly. + 434009 Improved javadoc for accessing HttpChannel and HttpConnection + 434027 ReadListener.onError() not invoked in case of read failures. + 434056 Support content consumed asynchronously. + 434074 Avoid double dispatch by returning false from messageComplete + 434077 AnnotatedServerEndpointTest emits strange exception + 434247 Redirect loop in FastCGI proxying for HTTPS sites. jetty-8.1.15.v20140411 - 11 April 2014 + 397167 Remote Access documentation is wrong + 419799 complete after exceptions thrown from async error pages + 420776 complete error pages after startAsync + 421197 fix method comment and ensure close synchronized + 422137 Added maxQueued to QueuedThreadPool MBean + 424180 improve bad message errors + 425038 WebSocketClient leaks file handles when exceptions are thrown from open() + 425551 Memory Leak in SelectConnector$ConnectTimeout.expired. + 426658 backport Bug 425930 to jetty-8 + 427761 allow endpoints to be interrupted + 428708 JDBCSessionIdManager when clearing expired sessions failed, jetty should still be able to startup + 428710 JDBCSession(Id)Manager use 'read committed isolation level' + 430968 Use wrapped response with async dispatch + 432452 ConnectHandler does not timeout sockets in FIN_WAIT2. jetty-7.6.15.v20140411 - 11 April 2014 + 422137 Added maxQueued to QueuedThreadPool MBean + 425038 WebSocketClient leaks file handles when exceptions are thrown from open() + 425551 Memory Leak in SelectConnector$ConnectTimeout.expired. + 432452 ConnectHandler does not timeout sockets in FIN_WAIT2. jetty-9.2.0.M0 - 09 April 2014 + 419801 Upgrade to asm5 for jdk8 + 423392 Fix buffer overflow in AsyncGzipFilter + 425736 jetty-start / Jetty 9 fails to startup with --exec option if Java path contain + 426920 jetty-start / BaseHome.listFilesRegex() and .recurseDir() do not detect filesystem loops + 427188 Re-enable automatic detection of logging-dependencies with logging-module + 429734 Implemented the HA ProxyProtocol + 430341 use apache jsp/jstl for maven plugins + 430747 jetty-start / Allow --lib and module [lib] to recursively add jars + 430825 jetty-start / use of jetty-jmx.xml prevents configuration of ThreadPool in jetty.xml + 431279 jetty-start / Unable to start jetty if no properties are defined. + 431892 DefaultFileLocatorHelper.getBundleInstallLocation fails for equinox 3.10 + 432122 ignore frequently failing test + 432145 Pending request is not failed when HttpClient is stopped. + 432270 Slow requests with response content delimited by EOF fail. jetty-9.1.5.v20140505 - 05 May 2014 + 431459 Jetty WebSocket compression extensions fails to handle big messages properly + 431519 Fixed NetworkTrafficListener + 432145 Pending request is not failed when HttpClient is stopped. + 432270 Slow requests with response content delimited by EOF fail. + 432473 web.xml declaration order of filters not preserved on calls to init() + 432483 make osgi.serviceloader support for javax.servlet.ServletContainerInitializer optional (cherry picked from commit 31043d25708edbea9ef31948093f4eaf2247919b) + 432528 IllegalStateException when using DeferredContentProvider. + 432777 Async Write Loses Data with HTTPS Server. + 432901 ensure a single onError callback only in pending and unready states + 432993 Improve handling of ProxyTo and Prefix parameters in ProxyServlet.Transparent. + 433365 No such servlet: __org.eclipse.jetty.servlet.JspPropertyGroupServlet__ (cherry picked from commit e2ed934978b958d6fccb28a8a5d04768f7c0432d) + 433370 PATCH method does not work with ProxyServlet. + 433483 sync log initialize + 433692 improved buffer resizing + 433916 HttpChannelOverHttp handles HTTP 1.0 connection reuse incorrectly. + 434027 ReadListener.onError() not invoked in case of read failures. jetty-9.1.4.v20140401 - 01 April 2014 + 414206 Rewrite rules re-encode requestURI + 414885 Don't expose JDT classes by default + 417022 Access current HttpConnection from Request not ThreadLocal + 423619 set Request timestamp on startRequest + 423982 removed duplicate UrlResource toString + 424107 Jetty should not finish chunked encoding on exception. + 425991 added qml mime type + 426897 improved ContainerLifeCycle javadoc + 427185 Add org.objectweb.asm. as serverClass + 427204 jetty-start / startup incorrectly requires directory in jetty.base + 427368 start.sh fails quietly on command line error + 428594 File upload with onMessage and InputStream fails + 428595 JSR-356 / ClientContainer does not support SSL + 428597 javax-websocket-client-impl and javax-websocket-server-impl jars Manifests do not export packages for OSGI + 428817 jetty-start / Allow for property to configure deploy manager `webapps` directory + 429180 Make requestlog filename parameterized + 429357 JDBCSessionManager.Session.removeAttribute don't set dirty flag if attribute already removed + 429409 osgi] jetty.websocket.servlet must import jetty.websocket.server + 429487 Runner code cleanups + 429616 Use UTF-8 encoding for XML + 429779 masked zero length websocket frame gives NullPointerException during streaming read + 430088 OnMessage*Callable decoding of streaming binary or text is not thread safe + 430242 added SharedBlockingCallback to support threadsafe blocking + 430273 Cancel async timeout breaks volatile link to avoid race with slow expire + 430341 add apache jsp and jstl optional modules + 430490 Added JETTY_SHELL 426738 Fixed JETTY_HOME comments + 430649 test form encoding + 430654 closing client connections can hang worker threads. + 430808 OutputStreamContentProvider violates OutputStream contract. + 430822 jetty-start / make soLingerTime configurable via property + 430823 jetty-start / make NeedClientAuth (ssl) configurable via property + 430824 jetty-start / use of jetty-logging.xml prevents configuration of ThreadPool in jetty.xml + 431103 Complete listener not called if request times out before processing exchange. + 431592 do not resolved forwarded-for address jetty-9.1.3.v20140225 - 25 February 2014 + 373952 Ensure MongoSessionManager un/binds session attributes on refresh only if necessary + 424899 Initialize GzipHandler mimeTypes + 426490 HttpServletResponse.setBufferSize(0) results in tight loop (100% cpu hog) + 427700 Outgoing extensions that create multiple frames should flush them in order and atomically. + 427738 fixed XSS in async-rest demo + 428157 Methods of anonymous inner classes can't be called via xml + 428232 Rework batch mode / buffering in websocket. + 428238 Test HEAD request with async IO + 428266 HttpRequest mangles URI query string. + 428383 limit white space between requests + 428418 JettyStopMojo prints some messages on System.err + 428435 Large streaming message fails in MessageWriter. + 428660 Delay closing async HttpOutput until after UNREADY->READY + 428710 JDBCSession(Id)Manager use read committed isolation level + 428859 Do not auto initialise jsr356 websocket if no annotations or EndPoints discovered jetty-9.1.2.v20140210 - 10 February 2014 + 408167 Complex object as session attribute not necessarily persisted. + 423421 remove org.slf4j and org.ow2.asm from jetty-all artifact + 424171 Old javax.activation jar interferes with email sending + 424562 JDBCSessionManager.setNodeIdInSessionId(true) does not work + 425275 org.eclipse.jetty.osgi.annotations.AnnotationConfiguration.BundleParserTask.getStatistic() returns null when debug is enabled. + 425638 Fixed monitor module/xml typos + 425696 start.jar --add-to-start={module} results in error + 425703 Review [Queued]HttpInput. + 425837 Upgrade to jstl 1.2.2 + 425930 JDBC Session Manager constantly reloading session if save intervall expired once + 425998 JDBCSessionIdManager fails to create maxinterval column + 426250 jetty-all should be deployed on release + 426358 NPE generating temp dir name if no resourceBase or war + 426481 fix < java 1.7.0_10 npn files + 426739 Response with Connection: keep-alive truncated. + 426750 isReady() returns true at EOF + 426870 HTTP 1.0 Request with Connection: keep-alive and response content hangs. + 427068 ServletContext.getClassLoader should only check privileges if a SecurityManager exists + 427128 Cookies are not sent to the server. + 427245 StackOverflowError when session cannot be de-idled from disk + 427254 Cookies are not sent to the client. + 427512 ReadPendingException in case of HTTP Proxy tunnelling. + 427570 externalize common http config to start.ini + 427572 Default number of acceptors too big. + 427587 MessageInputStream must copy the payload. + 427588 WebSocket Parser leaks ByteBuffers. + 427690 Remove Mux Extension and related support. + 427699 WebSocket upgrade response sends Sec-WebSocket-Protocol twice. jetty-9.1.1.v20140108 - 08 January 2014 + 408912 JDBCSessionIdManager should allow configuration of schema + 410750 NPE Protection in Mongo save session + 417202 Start / command line arguments with ${variable} should be expanded + 418622 WebSocket / When rejecting old WebSocket protocols, log client details + 418769 Allow resourceBases in run-forked Mojo + 418888 Added strict mode to HttpGenerator + 419309 encode alias URIs from File.toURI + 419911 Empty chunk causes ArrayIndexOutOfBoundsException in InputStreamResponseListener. + 421189 WebSocket / AbstractExtension's WebSocketPolicy is not Session-specific + 421314 Websocket / Connect attempt with Chrome 32+ fails with "Some extension already uses the compress bit" + 421697 IteratingCallback improvements + 421775 CookiePatternRule only sets cookie if not set already + 421794 Iterator from InputStreamProvider is not implemented properly. + 421795 ContentProvider should have a method to release resources. + 422192 ClientContainer.getOpenSessions() always returns null + 422264 OutputStreamContentProvider does not work with Basic Authentication. + 422308 Change all session/sessionid managers to use shared Scheduler + 422386 Comma-separated s not trimmed in GzipFilter + 422388 Test for GzipFilter apply to resources with charset appended to the MIME type + 422398 moved jmx remote config to jmx-remote.mod + 422427 improved TestConnection + 422703 Support reentrant HttpChannel and HttpConnection + 422723 Dispatch failed callbacks to avoid blocking selector + 422734 messages per second in ConnectorStatistics + 422807 fragment large written byte arrays to protect from JVM OOM bug + 423005 reuse gzipfilter buffers + 423048 Receiving a PING while sending a message kills the connection + 423060 Allow ${jetty.base}/work + 423118 ServletUpgradeRequest.getUserPrincipal() does not work + 423185 Update permessage-deflate for finalized spec + 423255 MBeans of SessionIdManager can leak memory on redeploy + 423361 Ensure ServletContainerInitializers called before injecting Listeners + 423373 Correct namespace use for JEE7 Schemas + 423392 GzipFilter without wrapping or blocking + 423395 Ensure @WebListeners are injected + 423397 Jetty server does not run on Linux server startup because of a bug in jetty.sh script. + 423476 WebSocket / JSR / @OnMessage(maxMessageSize=20000000) not properly supported + 423556 HttpSessionIdListener should be resource injectable + 423646 WebSocket / JSR / WebSocketContainer (Client) should have its LifeCycle stop on standalone use + 423692 use UrlEncoded.ENCODING for merging forwarded query strings + 423695 Horizontal-tab used as HTTP Header Field separator unsupported + 423724 WebSocket / Rename MessageAppender.appendMessage to .appendFrame + 423739 Start checks module files. + 423804 WebSocket / JSR improper use of ServerEndpointConfig.Configurator.getNegotiatedSubprotocol() + 423875 Update jetty-distro build to use jetty-toolchain jetty-schemas 3.1.M0 + 423915 WebSocket / Active connection from IOS that goes into airplane mode not disconnected on server side + 423926 Remove code duplication in class IdleTimeout. + 423930 SPDY streams are leaked. + 423948 Cleanup and consolidate testing utilities in WebSocket + 424014 PathContentProvider does not close its internal SeekableByteChannel. + 424043 IteratingCallback Idle race. + 424051 Using --list-config can result in NPE + 424168 Module [ext] should load libraries recursively from lib/ext/ + 424180 extensible bad message content + 424183 Start does not find LIB (Classpath) when on non-English locale + 424284 Identify conflicts in logging when error "Multiple servlets map to {pathspec}" occurs + 424303 @ServletSecurity not applied on non load-on-startup servlets + 424307 obfuscate unicode + 424380 Augment class / Jar scanning timing log events + 424390 Allow enabling modules via regex + 424398 Servlet load-on-startup ordering is not obeyed + 424497 Allow concurrent async sends + 424498 made bytebufferendpoint threadsafe + 424588 org.eclipse.jetty.ant.AntWebInfConfiguration does not add WEB-INF/classes for annotation scanning + 424598 Module [npn] downloads wrong npn jar + 424651 org.eclipse.jetty.spdy.Flusher use of non-growable ArrayQueue yield java.lang.IllegalStateException: Full. + 424682 Session cannot be deserialized with form authentication + 424706 The setMaxIdleTimeout of javax.websocket.Session does not take any affect + 424734 WebSocket / Expose Locale information from ServletUpgradeRequest + 424735 WebSocket / Make ServletUpgradeRequest expose its HttpServletRequest + 424743 Verify abort behavior in case the total timeout expires before the connect timeout. + 424762 ShutdownHandler hardcodes "127.0.0.1" and cannot be used with IPv6 + 424847 Deadlock in deflate-frame (webkit binary) + 424863 IllegalStateException "Unable to find decoder for type " + 425038 WebSocketClient leaks file handles when exceptions are thrown from open() + 425043 Track whether pools are used correctly. + 425049 add json mime mapping to mime.properties. jetty-9.1.0.v20131115 - 15 November 2013 + 397167 Remote Access documentation is wrong + 416477 QueuedThreadPool does not reuse interrupted threads + 420776 complete error pages after startAsync + 421362 When using the jetty.osgi.boot ContextHandler service feature the wrong ContextHandler can be undeployed jetty-9.1.0.RC2 - 07 November 2013 + 410656 WebSocketSession.suspend() hardcoded to return null + 417223 removed deprecated ThreadPool.dispatch + 418741 Threadlocal cookie buffer in response + 420359 fixed thread warnings + 420572 IOTest explicitly uses 127.0.0.1 + 420692 set soTimeout to try to avoid hang + 420844 Connection:close on exceptional errors + 420930 Use Charset to specify character encoding + 421197 synchronize gzip output finish + 421198 onComplete never call onComplete in BufferingResponseListener in 9.1. jetty-9.0.7.v20131107 - 07 November 2013 + 407716 fixed logs + 416597 Allow classes and jars on the webappcontext extraclasspath to be scanned for annotations by jetty-maven-plugin + 418636 Name anonymous filter and holders with classname-hashcode + 418732 Add whiteListByPath mode to IPAccessHandler + 418767 run-forked goal ingores test scope dependencies with useTestScope=true + 418792 Session getProtocolVersion always returns null + 418892 SSL session caching so unreliable it effectively does not work. + 419309 Added symlink checker to test webapp + 419333 treat // as an alias in path + 419344 NPNServerConnection does not close the EndPoint if it reads -1. + 419350 Do not borrow space from passed arrays + 419655 AnnotationParser throws NullPointerException when scanning files from jar:file urls + 419687 HttpClient's query parameters must be case sensitive. + 419799 Async timeout dispatches to error page + 419814 Annotation properties maxMessageSize and inputBufferSize don't work + 419846 JDBCSessionManager doesn't determine dirty state correctly + 419901 Client always adds extra user-agent header. + 419937 Request isSecure cleared on recycle + 419950 Provide constructor for StringContentProvider that takes Charset. + 419964 InputStreamContentProvider does not close provided InputStream. + 420033 AsyncContext.onTimeout exceptions passed to onError + 420039 BufferingResponseListener continues processing after aborting request. + 420048 DefaultServlet alias checks configured resourceBase + 420142 reimplemented graceful shutdown + 420362 Response/request listeners called too many times. + 420374 Call super.close() in a finally block + 420530 AbstractLoginModule never fails a login + 420572 IOTest explicitly uses 127.0.0.1 + 420776 complete error pages after startAsync + 420844 Connection:close on exceptional errors + 420930 Use Charset to specify character encoding + 421197 synchronize gzip output finish jetty-8.1.14.v20131031 - 31 October 2013 + 417772 fixed low resources idle timeout + 418636 Name anonymous filter and holders with classname-hashcode + 419432 Allow to override the SslContextFactory on a per-destination basis. + 420048 DefaultServlet alias checks configured resourceBase + 420530 AbstractLoginModule never fails a login jetty-7.6.14.v20131031 - 31 October 2013 + 417772 fixed low resources idle timeout + 418636 Name anonymous filter and holders with classname-hashcode + 419432 Allow to override the SslContextFactory on a per-destination basis. + 420048 DefaultServlet alias checks configured resourceBase + 420530 AbstractLoginModule never fails a login jetty-9.1.0.RC1 - 31 October 2013 + 294531 Unpacking webapp twice to the same directory name causes problems with updated jars in WEB-INF/lib + 397049 Cannot Provide Custom Credential to JDBCLoginService + 403591 improve the Blocking Q implementation. + 407716 fixed logs + 410840 Change SSLSession.getPeerCertificateChain() to SSLSession.getPeerCertificates(). + 415118 WebAppClassLoader.getResource(name) should strip .class from name + 415609 spdy replace SessionInvoker with IteratingCallback. Introduce Flusher class to separate queuing/flushing logic from StandardSession + 416300 Order ServletContainerInitializer callbacks + 416597 Allow classes and jars on the webappcontext extraclasspath to be scanned for annotations by jetty-maven-plugin + 417356 Add SOCKS support to jetty client. + 417932 resources.mod should make ${jetty.base}/resources/ directory + 417933 logging.mod ini template should include commented log.class settings + 418212 org.eclipse.jetty.spdy.server.http.SSLExternalServerTest hangs. + 418441 Use of OPTIONS= in Jetty 9.1 should display WARNING message + 418596 Faults in JARs during class scanning should report the jar that caused the problem + 418603 cannot specify a custom ServerEndpointConfig.Configurator + 418625 WebSocket / Jsr RemoteEndpoint.sendObject(java.nio.HeapByteBuffer) doesn't find encoder + 418632 WebSocket / Jsr annotated @OnMessage with InputStream fails to be called + 418636 Name anonymous filter and holders with classname-hashcode + 418732 Add whiteListByPath mode to IPAccessHandler + 418767 run-forked goal ingores test scope dependencies with useTestScope=true + 418792 Session getProtocolVersion always returns null + 418892 SSL session caching so unreliable it effectively does not work. + 418922 Missing parameterization of etc/jetty-xinetd.xml + 418923 Missing parameterization of etc/jetty-proxy.xml + 419146 Parameterize etc/jetty-requestlog.xml values + 419309 Added symlink checker to test webapp + 419330 Allow access to setters on jetty-jspc-maven-plugin + 419333 treat // as an alias in path + 419344 NPNServerConnection does not close the EndPoint if it reads -1. + 419350 Do not borrow space from passed arrays + 419655 AnnotationParser throws NullPointerException when scanning files from jar:file urls + 419687 HttpClient's query parameters must be case sensitive. + 419799 Async timeout dispatches to error page + 419814 Annotation properties maxMessageSize and inputBufferSize don't work + 419846 JDBCSessionManager doesn't determine dirty state correctly + 419899 Do not wrap SSL Exception as EoFException + 419901 Client always adds extra user-agent header. + 419904 Data corruption on proxy PUT requests. + 419914 QueuedThreadPool uses nanoTime + 419937 Request isSecure cleared on recycle + 419950 Provide constructor for StringContentProvider that takes Charset. + 419964 InputStreamContentProvider does not close provided InputStream. + 420012 Improve ProxyServlet.Transparent configuration in case prefix="/". + 420033 AsyncContext.onTimeout exceptions passed to onError + 420034 Removed threads/timers from Date caching + 420039 BufferingResponseListener continues processing after aborting request. + 420048 DefaultServlet alias checks configured resourceBase + 420103 Split out jmx-remote module from existing jmx module + 420142 reimplemented graceful shutdown + 420362 Response/request listeners called too many times. + 420364 Bad synchronization in HttpConversation. + 420374 Call super.close() in a finally block + 420530 AbstractLoginModule never fails a login + 420687 XML errors in jetty-plus/src/test/resources/web-fragment-*.xml + 420776 complete error pages after startAsync jetty-9.1.0.RC0 - 30 September 2013 + 412469 make module for jetty-jaspi + 416453 Add comments to embedded SplitFileServer example + 416577 enhanced shutdown handler to send shutdown at startup + 416674 run all jetty-ant tests on random ports + 416940 avoid download of spring-beans.dtd + 417152 WebSocket / Do all setup in websocket specific ServletContainerInitializer + 417239 re-implemented Request.getContentRead() + 417284 Precompiled regex in HttpField + 417289 SPDY replace use of direct buffers with indirect buffers or make it configurable + 417340 Upgrade JDT compiler to one that supports source/target of Java 1.7 + 417382 Upgrade to asm 4.1 and refactor annotation parsing + 417475 Do not null context Trie during dynamic deploy + 417490 WebSocket / @PathParam annotated parameters are null when the servlet mapping uses a wildcard + 417561 Refactor annotation related code: change log messages + 417574 Setting options with _JAVA_OPTIONS breaks run-forked with true + 417831 Remove jetty-logging.properties from distro/resources + 417938 Startup / Sort properties presented in --list-config alphabetically + 418014 Handle NTFS canonical exceptions during alias check + 418068 WebSocketClient has lazy or injected Executor + 418212 org.eclipse.jetty.spdy.server.http.SSLExternalServerTest hangs + 418227 Null cookie value test jetty-9.0.6.v20130930 - 30 September 2013 + 411069 better set compiler defaults to 1.7, including webdefault.xml for jsp + 411934 War overlay configuration assumes src/main/webapp exists + 413484 setAttribute in nosql session management better handles _dirty status + 413684 deprecated unsafe alias checkers + 413737 hide stacktrace in ReferrerPushStrategyTest + 414431 Avoid debug NPE race + 414898 Only upgrade v0 to v1 cookies on dquote , ; backslash space and tab in the value + 415192 maps to JspPropertyGroupServlet instead of JspServlet + 415194 Deployer gives management of context to context collection + 415302 + 415330 Avoid multiple callbacks at EOF + 415401 Add initalizeDefaults call to SpringConfigurationProcessor + 415548 migrate ProxyHTTPToSPDYTest to use HttpClient to avoid intermittent NPE part 2 + 415605 fix status code logging for async requests + 415999 Fix some of FindBugs warnings + 416015 Handle null Accept-Language and other headers + 416096 DefaultServlet leaves open file descriptors with file sizes greater than response buffer + 416102 Clean up of async sendContent process + 416103 Added AllowSymLinkAliasChecker.java + 416251 ProxyHTTPToSPDYConnection now sends a 502 to the client if it receives a rst frame from the upstream spdy server + 416266 HttpServletResponse.encodeURL() encodes on first request when only SessionTrackingMode.COOKIE is used + 416314 jetty async client wrong behaviour for HEAD Method + Redirect. + 416321 handle failure during blocked committing write + 416453 Add comments to embedded SplitFileServer example + 416477 Improved consumeAll error handling + 416568 Simplified servlet exception logging + 416577 enhanced shutdown handler to send shutdown at startup + 416585 WebInfConfiguration examines webapp classloader first instead of its parent when looking for container jars + 416597 Allow classes and jars on the webappcontext extraclasspath to be scanned for annotations + 416663 Content-length set by resourcehandler + 416674 run all jetty-ant tests on random ports + 416679 Change warning to debug if no transaction manager present + 416787 StringIndexOutOfBounds with a pathMap of "" + 416940 avoid download of spring-beans.dtd + 416990 JMX names statically unique + 417110 Demo / html body end tag missing in authfail.html + 417225 added Container.addEventListener method + 417260 Protected targets matched as true URI path segments + 417289 SPDY replace use of direct buffers with indirect buffers or make it configurable + 417475 Do not null context Trie during dynamic deploy + 417574 Setting options with _JAVA_OPTIONS breaks run-forked with true + 417831 Remove jetty-logging.properties from distro/resources + 418014 Handle NTFS canonical exceptions during alias check + 418212 org.eclipse.jetty.spdy.server.http.SSLExternalServerTest hangs + 418227 Null cookie value test jetty-9.1.0.M0 - 16 September 2013 + 393473 Add support for JSR-356 (javax.websocket) draft + 395444 Websockets not working with Chrome (deflate problem) + 396562 Add an implementation of RequestLog that supports Slf4j + 398467 Servlet 3.1 Non Blocking IO + 402984 WebSocket Upgrade must honor case insensitive header fields in upgrade request + 403280 Update to javax.el 2.2.4 + 403380 Introduce WebSocketTimeoutException to differentiate between EOF on write and Timeout + 403510 HttpSession maxInactiveInterval is not serialized in HashSession + 403591 do not use the ConcurrentArrayBlockingQueue for thread pool, selector and async request log + 403817 Use of WebSocket Session.close() results in invalid status code + 405188 HTTP 1.0 with GET returns internal IP address. + 405422 Implement servlet3.1 spec sections 4.4.3 and 8.1.4 for new HttpSessionIdListener class + 405432 Check implementation of section 13.4.1 @ServletSecurity for @HttpConstraint and HttpMethodConstraint clarifications + 405435 Implement servlet3.1 section 13.6.3 for 303 redirects for Form auth + 405437 Implement section 13.8.4 Uncovered HTTP methods + 405525 Throw IllegalArgumentException if filter or servlet name is null or empty string in ServletContext.addXXX() methods + 405526 Deployment must fail if more than 1 servlet maps to same url pattern + 405531 Implement Part.getSubmittedFileName() + 405533 Implement special role ** for security constraints + 405535 Implement Request.isUserInRole(role) check security-role-refs defaulting to security-role if no matching ref + 405944 Check annotation and resource injection is supported for AsyncListener + 406759 supressed stacktrace in ReferrerPushStrategyTest + 407708 HttpUpgradeHandler must support injection + 408782 Transparent Proxy - rewrite URL is ignoring query strings. + 408904 Enhance CommandlineBuilder to not escape strings inside single quotes + 409403 fix IllegalStateException when SPDY is used and the response is written through BufferUtil.writeTo byte by byte + 409796 fix and cleanup ReferrerPushStrategy. There's more work to do here, so it remains @Ignore for now + 409953 return buffer.slice() instead of buffer.asReadOnlyBuffer() in ResourceCache to avoid using inefficent path in BufferUtil.writeTo + 410083 Jetty clients submits incomplete URL to proxy. + 410098 inject accept-encoding header for all http requests through SPDY as SPDY clients MUST support spdy. Also remove two new tests that have been to implementation agnostic and not needed anymore due to recent code changes + 410246 HttpClient with proxy does not tunnel HTTPS requests. + 410341 suppress stacktraces that happen during test setup shutdown after successful test run + 410800 Make RewritePatternRule queryString aware + 411069 better set compiler defaults to 1.7, including webdefault.xml for jsp + 411934 War overlay configuration assumes src/main/webapp exists + 412205 SSL handshake failure leads to unresponsive UpgradeConnection + 412418 HttpTransportOverSPDY fix race condition while sending push streams that could cause push data not to be sent. Fixes intermittent test issues in ReferrerPushStrategyTest + 412729 SPDYClient needs a Promise-based connect() method. + 412829 Allow any mappings from web-default.xml to be overridden by web.xml + 412830 Error Page match ServletException then root cause + 412840 remove Future in SPDYClient.connect() and return Session instead in blocking version + 412934 Ignore any re-definition of an init-param within a descriptor + 412935 setLocale is not an explicit set of character encoding + 412940 minor threadsafe fixes + 413018 ServletContext.addListener() should throw IllegalArgumentException if arg is not correct type of listener + 413020 Second call to HttpSession.invalidate() should throw exception 413019 HttpSession.getCreateTime() should throw exception after session is invalidated + 413291 Avoid SPDY double dispatch + 413387 onResponseHeaders is not called multiple times when multiple redirects occur. + 413484 setAttribute in nosql session management better handles _dirty status + 413531 Introduce pluggable transports for HttpClient. + 413684 deprecated unsafe alias checkers + 413737 hide stacktrace in ReferrerPushStrategyTest + 413901 isAsyncStarted remains true while original request is dispatched + 414167 WebSocket handshake upgrade from FireFox fails due to keep-alive + 414431 Avoid debug NPE race + 414635 Modular start.d and jetty.base property + 414640 HTTP header value encoding + 414725 Annotation Scanning should exclude webapp basedir from path validation checks + 414731 Request.getCookies() should return null if there are no cookies + 414740 Removed the parent peeking Loader + 414891 Errors thrown by ReadListener and WriteListener not handled correctly. + 414898 Only upgrade v0 to v1 cookies on dquote , ; backslash space and tab in the value + 414913 WebSocket / Performance - reduce ByteBuffer allocation/copying during generation/writing + 414923 CompactPathRule needs to also compact the uri + 415047 Create URIs lazily in HttpClient. + 415062 SelectorManager wakeup optimisation. + 415131 Avoid autoboxing on debug + 415192 maps to JspPropertyGroupServlet instead of JspServlet + 415194 Deployer gives management of context to context collection + 415302 + 415314 Jetty should not commit response on output if < Response.setBufferSize() bytes are written + 415330 Avoid multiple callbacks at EOF + 415401 WebAppProvider: override XmlConfiguration.initializeDefaults + 415548 migrate ProxyHTTPToSPDYTest to use HttpClient to avoid intermittent NPE part 2 + 415605 fix status code logging for async requests + 415641 Remove remaining calls to deprecated HttpTranspoert.send + 415656 SPDY - add IdleTimeout per Stream functionality + 415744 Reduce Future usage in websocket + 415745 Include followed by forward using a PrintWriter incurs unnecessary delay + 415780 fix StreamAlreadyCommittedException in spdy build + 415825 fix stop support in modular start setup + 415826 modules initialised with --add-to-start and --add-to-startd + 415827 jetty-start / update --help text for new command line options + 415830 jetty-start / add more TestUseCases for home + base + modules configurations + 415831 rename ini keyword from MODULES= to --module= + 415832 jetty-start / fix ClassNotFound exception when starting from empty base directory + 415839 jetty-start / warning about need for --exec given when not needed by default configuration + 415899 jetty-start / add --lib= capability from Jetty 7/8 + 415913 support bootlib and download in modules + 415999 Fix some of FindBugs warnings + 416015 Handle null Accept-Language and other headers + 416026 improve error handlig in SPDY parsers + 416096 DefaultServlet leaves open file descriptors with file sizes greater than response buffer + 416102 Clean up of async sendContent process + 416103 Added AllowSymLinkAliasChecker.java + 416143 mod file format uses [type] + 416242 respect persistence headers in ProxyHTTPSPDYConnection + 416251 ProxyHTTPToSPDYConnection now sends a 502 to the client if it receives a rst frame from the upstream spdy server + 416266 HttpServletResponse.encodeURL() encodes on first request when only SessionTrackingMode.COOKIE is used + 416314 jetty async client wrong behaviour for HEAD Method + Redirect. + 416321 handle failure during blocked committing write + 416477 Improved consumeAll error handling + 416568 Simplified servlet exception logging + 416585 WebInfConfiguration examines webapp classloader first instead of its parent when looking for container jars + 416597 Allow classes and jars on the webappcontext extraclasspath to be scanned for annotations + 416663 Content-length set by resourcehandler + 416674 run all jetty-ant tests on random ports + 416679 Change warning to debug if no transaction manager present + 416680 remove uncovered constraint warning + 416681 Remove unnecessary security constraints in test-jetty-webapp + 416763 WebSocket / Jsr Session.getPathParameters() is empty + 416764 WebSocket / Jsr Session.getRequestURI() is missing scheme + host + port + query parameters + 416787 StringIndexOutOfBounds with a pathMap of "" + 416812 Don't start WebSocketClient for every context + 416990 JMX names statically unique + 417022 Request attribute access to Server,HttpChannel & HttpConnection + 417023 Add Default404Servlet if no default servlet set + 417108 demo-base uses HTTPS + 417109 Demo / Jaas test fails to find etc/login.conf + 417110 Demo / html body end tag missing in authfail.html + 417111 Demo / login with admin/admin fails + 417133 WebSocket / deflate-frame should accumulate decompress byte buffers properly + 417134 WebSocket / Jsr ServerEndpointConfig.Configurator.getNegotiatedExtensions() is never used + 417225 added Container.addEventListener method + 417260 Protected targets matched as true URI path segments jetty-8.1.13.v20130916 - 16 September 2013 + 412629 PropertyFileLoginModule doesn't cache user configuration file even for refreshInterval=0 + 413484 setAttribute in nosql session management better handles _dirty status + 413684 deprecated unsafe alias checkers + 414235 RequestLogHandler configured on a context fails to handle forwarded requests + 414393 StringIndexOutofBoundsException with > 8k multipart content without CR or LF + 414431 Avoid debug NPE race + 414507 Ensure AnnotationParser ignores parent dir hierarchy when checking for hidden dirnames + 414652 WebSocket's sendMessage() may hang on congested connections. + 415192 maps to JspPropertyGroupServlet instead of JspServlet + 415401 Add XmlConfiguration.initializeDefaults that allows to set default values for any XmlConfiguration that may be overridden in the config file + 416266 HttpServletResponse.encodeURL() encodes on first request when only SessionTrackingMode.COOKIE is used + 416585 WebInfConfiguration examines webapp classloader first instead of its parent when looking for container jars + 416787 StringIndexOutOfBounds with a pathMap of "" + 416990 JMX names statically unique jetty-7.6.13.v20130916 - 16 September 2013 + 412629 PropertyFileLoginModule doesn't cache user configuration file even for refreshInterval=0 + 413484 setAttribute in nosql session management better handles _dirty status + 413684 deprecated unsafe alias checkers + 414235 RequestLogHandler configured on a context fails to handle forwarded requests + 414393 StringIndexOutofBoundsException with > 8k multipart content without CR or LF + 414431 Avoid debug NPE race + 414507 Ensure AnnotationParser ignores parent dir hierarchy when checking for hidden dirnames + 414652 WebSocket's sendMessage() may hang on congested connections. + 415192 maps to JspPropertyGroupServlet instead of JspServlet + 415401 Add XmlConfiguration.initializeDefaults that allows to set default values for any XmlConfiguration that may be overridden in the config file + 416585 WebInfConfiguration examines webapp classloader first instead of its parent when looking for container jars + 416990 JMX names statically unique jetty-9.0.5.v20130815 - 15 August 2013 + 414898 Only upgrade v0 to v1 cookies on dquote , ; backslash space and tab in the value + 404468 Ported jetty-http-spi to Jetty-9 + 405424 add X-Powered-By and Server header to SPDY + 405535 implement Request.isUserInRole(role) check security-role-refs defaulting to security-role if no matching ref + 408235 SPDYtoHTTP proxy fix: remove hop headers from upstream server + 409028 Jetty HttpClient does not work with proxy CONNECT method. + 409282 fix intermittently failing MaxConcurrentStreamTest + 409845 add test that makes sure that DataFrameGenerator correctly prepends the header information + 410498 ignore type of exception in GoAwayTest.testDataNotProcessedAfterGoAway + 410668 HTTP client should support the PATCH method. + 410800 Make RewritePatternRule queryString aware + 410805 StandardSession: remove all frameBytes for a given stream from queue if the stream is reset + 411216 RequestLogHandler handles async completion + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued parameters 411459 MultiPartFilter.Wrapper getParameter should use charset encoding of part + 411538 Use Replacement character for bad parameter % encodings + 411545 SslConnection.DecryptedEndpoint.fill() sometimes misses a few network bytes + 411755 MultiPartInputStreamParser fails on base64 encoded content + 411844 ArrayIndexOutOfBoundsException on wild URL. + 411909 GzipFilter flushbuffer() results in erroneous finish() call + 412234 fix bug where NetworkTrafficSelectChannelEndpoint counted bytes wrong on incomplete writes + 412318 HttpChannel fix multiple calls to _transport.completed() if handle() is called multiple times while the channel is COMPLETED + 412418 HttpTransportOverSPDY fix race condition while sending push streams that could cause push data not to be sent. Fixes intermittent test issues in ReferrerPushStrategyTest + 412442 Avoid connection timeout after FIN-FIN close + 412466 Improved search for unset JETTY_HOME + 412608 EOF Chunk not sent on inputstream static content + 412629 PropertyFileLoginModule doesn't cache user configuration file even for refreshInterval=0 + 412637 ShutdownMonitorThread already started + 412712 HttpClient does not send the terminal chunk after partial writes. + 412713 add dumpOnStart configuration to jetty-maven-plugin + 412750 HttpClient close expired connections fix + 412814 HttpClient calling CompleteListener.onComplete() twice. + 412846 jetty Http Client Connection through Proxy is failing with Timeout. + 412938 Request.setCharacterEncoding now throws UnsupportedEncodingException instead of UnsupportedCharsetException + 413034 Multiple webapps redeploy returns NamingException with AppDynamics javaagent + 413066 accept lower case method: head + 413108 HttpClient hardcodes dispatchIO=false when using SSL. + 413113 Inconsistent Request.getURI() when adding parameters via Request.param(). + 413154 ContextHandlerCollection defers virtual host handling to ContextHandler + 413155 HttpTransportOverSPDY remove constructor argument for version and get version from stream.getSession instead + 413371 Default JSON.Converters for List and Set. + 413372 JSON Enum uses name rather than toString() + 413393 better logging of bad URLs in Resources + 413486 SessionCookieConfig setters should throw IllegalStateException if called after context started + 413568 Made AJP worker name generic + 413684 Trailing slash shows JSP source + 413901 isAsyncStarted remains true while original request is dispatched + 414085 Add jetty-continuations to plugin dependencies + 414101 Do not escape special characters in cookies + 414235 RequestLogHandler configured on a context fails to handle forwarded requests + 414393 StringIndexOutofBoundsException with > 8k multipart content without CR or LF + 414449 Added HttpParser strict mode for case sensitivity + 414507 Ensure AnnotationParser ignores parent dir hierarchy when checking for hidden dirnames + 414625 final static version fields + 414640 HTTP header value encoding + 414652 WebSocket's sendMessage() may hang on congested connections. + 414727 Ensure asynchronously flushed resources are closed + 414763 Added org.eclipse.jetty.util.log.stderr.ESCAPE option + 414833 HttpSessionListener.destroy must be invoked in reverse order + 414840 Request.login() throws NPE if username is null + 414951 QueuedThreadPool fix constructor that missed to pass the idleTimeout + 414972 HttpClient may read bytes with pre-tunnelled connection. jetty-9.0.4.v20130625 - 25 June 2013 + 396706 CGI support parameters + 397051 Make JDBCLoginService data members protected to facilitate subclassing + 397193 MongoSessionManager refresh updates last access time + 398467 Servlet 3.1 Non Blocking IO + 400503 WebSocket - squelch legitimate Exceptions during testing to avoid false positives + 401027 javadoc JMX annotations + 404508 enable overlay deployer + 405188 HTTP 1.0 with GET returns internal IP address. + 405313 Websocket client SSL hostname verification is broken, always defaults to raw IP as String + 406759 supressed stacktrace in ReferrerPushStrategyTest + 406923 Accept CRLF or LF but not CR as line termination + 407246 Test harness checked results in callbacks ignored. + 407325 Test Failure: org.eclipse.jetty.servlets.EventSourceServletTest.testEncoding + 407326 Test Failure: org.eclipse.jetty.client.HttpClientStreamTest.testInputStreamResponseListenerFailedBeforeResponse[0]. + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7 + 407386 Cookies not copied in ServletWebSocketRequest + 407469 Method parameters for @OnWebSocketError should support Throwable + 407470 Javadoc for @OnWebSocketFrame incorrectly references WebSocketFrame object + 407491 Better handle empty Accept-Language + 407614 added excludedMimeTypes to gzipFilter + 407812 jetty-maven-plugin can not handle whitespaces in equivalent of WEB-INF/classes paths + 407931 Add toggle for failing on servlet availability + 407976 JDBCSessionIdManager potentially leaves server in bad state after startup + 408077 HashSessionManager leaves file handles open after being stopped + 408117 isAsyncStarted is false on redispatch + 408118 NullPointerException when parsing request cookies + 408167 JDBCSessionManager don't mark session as dirty if same attribute value set + 408281 Inconsistent start/stop handling in ContainerLifeCycle + 408446 Multipart parsing issue with boundry and charset in ContentType header + 408529 Etags set in 304 response + 408600 set correct jetty.url in all pom files + 408642 setContentType from addHeader + 408662 In pax-web servlet services requests even if init() has not finished running + 408709 refactor test-webapp's chat application. Now there's only a single request for user login and initial chat message. + 408720 NPE in AsyncContext.getRequest() + 408723 Jetty Maven plugin reload ignores web.xml listeners + 408768 JSTL jars not scanned by jetty-ant + 408771 Problem with ShutdownMonitor for jetty-ant + 408782 Transparent Proxy - rewrite URL is ignoring query strings. + 408806 getParameter returns null on Multipart request if called before request.getPart()/getParts() + 408904 Enhance CommandlineBuilder to not escape strings inside single quotes + 408909 GzipFilter setting of headers when reset and/or not compressed + 408910 META-INF/jetty-webapp-context.xml file should be able to refer to bundle-relative locations + 408923 Need to be able to configure the ThreadPool for the default jetty server in osgi + 408945 XML Args ignored without DTD + 409012 added reference to example rewrite rules + 409133 Empty causes StackOverflowError + 409228 Set jetty.home property so config files work even if deployed inside a bundle + 409403 fix IllegalStateException when SPDY is used and the response is written through BufferUtil.writeTo byte by byte + 409436 NPE on context restart using dynamic servlet registration + 409441 jetty.xml threadpool arg injection + 409449 Ensure servlets, filters and listeners added via dynamic registration, annotations or descriptors are cleaned on context restarts + 409545 Change HttpChannel contract + 409556 Resource files not closed + 409598 spdy: Fix NPE when a broken client tried to create duplicate stream IDs + 409684 Ids and properties not set for execution of jetty xml config files with mvn plugin + 409796 fix intermittent test issue in ReferrerPushStrategy.testResourceOrder. Happened when the client got closed before the server finished sending all data frames. Client waits now until all data is received. + 409801 Jetty should allow webdefault to be specified using a relative location when running in OSGi + 409842 Suspended request completed by a request thread does not set read interest. + 409953 return buffer.slice() instead of buffer.asReadOnlyBuffer() in ResourceCache to avoid using inefficent path in BufferUtil.writeTo + 409978 Websocket shouldn't create HttpSession if not present + 410083 Jetty clients submits incomplete URL to proxy. + 410098 inject accept-encoding header for all http requests through SPDY as SPDY clients MUST support spdy. Also remove two new tests that have been to implementation agnostic and not needed anymore due to recent code changes + 410175 WebSocketSession#isSecure() doesn't return true for SSL session on the server side + 410246 HttpClient with proxy does not tunnel HTTPS requests. + 410337 throw EofException instead of EOFException in HttpOutput.write() if HttpOutpyt is closed + 410341 suppress stacktraces that happen during test setup shutdown after successful test run + 410370 WebSocketCreator.createWebSocket() should use servlet specific parameters + 410372 Make SSL client certificate information available to server websockets + 410386 WebSocket Session.getUpgradeRequest().getRequestURI() returns bad URI on server side + 410405 Avoid NPE for requestDispatcher(../) + 410469 UpgradeRequest is sent twice when using SSL, one fails warning about WritePendingException + 410522 jetty start broken for command line options + 410537 Exceptions during @OnWebSocketConnect not reported to @OnWebSocketError + 410559 Removed FillInterest race + 410630 MongoSessionManager conflicting session update op + 410693 ServletContextHandler.setHandler does not relink handlers - check for null + 410750 NoSQLSessions: implement session context data persistence across server restarts + 410799 errors while creating push streams in HttpTransportOverSPDY are now logged to debug instead of warn + 410893 async support defaults to false for spec created servlets and filters + 410911 Continuation isExpired handling. + 410995 Avoid reverse DNS lookups when creating SSLEngines. + 411061 fix cookie handling in spdy. If two different HTTP headers with the same name are set, they should be translated to a single multiheader value according to: http://www.chromium.org/spdy/spdy-protocol/spdy-protocol-draft3#TOC-2.6.10-Name-Value-Header-Block. That applies for Set-Cookie headers for example. Before this changed duplicate header names have overwritten the previous one + 411135 HttpClient may send proxied https requests to the proxy instead of the target server. + 411340 add comment why executeOnFillable defaults to true + 411545 SslConnection.DecryptedEndpoint.fill() sometimes misses a few network bytes jetty-9.0.3.v20130506 - 06 May 2013 + 404010 fix cast exception in mongodb session manager + 404911 WebSocketCloseTest fails spuriously + 405281 allow filemappedbuffers to not be used + 405327 Modular Start.ini + 405530 Wrap AsyncContext to throw ISE after complete + 405537 NPE in rendering JSP using SPDY and wrapped ServletRequest + 405570 spdy push: resource ordering and sequential push. + 405631 Plugin gives error when its started twice + 405925 Redeploy with jetty-maven-plugin fails + 406015 Query parameters and POST queries. Fixed proxy case where the path is rewritten to be absolute. + 406202 re-enabled connector statistics + 406214 fix constructor for PushSynInfo ignores timeout, remove timeout for creating push streams in HttpTransportOverSPDY + 406272 Security constraints with multiple http-method-omissions can be incorrectly applied + 406390 406617 removed tiny race from handling of suspend and complete + 406437 Digest Auth supports out of order nc + 406449 Session's disconnect not detected + 406617 Spin in Request.recycle + 406618 Jetty startup in OSGi Equinox fails when using option jetty.home.bundle=org.eclipse.jetty.osgi.boot + 406753 jetty-runner contains invalid signature files + 406768 Improved handling of static content resources + 406861 IPv6 redirects fail. + 406923 Accept CRLF or LF but not CR as line termination + 406962 Improve attribute names in Request + 407075 Do not dispatch from complete + 407135 Unauthorized response causes retry loop. + 407136 @PreDestroy called after Servlet.destroy() + 407173 java.lang.IllegalStateException: null when using JDBCSessionManager + 407214 Reduce build logging of OSGi modules jetty-9.0.2.v20130417 - 17 April 2013 + 364921 FIN WAIT sockets + 402885 reuse Deflaters in GzipFilter + 403591 do not use the ConcurrentArrayBlockingQueue for thread pool, selector and async request log + 404511 fixed poor methods in ArrayTernaryTrie + 405119 Tidy up comments and code formatting for osgi + 405352 Servlet init-param always overridden by WebServlet annotation + 405364 spdy imeplement MAX_CONCURRENT_STREAMS + 405449 spdy improve handling of duplicate stream Ids + 405540 ServletContextListeners call in reverse in doStop + 405551 InputStreamResponseListener.await returns null when request fails. + 405679 example other server for documentation jetty-9.0.1.v20130408 - 08 April 2013 + 384552 add comment to jetty-https.xml describing keymanager password + 385488 non existing resources in collection are just warnings + 392129 fixed merged of handling of timeouts after startAsync + 393971 Improve setParentLoaderPriorty javadoc + 393972 Improve WebAppContext classloading javadoc + 395620 do not managed inherited life cycle listeners + 396562 Add an implementation of RequestLog that supports Slf4j + 399967 Destroyables destroyed on undeploy and shutdown hook + 400142 ConcurrentModificationException in JDBC SessionManger + 400144 When loading a session fails the JDBCSessionManger produces duplicate session IDs + 400689 Add support for Proxy authentication. + 401150 close input stream used from cached resource + 401806 spdy push properly pass through request and response headers for pushed resources + 402397 InputStreamResponseListener early close inputStream cause hold lock. + 402485 reseed secure random + 402626 Do not required endpoint host checking by default in server and configure in client + 402666 Improve handling of TLS exceptions due to raw socket close. + 402694 setuid as LifeCycle listener + 402706 HttpSession.setMaxInactiveInterval(int) does not change JDBCSession expiry + 402726 WebAppContext references old WebSocket packages in system and server classes + 402735 jetty.sh to support status which is == check + 402757 WebSocket client module can't be used with WebSocket server module in the same WAR. + 402833 Test harness for global error page and hide exception message from reason string + 402844 STOP.PORT & STOP.KEY behaviour has changed + 402982 Premature initialization of Servlets + 402984 WebSocket Upgrade must honor case insensitive header fields in upgrade request + 403122 Session replication fails with ClassNotFoundException when session attribute is Java dynamic proxy + 403280 Update to javax.el 2.2.4 + 403281 jetty.sh waits for started or failure before returning + 403360 Named connectors + 403370 move frameBytes.fail() call in StandardSession.flush() outside the synchronized block to avoid deadlock + 403373 WebSocket change timeout log level from warn -> info + 403380 Introduce WebSocketTimeoutException to differentiate between EOF on write and Timeout + 403451 Review synchronization in SslConnection. + 403510 HttpSession maxInactiveInterval is not serialized in HashSession + 403513 jetty:run goal cannot be executed twice during the maven build + 403570 Asynchronous Request Logging + 403591 do not use the ConcurrentArrayBlockingQueue for thread pool, selector and async request log + 403817 Use of WebSocket Session.close() results in invalid status code + 404029 port jetty-monitor to jetty-9 and activate it + 404036 JDBCSessionIdManager.doStart() method should not call cleanExpiredSessions() because Listeners can't be notified + 404067 If cannot connect to db fail startup of JDBCSessionIdManager + 404128 Add Vary headers rather than set them + 404176 Jetty's AnnotationConfiguration class does not scan non-jar resources on the container classpath + 404204 Exception from inputstream cause hang or timeout. + 404283 org.eclipse.jetty.util.Scanner.scanFile() dies with an NPE if listFiles() returns null + 404323 Improved parameterization of https and SPDY + 404325 data constraint redirection does send default port + 404326 set status when Request.setHandled(true) is called + 404511 Replaced all StringMap usage with Tries + 404517 Close connection if request received after half close + 404610 Reintroduce ability to disallow TLS renegotiation. + 404757 SPDY can only be built with the latest JDK version. + 404789 Support IPv6 addresses in DoSFilter white list. + 404881 Allow regexs for SslContextFactory.setIncludeCipherSuites() and .setExcludeCipherSuites() + 404889 SelectorManager accepts attachments with sockets + 404906 servlets with load-on-startup = 0 are not fired up on jetty 9 startup + 404958 Fixed Resource.newSystemResource striped / handling + 405044 Query parameters lost for non GET or POST. jetty-9.0.0.v20130308 - 08 March 2013 + 399070 add updated version of npn-boot jar to start.ini + 399799 do not hold lock while calling invalidation listeners + 399967 Destroyables destroyed on undeploy and shutdown hook + 400312 ServletContextListener.contextInitialized() is not called when added in ServletContainerInitializer.onStartup + 401495 removed unused getOutputStream + 401531 StringIndexOutOfBoundsException for "/*" of fix for multiple mappings to *.jsp + 401641 Fixed MBean setter for String[] + 401642 Less verbose INFOs + 401643 Improved Authentication exception messages and provided quiet servlet exception + 401644 Dump does not login user already logged in + 401651 Abort request if maxRequestsQueuedPerDestination is reached. + 401777 InputStreamResponseListener CJK byte (>=128) cause EOF. + 401904 fixed getRemoteAddr to return IP instead of hostname + 401908 Enhance DosFilter to allow dynamic configuration of attributes. + 401966 Ensure OSGI WebApp as Service (WebAppContext) can be deployed only through ServiceWebAppProvider + 402008 Websocket blocking write hangs when remote client dies (or is killed) without going thru Close handshake + 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty server is stopped + 402075 Massive old gen growth when hit by lots of non persistent connections. + 402090 httpsender PendingState cause uncertain data send to server. + 402106 fixed URI resize in HttpParser + 402148 Update Javadoc for WebSocketServlet for new API + 402154 WebSocket / Session.setIdleTimeout(ms) should support in-place idle timeout changes + 402185 updated javascript mime-type + 402277 spdy proxy: fix race condition in nested push streams initiated by upstream server. Fix several other small proxy issues + 402316 HttpReceiver and null pointer exception. + 402341 Host with default port causes redirects loop. + 402726 WebAppContext references old WebSocket packages in system and server classes + 402757 WebSocket client module can't be used with WebSocket server module in the same WAR jetty-8.1.12.v20130726 - 26 July 2013 + 396706 CGI support parameters + 397193 MongoSessionManager refresh updates last access time + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7 + 408529 Etags set in 304 response + 408600 set correct jetty.url in all pom files + 408642 setContentType from addHeader + 408662 In pax-web servlet services requests even if init() has not finished running + 408806 getParameter returns null on Multipart request if called before request.getPart()/getParts() + 408909 GzipFilter setting of headers when reset and/or not compressed + 409028 Jetty HttpClient does not work with proxy CONNECT method. + 409133 Empty causes StackOverflowError + 409436 NPE on context restart using dynamic servlet registration + 409449 Ensure servlets, filters and listeners added via dynamic registration, annotations or descriptors are cleaned on context restarts + 409556 FileInputStream not closed in DirectNIOBuffer + 410405 Avoid NPE for requestDispatcher(../) + 410630 MongoSessionManager conflicting session update op + 410750 NoSQLSessions: implement session context data persistence across server restarts + 410893 async support defaults to false for spec created servlets and filters + 411135 HttpClient may send proxied https requests to the proxy instead of the target server. + 411216 RequestLogHandler handles async completion + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued parameters 411459 MultiPartFilter.Wrapper getParameter should use charset encoding of part + 411755 MultiPartInputStreamParser fails on base64 encoded content + 411909 GzipFilter flushbuffer() results in erroneous finish() call + 412712 HttpClient does not send the terminal chunk after partial writes. + 412750 HttpClient close expired connections fix + 413371 Default JSON.Converters for List and Set. + 413372 JSON Enum uses name rather than toString() + 413684 Trailing slash shows JSP source + 413812 Make RateTracker serializable jetty-7.6.12.v20130726 - 26 July 2013 + 396706 CGI support parameters + 397193 MongoSessionManager refresh updates last access time + 407342 ReloadedSessionMissingClassTest uses class compiled with jdk7 + 408529 Etags set in 304 response + 408600 set correct jetty.url in all pom files + 408642 setContentType from addHeader + 408662 In pax-web servlet services requests even if init() has not finished running + 408909 GzipFilter setting of headers when reset and/or not compressed + 409028 Jetty HttpClient does not work with proxy CONNECT method. + 409133 Empty causes StackOverflowError + 409556 FileInputStream not closed in DirectNIOBuffer + 410630 MongoSessionManager conflicting session update op + 410750 NoSQLSessions: implement session context data persistence across server restarts + 411135 HttpClient may send proxied https requests to the proxy instead of the target server. + 411216 RequestLogHandler handles async completion + 411458 MultiPartFilter getParameterMap doesn't preserve multivalued parameters 411459 MultiPartFilter.Wrapper getParameter should use charset encoding of part + 411755 MultiPartInputStreamParser fails on base64 encoded content + 411909 GzipFilter flushbuffer() results in erroneous finish() call + 412712 HttpClient does not send the terminal chunk after partial writes. + 412750 HttpClient close expired connections fix + 413371 Default JSON.Converters for List and Set. + 413372 JSON Enum uses name rather than toString() + 413684 Trailing slash shows JSP source + 413812 Make RateTracker serializable jetty-8.1.11.v20130520 - 20 May 2013 + 402844 STOP.PORT & STOP.KEY behaviour has changed + 403281 jetty.sh waits for started or failure before returning + 403513 jetty:run goal cannot be executed twice during the maven build + 403570 Asynchronous Request Logging + 404010 fix cast exception in mongodb session manager + 404128 Add Vary headers rather than set them + 404283 org.eclipse.jetty.util.Scanner.scanFile() dies with an NPE if listFiles() returns null + 404325 data constraint redirection does send default port + 404517 Close connection if request received after half close + 404789 Support IPv6 addresses in DoSFilter white list. + 404958 Fixed Resource.newSystemResource striped / handling + 405281 allow filemappedbuffers to not be used + 405537 NPE in rendering JSP using SPDY and wrapped ServletRequest + 406437 Digest Auth supports out of order nc + 406618 Jetty startup in OSGi Equinox fails when using option jetty.home.bundle=org.eclipse.jetty.osgi.boot + 406923 CR line termination + 407136 @PreDestroy called after Servlet.destroy() + 407173 java.lang.IllegalStateException: null when using JDBCSessionManager + 407931 Add toggle for failing on servlet availability + 407976 JDBCSessionIdManager potentially leaves server in bad state after startup + 408077 HashSessionManager leaves file handles open after being stopped + 408446 Multipart parsing issue with boundry and charset in ContentType header jetty-8.1.10.v20130312 - 12 March 2013 + 376273 Early EOF because of SSL Protocol Error on https://api-3t.paypal.com/nvp. + 381521 allow compress methods to be configured + 392129 fixed handling of timeouts after startAsync + 394064 ensure that JarFile instances are closed on JarFileResource.release() + 398649 ServletContextListener.contextDestroyed() is not called on ContextHandler unregistration + 399703 made encoding error handling consistent + 399799 do not hold lock while calling invalidation listeners + 399967 Shutdown hook calls destroy + 400040 NullPointerException in HttpGenerator.prepareBuffers + 400142 ConcurrentModificationException in JDBC SessionManger + 400144 When loading a session fails the JDBCSessionManger produces duplicate session IDs + 400312 ServletContextListener.contextInitialized() is not called when added in ServletContainerInitializer.onStartup + 400457 Thread context classloader hierarchy not searched when finding webapp's java:comp/env + 400859 limit max size of writes from cached content + 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib + 401317 Make Safari 5.x websocket support minVersion level error more clear + 401382 Prevent parseAvailable from parsing next chunk when previous has not been consumed. Handle no content-type in chunked request. + 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser + 401485 zip file closed exception + 401531 StringIndexOutOfBoundsException for "/*" of fix for multiple mappings to *.jsp + 401908 Enhance DosFilter to allow dynamic configuration of attributes. + 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty server is stopped + 402485 reseed secure random + 402735 jetty.sh to support status which is == check + 402833 Test harness for global error page and hide exception message from reason string jetty-7.6.11.v20130520 - 20 May 2013 + 402844 STOP.PORT & STOP.KEY behaviour has changed + 403281 jetty.sh waits for started or failure before returning + 403513 jetty:run goal cannot be executed twice during the maven build + 403570 Asynchronous Request Logging + 404010 fix cast exception in mongodb session manager + 404128 Add Vary headers rather than set them + 404283 org.eclipse.jetty.util.Scanner.scanFile() dies with an NPE if listFiles() returns null + 404325 data constraint redirection does send default port + 404517 Close connection if request received after half close + 404789 Support IPv6 addresses in DoSFilter white list. + 404958 Fixed Resource.newSystemResource striped / handling + 405281 allow filemappedbuffers to not be used + 405537 NPE in rendering JSP using SPDY and wrapped ServletRequest + 406437 Digest Auth supports out of order nc + 406923 CR line termination + 407136 @PreDestroy called after Servlet.destroy() + 407173 java.lang.IllegalStateException: null when using JDBCSessionManager + 407976 JDBCSessionIdManager potentially leaves server in bad state after startup + 408077 HashSessionManager leaves file handles open after being stopped + 408446 Multipart parsing issue with boundry and charset in ContentType header jetty-7.6.10.v20130312 - 12 March 2013 + 376273 Early EOF because of SSL Protocol Error on https://api-3t.paypal.com/nvp. + 381521 allow compress methods to be configured + 394064 ensure that JarFile instances are closed on JarFileResource.release() + 398649 ServletContextListener.contextDestroyed() is not called on ContextHandler unregistration + 399703 made encoding error handling consistent + 399799 do not hold lock while calling invalidation listeners + 399967 Shutdown hook calls destroy + 400040 NullPointerException in HttpGenerator.prepareBuffers + 400142 ConcurrentModificationException in JDBC SessionManger + 400144 When loading a session fails the JDBCSessionManger produces duplicate session IDs + 400457 Thread context classloader hierarchy not searched when finding webapp's java:comp/env + 400859 limit max size of writes from cached content + 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib + 401317 Make Safari 5.x websocket support minVersion level error more clear + 401382 Prevent parseAvailable from parsing next chunk when previous has not been consumed. Handle no content-type in chunked request. + 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser + 401531 StringIndexOutOfBoundsException for "/*" of fix for multiple mappings to *.jsp + 401908 Enhance DosFilter to allow dynamic configuration of attributes. + 402048 org.eclipse.jetty.server.ShutdownMonitor doesn't stop after the jetty server is stopped + 402485 reseed secure random + 402735 jetty.sh to support status which is == check + 402833 Test harness for global error page and hide exception message from reason string jetty-9.0.0.RC2 - 24 February 2013 + Fix etc/jetty.xml TimerScheduler typo that is preventing normal startup + Fix etc/jetty-https.xml ExcludeCipherSuites typo that prevents SSL startup + Fix websocket memory use jetty-9.0.0.RC1 - 22 February 2013 + 227244 Remove import of backport-util-concurrent Arrays class + 362854 Continuation implementations may deadlock. + 376273 Early EOF because of SSL Protocol Error on https://api-3t.paypal.com/nvp. + 381521 allow compress methods to be configured + 388103 Add API for tracking down upload progress. + 394064 ensure that JarFile instances are closed on JarFileResource.release() + 398649 ServletContextListener.contextDestroyed() is not called on ContextHandler unregistration + 399463 add start.ini documentation for OPTIONS. Remove reference to start_config + 399520 Websocket Server Connection needs session idle timeouts + 399535 Websocket-client connect should have configurable connect timeout + 400014 Http async client DNS performance. + 400040 NullPointerException in HttpGenerator.prepareBuffers + 400184 SslContextFactory change. Disable hostname verification if trustAll is set + 400255 Using WebSocket.maxMessageSize results in IllegalArgumentException + 400434 Add support for an OutputStream ContentProvider. + 400457 Thread context classloader hierarchy not searched when finding webapp's java:comp/env + 400512 ClientUpgradeRequet.addExtension() should fail if extension is not installed + 400555 HttpProxyEngine: Add http version header in response + 400631 Calling flush() on HttpServletResponse.getOutputStream() after last byte of body causes EofException. + 400734 NPE for redirects with relative location. + 400738 ResourceHandler doesn't support range requests + 400848 Redirect fails with non-encoded location URIs. + 400849 Conversation hangs if non-first request fails when queued. + 400859 limit max size of writes from cached content + 400864 Added LowResourcesMonitor + 401177 Make org.eclipse.jetty.websocket.api.WebSocketAdapter threadsafe + 401183 Handle push streams in new method StreamFrameListener.onPush() instead of SessionFrameListener.syn() + 401211 Remove requirement for jetty-websocket.jar in WEB-INF/lib + 401317 Make Safari 5.x websocket support minVersion level error more clear + 401382 Prevent parseAvailable from parsing next chunk when previous has not been consumed. Handle no content-type in chunked request. + 401414 Hostname verification fails. + 401427 WebSocket messages sent from onConnect fail to be read by jetty websocket-client + 401474 Performance problem in org.eclipse.jetty.annotation.AnnotationParser + 401485 zip file closed exception jetty-9.0.0.RC0 - 01 February 2013 + 362226 HttpConnection "wait" call causes thread resource exhaustion + 370384 jetty-aggregate not used in jetty-distribution + 381351 defaults for keymanager and trustmanager come from their factories and not hardcoded + 381521 Only set Vary header when content could be compressed + 381689 Allow jetty-runner to specify listen host along with listen port + 382237 support non java JSON classes + 385306 added getURI method + 391248 fixing localhost checking in statistics servlet + 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet + 391345 fix missing br tag in statistics servlet + 393933 remove deprecated classes/methods and consolidate some static methods to SslContextFactory + 393968 fix typo in javadoc + 394541 remove continuation jar from distro, add as dep to test-jetty-webapp + 395232 UpgradeRequest object passed to createWebSocket() has null Session + 395444 Disabling Websocket Compress Extensions (not working with Chrome / deflate problem) + 396428 Test for WebSocket masking on client fragments per RFC 6455 Sec 5.1 + 396574 add JETTY_HOME as a location for pid to be found + 396606 make spdy proxy capable of receiving SPDY and talk HTTP to the upstream server + 397168 backed of test timing + 397769 TimerScheduler does not relinquish cancelled tasks. + 398872 SslConnection should not be notified of idle timeouts. First solution. Merge branch 'ssl_idle_timeout_ignored'. + 399132 check parent dir of session store against file to be removed + 399173 UpgradeRequest.getParameterMap() should never return null + 399242 Reduce/eliminate false sharing in BlockingArrayQueue. + 399319 Request.getURI() may return negative ports. + 399324 HttpClient does not handle correctly UnresolvedAddressException. + 399343 OnWebSocketConnect should use api.Session parameter instead. + 399344 Add missing @OnWebSocketError annotation + 399397 websocket-client needs better upgrade failure checks + 399421 Add websocket.api.Session.disconnect() for harsh low level connection disconnect + 399515 Websocket-client connect issues should report to websocket onError handlers + 399516 Websocket UpgradeException should contain HTTP Request/Response information + 399566 Running org.eclipse.jetty.server.session.MaxInactiveMigrationTest produces stack trace + 399568 OSGi tests can't find websocket classes + 399576 Server dumpStdErr throws exception if server is stopping + 399669 Remove WebSocketConnection in favor of websocket.api.Session + 399689 Websocket RFC6455 extension handshake fails if server doesn't have extension + 399703 made encoding error handling consistent + 399721 Change to jetty-9.0.0.M5 - 19 January 2013 + 367638 throw exception for excess form keys + 381521 Only set Vary header when content could be compressed + 391623 Making --stop with STOP.WAIT perform graceful shutdown + 393158 java.lang.IllegalStateException when sending an empty InputStream + 393220 remove dead code from ServletHandler and log ServletExceptions in warn instead of debug + 393733 WebSocketClient interface should support multiple connections + 395885 ResourceCache should honor useFileMappedBuffer if set + 396253 FilterRegistration wrong order + 396459 Log specific message for empty request body for multipart mime requests + 396500 HttpClient Exchange takes forever to complete when less content sent than Content-Length + 396886 MultiPartFilter strips bad escaping on filename="..." + 397110 Accept %uXXXX encodings in URIs + 397111 Tolerate empty or excessive whitespace preceeding MultiParts + 397112 Requests with byte-range throws NPE if requested file has no mimetype (eg no file extension) + 397114 run-forked with waitForChild=false can lock up + 397130 maxFormContentSize set in jetty.xml is ignored + 397190 improve ValidUrlRule to iterate on codepoints + 397321 Wrong condition in default start.config for annotations + 397535 Support pluggable alias checking to support symbolic links + 397769 TimerScheduler does not relinquish cancelled tasks. + 398105 Clean up WebSocketPolicy + 398285 ProxyServlet mixes cookies from different clients. + 398337 UTF-16 percent encoding in UTF-16 form content + 398582 Move lib/jta jar into lib/jndi + JETTY-1533 handle URL with no path jetty-9.0.0.M4 - 21 December 2012 + 392417 Prevent Cookie parsing interpreting unicode chars + 393220 remove dead code from ServletHandler and log ServletExceptions in warn instead of debug + 393770 Error in ContextHandler.setEventListeners(EventListener[]) + 394210 spdy api rename stream.syn() to stream.push() + 394211 spdy: Expose RemoteServerAddress and LocalServerAddress in StandardSession + 394294 Start web-bundles started before jetty + 394370 Add integration test for client resetting SPDY push SYN's + 394514 Preserve URI parameters in sendRedirect + 394552 HEAD requests don't work for jetty-client. + 394719 remove regex from classpath matching + 394829 Session can not be restored after SessionManager.setIdleSavePeriod has saved the session + 394839 Allow multipart mime with no boundary + 394854 optimised promise implementation + 394870 Make enablement of remote access to test webapp configurable in override-web.xml + 395168 fix unavailable attributes when return type has annotation on super class + 395215 Multipart mime with just LF and no CRLF: add test for legacy filter + 395220 New InputStream extension to allow a mix of EOL styles between headers and content + 395312 log.warn if a SPDY stream gets committed twice + 395313 HttpTransportOverSPDY.send() does not rethrow exceptions, but call Callback.failed() only + 395314 Add missing flush() call after StandardSession.complete() has been called. Some test cleanup. + 395344 Move JSR-356 (Java WebSocket API) work off to Jetty 9.1.x + 395380 add ValidUrlRule to jetty-rewrite + 395394 allow logging from boot classloader + 395574 port jetty-runner and StatisticsServlet to jetty-9 + 395605 class cast exception in XMLConfiguration fixed + 395649 add jetty-setuid back into jetty 9 and distribution + 395794 slightly modified fix for empty file extenstion to mime type mapping. Added a default, so it will also work with unknown file extensions + 396036 SPDY send controlFrames even if Stream is reset to avoid breaking the compression context + 396193 spdy remove timeout parameters from api and move them to the Info* classes + 396459 Log specific message for empty request body for multipart mime requests + 396460 Make ServerConnector configurable with jetty-maven-plugin + 396472 org.eclipse.jetty.websocket needs to be removed from serverclasses as it should only be a systemclass + 396473 JettyWebXMlConfiguration does not reset serverclasses + 396474 add websocket server classes to jetty-maven-plugin classpath + 396475 Remove unneeded websocket-server dependency from test-jetty-webapp + 396518 Websocket AB Tests should test for which side disconnected and closed.wasClean + 396687 missing jetty-io dependency in jetty-servlets + JETTY-796 jetty ant plugin improvements jetty-9.0.0.M3 - 20 November 2012 + 391623 Add option to --stop to wait for target jetty to stop + 392237 Port test-integration to jetty-9 + 392492 expect headers only examined for requests>=HTTP/1.1 + 392850 ContextLoaderListener not called in 9.0.0.M1 and M2 + 393075 1xx, 204, 304 responses ignore headers that suggest content + 393832 start connectors last + 393947 additional tests + 394143 add jetty-all aggregate via release profile + 394144 add jetty-jaspi jetty-8.1.9.v20130131 - 31 January 2013 + 362226 HttpConnection "wait" call causes thread resource exhaustion + 367638 throw exception for excess form keys + 381521 Only set Vary header when content could be compressed + 382237 support non java JSON classes + 391248 fixing localhost checking in statistics servlet + 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet + 391345 fix missing br tag in statistics servlet + 391623 Add option to --stop to wait for target jetty to stop + 392417 Prevent Cookie parsing interpreting unicode chars + 392492 expect headers only examined for requests>=HTTP/1.1 + 393075 1xx 204 and 304 ignore all headers suggesting content + 393158 java.lang.IllegalStateException when sending an empty InputStream + 393220 remove dead code from ServletHandler and log ServletExceptions in warn instead of debug + 393947 additional tests + 393968 fix typo in javadoc + 394294 A web-bundle started before jetty-osgi should be deployed as a webapp when jetty-osgi starts + 394514 Preserve URI parameters in sendRedirect + 394541 remove continuation jar from distro, add as dep to test-jetty-webapp + 394719 remove regex from classpath matching + 394811 Make JAASLoginService log login failures to DEBUG instead of WARN. Same for some other exceptions. + 394829 Session can not be restored after SessionManager.setIdleSavePeriod has saved the session + 394839 Allow multipart mime with no boundary + 394870 Make enablement of remote access to test webapp configurable in override-web.xml + 395215 Multipart mime with just LF and no CRLF + 395380 add ValidUrlRule to jetty-rewrite + 395394 allow logging from boot classloader + 396253 FilterRegistration wrong order + 396459 Log specific message for empty request body for multipart mime requests + 396500 HttpClient Exchange takes forever to complete when less content sent than Content-Length + 396574 add JETTY_HOME as a location for pid to be found + 396886 MultiPartFilter strips bad escaping on filename="..." + 397110 Accept %uXXXX encodings in URIs + 397111 Tolerate empty or excessive whitespace preceeding MultiParts + 397112 Requests with byte-range throws NPE if requested file has no mimetype (eg no file extension) + 397130 maxFormContentSize set in jetty.xml is ignored + 397190 improve ValidUrlRule to iterate on codepoints + 397321 Wrong condition in default start.config for annotations + 397535 Support pluggable alias checking to support symbolic links + 398337 UTF-16 percent encoding in UTF-16 form content + 399132 check parent dir of session store against file to be removed + JETTY-1533 handle URL with no path jetty-7.6.9.v20130131 - 31 January 2013 + 362226 HttpConnection "wait" call causes thread resource exhaustion + 367638 throw exception for excess form keys + 381521 Only set Vary header when content could be compressed + 382237 support non java JSON classes + 391248 fixing localhost checking in statistics servlet + 391249 fix for invalid XML node dispatchedTimeMean in statistics servlet + 391345 fix missing br tag in statistics servlet + 391623 Add option to --stop to wait for target jetty to stop + 392417 Prevent Cookie parsing interpreting unicode chars + 392492 expect headers only examined for requests>=HTTP/1.1 + 393075 1xx 204 and 304 ignore all headers suggesting content + 393220 remove dead code from ServletHandler and log ServletExceptions in warn instead of debug + 393947 additional tests + 393968 fix typo in javadoc + 394514 Preserve URI parameters in sendRedirect + 394541 remove continuation jar from distro, add as dep to test-jetty-webapp + 394719 remove regex from classpath matching + 394811 Make JAASLoginService log login failures to DEBUG instead of WARN. Same for some other exceptions. + 394829 Session can not be restored after SessionManager.setIdleSavePeriod has saved the session + 394839 Allow multipart mime with no boundary + 395215 Multipart mime with just LF and no CRLF + 395380 add ValidUrlRule to jetty-rewrite + 395394 allow logging from boot classloader + 396459 Log specific message for empty request body for multipart mime requests + 396500 HttpClient Exchange takes forever to complete when less content sent than Content-Length + 396574 add JETTY_HOME as a location for pid to be found + 396886 MultiPartFilter strips bad escaping on filename="..." + 397110 Accept %uXXXX encodings in URIs + 397111 Tolerate empty or excessive whitespace preceeding MultiParts + 397112 Requests with byte-range throws NPE if requested file has no mimetype (eg no file extension) + 397130 maxFormContentSize set in jetty.xml is ignored + 397190 improve ValidUrlRule to iterate on codepoints + 397321 Wrong condition in default start.config for annotations + 397535 Support pluggable alias checking to support symbolic links + 398337 UTF-16 percent encoding in UTF-16 form content + 399132 check parent dir of session store against file to be removed + JETTY-1533 handle URL with no path + 394215 Scheduled tasks throwing exceptions kill java.util.Timer thread. + 394232 add jetty-ant into jetty9 + 394357 Make JarResource constructors protected + 394370 Add unit tests for HttpTransportOverSPDY.send() + 394383 add logging of the SSLEngine + 394545 Add jetty-jaas dependency to jetty-maven-plugin + 394671 Fix setting loglevel on commandline, organize import, fix javadoc + JETTY-846 Support maven-war-plugin configuration for jetty-maven-plugin; fix NPE jetty-9.0.0.M2 - 06 November 2012 + 371170 MongoSessionManager LastAccessTimeTest fails + 391877 org.eclipse.jetty.webapp.FragmentDescriptor incorrectly reporting duplicate others for after ordering + 392237 Split jaas from jetty-plus into jetty-jaas and port the test-jaas-webapp from codehaus + 392239 Allow no error-code or exception for error-pages + 392304 fixed intermittent client SSL failure. Correctly compact in flip2fill + 392525 Add option to --stop-wait to specify timeout + 392641 JDBC Sessions not scavenged if expired during downtime + 392812 MongoSessionIDManager never purges old sessions + 392959 Review HttpClient.getConversation(long). + 393014 Mongodb purgevalid using query for purgeinvalid + 393015 Mongodb purge not rescheduled + 393075 Jetty WebSocket client cannot connect to Tomcat WebSocket Server + 393218 add xsd=application/xml mime mapping to defaults + 393291 Confusing log entry about (non) existing webAppSourceDirectory + 393303 use jetty-web.xml to explicitly add the jetty packages that need visability. This commit also sucked in some changes made to help with the documentation process (improving deployer configuration management + 393363 Use Locale.ENGLISH for all toUpperCase and toLowerCase calls + 393368 min websocket version + 393383 delay onClose call until closeOut is done + 393494 HashSessionManager can't delete unrestorable sessions on Windows + JETTY-1547 Jetty does not honor web.xml web-app/jsp-config/jsp-property-group/default-content-type + JETTY-1549 jetty-maven-plugin fails to reload the LoginService properly + JETTY-1550 virtual WEB-INF not created if project has overlays jetty-8.1.8.v20121106 - 06 November 2012 + 371170 MongoSessionManager LastAccessTimeTest fails + 388675 Non utf8 encoded query strings not decoded to parameter map using queryEncoding + 388706 Avoid unnecessary indirection through Charset.name + 389390 AnnotationConfiguration is ignored if the metadata-complete attribute is present in an override descriptor regardless of the value + 389452 if web-fragment metadata-complete==true still scan its related jar if there there is a ServletContainerInitializer, ensure webapp restarts work + 389686 Fix reference to org.eclipse.jetty.util.log.stderr.LONG system property in javadoc for StdErrLog + 389956 Bad __context set in WebAppContext.start sequence with respect to ENC setup + 389965 OPTIONS should allow spaces in comma separated list + 390108 Servlet 3.0 API for programmatic login doesn't appear to work + 390161 Apply DeferredAuthentication fix to jaspi + 390163 Implement ServletRegistration.Dynamic.setServletSecurity + 390503 http-method-omission element not being processed + 390560 The method AnnotationParser.getAnnotationHandlers(String) always returns a empty collection. + 391080 Multipart temp files can be left on disk from Request.getPart and getParts + 391082 No exception if multipart input stream incomplete + 391188 Files written with Request.getPart().write(filename) should not be auto-deleted + 391483 fix bad javadoc example in shutdown handler + 391622 Be lenient on RFC6265 restriction on duplicate cookie names in same response + 391623 Add option to --stop to wait for target jetty to stop + 391877 org.eclipse.jetty.webapp.FragmentDescriptor incorrectly reporting duplicate others for after ordering + 392239 Allow no error-code or exception for error-pages + 392525 Add option to --stop-wait to specify timeout + 392641 JDBC Sessions not scavenged if expired during downtime + 392812 MongoSessionIDManager never purges old sessions + 393014 Mongodb purgevalid using query for purgeinvalid + 393015 Mongodb purge not rescheduled + 393075 Jetty WebSocket client cannot connect to Tomcat WebSocket Server + 393218 add xsd=application/xml mime mapping to defaults + 393363 Use Locale.ENGLISH for all toUpperCase and toLowerCase calls + 393368 min websocket version + 393383 delay onClose call until closeOut is done + 393494 HashSessionManager can't delete unrestorable sessions on Windows + JETTY-1547 Jetty does not honor web.xml web-app/jsp-config/jsp-property-group/default-content-type jetty-7.6.8.v20121106 - 06 November 2012 + 371170 MongoSessionManager LastAccessTimeTest fails + 388675 Non utf8 encoded query strings not decoded to parameter map using queryEncoding + 389686 Fix reference to org.eclipse.jetty.util.log.stderr.LONG system property in javadoc for StdErrLog + 389956 Bad __context set in WebAppContext.start sequence with respect to ENC setup + 389965 OPTIONS should allow spaces in comma separated list + 390161 Apply DeferredAuthentication fix to jaspi + 390560 The method AnnotationParser.getAnnotationHandlers(String) always returns a empty collection. + 391483 fix bad javadoc example in shutdown handler + 391622 Be lenient on RFC6265 restriction on duplicate cookie names in same response + 391623 Add option to --stop to wait for target jetty to stop + 392239 Allow no error-code or exception for error-pages + 392525 Add option to --stop-wait to specify timeout + 392641 JDBC Sessions not scavenged if expired during downtime + 392812 MongoSessionIDManager never purges old sessions + 393014 Mongodb purgevalid using query for purgeinvalid + 393015 Mongodb purge not rescheduled + 393075 Jetty WebSocket client cannot connect to Tomcat WebSocket Server + 393218 add xsd=application/xml mime mapping to defaults + 393363 Use Locale.ENGLISH for all toUpperCase and toLowerCase calls + 393368 min websocket version + 393383 delay onClose call until closeOut is done + 393494 HashSessionManager can't delete unrestorable sessions on Windows jetty-9.0.0.M1 - 15 October 2012 + 369349 directory with spaces --dry-run fix + 385049 fix issue with pipelined connections when switching protocols + 387896 populate session in SessionAuthentication as a valueBound in addition to activation so it is populate when needed + 387919 throw EOFException on early eof from client on http requests + 387943 Catch CNFE when no jstl jars are installed + 387953 jstl does not work with jetty-7 in osgi + 388072 GZipFilter incorrectly gzips when Accept-Encoding: gzip; q=0 + 388073 null session id from cookie causes NPE fixed + 388079 AbstractHttpConnection. Flush the buffer before shutting output down on error condition + 388102 Jetty HttpClient memory leaks when sending larger files + 388393 WebAppProvider doesn't work alongside OSGi deployer + 388502 handle earlyEOF with 500 + 388652 Do not flush on handle return if request is suspended + 388675 Non utf8 encoded query strings not decoded to parameter map using queryEncoding + 388706 Avoid unnecessary indirection through Charset.name + 388895 Update dependencies for jetty-jndi + 389390 AnnotationConfiguration is ignored if the metadata-complete attribute is present in an override descriptor regardless of the value + 389452 if web-fragment metadata-complete==true still scan its related jar if there there is a ServletContainerInitializer, ensure webapp restarts work + 389686 Fix reference to org.eclipse.jetty.util.log.stderr.LONG system property in javadoc for StdErrLog + 389956 Bad __context set in WebAppContext.start sequence with respect to ENC setup + 389965 OPTIONS should allow spaces in comma separated list + 390108 Servlet 3.0 API for programmatic login doesn't appear to work + 390161 Apply DeferredAuthentication fix to jaspi + 390163 Implement ServletRegistration.Dynamic.setServletSecurity + 390256 Remove Jetty6 Support + 390263 Sec-WebSocket-Extensions from Chrome and Safari badly handled + 390503 http-method-omission element not being processed + 390560 The method AnnotationParser.getAnnotationHandlers(String) always returns a empty collection. + 391080 Multipart temp files can be left on disk from Request.getPart and getParts + 391082 No exception if multipart input stream incomplete + 391140 Implement x-webkit-deflate-frame extension as-used by Chrome/Safari + 391188 Files written with Request.getPart().write(filename) should not be auto-deleted + 391483 fix bad javadoc example in shutdown handler + 391588 WebSocket Client does not set masking on close frames + 391590 WebSocket client needs ability to set requested extensions + 391591 WebSocket client should support x-webkit-deflate-frame + 391622 Be lenient on RFC6265 restriction on duplicate cookie names in same response + 391623 Add option to --stop to wait for target jetty to stop + JETTY-1515 Include cookies on 304 responses from DefaultServlet. + JETTY-1532 HTTP headers decoded with platform's default encoding + JETTY-1541 fixed different behaviour for single byte writes + JETTY-1547 Jetty does not honor web.xml web-app/jsp-config/jsp-property-group/default-content-type jetty-9.0.0.M0 - 21 September 2012 + 380924 xmlconfiguration is not respected in + JETTY-1495 Ensure dynamic servlet addition does not cause servlets to be inited. + JETTY-1500 form parameters from multipart request not available via request.getParameter + JETTY-1504 HttpServletResponseWrapper ignored when using asyncContext? jetty-7.6.3.v20120416 - 16 April 2012 + 367172 Remove detection for slf4j NOPLogger + 373269 Make ServletHandler.notFound() method impl do nothing - override to send back 404. + 373421 address potential race condition related to the nonce queue removing the same nonce twice + 373952 bind called too frequently on refresh + 374018 correctly handle requestperminuted underflow + 374252 SslConnection.onClose() does not forward to nested connection. + 374258 SPDY leaks SSLEngines. Made the test more reliable. + 374367 NPE in QueuedThreadPool.dump() with early java6 jvms + 374475 Response.sendRedirect does not encode UTF-8 characters properly + 374881 Set copyWebInf to false by default + 374891 enhancement to how ProxyServlet determines the proxy target + 375009 Filter initialization error will throw MultiException + 375083 Flow control should take in account window size changes from concurrent SETTINGS + 375096 If starting a server instance fails in osgi it is cleaned up. + 375490 NPE with --help on command line + 375509 Stalled stream stalls other streams or session control frames. Now using a "death pill" instead of a boolean in order to avoid race conditions where DataInfos were read from the queue (but the boolean not updated yet), and viceversa. + 375594 fixed SSL tests so they are not order dependent + 375709 Ensure resolveTempDirectory failure does not deadlock; improve error message + 375970 HttpServletRequest.getRemoteAddr() returns null when HTTP is over SPDY. + 376201 HalfClosed state not handled properly. Addendum to restore previous behavior, where a closed stream was also half closed. + JETTY-1504 HttpServletResponseWrapper ignored when using asyncContext? jetty-8.1.2.v20120308 - 08 March 2012 + 370387 SafariWebsocketDraft0Test failure during build. + 371168 Update ClientCrossContextSessionTest + 372093 handle quotes in Require-Bundle manifest string + 372457 Big response + slow clients + pipelined requests cause Jetty spinning and eventually closing connections. Added a TODO for a method renaming that will happen in the next major release (to avoid break implementers). + 372487 JDBCSessionManager does not work with Oracle + 372806 Command line should accept relative paths for xml config files + 373037 jetty.server.Response.setContentLength(int) should not close a Writer when length=0 + 373162 add improved implementation for getParameterMap(), needs a test though and the existing setup doesn't seem like it would easily support the needed test so need to do that still + 373306 Set default user agent extraction pattern for UserAgentFilter + 373567 cert validation issue with ocsp and crldp always being enabled when validating turned on fixed + 373603 NullPointer in WebServletAnnotation + JETTY-1409 GzipFilter will double-compress application/x-gzip content + JETTY-1489 WebAppProvider attempts to deploy .svn folder + JETTY-1494 . jetty-7.6.2.v20120308 - 08 March 2012 + 370387 SafariWebsocketDraft0Test failure during build. + 371168 Update ClientCrossContextSessionTest + 372093 handle quotes in Require-Bundle manifest string + 372457 Big response + slow clients + pipelined requests cause Jetty spinning and eventually closing connections. Added a TODO for a method renaming that will happen in the next major release (to avoid break implementers). + 372487 JDBCSessionManager does not work with Oracle + 372806 Command line should accept relative paths for xml config files + 373037 jetty.server.Response.setContentLength(int) should not close a Writer when length=0 + 373162 add improved implementation for getParameterMap(), needs a test though and the existing setup doesn't seem like it would easily support the needed test so need to do that still + 373306 Set default user agent extraction pattern for UserAgentFilter + 373567 cert validation issue with ocsp and crldp always being enabled when validating turned on fixed + JETTY-1409 GzipFilter will double-compress application/x-gzip content + JETTY-1489 WebAppProvider attempts to deploy .svn folder + JETTY-1494 . jetty-8.1.1.v20120215 - 15 February 2012 + 369121 simplified test + 370120 jvm arguments added via start.ini and --exec are missing spaces + 370137 SslContextFactory does not respect order for [included|excluded]Protocols() and [included|excluded]CipherSuites(). + 370368 resolve stack overflow in mongo db session manager + 370386 Remove META-INF from jetty distro + 371040 nosqlsession needs to call correct super contructor for new sessions + 371041 valid was not being set to new mongo db sessions, and the call to mongodb api was wrong in isIdInUse + 371162 NPE protection for nested security handlers + JETTY-1484 Add option for HashSessionManager to delete session files if it can't restore them jetty-7.6.1.v20120215 - 15 February 2012 + 369121 simplified test + 370120 jvm arguments added via start.ini and --exec are missing spaces + 370137 SslContextFactory does not respect order for [included|excluded]Protocols() and [included|excluded]CipherSuites(). + 370368 resolve stack overflow in mongo db session manager + 370386 Remove META-INF from jetty distro + 371040 nosqlsession needs to call correct super contructor for new sessions + 371041 valid was not being set to new mongo db sessions, and the call to mongodb api was wrong in isIdInUse + 371162 NPE protection for nested security handlers + JETTY-1484 Add option for HashSessionManager to delete session files if it can't restore them jetty-8.1.0.v20120127 - 27 January 2012 + 368773 allow authentication to be set by non securityHandler handlers + 368992 avoid update key while flushing during a write + 369216 turned off the shared resource cache + 369349 replace quotes with a space escape method jetty-7.6.0.v20120127 - 27 January 2012 + 368773 allow authentication to be set by non securityHandler handlers + 368992 avoid update key while flushing during a write + 369216 turned off the shared resource cache + 369349 replace quotes with a space escape method jetty-8.1.0.RC5 - 20 January 2012 + 359329 Prevent reinvocation of LoginModule.login with jaspi for already authed user + 368632 Remove superfluous removal of org.apache.catalina.jsp_file + 368633 fixed configure.dtd resource mappings + 368635 moved lifecycle state reporting from toString to dump + 368773 process data constraints without realm + 368787 always set token view to new header buffers in httpparser + 368821 improved test harness + 368920 JettyAwareLogger always formats the arguments. + 368948 POM for jetty-jndi references unknown version for javax.activation. + 368992 NPE in HttpGenerator.prepareBuffers() test case. + JETTY-1475 made output state fields volatile to provide memory barrier for non dispatched thread IO jetty-7.6.0.RC5 - 20 January 2012 + 359329 Prevent reinvocation of LoginModule.login with jaspi for already authed user + 368632 Remove superfluous removal of org.apache.catalina.jsp_file + 368633 fixed configure.dtd resource mappings + 368635 moved lifecycle state reporting from toString to dump + 368773 process data constraints without realm + 368787 always set token view to new header buffers in httpparser + 368821 improved test harness + 368920 JettyAwareLogger always formats the arguments. + 368948 POM for jetty-jndi references unknown version for javax.activation. + 368992 avoid non-blocking flush when writing to avoid setting !_writable without _writeblocked + JETTY-1475 made output state fields volatile to provide memory barrier for non dispatched thread IO jetty-8.1.0.RC4 - 13 January 2012 + 365048 jetty Http client does not send proxy authentication when requesting a Https-resource through a web-proxy. + 366774 removed XSS vulnerbility + 367099 Upgrade jetty-websocket for RFC 6455 - Addendum. + 367433 added tests to investigate + 367435 improved D00 test harness + 367485 HttpExchange canceled before response do not release connection. + 367502 WebSocket connections should be closed when application context is stopped. + 367548 jetty-osgi-boot must not import the nested package twice + 367591 corrected configuration.xml version to 7.6 + 367635 Added support for start.d directory + 367638 limit number of form parameters to avoid DOS + 367716 simplified idleTimeout logic + 368035 WebSocketClientFactory does not invoke super.doStop(). + 368060 do not encode sendRedirect URLs + 368112 NPE on element parsing web.xml + 368113 Support servlet mapping to "" + 368114 Protect against non-Strings in System properties for Log + 368189 WebSocketClientFactory should not manage external thread pool. 368240 - Improve AggregateLifeCycle handling of shared lifecycles + 368215 Remove debug from jaspi + 368240 Better handling of locally created ThreadPool. Forgot to null out field. + 368291 Change warning to info for NoSuchFieldException on BeanELResolver.properties + JETTY-1467 close half closed when idle jetty-7.6.0.RC4 - 13 January 2012 + 365048 jetty Http client does not send proxy authentication when requesting a Https-resource through a web-proxy. + 366774 removed XSS vulnerbility + 367099 Upgrade jetty-websocket for RFC 6455 - Addendum. + 367716 simplified maxIdleTime logic + 368035 WebSocketClientFactory does not invoke super.doStop(). + 368060 do not encode sendRedirect URLs + 368114 Protect against non-Strings in System properties for Log + 368189 WebSocketClientFactory should not manage external thread pool. + 368215 Remove debug from jaspi + 368240 Improve AggregateLifeCycle handling of shared lifecycles + 368291 Change warning to info for NoSuchFieldException on BeanELResolver.properties jetty-8.1.0.RC2 - 22 December 2011 + 359329 jetty-jaspi must exports its packages. jetty-plus must import javax.security + 364638 HttpParser closes if data received while seeking EOF. Tests fixed to cope + 364921 Made test less time sensitive + 364936 use Resource for opening URL streams + 365267 NullPointerException in bad Address + 365375 ResourceHandler should be a HandlerWrapper + 365750 Support WebSocket over SSL, aka wss:// + 365932 Produce jetty-websocket aggregate jar for android use + 365947 Set headers for Auth failure and retry in http-spi + 366316 Superfluous printStackTrace on 404 + 366342 Dont persist DosFilter trackers in http session + 366730 pass the time idle to onIdleExpire + 367048 test harness for guard on suspended requests + 367175 SSL 100% CPU spin in case of blocked write and RST. + 367219 WebSocketClient.open() fails when URI uses default ports. + 367383 jsp-config element must be returned for ServletContext.getJspConfigDescriptor + JETTY-1460 suppress PrintWriter exceptions + JETTY-1463 websocket D0 parser should return progress even if no fill done + JETTY-1465 NPE in ContextHandler.toString jetty-7.6.0.RC3 - 05 January 2012 + 367433 added tests to investigate + 367435 improved D00 test harness + 367485 HttpExchange canceled before response do not release connection. + 367502 WebSocket connections should be closed when application context is stopped. + 367591 corrected configuration.xml version to 7.6 + 367635 Added support for start.d directory + 367638 limit number of form parameters to avoid DOS + JETTY-1467 close half closed when idle jetty-7.6.0.RC2 - 22 December 2011 + 364638 HttpParser closes if data received while seeking EOF. Tests fixed to cope + 364921 Made test less time sensitive for ssl + 364936 use Resource for opening URL streams + 365267 NullPointerException in bad Address + 365375 ResourceHandler should be a HandlerWrapper + 365750 Support WebSocket over SSL, aka wss:// + 365932 Produce jetty-websocket aggregate jar for android use + 365947 Set headers for Auth failure and retry in http-spi + 366316 Superfluous printStackTrace on 404 + 366342 Dont persist DosFilter trackers in http session + 366730 pass the time idle to onIdleExpire + 367048 test harness for guard on suspended requests + 367175 SSL 100% CPU spin in case of blocked write and RST. + 367219 WebSocketClient.open() fails when URI uses default ports. + JETTY-1460 suppress PrintWriter exceptions + JETTY-1463 websocket D0 parser should return progress even if no fill done + JETTY-1465 NPE in ContextHandler.toString jetty-8.1.0.RC1 - 06 December 2011 + 360245 The version of the javax.servlet packages to import is 2.6 instead of 3.0 + 365370 ServletHandler can fall through to nested handler jetty-8.1.0.RC0 - 30 November 2011 + 352565 cookie httponly flag ignored + 353285 ServletSecurity annotation ignored + 357163 jetty 8 ought to proxy jetty8 javadocs + 357209 JSP tag listeners not called + 360051 SocketConnectionTest.testServerClosedConnection is excluded. + 361135 Allow session cookies to NEVER be marked as secure, even on HTTPS requests. + 362249 update shell scripts to jetty8 + 363878 Add ecj compiler to jetty-8 for jsp + 364283 can't parse the servlet multipart-config for the web.xml + 364430 Support web.xml enabled state for servlets jetty-7.6.0.RC5 - 20 January 2012 + 359329 Prevent reinvocation of LoginModule.login with jaspi for already authed user + 368632 Remove superfluous removal of org.apache.catalina.jsp_file + 368633 fixed configure.dtd resource mappings + 368635 moved lifecycle state reporting from toString to dump + 368773 process data constraints without realm + 368787 always set token view to new header buffers in httpparser + 368821 improved test harness + 368920 JettyAwareLogger always formats the arguments. + 368948 POM for jetty-jndi references unknown version for javax.activation. + 368992 avoid non-blocking flush when writing to avoid setting !_writable without _writeblocked + JETTY-1475 made output state fields volatile to provide memory barrier for non dispatched thread IO jetty-7.6.0.RC4 - 13 January 2012 + 365048 jetty Http client does not send proxy authentication when requesting a Https-resource through a web-proxy. + 366774 removed XSS vulnerbility + 367099 Upgrade jetty-websocket for RFC 6455 - Addendum. + 367716 simplified idleTimeout logic + 368035 WebSocketClientFactory does not invoke super.doStop(). + 368060 do not encode sendRedirect URLs + 368114 Protect against non-Strings in System properties for Log + 368189 WebSocketClientFactory should not manage external thread pool. + 368215 Remove debug from jaspi + 368240 Improve AggregateLifeCycle handling of shared lifecycles + 368291 Change warning to info for NoSuchFieldException on BeanELResolver.properties jetty-7.6.0.RC3 - 05 January 2012 + 367433 added tests to investigate + 367435 improved D00 test harness + 367485 HttpExchange canceled before response do not release connection. + 367502 WebSocket connections should be closed when application context is stopped. + 367591 corrected configuration.xml version to 7.6 + 367635 Added support for start.d directory + 367638 limit number of form parameters to avoid DOS + JETTY-1467 close half closed when idle jetty-7.6.0.RC2 - 22 December 2011 + 364638 HttpParser closes if data received while seeking EOF. Tests fixed to cope + 364921 Made test less time sensitive for ssl + 364936 use Resource for opening URL streams + 365267 NullPointerException in bad Address + 365375 ResourceHandler should be a HandlerWrapper + 365750 Support WebSocket over SSL, aka wss:// + 365932 Produce jetty-websocket aggregate jar for android use + 365947 Set headers for Auth failure and retry in http-spi + 366316 Superfluous printStackTrace on 404 + 366342 Dont persist DosFilter trackers in http session + 366730 pass the time idle to onIdleExpire + 367048 test harness for guard on suspended requests + 367175 SSL 100% CPU spin in case of blocked write and RST. + 367219 WebSocketClient.open() fails when URI uses default ports. + JETTY-1460 suppress PrintWriter exceptions + JETTY-1463 websocket D0 parser should return progress even if no fill done + JETTY-1465 NPE in ContextHandler.toString jetty-7.6.0.RC1 - 04 December 2011 + 352565 cookie httponly flag ignored + 353285 ServletSecurity annotation ignored + 357163 jetty 8 ought to proxy jetty8 javadocs + 357209 JSP tag listeners not called + 360051 SocketConnectionTest.testServerClosedConnection is excluded. + 361135 Allow session cookies to NEVER be marked as secure, even on HTTPS requests. + 362249 update shell scripts to jetty8 + 363878 Add ecj compiler to jetty-8 for jsp + 364283 can't parse the servlet multipart-config for the web.xml + 364430 Support web.xml enabled state for servlets + 365370 ServletHandler can fall through to nested handler jetty-7.6.0.RC0 - 29 November 2011 + 349110 fixed bypass chunk handling + 360546 handle set count exceeding max integer + 362111 StdErrLog.isDebugEnabled() returns true too often + 362113 Improve Test Coverage of org.eclipse.jetty.util.log classes + 362407 setTrustStore(Resource) -> setTrustStoreResource(R) + 362447 add setMaxNonceAge() to DigestAuthenticator + 362468 NPE at line org.eclipse.jetty.io.BufferUtil.putHexInt + 362614 NPE in accepting connection + 362626 IllegalStateException thrown when SslContextFactory preconfigured with SSLContext + 362696 expand virtual host configuration options to ContextHandler and add associated test case for new behavior + 362742 improved UTF8 exception reason + 363124 improved websocket close handling + 363381 Throw IllegalStateException if Request uri is null on getServerName + 363408 GzipFilter should not attempt to compress HTTP status 204 + 363488 ShutdownHandler use stopper thread + 363718 Setting java.rmi.server.hostname in jetty-jmx.xml + 363757 partial fix + 363785 StdErrLog must use system-dependent EOL. + 363943 ignore null attribute values + 363993 EOFException parsing HEAD response in HttpTester + 364638 SCEP does idle timestamp checking. New setCheckForIdle method controls onIdleExpired callback. 364921 a second onIdleExpired callback will result in close rather than a shutdown output. + 364657 Support HTTP only cookies from standard API + JETTY-1442 add _hostHeader setter for ProxyRule + Refactored NIO layer for better half close handling jetty-8.0.4.v20111024 - 24 October 2011 + 358263 JDBCSessionIdManager add setDatasource(DataSource) method + 358649 Replace existing StdErrLog system properties for DEBUG/IGNORED with LEVEL instead. + 360836 Accept parameters with bad UTF-8. Use replacement character + 360912 CrossOriginFilter does not send Access-Control-Allow-Origin on responses. 355103 Make allowCredentials default to true in CrossOriginFilter. + 360938 Connections closed after a while. + 361135 secure cookies for sessions + 361319 Log initialization does not catch correct exceptions on all jvms + 361325 359292 Allow KeyStore to be set + 361456 release timer task on connection failed + 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value. + JETTY-1444 start threadpool before selector manager jetty-7.5.4.v20111024 - 24 October 2011 + 358263 JDBCSessionIdManager add setDatasource(DataSource) method + 358649 Replace existing StdErrLog system properties for DEBUG/IGNORED with LEVEL instead. + 360836 Accept parameters with bad UTF-8. Use replacement character + 360912 CrossOriginFilter does not send Access-Control-Allow-Origin on responses. 355103 Make allowCredentials default to true in CrossOriginFilter. + 360938 Connections closed after a while. + 361319 Log initialization does not catch correct exceptions on all jvms + 361325 359292 Allow KeyStore to be set + 361456 release timer task on connection failed + 361655 ExecutorThreadPool.isLowOnThreads() returns wrong value. + JETTY-1444 start threadpool before selector manager jetty-8.0.3.v20111011 - 11 October 2011 + 348978 migrate jetty-http-spi + 358649 StdErrLog system properties for package/class logging LEVEL. jetty-8.0.2.v20111006 - 06 October 2011 + 336443 add missing comma in DigestAuthenticator string + 342161 ScannerTest fails intermittently on Mac OS X + 346419 testing HttpClient FDs + 353267 Request._parameters initialization bug + 353509 jetty-client unit tests are running too long + 353627 Basic Auth checks that Basic method has been send + 356144 Allow SelectorManager thread priority to be set + 356274 Start SSL socket factory in call to open() + 357163 jetty 8 ought to proxy jetty8 javadocs + 357178 websockets draft 14 support + 357188 Send content buffer directly + 357209 JSP tag listeners not called + 357216 Logging via Log4J does not expand braces in format strings + 357240 more half close refinements + 357338 remove debug + 357672 resolve issue with serializing pojos with mongodb session manager, thanks to john simone for the discovery and fix + 357959 Include javadoc in distribution + 358027 NullPointerException in ResourceHandler with jetty-stylesheet.css + 358035 idle time only active if > 0 + 358147 Add catch for UnknownHostException to fix leaky file descriptor in client + 358164 Dispatch from servlet to handler + 358263 add method for osgi users to register a driver as Class.forName does not work for them + 358649 StdErrLog system properties for package/class logging LEVEL. + 358674 Still allows sslv3 for now + 358687 Updated jsp does not scan for system tlds Fixed pattern. + 358784 JSP broken on Java 1.5 + 358925 bit more javadoc on usage + 358959 File descriptor leak with UnresolvedAddressException + 359309 adjust previous test for servletPath to include pathInfo + 359673 updated websocket version handling + 359675 Principal != String, fix for issue in property file login manager + 360051 SocketConnectionTest.testServerClosedConnection is excluded. + 360066 jsps referenced in web.xml elements do not compile + JETTY-1130 Access Sessions from HashSessionIdManager + JETTY-1277 Fixed sendRedirect encoding of relative locations + JETTY-1322 idle sweeper checks for closed endp + JETTY-1377 extra logging for busy selector + JETTY-1378 new sys property for the latest jsp-impl to force the use of the JDTCompiler when running in OSGi. + JETTY-1414 applied to PropertyUserStore + JETTY-1415 Start/Stop Server and Client only once in test, code format + JETTY-1420 Set Host header for new request in RedirectListener + JETTY-1421 Implement RedirectListener.onException,onConnectionFailed + JETTY-1423 force connection to be closed returned + JETTY-1430 local JNDI contexts don't carry environment + JETTY-1434 Add a jsp that exercises jstl. + JETTY-1439 space in directory installation path causes classloader problem jetty-7.5.3.v20111011 - 11 October 2011 + 348978 migrate jetty-http-spi + 358649 StdErrLog system properties for package/class logging LEVEL. jetty-7.5.2.v20111006 - 06 October 2011 + 336443 check nonce count is increasing + 342161 ScannerTest fails intermittently on Mac OS X + 346419 testing HttpClient FDs + 353267 Request._parameters initialization bug + 353509 jetty-client unit tests are running too long + 353627 Basic Auth checks that Basic method has been send + 356144 Allow SelectorManager thread priority to be set + 356274 Start SSL socket factory in call to open() + 357178 websockets draft 14 support + 357188 Send content buffer directly + 357209 JSP tag listeners not called + 357216 Logging via Log4J does not expand braces in format strings + 357240 more half close refinements + 357338 remove debug + 357672 resolve issue with serializing pojos with mongodb session manager, thanks to john simone for the discovery and fix + 357959 Include javadoc in distribution + 358027 NullPointerException in ResourceHandler with jetty-stylesheet.css + 358035 idle time only active if > 0 + 358147 Add catch for UnknownHostException to fix leaky file descriptor in client + 358164 Dispatch from servlet to handler + 358263 add method for osgi users to register a driver as Class.forName does not work for them + 358649 StdErrLog system properties for package/class logging LEVEL. + 358674 Still allows sslv3 for now + 358687 Updated jsp does not scan for system tlds Fixed pattern. + 358784 JSP broken on Java 1.5 + 358925 bit more javadoc on usage + 358959 File descriptor leak with UnresolvedAddressException + 359309 adjust previous test for servletPath to include pathInfo + 359673 updated websocket version handling + 359675 Principal != String, fix for issue in property file login manager + 360051 SocketConnectionTest.testServerClosedConnection is excluded. + 360066 jsps referenced in web.xml elements do not compile + JETTY-1130 Access Sessions from HashSessionIdManager + JETTY-1277 Fixed sendRedirect encoding of relative locations + JETTY-1322 idle sweeper checks for closed endp + JETTY-1377 extra logging for busy selector + JETTY-1378 new sys property for the latest jsp-impl to force the use of the JDTCompiler when running in OSGi. + JETTY-1414 applied to PropertyUserStore + JETTY-1415 Start/Stop Server and Client only once in test, code format + JETTY-1420 Set Host header for new request in RedirectListener + JETTY-1421 Implement RedirectListener.onException,onConnectionFailed + JETTY-1423 force connection to be closed returned + JETTY-1430 local JNDI contexts don't carry environment + JETTY-1434 Add a jsp that exercises jstl. + JETTY-1439 space in directory installation path causes classloader problem jetty-8.0.1.v20110908 - 08 September 2011 + 350634 Added Resource.newResource(File) + 356190 fix monodb tests for changed test api + 356428 removed timed waits from test + 356693 reduce visibility to webapp of websocket implementations + 356695 jetty server jars are provided for websockets + 356726 Instead of the sessionDestroyed called sessionCreated after invalidate session + 356751 Add null protection to ServletContextHandler.doStop + 356823 correctly decode close codes. Send not utf-8 close code. + 357058 Acceptor thread blocking jetty-7.5.1.v20110908 - 08 September 2011 + 350634 Added Resource.newResource(File) + 356190 fix monodb tests for changed test api + 356428 removed timed waits from test + 356693 reduce visibility to webapp of websocket implementations + 356695 jetty server jars are provided for websockets + 356726 Instead of the sessionDestroyed called sessionCreated after invalidate session + 356751 Add null protection to ServletContextHandler.doStop + 356823 correctly decode close codes. Send not utf-8 close code. + 357058 Acceptor thread blocking jetty-8.0.0.v20110901 - 01 September 2011 + 352565 cookie httponly flag ignored + 353073 better warnings + 353285 ServletSecurity annotation ignored + 356421 Upgraded websocket to draft 13 support jetty-7.5.0.v20110901 - 01 September 2011 + 353073 better warnings + 356421 Upgraded websocket to draft 13 support jetty-7.5.0.RC2 - 30 August 2011 + 293739 Hide stacks in named log testing. Various other minor log cleanups in output. + 352188 TestClient correctly processes --host option in jetty-websocket + 352222 Moved JmxMonitor functionality from Codehaus + 353014 TimeoutExchangeTest run time reduced + 353073 deprecated non factory method for websocket clients + 353192 Better warning for classes of wrong type + 353623 Added new methods to HttpExchange + 353624 HttpURI accepts java.net.URI object in constructor + 354080 ServletContextHandler allows to replace any subordinate handler when restarted + 355478 set public to HashedSession, looks like honest mistake and not by design to be this way + 355854 remove automatic conversion in favor of issuing a warning for jetty-web.xml that can't be processed + 356128 Moved integration tests from jetty-monitor to test-integration module + 356137 Upgrade to jsp implementation version 2.1.3-b10 + 356144 added SelectorManager.setSelectorPriorityDelta(int) + JETTY-1410 handle 1xx in similar fashion to 401s and 302s jetty-7.5.0.RC1 - 19 August 2011 + 276670 SLF4J loggers show correct location information + 335001 Eliminate expected exceptions from log when running in JBoss + 355103 Make allowCredentials default to true in CrossOriginFilter + 355162 Allow creating an empty resource collection + JETTY-1410 HTTP client handles CONTINUE 100 response correctly + JETTY-1414 HashLoginService doesn't refresh realm if specified config filename is not an absolute platform specific value jetty-8.0.0.RC0 - 16 August 2011 + 352565 cookie httponly flag ignored + 353285 ServletSecurity annotation ignored + Enable annotations by default + Merge from jetty-7.4.3 jetty-8.0.0.M3 - 27 May 2011 + 324505 Implement API login + 335500 request.getParts() throws a NullPointerException + 343472 isUserInRole does not prevent subsequent login call. + 346180 jsp-2.2 support + Updated to jetty-7.4.2.v20110526 jetty-7.5.0.RC0 - 15 August 2011 + 298502 Handle 200 Connect responses with no content-length + 347484 / - > ${/} in some paths in grant codebases + 349005 add javadoc detailing the convenience hack of removing leading /'s + 351516 Refactored sessions to better support nosql session managers + 351576 Do not use deprecated method File.toURL() + 352046 Need try/catch around features set in XmlParser + 352133 Generally resolve java 1.5isms + 352176 xml parsing on startElement should be more flexible on using qName or localName + 352421 HttpURI paths beginning with '.' + 352684 Implemented spinning thread analyzer + 352786 GzipFilter fails to pass parameters to GzipResponseWrapper + 352999 ExpireTest running too long + 353073 WebSocketClient + 353095 maven-jetty-plugin: PermGen leak due to javax.el.BeanELResolver + 353165 addJars can follow symbolic link jar files + 353210 Bundle-Version in o.e.j.o.boot.logback fix + 353465 JAASLoginService ignores callbackHandlerClass + 353563 HttpDestinationQueueTest too slow + 353862 Improve performance of QuotedStringTokenizer.quote() + 354014 Content-Length is passed to wrapped response in GZipFilter + 354204 Charset encodings property file not used + 354397 RewriteRegexRule handles special characters in regex group + 354466 Typo in example config of jetty-plus.xml jetty-7.4.5.v20110725 - 25 July 2011 + 347484 / - > ${/} in some paths in grant codebases + 352133 resolve some 1.5isms + 352421 HttpURI paths beginning with '.' + 352786 GzipFilter fails to pass parameters to GzipResponseWrapper jetty-7.4.4.v20110707 - 07 July 2011 + 308851 Converted all jetty-client module tests to JUnit 4 + 345268 JDBCSessionManager does not work with maxInactiveInterval = -1 + 350397 SelectChannelConnector does not shutdown gracefully + 350634 Reverted FileResource constructor changes + 351039 Forward dispatch should retain locale + 351199 HttpServletResponse.encodeURL() wrongly encodes an url without path when cookies are disabled + JETTY-1153 Default charset/encoding of HTTP POST requests + JETTY-1380 Jetty Rewrite example does not work in Hightide jetty-7.4.3.v20110701 - 01 July 2011 + 295832 ProxyServlet more extensible and configurable + 302566 GZIP handler for embedded Jetty servers + 308851 Converted HttpExchangeTest and related tests to JUnit 4 + 324704 JDBC Session Manager reloading session + 332200 Eliminate expected exceptions from log while using org.eclipse.jetty.jmx bundle + 347468 o.e.j.deploy.binding.GlobalWebappConfigBindingTest fails on Windows platform + 347617 Dynamically install/update/remove OSGi bundles discovered in the contexts folder + 347717 start.jar destroys dependent child of --exec + 347889 OSGi should follow directive visibility:=reexport for META-INF/web-fragments and resources + 347898 Close channel on JVM exceptions + 348652 jetty.sh starts two unix processes + 348935 Close A tag in directory listing + 349344 Passing empty query string to UrlEncoded#decodeTo(String, MultiMap, String) does not yield an empty map + 349738 set buffer sizes for http client in proxy servlet + 349870 proxy servlet protect continuation against fast failing exchanges + 349896 SCEP supports zero idleTimeout + 349897 draft -09 websockets + 349997 MBeanContainer uses weak references + 350533 Add "Origin" to the list of allowed headers in CrossOriginFilter + 350634 Cleanup FileResource construction + 350642 Don't close SCEP during NIOBuffer manipulation + JETTY-1342 Recreate selector in change task + JETTY-1385 NPE in jetty client's HTttpExchange.setRequestContentSource(InputStream) + JETTY-1390 RewriteHandler handles encoded URIs jetty-7.4.2.v20110526 + 334443 Improve the ability to specify extra class paths using the Jetty Maven Plugin + 336220 tmp directory is not set if you reload a webapp with jetty-maven-plugin + 338364 Fixed expires header for set cookies + 345615 Enable SSL Session caching + 345729 binding for managing server and system classes globally + 345763 Source file is updated during the build + 345873 Update jetty-ssl.xml to new style + 345900 Handle IPv6 with default port + 346014 Fixed full HttpGenerator + 346124 ServletContext resources paths not resolved correctly when using UNC shares + 346179 o.e.j.util.ScannerTest fails on MacOS X platform + 346181 o.e.j.server.StressTest stalls on MacOS X platform + 346614 HttpConnection.handle() spins in case of SSL truncation attacks + 346764 OrderedGroupBinding deployment binding + 346998 AbstractLifeCycle.isRunning() returns false if state changes from STARTING to STARTED during call + 347137 Allow SSL renegotiations by default in HttpClient + 374174 Consistent mbean names + JETTY-1146 Encode jsessionid in sendRedirect + JETTY-1342 Recreate selector if wakeup throws JVM bug jetty-7.4.1.v20110513 + 288563 remove unsupported and deprecated --secure option + 332907 Add context property to ObjectName of JMX MBeans + 336056 Ability to override the computation of the ContextHandler to deploy the DefaultServlet on the HttpService + 340040 Support for a total timeout + 343083 Set nested dispatch type and connection + 343172 Check package implementor for version + 343277 add support for a context white list + 343352 make sure that jetty.osgi.boot is activated when a WAB is registered + 343482 refactored overlay deployer layout to use WAR layout + 343567 HttpClient does not limit the destination's exchange queue + 343680 Handle OSGi bundle jars not ending in ".war" + 343707 'REQUEST' is printed on console for each incoming HTTP request + 343923 flush timeouts applied to outer loop + 343936 Session idle calls unbind and remove listeners + 344059 Websockets draft-07 + 344067 Add support for OSGi fragment bundles to add static resources to web-bundles + 344513 Attempting to set ConfigurationClasses in jetty-web.xml causes NPE + 344529 Ability to customize the error handling of the OSGi HttpService + 345047 Readded deprecated ScanningAppDeployer#setMonitoredDir + 345290 Weak references from SessionIdManager. HashSessionManager cleanup. + 345543 Always close endpoint on SSLException + 345656 Disambiguate SslContextFactory#validateCerts property + 345679 Allow setting an initialized KeyStore as keystore/truststore of SslContextFactory + 345704 jetty-nested works with forwarded SSL in cloudfoundry + JETTY-954 WebAppContext eats any start exceptions instead of stopping the server load + JETTY-1314 Handle bad URI encodings + JETTY-1324 Tested not using CESU-8 instead of UTF-8 + JETTY-1326 Invoker names not hashCode based + JETTY-1343 IllegalArgumentException for bad % encodings + JETTY-1347 Updated ServletHander javadoc jetty-7.4.0.v20110414 + 342504 Scanner Listener + 342700 refine websocket API for anticipated changes + JETTY-1362 Set root cause of UnavailableException + Various test harness cleanups to avoid random failures jetty-7.4.0.RC0 + 324110 Added test harnesses for merging of QueryStrings. + 337685 Update websocket API in preparation for draft -07 + 338627 HashSessionManager.getIdleSavePeriod returns milliseconds instead of seconds + 338807 Ignore content length in 1xx, 204, 304 responses + 338819 Externally control Deployment Manager application lifecycle + 339084 Fixed NPE with servlet 3.0 async listener + 339150 Validate client certificate when it is used for authentication + 339187 In the OSGi manifest of the jetty-all-server aggregate, mark javax.annotation as optional + 339543 Add configuration options for Certificate Revocation checking + 340265 Improve handling of io shutdown in SSL + 340621 Added SizedThreadPool interface + 340636 HashSessionManager lazy loads all sessions + 340838 Update ConnectHandler to perform half closes properly + 340878 Integrations should be able to load their own keystores + 340920 Dynamically assign RMI registry port for integration testing + 340949 Scanner delays file notifications until files are stable + 341006 Move inner enums out into separate file + 341105 Stack trace is printed for an ignored exception + 341145 WebAppContext MBean attribute serverClasses returns empty value + 341171 Locking in HttpDestination blocks all requests to the same address + 341206 Stop order is wrong in HandlerWrapper + 341255 org.eclipse.http usage in AJP/SessionId linkage + 341386 Remote close not detected by HttpClient + 341394 Remove 'Unavailable' JMX attributes of WebAppContext MBean + 341439 Blocking HttpClient does not use soTimeout for timeouts + 341561 Exception when adding o.e.j.s.DoSFilter as managed attribute + 341692 Fixed deadlock if stopped while starting + 341694 Disable AJP buffer resizing + 341726 JSONPojoConverter handles characters + 341736 Split jetty-nested out of war module + 341850 Protect QTP dump from bad stacks + 341992 Overlayed context deployer + JETTY-1245 Pooled Buffers implementation + JETTY-1354 Added jetty-nested + Added extra session removal test + Ensure generated fragment names are unique jetty-8.0.0.M2 - 16 November 2010 + 320073 Reconsile configuration mechanism + 321068 JSF2 fails to initialize + 324493 Registration init parameter handling null check, setInitParameters additive + 324505 Request.login method must throw ServletException if it cant login + 324872 allow disabling listener restriction from using *Registration interfaces + 327416 Change meaning of @HandlesTypes in line with latest interpretation by JSR315 + 327489 Change meaning of @MultipartConfig to match servlet spec 3.0 maintenance release 3.0a + 328008 Handle update to Servlet Spec 3 Section 8.2.3.h.ii + 330188 Reject web-fragment.xml with same as another already loaded one + 330208 Support new wording on servlet-mapping and filter-mapping merging from servlet3.0a + 330292 request.getParts() returns only one part when the name is the same + Update to jetty-7.2.1.v20101111 jetty-7.3.1.v20110307 - 07 March 2011 + 316382 Support a more strict SSL option with certificates + 333481 Handle UCS-4 codepoints in decode and encode + 335329 Moved blocking timeout handling to outside try catch + 336668 policy supports cert validation + 336691 Possible wrong length returned by ChannelEndPoint.flush() in case of RandomAccessFileBuffer + 336781 If xml parser is not validating, turn off external dtd resolution + 336793 Tee data filled and flushed from endpoint + 337258 Scanner start and end cycle notification + 337268 Allow specifying alias of a certificate to be used by SSL connector + 337270 Shared Timer for session management + 337271 Flush SSL endpoint when dispatch thread held forever + 337678 Readded optional async connection mode for HttpClient + 337685 Work in progress on draft 6 websockets + 337746 Fixed Session deIdle recursion + 337784 Improve HashSessionManager for session migrations + 337878 Extra tests of security constraints + 337896 HttpExchange.timeout does not override HttpClient.timeout + 337898 set client HttpConnection max idle time from exchange timeout + 338035 Default acceptors 0.25*CPUs and improved selector/acceptor thread names. + 338068 Leaking ConstraintMappings on redeploy + 338092 ProxyServlet leaks memory + 338607 Removed managed attributes when context is stopped + 338819 Externally control Deployment Manager application lifecycle + JETTY-1304 Allow quoted boundaries in Multipart filter + JETTY-1317 More elegent handling of bad URIs in requests + JETTY-1331 Allow alternate XML configuration processors (eg spring) + JETTY-1333 HttpClient _timeout and _soTimeout is messed up + JETTY-1335 HttpClient's SelectConnector clean-up + JETTY-1337 Workname cannot contain '.' + JETTY-1338 Trust default SecureRandom seed jetty-7.3.0.v20110203 - 03 February 2011 + 296978 standardizing various Testing Util Classes to jetty-test-helper + 319178 test failure fix in jetty-util on windows + 320457 add SPNEGO support + 324505 Implement API login + 328872 Multi Jetty xml files not loading if directory is referenced in jetty.conf + 329746 client option to set just truststore and use strict ssl context + 331803 Update XML configuration files to use proper arguments for startup command in examples + 332179 Fixed formatting of negative dates + 332432 Scanner.java now always scanning the canonical form of File + 332517 Improved DefaultServlet debug + 332703 Cleanup context scope JNDI at stop + 332796 Annotations inheritance does not work with jetty7 + 332799 100% CPU on redeploy session invalidation + 332937 Added Destroyable Dumpable interfaces and reworked dependent lifecycles, specially of JNDI + 333247 fix api compat issue in ConstraintSecurityHandler + 333415 wired up HttpInput.available and added test harnesses + 333481 Handle UTF-32 codepoints in decode and encode + 333608 tlds defined in web.xml are not picked up + 333679 Refactored jetty-jmx. Moved mbeans to modules + 333717 HttpExchange able to return local address used + 333771 System properties are not available inside XML configuration file by using the 'property' tag + 333875 Monitor public constructor + 333892 Improved JVM bug detection + 334062 It should be possible to embed in the jetty.home.bundle the ssl keystore files + 334229 javax-security needs to import the package javax.security.cert in its OSGi manifest + 334311 fix buffer reuse issue in CachedExchange + 335329 Stop SSL spin during handshake and renogotiate + 335361 Fixed 'jetty.sh check' to show current PID when JETTY_PID env. variable is set + 335641 Cleaned up dispatch handling to avoid key.interestOps==0 when undispatched + 335681 Improve ChannelEndPoint.close() to avoid spinning + 335836 Race when updating SelectChannelEndPoint._dispatched + JETTY-1259 NullPointerException in JDBCSessionIdManager when invalidating session (further update) jetty-7.2.2.v20101205 - 05 December 2010 + 328789 Clean up tmp files from test harnesses + 330188 Reject web-fragment.xml with same as another already loaded one + 330208 Support new wording on servlet-mapping and filter-mapping merging from servlet3.0a + 330210 Improve performance of writing large bytes arrays + 330229 Jetty tries to parse META-INF/*.tld when jsp-api is not on classpath, causing DTD entity resoluton to fail + 330265 start.jar --stop kills --exec subprocess + 330417 Atomic PUT in PutFilter + 330419 Reloading webapp duplicates StandardDescriptorProcessor + 330686 OSGi: Make org.eclipse.jetty.jsp-2.1 a fragment of org.apache.jasper.glassfish + 330732 Removed System.err debugging + 330764 Command line properties passed to start.jar --exec + 331230 Fixed low thread warnings when acceptors>threadpool + 331461 Fixed idle timeout for unflushed HTTP/1.0 + 331567 IPAccessHandlerTest failed on MacOS fix + 331703 Fixed failing OSGI test TestJettyOSGiBootWithJsp.java on MacOSX + JETTY-1297 Improved matching of vhosts so that a vhost match has priority + JETTY-1307 Check that JarFileResource directories end with / + JETTY-1308 327109 (re)fixed AJP handling of empty packets jetty-7.2.1.v20101111 - 11 November 2010 + 324679 Fixed dedection of write before static content + 328008 Handle update to Servlet Spec 3 Section 8.2.3.h.ii + 328199 Ensure blocking connectors always close socket + 328205 Improved SelectManager stopping + 328306 Serialization of FormAuthentication + 328332 Response.getContentType works with setHeader + 328523 Fixed overloaded setters in AppProvider + 328778 Improved javadoc for secure session cookies + 328782 allow per connection max idle time to be set + 328885 web overrides do not override + 328988 Idle saving of session values + 329180 Spin check for Selector to stop + 329410 Enforce XmlConfiguration properties as Map + 329602 only clear ServletContext attributes on doStop + 329642 Concurrent modification exception in Deployment Manager + 329643 Improved deployment of resource collections + JETTY-748 Prevent race close of socket by old acceptor threads + JETTY-1291 Extract query parameters even if POST content consumed + JETTY-1295 Contexts mixed up when hot-deploying on virtual hosts + JETTY-1297 Make ServletContext.getContext(String) virtual host aware jetty-6.1.26 - 10 November 2010 + JETTY-748 Prevent race close of socket by old acceptor threads + JETTY-1239 HTAccessHandler [allow from 127.0.0.1] does not work + JETTY-1291 Extract query parameters even if POST content consumed + JETTY-1293 Avoid usage of String.split + JETTY-1296 Always clear changes list in selectManager jetty-6.1.26.RC0 - 20 October 2010 + 325468 Clean work webapp dir before unpack + 327109 Fixed AJP handling of empty packets + 327562 Implement all X-Forwarded headers in ProxyServlet + JETTY-547 Improved usage of shutdownOutput before close. + JETTY-912 add per exchange timeout + JETTY-1051 offer jetty.skip flag for maven plugin + JETTY-1096 exclude maven and plexus classes from jetty plugin + JETTY-1248 Infinite loop creating temp MultiPart files + JETTY-1264 Idle timer deadlock + JETTY-1271 Handle unavailable request + JETTY-1278 J2se6 SPI filter handling fix + JETTY-1283 Allow JSONPojoConvertorFactory to set fromJSON + JETTY-1287 rewrite handler thread safe issue resolved + JETTY-1288 info when atypical classloader set to WebAppContext + JETTY-1289 MRU cache for filter chains + JETTY-1292 close input streams after keystore.load() jetty-7.2.0.v20101020 - 20 October 2010 + 289540 added javadoc into distribution + 297154 add source distribution artifact + 323985 Xmlconfiguration pulls start.jar config properties + 324369 Improved handling of multiple versions of draft-ietf-hybi-thewebsocketprotocol + 326734 Configure Digest maxNonceAge with Security handler init param + 327109 Fixed AJP handling of empty packets + 327183 Allow better configurability of HttpClient for TLS/SSL + 327469 removed needless java6 dependencies + 327562 Implement all X-Forwarded headers in ProxyServlet + 327601 Multipart Filter handles quoted tokens + 327725 Nested ResourceCaches + 328199 Ensure blocking connectors always close socket + 328205 Improved SelectManager stopping + 328273 Added serializable to default user identity + JETTY-1288 Info statement when atypical classloader set on WebAppContext + JETTY-1289 LRU cache for filter chains jetty-7.2.0.RC0 - 01 October 2010 + 314087 Simplified SelectorManager + 319334 Concurrent, sharable ResourceCache + 319370 WebAppClassLoader.Context + 319444 Two nulls are appended to log statements from ContextHanler$Context + 320073 Reconsile configuration mechanism + 320112 Websocket in aggregate jars + 320264 Removed duplicate mime.property entries + 320457 Added rfc2045 support to B64Code + 321232 BasicAuthenticator ignores bad Authorization header. + 321307 HashSessionManager calls passivation listeners. + 321730 SelectChannelEndPoint prints to System.err + 321735 HttpClient onException called for buffer overflow. + 322448 Added jetty-dir.css for directory listings + 322575 NPE in HotSwapHandler if old handler null + 322683 RewriteHandler thread safety + 323196 org.mortbay properties to org.eclipse + 323435 MovedContextHandler permanent redirection + 323464 IPv6 localhost with no Host header + 324110 Merge async dispatch parameters + 324158 Durable download or Orbit jars + 324260 Jetty-6 continuations handle complete calls + 324359 illegal actions on AsyncContext should not change its state. + 324360 validate input on getResource since loop logic obscures subclass input validation. + 324369 Implement draft-ietf-hybi-thewebsocketprotocol-01 + 324377 Allow dispatch of ServletRequest and ServletResponse + 324379 Change content type after getWriter + 324501 Fire RequestListener.requestDestroyed in last-to-first order. + 324601 Check session expiry on access + 324679 Allow filter to write before static content + 324811 NPE in Server.dump + 324812 restore WebAppContext constructor used by geronimo integration + 325072 include to DefaultServlet of missing file throws FileNotFoundException + 325105 websocket ondisconnect fixed + 325128 websocket send during onConnect + 325468 Clean work webapp dir before unpack + 326612 Handle X-Forwarded-Proto header + JETTY-912 added per exchange timeout api + JETTY-1063 Plugin problems with spaces in classpath resource references + JETTY-1245 Do not use direct buffers with NIO SSL + JETTY-1249 Apply max idle time to all connectors + JETTY-1250 Parallel start of HandlerCollection + JETTY-1256 annotation and jta jars from Orbit + JETTY-1259 NullPointerException in JDBCSessionIdManager when invalidating session + JETTY-1261 errant listener usage in StandardDescriptorProcessor + JETTY-1263 JDBCSessionIdManager table creation fails on Oracle + JETTY-1265 Reason field option in client response + JETTY-1266 Destroy sessions before filters/servlets + JETTY-1268 Form Auth saves POST data + JETTY-1269 Improve log multithreadedness + JETTY-1270 Websocket closed endp protection + JETTY-1271 handled unavailable exception + JETTY-1279 Make jetty-plus.xml enable plus features for all webapps by default + JETTY-1281 Create new session after authentication + JETTY-1283 JSONPojoConvertorFactory can turn off fromJSON + Added ignore to Logger interface + Fix jetty-plus.xml for new configuration names + Improved debug dump jetty-7.1.6.v20100715 + 319519 Warn about duplicate configuration files + 319655 Reset HEAD status + JETTY-1247 synchronize recylcing of SSL NIO buffers + JETTY-1248 fix parsing of bad multiparts + JETTY-1249 Apply max idle time to all connectors + JETTY-1251 Replace then close selector for JVM bugs jetty-8.0.0.M1 - 12 July 2010 + 306350 Ensure jars excluded by ordering are not scanned for annotations + JETTY-1224 Change jetty-8 merge rules for fragment descriptors and annotations + Ensure in web.xml overrides relative in fragments + Ensure empty implies exclusion of all fragments + Ensure servlet-api jar class inheritance hierarchy is scanned jetty-7.1.5.v20100705 + 288194 Add blacklist/whitelist to ProxyServlet and ProxyHandler + 296570 EOFException for HttpExchange when HttpClient.stop called. + 311550 The WebAppProvider should allow setTempDirectory + 316449 Websocket disconnect fix + 316584 Exception on startup if temp path has spaces and extractWAR=false + 316597 Removed null check and fixed name in Resource#hrefEncodeURI + 316909 CNFE: org.xml.sax.SAXException on org.eclipse.jetty.osgi.boot start with jsp fragment + 316970 jetty.sh fails to find JETTY_HOME in standard directories + 316973 jetty.sh claims java installation is invalid + 316976 removed quotes of JAVA_OPTIONS in jetty.sh + 317007 Unable to run Jetty OSGi when -Dosgi.compatibility.bootdelegation=false + 317019 Date HTTP header not sent for HTTP/1.0 requests + 317231 Ability to configure jetty with a fragment bundle that contains etc/jetty.xml + 317759 Allow roles and constraints to be added after init + 317906 OPTIONS correctly handles TRACE + 318308 Correct quoting of unicode control characters + 318470 unboxing NPE protection in HttpConnection + 318551 Optional uncheck Printwriter + 319060 Support web-bundles that are not expanded (bundle is zipped) + JETTY-1237 Save local/remote address to be available after close + Update ecj to 3.6 Helios release drop jetty-6.1.25 - 26 July 2010 + 320264 Removed duplicate mime.property entries + JETTY-1212 Long content lengths + JETTY-1214 Avoid ISE when scavenging invalid session + JETTY-1223 DefaultServlet: NPE when setting relativeResourceBase and resourceBase is not set + JETTY-1226 javax.activation needs to be listed in the system classes + JETTY-1237 Remember local/remote details of endpoint + JETTY-1251 protected against closed selector + COMETD-112 if two threads create the same channel, then create events may occur after subscribe events + Jetty-6 is now in maintenance mode. jetty-7.1.4.v20100610 + 292326 Stop continuations if server is stopped. + 292814 Make QoSFilter and DoSFilter JMX manageable + 293222 Improve request log to handle/show asynchronous latency + 294212 Can not customize session cookie path + 295715 AbstractSessionManager decoupled from Context + 298551 SslSocketConnector does not need keystore stream + 301608 Deregister shutdown hooks + 302350 org.eclipse.jetty.server.NCSARequestLog is missing JavaDoc + 303661 jetty.sh failes if JETTY_HOME is not writeable + 304100 Better document JMX setup in jetty-jmx.xml + 305300 AsyncContext.start dispatches runnable + 314299 Create test harness for JDBCLoginService + 314581 Implement the Sec-Websocket handshake + 315190 CrossOriginFilter avoid headers not understood by WebSocket + 315687 included init script fails to test for JETTY_HOME as empty + 315715 Improved Cookie version handling. Server.setMaxCookieVersion + 315744 Fixed STOP.PORT and STOP.KEY in start.jar + 315748 Removed --fromDaemon from start.jar (replaced with --daemon) + 315925 Improved context xml configuration handling + 315995 Incorrect package name in system classes list + 316119 Fixed idleTimeout for SocketEndPoint + 316254 Implement @DeclareRoles + 316334 Breaking change on org.eclipse.jetty.client.HttpExchange + 316399 Debug output in MultiPartFilter + 316413 Restarting webapp for packed war fails + 316557 OSGi HttpService failure due to undeployed context handlers + JETTY-547 Delay close after shutdown until request read + JETTY-1231 Support context request log handler jetty-7.1.3.v20100526 + 296567 HttpClient RedirectListener handles new HttpDestination + 297598 JDBCLoginService uses hardcoded credential class + 305898 Websocket handles query string in URI + 307457 Exchanges are left unhandled when connection is lost + 313205 Unable to run test-jdbc-sessions tests + 314009 jetty.xml configuration file on command line + 314177 JSTL support is broken + 314459 support maven3 for builds jetty-7.1.2.v20100523 + 308866 Update test suite to JUnit4 - Module jetty-util + 312948 Recycle SSL crypto buffers + 313196 randomly allocate ports for session test. + 313278 Implement octet ranges in IPAccessHandler + 313336 secure websockets + 314009 updated README.txt + Update links to jetty website and wiki on test webapp jetty-7.1.1.v20100517 + 302344 Make the list of available contexts if root context is not configured optional + 304803 Remove TypeUtil Integer and Long caches + 306226 HttpClient should allow changing the keystore and truststore type + 308850 Update test suite to JUnit4 - Module jetty-annotations + 308853 Update test suite to JUnit4 - Module jetty-deploy + 308854 Update test suite to JUnit4 - Module jetty-http + 308855 Update test suite to JUnit4 - Module jetty-io + 308856 Update test suite to JUnit4 - Module jetty-jmx + 308857 Update test suite to JUnit4 - Module jetty-jndi + 308858 Update test suite to JUnit4 - Module jetty-plus + 308859 Update test suite to JUnit4 - Module jetty-policy + 308860 Update test suite to JUnit4 - Module jetty-rewrite + 308862 Update test suite to JUnit4 - Module jetty-server + 308863 Update test suite to JUnit4 - Module jetty-servlet + 308867 Update test suite to JUnit4 - Module jetty-webapp + 310918 Fixed write blocking for client HttpConnection + 312526 Protect shutdown thread initialization during shutdown jetty-7.1.0 - 05 May 2010 + 306353 fixed cross context dispatch to root context. + 311154 Added deprecated StringBuffer API for backwards compatibility + 311554 Protect shutdown thread from Server#doStop + 312243 Optimized timeout handling jetty-7.1.0.RC1 - 05 May 2010 + 286889 Allow System and Server classes to be set on Server instance and when applied to all webapps + 291448 SessionManager has isCheckingRemoteSessionIdEncoding + 296650 JETTY-1198 reset idle timeout on request body chunks + 297104 HTTP CONNECT does not work correct with SSL destinations + 306782 Close connection when expected 100 continues is not sent + 308848 Update test suite to JUnit4 - Module jetty-ajp + 308861 Update test suite to JUnit4 - Module jetty-security + 308864 Update test suite to JUnit4 - Module jetty-servlets + 308865 Update test suite to JUnit4 - Module jetty-start + 308868 Update test suite to JUnit4 - Module jetty-websocket + 308869 Update test suite to JUnit4 - Module jetty-xml + 309153 Hide extracted WEB-INF/lib when running a non-extracted war + 309369 Added WebSocketLoadTest + 309686 Fixed response buffers usage + 310094 Improved start.jar options handling and configs + 310382 NPE protection when WAR is not a file + 310562 SslSocketConnector fails to start if excludeCipherSuites is set + 310634 Get the localport when opening a server socket. + 310703 Update test suite to JUnit4 - Module tests/test-integration + 310918 Synchronize content exchange + 311154 Use Appendable in preference to StringBuilder/StringBuffer in APIs + 311362 Optional org.eclipse.jetty.util.log.stderr.SOURCE + JETTY-1030 Improve jetty.sh script + JETTY-1142 Replace Set-Cookies with same name jetty-7.1.0.RC0 - 27 April 2010 + 294563 Websocket client connection + 297104 Improve handling of CONNECT method + 306349 ProxyServlet does not work unless deployed at / + 307294 Add AbstractLifeCycle.AbstractLifeCycleListener implementation + 307847 Fixed combining mime type parameters + 307898 Handle large/async websocket messages + 308009 ObjectMBean incorrectly casts getTargetException() to Exception + 308420 convert jetty-plus.xml to use DeploymentManager + 308925 Protect the test webapp from remote access + 309466 Removed synchronization from StdErrLog + 309765 Added JSP module + 310051 _configurationClasses now defaults to null in WebAppContext + 310094 Improved start.jar usage and config files + 310431 Default ErrorHandler as server Bean + 310467 Allow SocketConnector to create generic Connection objects + 310603 Make Logger interface consistent + 310605 Make a clean room implementation of the JSP logger bridge + JETTY-903 Stop both caches + JETTY-1200 SSL NIO Endpoint wraps non NIO buffers + JETTY-1202 Use platform default algorithm for SecureRandom + JETTY-1212 handle long content lengths + JETTY-1214 avoid ISE when scavenging invalid session + Add AnnotationConfiguration to jetty-plus.xml + Add NPE protection to ContainerInitializerConfiguration + Fix jetty-plus.xml reference to addLifeCycle + Merged 7.0.2.v20100331 + Temporarily remove jetty-osgi module to clarify jsp version compatibility jetty-7.0.2.v20100331 - 31 March 2010 + 297552 Don't call Continuation timeouts from acceptor tick + 298236 Additional unit tests for jetty-client + 306782 httpbis interpretation of 100 continues. Body never skipped + 306783 NPE in StdErrLog when Throwable is null + 306840 Suppress content-length in requests with no content + 306880 Support for UPGRADE in HttpClient + 306884 Suspend with timeout <=0 never expires + 307589 updated servlet 3.0 continuations for final API + Allow Configuration array to be set on Server instance for all web apps + Ensure webapps with no WEB-INF don't scan WEB-INF/lib + Take excess logging statements out of startup jetty-6.1.24 - 21 April 2010 + 308925 Protect the test webapp from remote access + JETTY-903 Stop both caches + JETTY-1198 reset idle timeout on request body chunks + JETTY-1200 SSL NIO Endpoint wraps non NIO buffers + JETTY-1211 SetUID loadlibrary name and debug + COMETD-100 ClientImpl logs "null" as clientId + COMETD-107 Reloading the application with reload extension does not fire /meta/connect handlers until long poll timeout expires + COMETD-99 ClientImpl logs exceptions in listeners with "debug" level + Upgraded to cometd 1.1.1 client jetty-6.1.23 - 02 April 2010 + 292800 ContextDeployer - recursive setting is undone by FilenameFilter + 296569 removeLifeCycleListener() has no effect + 300178 HttpClients opens too many connections that are immediately closed + 304658 Inconsistent Expires date format in Set-Cookie headers with maxAge=0 + 304698 org.eclipse.jetty.http.HttpFields$DateGenerator.formatCookieDate() uses wrong (?) date format + 306331 Session manager is kept after call to doScope + 306840 suppress content-length in requests without content + JETTY-875 Allow setting of advice field in response to Handshake + JETTY-983 Range handling cleanup + JETTY-1133 Handle multiple URL ; parameters + JETTY-1134 BayeuxClient: Connect msg should be sent as array + JETTY-1149 transient should be volatile in AbstractLifeCycle + JETTY-1153 System property for UrlEncoded charset + JETTY-1155 HttpConnection.close notifies HttpExchange + JETTY-1156 SSL blocking close with JVM Bug busy key fix + JETTY-1157 Don't hold array passed in write(byte[]) + JETTY-1158 NPE in StdErrLog when Throwable is null + JETTY-1161 An Extension that measures round-trip delay for cometd messages. + JETTY-1162 Add support for async/sync message delivery to BayeuxClient + JETTY-1163 AJP13 forces 8859-1 encoding + JETTY-1168 Don't hold sessionIdManager lock when invalidating sessions + JETTY-1170 NPE on client when server-side extension returns null + JETTY-1174 Close rather than finish Gzipstreams to avoid JVM leak + JETTY-1175 NPE in TimesyncExtension + JETTY-1176 NPE in StatisticsExtension if client is null + JETTY-1177 Allow error handler to set cacheControl + JETTY-1178 Make continuation servlet to log the incoming JSON in case of parsing errors + JETTY-1180 Extension methods are wrongly called + JETTY-1182 COMETD-76 do not lock client while sending messages. + JETTY-1183 AcknowledgedMessagesClientExtension does not handle correctly message resend when client long polls again + JETTY-1186 Better document JMX setup in jetty-jmx.xml + JETTY-1188 Null old jobs in QueuedThreadPool + JETTY-1191 Limit size of ChannelId cache + JETTY-1192 Fixed Digested POST and HttpExchange onRetry + JETTY-1193 Exception details are lost in AbstractCometdServlet.getMessages + JETTY-1195 Coalesce buffers in ChannelEndPoint.flush() + JETTY-1196 Enable TCP_NODELAY by default in client connectors + JETTY-1197 SetUID module test fails when using Java 1.6 to build + JETTY-1199 FindBugs cleanups + JETTY-1202 Use platfrom default algorithm for SecureRandom + JETTY-1205 Memory leak in browser-to-client mapping + JETTY-1207 NPE protection in FormAuthenticator + COMETD-28 Improved concurrency usage in Bayeux and channel handling + COMETD-46 reset ContentExchange content on resend + COMETD-58 Extension.rcv() return null causes NPE in AbstractBayeux.PublishHandler.publish + COMETD-59 AcknowledgeExtension does not handle null channel in Message + COMETD-62 Delay add listeners until after client construction + JSON parses NaN as null + Remove references to old content in HttpClient client tests for www.sun.com + Updated JSP to 2.1.v20091210 jetty-7.0.2.RC0 + 290765 Reset input for HttpExchange retry. + 292799 WebAppDeployer - start a started context? + 292800 ContextDeployer - recursive setting is undone by FilenameFilter + 294799 when configuring a webapp, don't look for WEB-INF/jetty6-web.xml + 296569 removeLifeCycleListener() has no effect + 296765 JMX Connector Server and ShutdownThread + 297421 Hide server/system classes from WebAppClassLoader.getResources + 297783 Handle HEAD reponses in HttpClient + 298144 Unit test for jetty-client connecting to a server that uses Basic Auth + 298145 Reorganized test harness to separate the HTTP PUT and HTTP GET test URLs + 298234 Unit test for jetty-client handling different HTTP error codes + 298667 DeploymentManager uses ContextProvider and WebAppProvider + 299455 Enum support in JSONPojoConvertor + 300178 HttpClients opens too many connections that are immediately closed + 300733 Jars from lib/ext are not visible for my web application + 300933 AbstractConnector uses concurrent objects for stats + 301089 Improve statistics available in StatisticsHandler and AbstractConnector + 302018 Improve statistics available in AbstractSessionHandler + 302198 Rename HttpClient authorization classes to Authentication + 302244 invalid configuration boolean conversion in FormAuthenticator + 302246 redirect loop using form authenticator + 302556 CrossOriginFilter does not work correctly when Access-Control-Request-Headers header is not present + 302669 WebInfConfiguration.unpack() unpacks WEB-INF/* from a ResourceCollection, breaking JSP reloading with ResourceCollections + 303526 Added include cyphers + 304307 Handle ;jsessionid in FROM Auth + 304532 Skip some tests on IBM JVMs until resolved + 304658 Inconsistent Expires date format in Set-Cookie headers with maxAge=0 + 304698 org.eclipse.jetty.http.HttpFields$DateGenerator.formatCookieDate() uses wrong (?) date format + 304781 Reset HttpExchange timeout on slow request content. + 304801 SSL connections FULL fix + 305997 Coalesce buffers in ChannelEndPoint.flush() + 306028 Enable TCP_NODELAY by default in client connectors + 306330 Flush filter chain cache after Invoker servlet + 306331 Session manager is kept after call to doScope + JETTY-776 Make new session-tests module to concentrate all reusable session clustering test code + JETTY-910 Allow request listeners to access session + JETTY-983 Range handling cleanup + JETTY-1133 Handle multiple URL ; parameters + JETTY-1151 JETTY-1098 allow UTF-8 with 0 carry bits + JETTY-1153 System property for UrlEncoded charset + JETTY-1155 HttpConnection.close notifies HttpExchange + JETTY-1156 SSL blocking close with JVM Bug busy key fix + JETTY-1157 Don't hold array passed in write(byte[]) + JETTY-1163 AJP13 forces 8859-1 encoding + JETTY-1174 Close rather than finish Gzipstreams to avoid JVM leak + JETTY-1177 Allow error handler to set cacheControl + JETTY-1179 Persistant session tables created on MySQL use wrong datatype + JETTY-1184 shrink thread pool even with frequent small jobs + JETTY-1192 Fixed Digested POST + JETTY-1199 FindBugs cleanups + Added IPAccessHandler + COMETD-46 reset ContentExchange response content on resend + JSON parses NaN as null + Updated Servlet3Continuation to final 3.0.20100224 jetty-8.0.0.M0 - 28 February 2010 + Merged 7.0.1.v20091116 + Updated servlet 3.0 spec 20100224 + Updated to cometd 1.0.1 jetty-7.0.1.v20091125 - 25 November 2009 + 274251 DefaultServlet supports exact match mode. + 288401 HttpExchange.cancel() Method Unimplemented + 289027 deobfuscate HttpClient SSL passwords + 289265 Test harness for async input + 289959 Improved ContextDeployer configuration + 289960 start.jar assumes command line args are configs + 291019 Fix default DEBUG option; "-D.DEBUG=true" now works + 291340 Race condition in onException() notifications + 291543 make bin/*.sh scripts executable in distribution + 291589 Update jetty-rewrite demo + 292546 Proactively enforce HttpClient idle timeout + 292642 Fix errors in embedded Jetty examples + 292825 Continuations ISE rather than ignore bad transitions + 293222 Improved StatisticsHandler for async + 293506 Unable to use jconsole with Jetty when running with security manager + 293557 Add "jad" mime mapping + 294154 Patched jetty-osgi + 294224 HttpClient timeout setting has no effect when connecting to host + 294345 Support for HTTP/301 + HTTP/302 response codes + 294563 Initial websocket implementation + 295421 Cannot reset() a newly created HttpExchange: IllegalStateException 0 => 0 + 295562 CrossOriginFilter does not work with default values in Chrome and Safari + JETTY-937 More JVM bug work arounds. Insert pause if all else fails + JETTY-983 Send content-length with multipart ranges + JETTY-1114 unsynchronised WebAppClassloader.getResource(String) + JETTY-1121 Merge Multipart query parameters + JETTY-1122 Handle multi-byte utf that causes buffer overflow + JETTY-1125 TransparentProxy incorrectly configured for test webapp + JETTY-1129 Filter control characters out of StdErrLog + JETTY-1135 Handle connection closed before accepted during JVM bug work around + JETTY-1144 fixed multi-byte character overflow + JETTY-1148 Reset partially read request reader. + COMETD-34 Support Baeyux MBean + CQ-3581 jetty OSGi contribution + CVE-2009-3555 Prevent SSL renegotiate for SSL vulnerability + Fixed client abort asocciation + Fixed XSS issue in CookieDump demo servlet. + Improved start.jar usage text for properties + Moved centralized logging and verifier back to sandbox + Promoted Jetty Centralized Logging from Sandbox + Promoted Jetty WebApp Verifier from Sandbox + Refactored continuation test harnessess jetty-7.0.0.v20091005 - 05 October 2009 + 291340 Race condition in onException() notifications jetty-6.1.21 - 22 September 2009 + 282543 HttpClient SSL buffer size fix + 288055 fix jetty-client for failed listener state machine + 288153 reset exchange when resending + 288182 PUT request fails during retry + JETTY-719 Document state machine of jetty http client + JETTY-933 State == HEADER in client + JETTY-936 Improved servlet matching and optimized + JETTY-1038 ChannelId.isParentOf returns the wrong result + JETTY-1061 Catch exceptions from cometd listeners + JETTY-1072 maven plugin handles context path not as documented + JETTY-1080 modified previous fix for windows + JETTY-1084 HEAD command not setting content-type in response under certain circumstances + JETTY-1090 resolve inifinte loop condition for webdav listener + JETTY-1092 MultiPartFilter can be pushed into infinite loop + JETTY-1093 Request.toString throws exception when size exceeds 4k + JETTY-1098 Default form encoding is UTF8 + JETTY-1099 Improve cookie handling in BayeuxClient + JETTY-1100 extend setuid feature to allow setting max open file descriptors + JETTY-1102 Wrong usage of deliver() in private chat messages + JETTY-1108 SSL EOF detection + JETTY-1109 Improper handling of cookies in Terracotta tests + JETTY-1112 Response fails if header exceeds buffer size + JETTY-1113 IllegalStateException when adding servlet filters programmatically + JETTY-1114 Unsynchronize webapp classloader getResource + Fix DefaultServletTest for windows + Include tmp directory sweeper in build + Streamline jetty-jboss build, update sar to QueuedThreadPool + Update Jetty implementation of com.sun.net.httpserver.* jetty-7.0.0.RC6 - 21 September 2009 + 280723 Add non blocking statistics handler + 282543 HttpClient SSL buffer size fix + 283357 org.eclipse.jetty.server.HttpConnectionTest exceptions + 288055 jetty-client fails to resolve failed resolution attempts correctly + 288153 jetty-client resend doesn't reset exchange + 288182 PUT request fails during retry + 288466 LocalConnector is not thread safe + 288514 AbstractConnector does not handle InterruptedExceptions on shutdown + 288772 Failure to connect does not set status to EXCEPTED + 289146 formalize reload policy functionality + 289156 jetty-client: no longer throw runtime exception for bad authn details + 289221 HttpExchange does not timeout when using blocking connector + 289285 org.eclipse.jetty.continuation 7.0.0.RC5 imports the org.mortbay.util.ajax package + 289686 HttpExchange.setStatus() has too coarse synchronization + 289958 StatisticsServlet incorrectly adds StatisticsHandler + 289960 start.jar assumes command line args are configs + 290081 Eager consume LF after CR + 290761 HttpExchange isDone handles intercepted events. + JETTY-719 Document state machine of jetty http client + JETTY-780 CNFE during startup of webapp with spring-context >= 2.5.1 + JETTY-936 274251 Improved servlet matching and optimized' + JETTY-1080 modify previous fix to work on windows + JETTY-1084 HEAD command not setting content-type in response under certain circumstances + JETTY-1086 Use UncheckedPrintWriter & cleaned up HttpStatus.Code usage + JETTY-1090 resolve potential infinite loop with webdav listener + JETTY-1092 MultiPartFilter can be pushed into infinite loop + JETTY-1093 Request.toString throws exception when size exceeds 4k + JETTY-1098 Default form encoding is UTF8 + JETTY-1101 Updated servlet3 continuation constructor + JETTY-1105 Custom error pages aren't working + JETTY-1108 SSL EOF detection + JETTY-1112 Response fails if header exceeds buffer size + JETTY-1113 IllegalStateException when adding servlet filters programmatically + Copy VERSION.txt to distro + Fixed XSS issue in CookieDump demo servlet. + Remove printlns from jetty-plus + Tweak DefaultServletTest under windows jetty-6.1.20 - 27 August 2009 + 283513 Check endp.isOpen when blocking read + 283818 fixed merge of forward parameters + 285006 Fixed NPE in AbstractConnector during shutdown + 286535 ContentExchange status code + 286911 Clean out cache when recycling HTTP fields + JETTY-838 Don't log and throw + JETTY-874 Better error on full header. + JETTY-960 Support ldaps + JETTY-1046 maven-jetty-jspc-plugin keepSources takes affect only in packageRoot + JETTY-1057 XSS error page + JETTY-1065 Add RedirectRegexRule to provide match/replace/group redirect support + JETTY-1066 Send 400 error for request URI parse exceptions + JETTY-1068 Avoid busy flush of async SSL + JETTY-1069 Adjust Bayeux Java client backoff algorithm + JETTY-1070 Java Bayeux Client not sending /meta/disconnect on stop + JETTY-1074 JMX thread manipulation + JETTY-1077 HashSSORealm shares Principals between UserRealms + JETTY-1078 Automatic JSON Pojo Conversion + JETTY-1079 ResourceCollection.toString() can throw IllegalStateException + JETTY-1080 Ignore files that would be extracted outside the destination directory when unpacking WARs + JETTY-1081 Handle null content type in GzipFilter + JETTY-1084 Disable GzipFilter for HEAD requests + JETTY-1085 Allow url sessionID if cookie invalid + JETTY-1086 Added UncheckedPrintWriter to avoid ignored EOFs + JETTY-1087 Chunked SSL non blocking input + JETTY-1098 Upgrade jsp to SJSAS-9_1_1-B60F-07_Jan_2009 + Added DebugHandler + Added getSubscriptions to cometd client + Clarified cometd interval timeout and allow per client intervals + COMETD-7 max latency config for lazy messages + Made unSubscribeAll public on cometd client + Removed clearing of queue in unSubscribeAll for cometd client + Update Main.main method to call setWar + Update test-jndi and test-annotation examples for atomikos 3.5.5 jetty-7.0.0.RC5 - 27 August 2009 + 286911 Clean out cache when recycling HTTP fields + 287496 Use start.ini always and added --exec + 287632 FilterContinuations for blocking jetty6 + JETTY-838 Don't log and throw + JETTY-874 Better header full warnings + JETTY-960 Support for ldaps + JETTY-1081 Handle null content type in GzipFilter + JETTY-1084 Disable GzipFilter for HEAD requests + JETTY-1085 Allow url sessionID if cookie invalid + JETTY-1086 Added UncheckedPrintWriter to avoid ignored EOFs + JETTY-1087 Chunked SSL non blocking input jetty-6.1.19 - 01 July 2009 + JETTY-799 shell script for jetty on cygwin + JETTY-863 Non blocking stats handler + JETTY-937 Further Improvements for sun JVM selector bugs + JETTY-970 BayeuxLoadGenerator latency handling + JETTY-1011 Grizzly uses queued thread pool + JETTY-1028 jetty:run plugin should check for the web.xml from the overlays if not found in src/main/webapp/WEB-INF/ + JETTY-1029 Handle quoted cookie paths + JETTY-1031 Handle large pipeline + JETTY-1033 jetty-plus compiled with jdk1.5 + JETTY-1034 Cookie parsing + JETTY-1037 reimplemented channel doRemove + JETTY-1040 jetty.client.HttpConnection does not handle non IOExceptions + JETTY-1042 Avoid cookie reuse on shared connection + JETTY-1044 add commons-daemon support as contrib/start-daemon module + JETTY-1045 Handle the case where request.PathInfo() should be "/*" + JETTY-1046 maven-jetty-jspc-plugin keepSources takes affect only in packageRoot + JETTY-1047 Cometd client can grow cookie headers + JETTY-1048 Default servlet can handle partially filtered large static content + JETTY-1049 Improved transparent proxy usability + JETTY-1054 Avoid double deploys + JETTY-1055 Cookie quoting + JETTY-1057 Error page stack trace XSS + JETTY-1058 Handle trailing / with aliases on + JETTY-1062 Don't filter cometd message without data jetty-7.0.0.RC4 - 18 August 2009 + 279820 Fixed HotSwapHandler + 285891 SessionAuthentication is serializable + 286185 Implement ability for JSON implementation to automatically register convertors + 286535 ContentExchange status code + JETTY-1057 XSS error page + JETTY-1079 ResourceCollection.toString + JETTY-1080 Ignore files that would be extracted outside the destination directory when unpacking WARs + Added discoverable start options jetty-7.0.0.RC3 - 07 August 2009 + 277403 remove system properties + 282447 concurrent destinations in HttpClient + 283172 fix Windows build, broken on directory creation with the DefaultServlet + 283375 additional error-checking on SSL connector passwords to prevent NPE + 283513 Check endp.isOpen when blocking read + 285697 extract parameters if dispatch has query + JETTY-1074 JMX thread manipulation + Improved deferred authentication handling jetty-7.0.0.RC2 - 29 June 2009 + 283375 improved extensibility of SSL connectors + 283818 fixed merge of forward parameters + 283844 Webapp / TLD errors are not clear + 284475 update jetty.sh for new OPTIONS syntax + 284510 Enhance jetty-start for diagnosis and unit testing + 284981 Implement a cross-origin filter + 285006 fix AbstractConnector NPE during shutdown. + Added DebugHandler + Added JavaUtilLog for Jetty logging to java.util.logging framework + backport jetty-8 annotation parsing to jetty-7 + Disassociate method on IdentityService + Improved handling of overlays and resourceCollections jetty-7.0.0.RC1 - 15 June 2009 + 283344 Startup on windows is broken + JETTY-1066 283357 400 response for bad URIs + JETTY-1068 Avoid busy flush of async SSL jetty-7.0.0.RC0 - 08 June 2009 + 271535 Adding integration tests, and enabling RFC2616 tests + 280843 Buffer pool uses isHeader + 281287 Handle date headers before 1 Jan 1970 + 282807 Better handling of 100 continues if response committed. + JETTY-967 create standalone build for PKCS12Import at codehaus + JETTY-1056 update jetty-ant module for Jetty 7 at codehaus trunk + JETTY-1058 Handle trailing / with aliases jetty-7.0.0.M4 - 01 June 2009 + 281059 NPE in QTP with debug on + JETTY-799 shell script for jetty on cygwin + JETTY-1031 Handle large pipeline + JETTY-1034 Cookie parsing + JETTY-1042 Prevent cookie leak between shared connection + JETTY-1048 Fix for large partially filtered static content + JETTY-1049 Improved transparent proxy usability + JETTY-1054 Avoid double deploys + JETTY-1055 Cookie quoting + JETTY-1057 Error page stack trace XSS jetty-7.0.0.M3 - 20 June 2009 + 274251 Allow dispatch to welcome files that are servlets (configurable) + 276545 Quoted cookie paths + 277403 Cleanup system property usage. + 277798 Denial of Service Filter + 279725 Support 100 and 102 expectations + 280707 client.HttpConnection does not catch and handle non-IOExceptions + 281470 Handle the case where request.PathInfo() should be "/*" + Added ContinuationThrowable + added WebAppContext.setConfigurationDiscovered for servlet 3.0 features + fixed race with expired async listeners + Numerous cleanups from static code analysis + Portable continuations for jetty6 and servlet3 + Refactored AbstractBuffers to HttpBuffers for performance + refactored configuration mechanism + Refactored continuations to only support response wrapping jetty-7.0.0.M2 - 18 May 2009 + 273767 Update to use geronimo annotations spec 1.1.1 + 275396 Added ScopedHandler to set servlet scope before security handler + JETTY-937 Work around Sun JVM bugs + JETTY-941 Linux chkconfig hint + JETTY-959 CGI servlet doesn't kill the CGI in case the client disconnects + JETTY-980 Fixed ResourceHandler ? handling, and bad URI creation in listings + JETTY-996 Make start-stop-daemon optional + JETTY-1003 java.lang.IllegalArgumentException: timeout can't be negative + JETTY-1004 CERT VU#402580 Canonical path handling includes ? in path segment + JETTY-1013 MySql Error with JDBCUserRealm + JETTY-1014 Enable start-stop-daemon by default on jetty.sh (START_STOP_DAEMON=1) + JETTY-1015 Reduce BayeuxClient and HttpClient lock contention + JETTY-1020 ZipException in org.mortbay.jetty.webapp.TagLibConfiguration prevents all contexts from being loaded jetty-6.1.18 - 16 May 2009 + JETTY-937 Improved work around sun JVM selector bugs + JETTY-1004 CERT VU#402580 Canonical path handling includes ? in path segment + JETTY-1008 ContinuationBayeux destroy is called + JETTY-1013 MySql Error with JDBCUserRealm + JETTY-1014 Enable start-stop-daemon by default on jetty.sh (START_STOP_DAEMON=1) + JETTY-1015 Reduce BayeuxClient and HttpClient lock contention + JETTY-1017 HttpDestination has too coarse locking + JETTY-1018 Denial of Service Filter + JETTY-1020 ZipException in org.mortbay.jetty.webapp.TagLibConfiguration prevents all contexts from being loaded + JETTY-1022 Removed several 1.5isms jetty-5.1.15 - 18 May 2009 + JETTY-418 synchronized load class + JETTY-1004 CERT VU402580 Canonical path handling includes ? in path segment + Fixes for CERT438616-CERT237888-CERT21284 jetty-6.1.17 - 30 April 2009 + JETTY-936 Make optional dispatching to welcome files as servlets + JETTY-937 Work around sun JVM selector bugs + JETTY-941 Linux chkconfig hint + JETTY-957 Reduce hardcoded versions + JETTY-980 Security / Directory Listing XSS present + JETTY-982 Make test-jaas-webapp run with jetty:run + JETTY-983 Default Servlet sets accept-ranges for cached/gzipped content + JETTY-985 Allow listeners to implement both interfaces + JETTY-988 X-Forwarded-Host has precedence over X-Forwarded-Server + JETTY-989 GzipFilter handles addHeader + JETTY-990 Async HttpClient connect + JETTY-992 URIUtil.encodePath encodes markup characters + JETTY-996 Make start-stop-daemon optional + JETTY-997 Remove jpackage-utils dependency on rpm install + JETTY-1000 Avoided needless 1.5 dependency + JETTY-1002 cometd-api to 1.0.beta8 + JETTY-1003 java.lang.IllegalArgumentException: timeout can't be negative + JETTY-1004 CERT VU#402580 Canonical path handling includes ? in path segment + JETTY-1006 Resume meta connect on all XD messages jetty-7.0.0.M1 - 22 April 2009 + 271258 FORM Authentication dispatch handling avoids caching + 271536 Add support to IO for quietly closing Readers / Writers + 273011 JETTY-980 JETTY-992 Security / Directory Listing XSS present + 273101 Fix DefaultServletTest XSS test case + 273153 Test for Nested references in DispatchServlet + JETTY-695 Handler dump + JETTY-983 DefaultServlet generates accept-ranges for cached/gzip content + Initial support for LoginService.logout + Removed HTTPConnection specifics from connection dispatching + Reworked authentication for deferred authentication + Reworked JMX for new layout jetty-6.1.16 - 01 April 2009 + JETTY-702 Create "jetty-tasks.xml" for the Ant plugin + JETTY-899 Standardize location for configuration files which go into etc + JETTY-936 Allow dispatch to welcome files that are servlets + JETTY-944 Lazy messages don't prevent long polls waiting + JETTY-946 Redeploys with maven jetty plugin of webapps with overlays don't work + JETTY-947 Exception stops terracotta session scavenger + JETTY-948 ConcurrentModificationException in TerracottaSessionManager scavenger + JETTY-949 Move cometd source to cometd.org project + JETTY-953 SSL keystore file input stream is not being closed directly + JETTY-956 SslSelectChannelConnector - password should be the default value of keyPassword if not specified + JETTY-959 CGI servlet doesn't kill the CGI in case the client disconnects + JETTY-964 Typo in Jetty 6.1.15 Manifest - Bundle-RequiredExcutionEnvironment + JETTY-972 Move cometd code back from cometd.org project (temporarily) + JETTY-973 Deliver same message to a collection of cometd Clients jetty-7.0.0.M0 - 27 March 2009 + JETTY-496 Support inetd/xinetd through use of System.inheritedChannel() + JETTY-540 Merged 3.0 Public Review changes + JETTY-567 Delay in initial TLS Handshake With FireFox 3 beta5 and SslSelectChannelConnector + JETTY-600 Automated tests of WADI integration + upgrade to WADI 2.0 + JETTY-691 System.getProperty() calls ... wrap them in doPrivileged + JETTY-713 Expose additional AbstractConnector methods via MBean + JETTY-731 Completed DeliverListener for cometd + JETTY-748 RandomAccessFileBuffer for hadoop optimization + JETTY-749 Improved ArrayQueue + JETTY-765 ensure stop mojo works for all execution phases + JETTY-774 Improved caching of mime types with charsets + JETTY-775 AbstractSessionTest remove timing related test + JETTY-778 handle granular windows timer in lifecycle test + JETTY-779 Fixed line feed in request log + JETTY-781 Add "mvn jetty:deploy-war" for deploying a pre-assembled war + JETTY-782 Implement interval advice for BayeuxClient + JETTY-783 Update jetty self-signed certificate + JETTY-784 TerracottaSessionManager leaks sessions scavenged in other nodes + JETTY-786 Allow DataSourceUserRealm to create tables + JETTY-787 Handle MSIE7 mixed encoding + JETTY-788 Fix jotm for scoped jndi naming + JETTY-790 WaitingContinuations can change mutex if not pending + JETTY-792 TerracottaSessionManager does not unlock new session with requested id + JETTY-793 Fixed DataCache millisecond rounding + JETTY-794 WADI integration tests fail intermittently. + JETTY-795 NullPointerException in SocketConnector.java + JETTY-801 Bring back 2 arg EnvEntry constructor + JETTY-802 Modify the default error pages to make association with Jetty clearer + JETTY-804 HttpClient timeout does not always work + JETTY-805 Fix jetty-jaas.xml for new UserRealm package + JETTY-806 Timeout related Deadlocks in HTTP Client + JETTY-807 HttpTester to handle charsets + JETTY-808 cometd client demo run.sh + JETTY-809 Need a way to customize WEB-INF/lib file extensions that are added to the classpath + JETTY-811 Allow configuration of system properties for the maven plugin using a file + JETTY-813 Simplify NCSARequestLog.java + JETTY-814 Add org.eclipse.jetty.client.Address.toString() + JETTY-816 Implement reconnect on java bayeux client + JETTY-817 Aborted SSL connections may cause jetty to hang with full cpu + JETTY-818 Support javax.servlet.request.ssl_session_id + JETTY-821 Allow lazy loading of persistent sessions + JETTY-822 Commit when autocommit=true causes error with mysql + JETTY-823 Extend start.config profiles + JETTY-824 Access to inbound byte statistics + JETTY-825 URL decoding of spaces (+) fails for encoding not utf8 + JETTY-830 Add ability to reserve connections on http client + JETTY-831 Add ability to stop java bayeux client + JETTY-832 More UrlDecoded handling in relation to JETTY-825 + JETTY-834 Configure DTD does not allow children + JETTY-837 Response headers set via filter are ignored for static resources + JETTY-840 add default mime types to *.htc and *.pps + JETTY-841 Duplicate messages when sending private message to yourself with cometd chat demo + JETTY-842 NPE in jetty client when no path component + JETTY-843 META-INF/MANIFEST.MF is not present in unpacked webapp + JETTY-844 Replace reflection with direct invocation in Slf4jLog + JETTY-848 Temporary folder not fully cleanup after stop (via Sweeper) + JETTY-854 JNDI scope does not work with applications in a .war + JETTY-859 MultiPartFilter ignores the query string parameters + JETTY-861 switched buffer pools to ThreadLocal implementation + JETTY-862 EncodedHttpURI ignores given encoding in constructor + JETTY-866 jetty-client test case fix + JETTY-869 NCSARequestLog locale config + JETTY-870 NullPointerException in Response when performing redirect to wrong relative URL + JETTY-871 jetty-client expires() NPE race condition fixed + JETTY-876 Added new BlockingArrayQueue and new QueuedThreadPool + JETTY-890 merge jaspi branch to trunk + JETTY-894 Add android .apk to mime types + JETTY-897 Remove swing dependency in GzipFilter + JETTY-898 Allow jetty debs to start with custom java args provided by users + JETTY-899 Standardize location and build process for configuration files which go into etc + JETTY-909 Update useragents cache + JETTY-917 Change for JETTY-811 breaks systemProperties config parameter in maven-jetty-plugin + JETTY-922 Fixed NPE on getRemoteHost when socket closed + JETTY-923 Client supports attributes + JETTY-926 default location for generatedClasses of jspc plugin is incorrect + JETTY-938 Deadlock in the TerracottaSessionManager + JETTY-939 NPE in AbstractConfiguration.callPreDestroyCallbacks + JETTY-946 Redeploys with maven jetty plugin of webapps with overlays don't work + JETTY-950 Fix double-printing of request URI in request log + JETTY-953 SSL keystore file input stream is not being closed directly + JETTY-956 SslSelectChannelConnector - password should be the default value of keyPassword if not specified + moved to org.eclipse packages + simplified HandlerContainer API jetty-6.1.15 - 04 March 2009 + JETTY-923 BayeuxClient uses message pools to reduce memory footprint + JETTY-924 Improved BayeuxClient disconnect handling + JETTY-925 Lazy bayeux messages + JETTY-926 default location for generatedClasses of jspc plugin is incorrect + JETTY-931 Fix issue with jetty-rewrite.xml + JETTY-934 fixed stop/start of Bayeux Client + JETTY-938 Deadlock in the TerracottaSessionManager + JETTY-939 NPE in AbstractConfiguration.callPreDestroyCallbacks jetty-6.1.15 - 02 March 2009 + JETTY-923 BayeuxClient uses message pools to reduce memory footprint + JETTY-924 Improved BayeuxClient disconnect handling + JETTY-925 Lazy bayeux messages + JETTY-926 default location for generatedClasses of jspc plugin is incorrect jetty-6.1.15.rc4 - 19 February 2009 + JETTY-496 Support inetd/xinetd through use of System.inheritedChannel() + JETTY-713 Expose additional AbstractConnector methods via MBean + JETTY-749 Improved ack extension + JETTY-802 Modify the default error pages to make association with Jetty clearer + JETTY-811 Allow configuration of system properties for the maven plugin using a file + JETTY-815 Add comet support to jQuery javascript library + JETTY-840 add default mime types to *.htc and *.pps + JETTY-848 Temporary folder not fully cleanup after stop (via Sweeper) + JETTY-869 NCSARequestLog locale config + JETTY-870 NullPointerException in Response when performing redirect to wrong relative URL + JETTY-872 Handshake handler calls wrong extension callback + JETTY-878 Removed printStackTrace from WaitingContinuation + JETTY-879 Support extra properties in jQuery comet implementation + JETTY-882 ChannelBayeuxListener called too many times + JETTY-884 Use hashcode for threadpool ID + JETTY-887 Split configuration and handshaking in jquery comet + JETTY-888 Fix abort in case of multiple outstanding connections + JETTY-894 Add android .apk to mime types + JETTY-898 Allow jetty debs to start with custom java args provided by users + JETTY-909 Update useragents cache jetty-6.1.15.rc3 - 28 January 2009 + JETTY-691 System.getProperty() calls ... wrap them in doPrivileged + JETTY-844 Replace reflection with direct invocation in Slf4jLog + JETTY-861 switched buffer pools to ThreadLocal implementation + JETTY-866 jetty-client test case fix jetty-6.1.15.rc2 - 23 January 2009 + JETTY-567 Delay in initial TLS Handshake With FireFox 3 beta5 and SslSelectChannelConnector + adjustment to jetty-client assembly packaging jetty-6.1.15.pre0 - 20 January 2009 + JETTY-600 Automated tests of WADI integration + upgrade to WADI 2.0 + JETTY-749 Reliable message delivery + JETTY-781 Add "mvn jetty:deploy-war" for deploying a pre-assembled war + JETTY-794 WADI integration tests fail intermittently. + JETTY-795 NullPointerException in SocketConnector.java + JETTY-798 Jboss session manager incompatible with LifeCycle.Listener + JETTY-801 Bring back 2 arg EnvEntry constructor + JETTY-802 Modify the default error pages to make association with Jetty very clear + JETTY-804 HttpClient timeout does not always work + JETTY-806 Timeout related Deadlocks in HTTP Client + JETTY-807 HttpTester to handle charsets + JETTY-808 cometd client demo run.sh + JETTY-809 Need a way to customize WEB-INF/lib file extensions that are added to the classpath + JETTY-814 Add org.eclipse.jetty.client.Address.toString() + JETTY-816 Implement reconnect on java bayeux client + JETTY-817 Aborted SSL connections may cause jetty to hang with full cpu + JETTY-819 Jetty Plus no more jre 1.4 + JETTY-821 Allow lazy loading of persistent sessions + JETTY-824 Access to inbound byte statistics + JETTY-825 URL decoding of spaces (+) fails for encoding not utf8 + JETTY-827 Externalize servlet api + JETTY-830 Add ability to reserve connections on http client + JETTY-831 Add ability to stop java bayeux client + JETTY-832 More UrlDecoded handling in relation to JETTY-825 + JETTY-833 Update debian and rpm packages for new jsp-2.1-glassfish jars and servlet-api jar + JETTY-834 Configure DTD does not allow children + JETTY-837 Response headers set via filter are ignored for static resources + JETTY-841 Duplicate messages when sending private message to yourself with cometd chat demo + JETTY-842 NPE in jetty client when no path component + JETTY-843 META-INF/MANIFEST.MF is not present in unpacked webapp + JETTY-852 Ensure handshake and connect retried on failure for jquery-cometd + JETTY-854 JNDI scope does not work with applications in a .war + JETTY-855 jetty-client uber assembly support + JETTY-858 ContentExchange provides bytes + JETTY-859 MultiPartFilter ignores the query string parameters + JETTY-862 EncodedHttpURI ignores given encoding in constructor jetty-6.1.14 - 14 November 2008 + JETTY-630 jetty6-plus rpm is missing the jetty6-plus jar + JETTY-748 Reduced flushing of large content + JETTY-765 ensure stop mojo works for all execution phases + JETTY-777 include util5 on the jetty debs + JETTY-778 handle granular windows timer in lifecycle test + JETTY-779 Fixed line feed in request log + JETTY-782 Implement interval advice for BayeuxClient + JETTY-783 Update jetty self-signed certificate + JETTY-784 TerracottaSessionManager leaks sessions scavenged in other nodes + JETTY-787 Handle MSIE7 mixed encoding + JETTY-788 Fix jotm for new scoped jndi + JETTY-790 WaitingContinuations can change mutex if not pending + JETTY-791 Ensure jdk1.4 compatibility for jetty-6 + JETTY-792 TerracottaSessionManager does not unlock new session with requested id + JETTY-793 Fixed DataCache millisecond rounding jetty-6.1.12 - 04 November 2008 + JETTY-731 Completed DeliverListener for cometd + JETTY-772 Increased default threadpool size to 250 + JETTY-774 Cached text/json content type + JETTY-775 fix port of openspaces to jetty-6 jetty-7.0.0.pre5 - 30 October 2008 + JETTY-766 Fix npe + JETTY-767 Fixed SSL Client no progress handshake bug + JETTY-768 Remove EnvEntry overloaded constructors + JETTY-769 jquery example error + JETTY-771 Ensure NamingEntryUtil is jdk1.4 compliant + JETTY-772 Increased default threadpool size to 250 jetty-6.1.12.rc5 - 30 October 2008 + JETTY-703 maxStopTimeMs added to QueuedThreadPool + JETTY-762 improved QueuedThreadPool idle death handling + JETTY-763 Fixed AJP13 constructor + JETTY-766 Ensure SystemProperties set early on jetty-maven-plugin + JETTY-767 Fixed SSL Client no progress handshake bug + JETTY-768 Remove EnvEntry overloaded constructors + JETTY-771 Ensure NamingEntryUtil jdk1.4 compliant jetty-7.0.0.pre4 - 28 October 2008 + JETTY-241 Support for web application overlays in rapid application development (jetty:run) + JETTY-319 improved passing of exception when webapp unavailable + JETTY-331 SecureRandom hangs on systems with low entropy (connectors slow to start) + JETTY-591 No server classes for jetty-web.xml + JETTY-604 AbstractSession.setSessionURL + JETTY-670 $JETTY_HOME/bin/jetty.sh not worked in Solaris, because of /usr/bin/which has no error-code + JETTY-676 ResourceHandler doesn't support HTTP HEAD requests + JETTY-677 GWT serialization issue + JETTY-680 Can't configure the ResourceCollection with maven + JETTY-681 JETTY-692 MultiPartFilter is slow for file uploads + JETTY-682 Added listeners and queue methods to cometd + JETTY-686 LifeCycle.Listener + JETTY-687 Issue with servlet-mapping in dynamic servlet invoker + JETTY-688 Cookie causes NumberFormatException + JETTY-689 processing of non-servlet related annotations + JETTY-690 Updated XBean dependencies to XBean version 3.4.3 and Spring 2.0.5. + JETTY-696 jetty.sh restart not working + JETTY-698 org.eclipse.resource.JarResource.extract does not close JarInputStream jin + JETTY-699 Optimized cometd sending of 1 message to many many clients + JETTY-700 unit test for unread request data + JETTY-703 maxStopTimeMs added to QueuedThreadPool + JETTY-708 allow 3 scopes for jndi resources: jvm, server or webapp + JETTY-709 Jetty plugin's WebAppConfig configured properties gets overridden by AbstractJettyRunMojo even when already set + JETTY-710 Worked around poor implementation of File.toURL() + JETTY-711 DataSourceUserRealm implementation + JETTY-712 HttpClient does not handle request complete after response complete + JETTY-715 AJP Key size as Integer + JETTY-716 Fixed NPE on empty cometd message + JETTY-718 during ssl unwrap, return true if some bytes were read, even if underflow + JETTY-720 fix HttpExchange.waitForStatus + JETTY-721 Support wildcard in VirtualHosts configuration + JETTY-723 jetty.sh does not check if TMP already is set + JETTY-724 better handle EBCDIC default JVM encoding + JETTY-728 Improve Terracotta integration and performances + JETTY-730 Set SAX parse features to defaults + JETTY-731 DeliverListener for cometd + JETTY-732 Case Sensitive Basic Authentication Response Header Implementations + JETTY-733 Expose ssl connectors with xbean + JETTY-735 Wrong default jndi name on DataSourceUserRealm + JETTY-736 Client Specific cometd advice + JETTY-737 refactored jetty.jar into jetty, xml, security, ssl, webapp and deploy jars + JETTY-738 If jetty.sh finds a pid file is does not check to see if a process with that pid is still running + JETTY-739 Race in QueuedThreadPool + JETTY-741 HttpClient connects slowly due to reverse address lookup by InetAddress.getHostName() + JETTY-742 Private messages in cometd chat demo + JETTY-747 Handle HttpClient exceptions better + JETTY-755 Optimized HttpParser and buffers for few busy connections + JETTY-757 Unhide JAAS classes + JETTY-758 Update JSP to glassfish tag SJSAS-9_1_1-B51-18_Sept_2008 + JETTY-759 Fixed JSON small negative real numbers + JETTY-760 Handle wildcard VirtualHost and normalize hostname in ContextHandlerCollection + JETTY-762 improved QueuedThreadPool idle death handling + JETTY-763 Fixed AJP13 constructor + JETTY-766 Ensure SystemProperties set early on jetty-maven-plugin jetty-6.1.12.rc4 - 21 October 2008 + JETTY-319 improved passing of exception when webapp unavailable + JETTY-729 Backport Terracotta integration to Jetty6.1 branch + JETTY-744 Backport of JETTY-741: HttpClient connects slowly due to reverse address lookup by InetAddress.getHostName() + JETTY-747 Handle exceptions better in HttpClient + JETTY-755 Optimized HttpParser and buffers for few busy connections + JETTY-758 Update JSP 2.1 to glassfish tag SJSAS-9_1_1-B51-18_Sept_2008 + JETTY-759 Fixed JSON small negative real numbers + JETTY-760 Handle wildcard VirtualHost and normalize hostname in ContextHandlerCollection jetty-6.1.12.rc3 - 10 October 2008 + JETTY-241 Support for web application overlays in rapid application development (jetty:run) + JETTY-686 LifeCycle.Listener + JETTY-715 AJP key size + JETTY-716 NPE for empty cometd message + JETTY-718 during ssl unwrap, return true if some bytes were read, even if underflow + JETTY-720 fix HttpExchange.waitForStatus + JETTY-721 Support wildcard in VirtualHosts configuration + JETTY-722 jndi related threadlocal not cleared after deploying webapp + JETTY-723 jetty.sh does not check if TMP already is set + JETTY-725 port JETTY-708 (jndi scoping) to jetty-6 + JETTY-730 set SAX parser features to defaults + JETTY-731 DeliverListener for cometd + JETTY-732 Case Sensitive Basic Authentication Response Header Implementations + JETTY-736 Client Specific cometd advice + JETTY-738 If jetty.sh finds a pid file is does not check to see if a process with that pid is still running + JETTY-739 Race in QueuedThreadPool + JETTY-742 Private messages in cometd chat demo jetty-6.1.12rc2 - 12 September 2008 + JETTY-282 Support manually-triggered reloading + JETTY-331 SecureRandom hangs on systems with low entropy (connectors slow to startup) + JETTY-591 No server classes for jetty-web.xml + JETTY-670 $JETTY_HOME/bin/jetty.sh not worked in Solaris, because of /usr/bin/which has no error-code + JETTY-671 Configure DTD does not allow children + JETTY-672 Utf8StringBuffer doesn't properly handle null characters (char with byte value 0) + JETTY-676 ResourceHandler doesn't support HTTP HEAD requests + JETTY-677 GWT serialization issue + JETTY-680 Can't configure the ResourceCollection with maven + JETTY-681 JETTY-692 MultiPartFilter is slow for file uploads + JETTY-682 Added listeners and queue methods to cometd + JETTY-683 ResourceCollection works for jsp files but does not work for static resources under DefaultServlet + JETTY-687 Issue with servlet-mapping in dynamic servlet invoker + JETTY-688 Cookie causes NumberFormatException + JETTY-696 ./jetty.sh restart not working + JETTY-698 org.eclipse.resource.JarResource.extract does not close JarInputStream jin + JETTY-699 Optimize cometd sending of 1 message to many many clients + JETTY-709 Jetty plugin's WebAppConfig configured properties gets overridden by AbstractJettyRunMojo even when already set + JETTY-710 Worked around poor implementation of File.toURL() + JETTY-712 HttpClient does not handle request complete after response complete jetty-7.0.0pre3 - 06 August 2008 + JETTY-30 Externalize servlet-api to own project + JETTY-182 Support setting explicit system classpath for jasper Jsr199JavaCompiler + JETTY-319 Get unavailable exception and added startWithUnavailable option + JETTY-381 JETTY-622 Multiple Web Application Source Directory + JETTY-442 Accessors for mimeType on ResourceHandler + JETTY-502 forward of an include should hide include attributes + JETTY-562 RewriteHandler support for virtual hosts + JETTY-563 JETTY-482 OpenRemoteServiceServlet for GWT1.5M2+ + JETTY-564 Consider optionally importing org.apache.jasper.servlet + JETTY-571 SelectChannelConnector throws Exception on close on Windows + JETTY-608 Suspend/Resume/Complete request listeners + JETTY-621 Improved LazyList javadoc + JETTY-626 Null protect reading the dtd resource from classloader + JETTY-628 Rewrite rule for rewriting scheme + JETTY-629 Don't hold timeout lock during expiry call. + JETTY-632 OSGi tags for Jetty client + JETTY-633 Default form encoding 8859_1 rather than utf-8 + JETTY-635 Correctly merge request parameters when doing forward + JETTY-636 Separate lifeycle of jsp build + JETTY-637 empty date headers throw IllegalArgumentException + JETTY-641 JDBC Realm purge cache problem + JETTY-642 NPE in LdapLoginModule + JETTY-644 LdapLoginModule uses proper filters when searching + JETTY-645 Do not provide jetty-util to the webapps + JETTY-646 Should set Cache-Control header when sending errors to avoid caching + JETTY-647 suspended POSTs with binary data do too many resumes + JETTY-650 Parse "*" URI for HTTP OPTIONS request + JETTY-651 Release resources during destroy + JETTY-653 Upgrade jta api specs to more recent version + JETTY-654 Allow Cometd Bayeux object to be JMX manageable + JETTY-655 Support parsing application/x-www-form-urlencoded parameters via http PUT + JETTY-656 HttpClient defaults to async mode + JETTY-659 ContentExchange and missing headers in HttpClient + JETTY-663 AbstractDatabaseLoginModule handle not found UserInfo and userName + JETTY-665 Support merging class directories + JETTY-666 scanTargetPatterns override the values already being set by scanTarget + JETTY-667 HttpClient handles chunked content + JETTY-669 Http methods other than GET and POST should not have error page content + JETTY-671 Configure DTD does not allow children + JETTY-672 Utf8StringBuffer doesn't properly handle null characters (char with byte value 0) + JETTY-675 ServletContext.getRealPath("") returns null instead of returning the root dir of the webapp + Upgrade jsp 2.1 to SJSAS-9_1_02-B04-11_Apr_2008 jetty-6.1.12rc1 - 01 August 2008 + JETTY-319 Get unavailable exception and added startWithUnavailable option + JETTY-381 JETTY-622 Multiple Web Application Source Directory + JETTY-442 Accessors for mimeType on ResourceHandler + JETTY-502 forward of an include should hide include attributes + JETTY-562 RewriteHandler support for virtual hosts + JETTY-563 GWT OpenRemoteServiceServlet GWT1.5M2+ + JETTY-564 Consider optionally importing org.apache.jasper.servlet + JETTY-571 SelectChannelConnector throws Exception on close on Windows + JETTY-596 Proxy authorization support in HttpClient + JETTY-599 handle buffers consistently handle invalid index for poke + JETTY-603 Handle IPv6 in HttpURI + JETTY-605 Added optional threadpool to BayeuxService + JETTY-606 better writeTo impl for BIO + JETTY-607 Add GigaSpaces session clustering + JETTY-610 jetty.class.path not being interpreted + JETTY-613 website module now generates site-component for jetty-site + JETTY-614 scanner allocated hashmap on every scan + JETTY-623 ServletContext.getServerInfo() non compliant + JETTY-626 Null protect reading the dtd resource from classloader + JETTY-628 Rewrite rule for rewriting scheme + JETTY-629 Don't hold timeout lock during expiry call. + JETTY-632 OSGi tags for Jetty client + JETTY-633 Default form encoding 8859_1 rather than utf-8 + JETTY-635 Correctly merge request parameters when doing forward + JETTY-637 empty date headers throw IllegalArgumentException + JETTY-641 JDBC Realm purge cache problem + JETTY-642 NPE in LdapLoginModule + JETTY-644 LdapLoginModule uses proper filters when searching + JETTY-646 Should set Cache-Control header when sending errors to avoid caching + JETTY-647 suspended POSTs with binary data do too many resumes + JETTY-650 Parse "*" URI for HTTP OPTIONS request + JETTY-651 Release resources during destroy + JETTY-654 Allow Cometd Bayeux object to be JMX manageable + JETTY-655 Support parsing application/x-www-form-urlencoded parameters via http PUT + JETTY-656 HttpClient defaults to async mode + JETTY-657 Backport jetty-7 sslengine + JETTY-658 backport latest HttpClient from jetty-7 to jetty-6 + JETTY-659 ContentExchange and missing headers in HttpClient + JETTY-660 Backported QoSFilter + JETTY-663 AbstractDatabaseLoginModule handle not found UserInfo and userName + JETTY-665 Support merging class directories + JETTY-666 scanTargetPatterns override the values already being set by scanTarget + JETTY-667 HttpClient handles chunked content + JETTY-669 Http methods other than GET and POST should not have error page content + Upgrade jsp 2.1 to SJSAS-9_1_02-B04-11_Apr_2008 jetty-7.0.0pre2 - 30 June 2008 + JETTY-336 413 error for header buffer full + JETTY-425 race in stopping SelectManager + JETTY-568 Avoid freeing DirectBuffers. New locking NIO ResourceCache. + JETTY-569 Stats for suspending requests + JETTY-572 Unique cometd client ID + JETTY-576 servlet dtds and xsds not being loaded locally + JETTY-578 OSGI Bundle-RequiredExcutionEnvironment set to J2SE-1.5 + JETTY-579 OSGI resolved management and servlet.resources import error + JETTY-580 Fixed SSL shutdown + JETTY-581 ContextPath constructor + JETTY-582 final ISO_8859_1 + JETTY-584 handle null contextPath + JETTY-587 persist sessions to database + JETTY-588 handle Retry in ServletException + JETTY-589 Added Statistics Servlet + JETTY-590 Digest auth domain for root context + JETTY-592 expired timeout callback without synchronization + JETTY-595 SessionHandler only deals with base request session + JETTY-596 proxy support in HttpClient + JETTY-598 Added more reliable cometd message flush option + JETTY-599 handle buffers consistently handle invalid index for poke + JETTY-603 Handle IPv6 in HttpURI + JETTY-605 Added optional threadpool to BayeuxService + JETTY-606 better writeTo impl for BIO + JETTY-607 Add GigaSpaces session clustering + JETTY-609 jetty-client improvements for http conversations + JETTY-610 jetty.class.path not being interpreted + JETTY-611 make general purpose jar scanning mechanism + JETTY-612 scan for web.xml fragments + JETTY-613 various distribution related changes + JETTY-614 scanner allocates hashmap on every iteration + JETTY-615 Replaced CDDL servlet.jar with Apache-2.0 licensed version + JETTY-623 ServletContext.getServerInfo() non compliant jetty-6.1.11 - 06 June 2008 + JETTY-336 413 error for full header buffer + JETTY-425 race in stopping SelectManager + JETTY-580 Fixed SSL shutdown + JETTY-581 ContextPath constructor + JETTY-582 final ISO_8859_1 + JETTY-584 handle null contextPath + JETTY-588 handle Retry in ServletException + JETTY-590 Digest auth domain for root context + JETTY-592 expired timeout callback without synchronization + JETTY-595 SessionHandler only deals with base request session + JETTY-596 Proxy support in HttpClient + JETTY-598 Added more reliable cometd message flush option jetty-6.1.10 - 20 May 2008 + JETTY-440 allow file name patterns for jsp compilation for jspc plugin + JETTY-529 CNFE when deserializing Array from session resolved + JETTY-537 JSON handles Locales + JETTY-547 Shutdown SocketEndpoint output before close + JETTY-550 Reading 0 bytes corrupts ServletInputStream + JETTY-551 Upgraded to Wadi 2.0-M10 + JETTY-556 Encode all URI fragments + JETTY-557 Allow ServletContext.setAttribute before start + JETTY-558 optional handling of X-Forwarded-For/Host/Server + JETTY-566 allow for non-blocking behavior in jetty maven plugin + JETTY-572 unique cometd client ID + JETTY-579 osgi fixes with management and servlet resources + Use QueuedThreadPool as default jetty-7.0.0pre1 - 03 May 2008 + JETTY-440 allow file name patterns for jsp compilation for jspc plugin + JETTY-529 CNFE when deserializing Array from session resolved + JETTY-558 optional handling of X-Forwarded-For/Host/Server + JETTY-559 ignore unsupported shutdownOutput + JETTY-566 allow for non-blocking behavior in jetty maven plugin + address osgi bundling issue relating to build resources + Allow annotations example to be built regularly, copy to contexts-available + Improved suspend examples + Make annotations example consistent with servlet 3.0 + Refactor JNDI impl to simplify jetty-7.0.0pre0 - 21 April 2008 + JETTY-282 Support manually-triggered reloading by maven plugin + JETTY-341 100-Continues sent only after getInputStream called. + JETTY-386 backout fix and replaced with ContextHandler.setCompactPath(boolean) + JETTY-399 update OpenRemoteServiceServlet to gwt 1.4 + JETTY-467 allow URL rewriting to be disabled. + JETTY-468 unique holder names for addServletWithMapping + JETTY-471 LDAP JAAS Realm + JETTY-474 Fixed case sensitivity issue with HttpFields + JETTY-475 AJP connector in RPMs + JETTY-486 Improved jetty.sh script + JETTY-487 Handle empty chunked request + JETTY-494 Client side session replication + JETTY-519 HttpClient does not recycle closed connection. + JETTY-522 Add build profile for macos for setuid + JETTY-523 Default servlet uses ServletContext.getResource + JETTY-524 Don't synchronize session event listener calls + JETTY-525 Fixed decoding for long strings + JETTY-526 Fixed MMBean fields on JMX MBeans + JETTY-528 Factor our cookie parsing to CookieCutter + JETTY-530 Improved JMX MBeanContainer lifecycle + JETTY-531 Optional expires on MovedContextHandler + JETTY-532 MBean properties for QueuedThreadPool + JETTY-535 Fixed Bayeux server side client memory leak + JETTY-537 JSON handles Locales + JETTY-538 test harness fix for windows + JETTY-540 Servlet-3.0 & java5 support (work in progress) + JETTY-543 Atomic batch get and put of files. + JETTY-545 Rewrite handler + JETTY-546 Webapp runner. All in one jar to run a webapps + JETTY-547 Shutdown SocketEndpoint output before close + JETTY-550 Reading 0 bytes corrupts ServletInputStream + JETTY-551 Wadi 2.0-M10 + JETTY-553 Fixed customize override + JETTY-556 Encode all URI fragments + JETTY-557 Allow ServletContext.setAttribute before start + JETTY-560 Allow decoupling of jndi names in web.xml + Added option to dispatch to suspended requests. + BayeuxClient use a single connection for polling + Delay 100 continues until getInputStream + Ensure Jotm tx mgr can be found in jetty-env.xml + HttpClient supports pipelined request + Jetty-6.1.8 Changes + Make javax.servlet.jsp optional osgi import for jetty module + QueuedThreadPool default + Refactor of Continuation towards servlet 3.0 proposal + Renamed modules management and naming to jmx and jndi. + RetryRequest exception now extends ThreadDeath jetty-6.1.9 - 26 March 2008 + JETTY-399 update OpenRemoteServiceServlet to gwt 1.4 + JETTY-471 LDAP JAAS Realm + JETTY-475 AJP connector in RPMs + JETTY-482 update to JETTY-399 + JETTY-519 HttpClient does not recycle closed connection. + JETTY-522 Add build profile for macos for setuid + JETTY-525 Fixed decoding for long strings + JETTY-526 Fixed MMBean fields on JMX MBeans + JETTY-532 MBean properties for QueuedThreadPool + JETTY-535 Fixed Bayeux server side client memory leak + JETTY-538 test harness fix for windows + JETTY-541 Cometd per client timeouts + Ensure Jotm tx mgr can be found in jetty-env.xml + Make javax.servlet.jsp optional osgi import for jetty module jetty-6.1.8 - 28 February 2008 + JETTY-350 log ssl errors on SslSocketConnector + JETTY-417 JETTY_LOGS environment variable not queried by jetty.sh + JETTY-433 ContextDeployer constructor fails unnecessarily when using a security manager if jetty.home not set + JETTY-434 ContextDeployer scanning of sub-directories should be optional + JETTY-481 Handle empty Bayeux response + JETTY-489 Improve doco on the jetty.port property for plugin + JETTY-490 Fixed JSONEnumConvertor + JETTY-491 opendocument mime types + JETTY-492 Null pointer in HashSSORealm + JETTY-493 JSON handles BigDecimals + JETTY-498 Improved cookie parsing + JETTY-507 Fixed encoding from JETTY-388 and test case + JETTY-508 Extensible cometd handlers + JETTY-509 Fixed JSONP transport for changing callback names + JETTY-511 jetty.sh mishandled JETTY_HOME when launched from a relative path + JETTY-512 add slf4j as optional to manifest + JETTY-513 Terracotta session replication does not work when the initial page on each server does not set any attributes + JETTY-515 Timer is missing scavenging Task in HashSessionManager + Add "mvn jetty:stop" + Added BayeuxService + Added JSON.Convertor and non static JSON instances + Added QueuedThreadPool + add removeHandler(Handler) method to HandlerContainer interface + AJP handles bad mod_jk methods + Allow code ranges on ErrorPageErrorHandler + allow sessions to be periodically persisted to disk + Cookie support in BayeuxClient + Fixed JSON negative numbers + further Optimizations and improvements of Cometd + grizzly fixed for posts + Improved Bayeux API + Improved Cometd timeout handling + JSON unquotes / + Long cache for JSON + Optimizations and improvements of Cometd, more pooled objects + Optimized QuotedStringTokenizer.quote() + Remove duplicate commons-logging jars and include sslengine in jboss sar jetty-6.1.7 - 22 December 2007 + JETTY-386 CERT-553235 backout fix and replaced with ContextHandler.setCompactPath(boolean) + JETTY-467 allow URL rewriting to be disabled. + JETTY-468 unique holder names for addServletWithMapping + JETTY-474 Fixed case sensitivity issue with HttpFields + JETTY-486 Improved jetty.sh script + JETTY-487 Handle empty chunked request + Add "mvn jetty:stop" + Added BayeuxService + Added JSON.Convertor and non static JSON instances + allow sessions to be periodically persisted to disk + Cookie support in BayeuxClient + grizzly fixed for posts + jetty-6.1 branch created from 6.1.6 and r593 of jetty-contrib trunk + Optimizations and improvements of Cometd, more pooled objects + Update java5 patch jetty-6.1.6 - 18 November 2007 + JETTY-455 Optional cometd id + JETTY-459 Unable to deploy from Eclipse into the root context + JETTY-461 fixed cometd unknown channel + JETTY-464 typo in ErrorHandler + JETTY-465 System.exit() in constructor exception for MultiPartOutputStream + rudimentary debian packaging + updated grizzly connector to 1.6.1 jetty-6.1.6rc1 - 05 November 2007 + JETTY-388 Handle utf-16 and other multibyte non-utf-8 form content. + JETTY-409 String params that denote files changed to File + JETTY-438 handle trailing . in vhosts + JETTY-439 Fixed 100 continues clash with Connection:close + JETTY-443 windows bug causes Acceptor thread to die + JETTY-445 removed test code + JETTY-448 added setReuseAddress on AbstractConnector + JETTY-450 Bad request for response sent to server + JETTY-451 Concurrent modification of session during invalidate + JETTY-452 CERT VU#237888 Dump Servlet - prevent cross site scripting + JETTY-453 updated Wadi to 2.0-M7 + JETTY-454 handle exceptions with themselves as root cause + JETTY-456 allow null keystore for osX + JETTY-457 AJP certificate chains + Added configuration file for capturing stderr and stdout + CERT VU#38616 handle single quotes in cookie names. + Give bayeux timer name + Give Terracotta session scavenger a name + Housekeeping on poms + Improved JSON parsing from Readers + Jetty Eclipse Plugin 1.0.1: force copy of context file on redeploy + Moved some impl classes from jsp-api-2.1 to jsp-2.1 + Updated for dojo 1.0(rc) cometd + Upgrade jsp 2.1 to SJSAS-9_1-B58G-FCS-08_Sept_2007 jetty-6.1.6rc0 - 03 October 2007 + JETTY-259 SystemRoot set for windows CGI + JETTY-311 avoid json keywords + JETTY-376 allow anything but CRLF in reason string + JETTY-398 Allow same WADI Dispatcher to be used across multiple web-app contexts + JETTY-400 consume CGI stderr + JETTY-402 keep HashUserRealm in sync with file + JETTY-403 Allow long content length for range requests + JETTY-404 WebAppDeployer sometimes deploys duplicate webapp + JETTY-405 Default date formate for reqest log + JETTY-407 AJP handles unknown content length + JETTY-413 Make rolloveroutputstream timer daemon + JETTY-422 Allow values to be null in config files + JETTY-423 Ensure javax.servlet.forward parameters are latched on first forward + JETTY-425 Handle duplicate stop calls better + JETTY-430 improved cometd logging + JETTY-431 HttpClient soTimeout + Add ability to persist sessions with HashSessionManager + Added ConcatServlet to combine javascript and css + Added jetty.lib system property to start.config + Added JPackage RPM support + Added JSON.Convertable + Adding setUsername,setGroupname to setuid and mavenizing native build + Add jetty.host system property + AJP13 Fix on chunked post + Allow properties files on the XmlConfiguration command line. + Allow scan interval to be set after Scanner started + Avoid FULL exception in window between blockForOutput and remote close + Cached user agents strings in the /org/mortbay/jetty/useragents resource + CVE-2007-5615 Added protection for response splitting with bad headers. + Ensure session is completed only when leaving context. + Fix cached header optimization for extra characters + Fix Host header for async client + Fix patch for java5 to include cometd module + Fix typo in async client onResponsetHeader method name + Give deployment file Scanner threads a unique name + Make default time format for RequestLog match NCSA default + Make mx4j used only if runtime uses jdk<1.5 + Moved Grizzly to contrib + Prevent infinite loop on stopping with temp dir + Removal of unneeded dependencies from management, maven-plugin, naming & plus poms + SetUID option to support setgid + Tweak OSGi manifests to remove unneeded imports + Updated README, test index.html file and jetty-plus.xml file + Update jasper2.1 to tag SJSAS-9_1-B58C-FCS-22_Aug_2007 + Update terracotta to 2.4.1 and exclude ssl classes + Use terracotta repo for build; make jetty a terracotta module + UTF-8 for bayeux client jetty-6.1.5 - 19 July 2007 + JETTY-392 updated LikeJettyXml example + Fixed GzipFilter for dispatchers + Fixed reset of reason + Upgrade to Jasper 2.1 tag SJSAS-9_1-B50G-BETA3-27_June_2007 jetty-6.1.5rc0 - 15 July 0200 + JETTY-253 Improved graceful shutdown + JETTY-373 Stop all dependent lifecycles + JETTY-374 HttpTesters handles large requests/responses + JETTY-375 IllegalStateException when committed. + JETTY-376 allow spaces in reason string + JETTY-377 allow sessions to be wrapped with AbstractSesssionManager.SessionIf + JETTY-378 handle JVMs with non ISO/UTF default encodings + JETTY-380 handle pipelines of more than 4 requests! + JETTY-385 EncodeURL for new sessions from dispatch + JETTY-386 Allow // in file resources + Added GzipFilter and UserAgentFilter + Dispatch SslEngine expiry (non atomic) + Improved Request log configuration options + make jetty plus example webapps use ContextDeployer + make OSGi manifests for jetty jars + Make SLF4JLog impl public, add mbean descriptors + Protect SslSelectChannelConnector from exceptions during close + remove call to open connectors in jetty.xml + SetUID option to only open connectors before setUID. + SPR-3682 - dont hide forward attr in include. + update links on website + update terracotta configs for tc 2.4 stable1 + update terracotta session clustering to terracotta 2.4 + Upgrade to Jasper 2.1 tag SJSAS-9_1-B50G-BETA3-27_June_2007 jetty-6.1.4 - 15 June 2007 + JETTY-370 ensure idleTimeout<=0 means connections never expire + JETTY-371 Fixed chunked HEAD response + JETTY-372 make test for cookie caching more rigorous + fixed early open() call in NIO connectors jetty-6.1.4rc1 - 10 June 2007 + JETTY-310 better exception when no filter file for cometd servlet + JETTY-323 handle htaccess without a user realm + JETTY-346 add wildcard support to extra scan targets for maven plugin + JETTY-355 extensible SslSelectChannelConnector + JETTY-357 cleaned up ssl buffering + JETTY-360 allow connectors, userRealms to be added from a for maven plugin + JETTY-361 prevent url encoding of dir listings for non-link text + JETTY-362 More object locks + JETTY-365 make needClientAuth work on SslSelectChannelConnector + JETTY-366 JETTY-368 Improved bayeux disconnect + async client improvements + fixed handling of large streamed files + Fixed synchronization conflict SslSelectChannel and SelectChannel + moved documentation for jetty and jspc maven plugins to wiki + Optional static content cache + Work around IBM JVM socket close issue jetty-6.1.4rc0 - 01 June 2007 + JETTY-257 fixed comet cross domain + JETTY-309 fix applied to sslEngine + JETTY-317 rollback inclusion of cometd jar for maven plugin + JETTY-318 Prevent meta channels being created + JETTY-330 Allow dependencies with scope provided for jspc plugin + JETTY-335 SslEngine overflow fix + JETTY-337 deprecated get/setCipherSuites and added get/setExcludeCipherSuites + JETTY-338 protect isMoreInBuffer from destroy + JETTY-339 MultiPartFiler deletes temp files on IOException + JETTY-340 FormAuthentication works with null response + JETTY-344 gready fill in ByteArrayBuffer.readFrom + JETTY-345 fixed lost content with blocked NIO. + JETTY-347 Fixed type util init + JETTY-352 Object locks + Add (commented out) jspc precompile to test-webapp + Add ability to run cometd webapps to maven plugin + Add slf4j-api for upgraded version + Allow XmlConfiguration properties to be configured + Change scope of fields for Session + Delay ssl handshake until after dispatch in sslSocketConnector + fixed JSP close handling + fixed waiting continuation reset + improved date header handling + Optional send Date header. Server.setSendDateHeader(boolean) + Reorganized import of contrib modules + Set so_timeout during ssl handshake as an option on SslSocketConnector + Unified JMX configuration + Updated junit to 3.8.2 + Updated slf4j version to 1.3.1 + update etc/jetty-ssl.xml with new handshake timeout setting jetty-6.1.3 - 04 May 2007 + JETTY-309 don't clear writable status until dispatch + JETTY-315 suppressed warning + JETTY-322 AJP13 cping and keep alive + Handle CRLF for content in header optimization jetty-6.1.2 - 01 May 2007 + JETTY-322 fix ajp cpong response and close handling + JETTY-324 fix ant plugin + JETTY-328 updated jboss + Added static member definition in WadiSessionManager + Fixed session invalidation error in WadiSessionManager + Improved unavailabile handling + sendError resets output state + Updated Wadi to version 2.0-M3 jetty-6.1.2rc5 - 24 April 2007 + JETTY-305 delayed connection destroy + JETTY-309 handle close in multivalue connection fields. + JETTY-314 fix for possible NPE in Request.isRequestedSessionIdValid + Allow jsp-file to be / or /* + removed some compile warnings + set default keystore for SslSocketConnector jetty-6.1.2rc4 - 19 April 2007 + JETTY-294 Fixed authentication reset + JETTY-299 handle win32 paths for object naming + JETTY-300 removed synchronized on dispatch + JETTY-302 correctly parse quoted content encodings + JETTY-303 fixed dual reset of generator + JETTY-304 Fixed authentication reset jetty-6.1.2rc3 - 16 April 2007 + JETTY-283 Parse 206 and 304 responses in client + JETTY-285 enable jndi for mvn jetty:run-war and jetty:run-exploded + JETTY-289 fixed javax.net.ssl.SSLException on binary file upload + JETTY-292 Fixed error page handler error pages + JETTY-293 fixed NPE on fast init + JETTY-294 Response.reset() resets headers as well as content + JETTY-295 Optional support of authenticated welcome files + JETTY-296 Close direct content inputstreams + JETTY-297 Recreate tmp dir on stop/start + JETTY-298 Names in JMX ObjectNames for context, servlets and filters + AJP redirects https requests correctly + Fixed writes of unencoded char arrays. + Improved performance and exclusions for TLD scanning + Improvements to allow simple setting of Cache-Control headers + MBean properties assume writeable unless marked RO + refactor of SessionManager and SessionIdManager for clustering jetty-6.1.2rc2 - 27 March 2007 + JETTY-125 maven plugin: ensure test dependencies on classpath for + JETTY-246 path encode cookies rather than quote + JETTY-254 prevent close of jar entry by bad JVMs + JETTY-256 fixed isResumed and work around JVM bug + JETTY-258 duplicate log message in ServletHandler + JETTY-260 Close connector before stop + JETTY-262 Allow acceptor thread priority to be adjusted + JETTY-263 Added implementation for authorizationType Packets + JETTY-265 Only quote cookie values if needed + JETTY-266 Fix deadlock with shutdown + JETTY-271 ResourceHandler uses resource for MimeType mapping + JETTY-272 Activate and Passivate events for sessions + JETTY-274 Improve flushing at end of request for blocking + JETTY-276 Partial fix for reset/close race + JETTY-277 Improved ContextHandlerCollection + JETTY-278 Session invalidation delay until no requests + JETTY-280 Fixed deadlock with two flushing threads + JETTY-284 Fixed stop connector race + JETTY-286 isIntegral and isConfidential methods overridden in SslSelectChannelConnector + Added RestFilter for PUT and DELETE from Aleksi Kallio + AJP13 CPING request and CPONG response implemented + AJP13 remoteUser, contextPath, servletPath requests implemented + AJP13 Shutdown Request from peer implemented + Change some JNDI logging to debug level instead of info + Enable the SharedStoreContextualiser for the WadiSessionManager(Database store for clustering) + Make annotations work for maven plugin + Optimized multi threaded init on startup servlets + Refactor Scanner to increase code reuse with maven/ant plugins + Removed unneeded specialized TagLibConfiguration class from maven plugin + Update jasper to glassfish tag SJSAS-9_1-B39-RC-14_Mar_2007 jetty-6.1.2rc1 - 08 March 2007 + JETTY-157 make CGI handle binary data + JETTY-175 JDBCUserRealm use getInt instead of getObject + JETTY-188 Use timer for session scavaging + JETTY-235 default realm name + JETTY-242 fix race condition with scavenging sessions when stopping + JETTY-243 FULL + JETTY-244 Fixed UTF-8 buffer overflow + JETTY-245 Client API improvements + JETTY-246 spaces in cookies + JETTY-248 setContentLength after content written + JETTY-250 protect attribute enumerations from modification + JETTY-252 Fixed stats handling of close connection + JETTY-254 prevent close of jar file by bad JVMs + add ajp connector jar to jetty-jboss sar + Added option to allow null pathInfo within context + Added support for lowResourcesIdleTime to SelectChannelConnector + BoundedThreadPool queues rather than blocks excess jobs. + call preDestroy() after servlet/filter destroy() + Ensure jetty/jboss uses servlet-spec classloading order + Fix constructor for Constraint to detect wildcard role + fix Dump servlet to handle primitive array types + handle comma separated values for the Connection: header + Improved Context setters for wadi support + Improved handling of early close in AJP + Support null pathInfo option for webservices deployed to jetty/jboss + TagLibConfiguration uses resource input stream + Workaround to call SecurityAssocation.clear() for jboss webservices calls to ejbs jetty-6.1.2rc0 - 15 February 2007 + JETTY-223 Fix disassociate of UserPrincipal on dispatches + JETTY-226 Fixed SSLEngine close issue + JETTY-232 Fixed use of override web.xml + JETTY-236 Buffer leak + JETTY-237 AJPParser Buffer Data Handling + JETTY-238 prevent form truncation + Coma separated cookies + Cometd timeout clients + Patches from sybase for ClientCertAuthenticator jetty-6.1.2pre1 - 05 February 2007 + JETTY-224 run build up to process-test before invoking jetty:run + Added error handling for incorrect keystore/truststore password in SslSelectChannelConnector + added win32service to standard build + allow ResourceHandler to use resource base from an enclosing ContextHandler + fixed bug with virtual host handling in ContextHandlerCollection + refactored cometd to be continuation independent jetty-6.1.2pre0 - 01 February 2007 + JETTY-213 request.isUserInRole(String) fixed + JETTY-215 exclude more transitive dependencies from tomcat jars for jsp-2.0 + JETTY-216 handle AJP packet fragmentation + JETTY-218 handle AJP ssl key size and integer + JETTY-219 fixed trailing encoded chars in cookies + JETTY-220 fixed AJP content + JETTY-222 fix problem parsing faces-config.xml + Added cometd jsonp transport from aabeling + Added terracotta cluster support for cometd + add support for Annotations in servlet, filter and listener sources + enable SslSelectChannelConnector to modify the SslEngine's client authentication settings + Fixed 1.4 method in jetty plus + Fixed generation of errors during jsp compilation for jsp-2.1 + handle virtual hosts in ContextHandlerCollection + improved writer buffering + moved JSON parser to util to support reuse jetty-6.1.1 - 15 January 2007 jetty-6.1.1rc1 - 12 January 2007 + JETTY-210 Build jsp-api-2.0 for java 1.4 + Use timers for Rollover logs and scanner jetty-6.1.1rc0 - 10 January 2007 + JETTY-209 Added ServletTester.createSocketConnector + JETTY-210 Build servlet-api-2.5 for java 1.4 + JETTY-211 fixed jboss build + CGI servlet fails without exception + ensure response headers on AjaxFilter messsages turn off caching + extras/win32service download only if no JavaServiceWrapper exist + Fixed unpacking WAR + MultiPartFilter deleteFiles option + simplified chat demo + start webapps on deployment with jboss, use isDistributed() method from WebAppContext jetty-6.1.0 - 09 January 2007 + Fixed unpacking WAR jetty-6.1.0 - 05 January 2007 + JETTY-206 fixed AJP getServerPort and getRemotePort + Added extras/win32service + Added WebAppContext.setCopyWebDir to avoid JVM jar caching issues. + GERONIMO-2677 refactor of session id handling for clustering + Improved config of java5 threadpool + Protect context deployer from Errors + ServletTester sets content length jetty-6.1.0rc3 - 02 January 2007 + JETTY-195 fixed ajp ssl_cert handling + JETTY-197 fixed getRemoteHost + JETTY-203 initialize ServletHandler if no Context instance + JETTY-204 setuid fix + extras/servlet-tester + implement resource injection and lifecycle callbacks declared in web.xml + setLocale does not use default content type + Use standard releases of servlet and jsp APIs. jetty-6.1.0rc2 - 20 December 2006 + JETTY-167 cometd refactor + JETTY-194 doubles slashes are significant in URIs + JETTY-201 make run-as work for both web container and ejb container in jboss + AJP13Parser, throw IllegalStateException on unimplemented AJP13 Requests + ContextHandlerCollection is noop with no handlers + ensure classpath passed to jspc contains file paths not urls + ensure com.sun.el.Messages.properties included in jsp-2.1 jar + ensure servlets initialized if only using ServletHandler + fixed Jetty-197 AJP13 getRemoteHost() + Refactored AbstractSessionManager for ehcache + remove code to remove SecurityHandler if no constraints present jetty-6.1.0rc1 - 14 December 2006 + JETTY-193 MailSessionReference without authentication + JETTY-199 newClassPathResource + added cache session manager(pre-alpha) + ensure unique name for ServletHolder instances + simplified idle timeout handling jetty-6.1.0rc0 - 08 December 2006 + JETTY-123 fix improved + JETTY-181 Allow injection of a java:comp Context + JETTY-182 Optionally set JSP classpath initparameter + JETTY-184 cometd connect non blocking + JETTY-185 tmp filename generation + JETTY-189 ProxyConnection + 403 for BASIC authorization failure + Added extras/gwt + Added org.mortbay.thread.concurrent.ThreadPool + Added spring ejb3 demo example + DefaultHandler links virtual hosts. + Dispatcher does not protect javax.servlet attributes + Fixed cachesize on invalidate + Fixed idle timeout + flush if content-length written + forward query attribute fix + Handle request content encodings + null for unknown named dispatches + Optimization of writers + ServletHandler allows non REQUEST exceptions to propogate + Servlet role ref + session attribute listener + Support for RFC2518 102-processing response + TCK fixes from Sybase: + update jasper to glassfish SJSAS-9_1-B27-EA-07_Dec_2006 jetty-6.1.0pre3 - 22 November 2006 + JETTY-154 Cookies are double quotes only + JETTY-180 XBean support for context deploy + CVE-2006-6969 Upgraded session ID generation to use SecureRandom + Expose isResumed on Continuations + fixed NIO endpoint flush. Avoid duplicate sends + Refactored AJP generator + Support TLS_DHE_RSA_WITH_AES_256_CBC_SHA + updated glassfish jasper to tag SJSAS-9_1-B25-EA-08_Nov_2006 jetty-6.0.2 - 22 November 2006 + JETTY-118 ignore extra content after close. + JETTY-119 cleanedup Security optimizatoin + JETTY-123 handle windows UNC paths + JETTY-126 handle content > Integer.MAX_VALUE + JETTY-129 ServletContextListeners called after servlets are initialized + JETTY-151 Idle timeout only applies to blocking operations + JETTY-154 Cookies are double quotes only + JETTY-171 Fixed filter mapping + JETTY-172 use getName() instead of toString + JETTY-173 restore servletpath after dispatch + (re)make JAAS classes available to webapp classloader + add replacement in jetty xml config files + Added concept of bufferred endpoint + Added conversion Object -> ObjectName for the result of method calls made on MBeans + Added DataFilter configuration to cometd + added examples/test-jaas-webapp + Added extraClassPath to WebAppContext + Added hierarchical destroy of mbeans + Added ID constructor to AbstractSessionManager.Session + added isStopped() in LifeCycle and AbstractLifeCycle + Added override descriptor for deployment of RO webapps + Allow session cookie to be refreshed + alternate optimizations of writer (use -Dbuffer.writers=true) + Apply queryEncoding to getQueryString + CGI example in test webapp + change examples/test-jndi-webapp so it can be regularly built + Default soLinger is -1 (disabled) + ensure "" returned for ServletContext.getContextPath() for root context + ensure sessions nulled out on request recycle; ensure session null after invalidate + ensure setContextPath() works when invoked from jetty-web.xml + fixed NIO endpoint flush. Avoid duplicate sends + Fixed NPE in bio.SocketEndPoint.getRemoteAddr() + Fixed resource cache flushing + Fixed tld parsing for maven plugin + HttpGenerator can generate requests + Improved *-mbean.properties files and specialized some MBean + Major refactor of SelectChannel EndPoint for client selector + make .tag files work in packed wars + Moved all modules updates from 6.1pre2 to 6.0 + Plugin shutdown context before stopping it. + Refactored session lifecycle and additional tests + release resource lookup in Default servlet + Reverted UnixCrypt to use coersions (that effected results) + Session IDs can change worker ID + Simplified ResourceCache and Default servlet + SocketConnector closes all connections in doStop + Support TLS_DHE_RSA_WITH_AES_256_CBC_SHA + updated glassfish jasper to tag SJSAS-9_1-B25-EA-08_Nov_2006 + Upgraded session ID generation to use SecureRandom jetty-5.1.14 - 09 August 2007 + JETTY-155 force close with content length. + JETTY-369 failed state in Container + patched with correct version jetty-5.1.13 + Sourceforge 1648335: problem setting version for AJP13 jetty-5.1.12 - 22 November 2006 + JETTY-154 Cookies ignore single quotes + Added support for TLS_DHE_RSA_WITH_AES_256_CBC_SHA + AJP protected against bad requests from mod_jk + Quote single quotes in cookies + Upgraded session ID generation to use SecureRandom jetty-4.2.27 - 22 November 2006 + AJP protected against bad requests from mod_jk + Upgraded session ID generation to use SecureRandom jetty-6.1.0pre2 - 20 November 2006 + Added extraClassPath to WebAppContext + Clean up jboss module licensing + Fixed resource cache flushing jetty-6.1.0pre1 - 19 November 2006 + JETTY-151 Idle timeout only applies to blocking operations + JETTY-171 Fixed filter mapping + JETTY-172 use getName() instead of toString + JETTY-173 restore servletpath after dispatch + Added extras/jboss + Added hierarchical destroy of mbeans + Added override descriptor for deployment of RO webapps + alternate optimizations of writer (use -Dbuffer.writers=true) + Fixed NPE in bio.SocketEndPoint.getRemoteAddr() + Major refactor of SelectChannel EndPoint for client selector + release resource lookup in Default servlet + Reverted UnixCrypt to use coersions (that effected results) + Simplified ResourceCache and Default servlet + Use ContextDeployer as main deployer in jetty.xml jetty-6.1.0pre0 - 21 October 2006 + JETTY-112 ContextHandler checks if started + JETTY-113 support optional query char encoding on requests + JETTY-114 removed utf8 characters from code + JETTY-115 Fixed addHeader + JETTY-118 ignore extra content after close. + JETTY-119 cleanedup Security optimizatoin + JETTY-121 init not called on externally constructed servlets + JETTY-123 handle windows UNC paths + JETTY-124 always initialize filter caches + JETTY-126 handle content > Integer.MAX_VALUE + JETTY-129 ServletContextListeners called after servlets are initialized + (re)make JAAS classes available to webapp classloader + add replacement in jetty xml config files + add a maven-jetty-jspc-plugin to do jspc precompilation + added cometd chat demo + Added concept of bufferred endpoint + Added conversion Object -> ObjectName for the result of method calls made on MBeans + Added DataFilter configuration to cometd + added examples/test-jaas-webapp + Added extras/setuid to support start as root + Added ID constructor to AbstractSessionManager.Session + added isStopped() in LifeCycle and AbstractLifeCycle + add hot deployment capability + AJP Connector + Allow session cookie to be refreshed + Apply queryEncoding to getQueryString + CGI example in test webapp + change examples/test-jndi-webapp so it can be regularly built + Default soLinger is -1 (disabled) + ensure "" returned for ServletContext.getContextPath() for root context + ensure sessions nulled out on request recycle; ensure session null after invalidate + ensure setContextPath() works when invoked from jetty-web.xml + Factored ErrorPageErrorHandler out of WebAppContext + fixed ClassCastException in JAASUserRealm.setRoleClassNames(String[]) + fixed isUserInRole checking for JAASUserRealm + Fixed tld parsing for maven plugin + HttpGenerator can generate requests + Improved *-mbean.properties files and specialized some MBean + Improved charset handling in URLs + JETYY-120 SelectChannelConnector closes all connections on stop + make .tag files work in packed wars + minor optimization of bytes to UTF8 strings + Plugin shutdown context before stopping it. + Ported HtAccessHandler + Refactored ErrorHandler to avoid statics + Refactored session lifecycle and additional tests + Session IDs can change worker ID + SocketConnector closes all connections in doStop + Start of a client API + Transforming classloader does not transform resources. jetty-5.1.11 - 08 October 2006 + Default servlet only uses setContentLength on wrapped responses + Fixed AJP chunk header (1507377) + Fixed AJP handling of certificate length (1494939) + fixed ByteBufferOutputStream capacity calculation + Fixed order of destruction event calls + Fix to HttpOutputStream from M.Traverso jetty-4.2.26 - 08 October 2006 + Backport of AJP fixes jetty-6.0.1 - 24 September 2006 + JETTY-112 ContextHandler checks if started + JETTY-113 support optional query char encoding on requests + JETTY-114 removed utf8 characters from code + JETTY-115 Fixed addHeader + JETTY-121 init not called on externally constructed servlets + JETTY-124 always initialize filter caches + Factored ErrorPageErrorHandler out of WebAppContext + fixed ClassCastException in JAASUserRealm.setRoleClassNames(String[]) + fixed isUserInRole checking for JAASUserRealm + Improved charset handling in URLs + JETYY-120 SelectChannelConnector closes all connections on stop + minor optimization of bytes to UTF8 strings + Refactored ErrorHandler to avoid statics jetty-6.0.0 - 10 September 2006 + Conveniance builder methods for listeners and filters + Plugin shutdown context before stopping it. + SocketConnector closes all connections in doStop + Transforming classloader does not transform resources. jetty-6.0.0rc4 - 05 September 2006 + JETTY-107 Poor cast in SessionDump demo. + bind jetty-env.xml entries to java:comp/env + Set charset on error pages jetty-6.0.0rc3 - 01 September 2006 + JETTY-68 Complete request after sendRedirect + JETTY-104 (raised glassfish ISSUE-1044) hide JSP forced path attribute + Avoid double error handling of Bad requests + don't warn for content length on head requests + JETTY-103 + Less verbose handling of BadResources from bad URLs + Move MailSessionReference to org.mortbay.naming.factories + pulled 6.0.0 branch + Transferred the sslengine patch from the patches directory to extras jetty-6.0.0rc2 - 25 August 2006 + added org.apache.commons.logging package to system classes that can't be overridden by a webapp classloader + Destroy HttpConnection to improve buffer pooling + Direct buffer useage is optional + Fixed NPE when no resource cache + Moved more utility packagtes to the util jar + mvn -Djetty.port=x jetty:run uses port number given for the default connector + Refactored WebXmlConfiguration to allow custom web.xml resource + Timestamp in StdErrLog + use mvn -Dslf4j=false jetty:run to disable use of slf4j logging with jdk1.4/jsp2.0 jetty-6.0.0rc1 - 16 August 2006 + JETTY-85 JETTY-86 (TrustManager and SecureRandom are now configurable; better handling of null/default values) + add config param to jetty plugin + added modules/spring with XmlBeanFactory configuration + Added simple ResourceHandler and FileServer example + added start of cometd implementation (JSON only) + added start of grizzly connector + Added TransformingWebAppClassLoader for spring 2.0 byte code modification support + Allow direct filling of buffers for uncached static content. + Change path mapping so that a path spec of /foo/* does not match /foo.bar : JETTY-88 + -DSTOP.PORT must be specified. + fixed bug that caused Response.setStatus to ignore the provided message + Fixed FD leak for bad TCP acks. JETTY-63 + JETTY-87 + JETTY-90 + JETTY-91 + moved optional modules to extras + parse jsp-property-group in web.xml for additional JSP servlet mappings + protected setContentType from being set during include + refactored resource cache + removed org.mortbay. from context system classes configuration + removed support for lowResources from SelectChannelConnector + Support for binding References and Referenceables and javax.mail.Sessions in JNDI jetty-6.0.0rc0 - 07 July 2006 + add ability to have a lib/ext dir from which to recursively add all jars and zips to the classpath + Added 8 random letters&digits to Jetty-generated tmp work dir name to ensure uniqueness + added html module from jetty 5 - but deprecated until maintainer found + Added maximum limit to filter chain cache. + added setters and getters on SessionManager API for session related config: cookie name, url parameter name, domain, max age and path. + added StatisticsHandler and statistics on Connector. + Added WebAppContextClassLoader.newInstance to better support exensible loaders. + allow or in for plugin + changed ServletContext.getResourcePaths() to not return paths containing double slashes + change name of generated tmp directory to be "Jetty_"+host+"_"+port+"_"+contextpath+"_"+virtualhost + change prefix from "jetty6" to just "jetty" for plugin: eg is now mvn jetty:run + Cleaned up idle expiry. + ContextHandlerCollection addContext and setContextClass + Discard excess bytes in header buffer if connection is closing + Do not wrap EofException with EofException + ensure explicitly set tmp directory called "work" is not deleted on exit + Ensure mvn clean cleans the build + ensure war is only unpacked if war is newer than "work" directory + fixed classesDirectory param for maven plugin to be configurable + fixed HttpGenerator convertion of non UTF-8: JETTY-82 + immutable getParameterMap() + patch to allow Jetty to use JSP2.1 from Glassfish instead of Jasper from Tomcat + refactor HttpChannelEndPoint in preparation for SslEngine + reverse order for destroy event listeners + simplified jetty.xml with new constructor injections + Simplified Servlet Context API + Simplify runtime resolution of JSP library for plugin + Ssl algorithm taken from system property + support for SingleThreadModel + support graceful shutdown + Threadpool does not need to be a LifeCycle + Updated javax code from http://svn.apache.org/repos/asf/tomcat/tc6.0.x/trunk/java/javax@417727 jetty-6.0.0beta17 - 01 June 2006 + Added clover reports and enough tests to get >50% coverage + Added config to disable file memory mapped buffers for windows + Added Request.isHandled() + BoundedThreadPool.doStop waits for threads to complete + Connector lowResourceMaxIdleTime implemented. + ContextHandler.setConnectors replace setHosts + Default servlet checks for aliases resources + don't reset headers during forward + Fixed IE SSL issue. + Flush will flush all bytes rather than just some. + Implemented runAs on servlets + Protected WEB-INF and META-INF + Recovered repository from Codehaus crash + Refactored Synchronization of SelectChannelConnector jetty-6.0.0beta16 - 12 May 2006 + remove a couple of System.err.printlns + replace backwards compativle API in UrlEncoded jetty-6.0.0beta15 - 11 May 2006 + Added parameter to allow other locations to scan for plugin + Added automatic scan of all WEB-INF/jetty-*.xml files for plugin + Added embedded examples + Added Server attribute org.mortbay.jetty.Request.maxFormContentSize + Added taglib resources to 2.1 jsp api jar + Added ThrottlingFilter and fixed race in Continuations + Added --version to start.jar + ContextHandler.setContextPath can be called after start. + don't accept partial authority in request line. + enforce 204 and 304 have no content + Fixed handling of params after forward + Improved HttpException + improved MBeanContainer object removal + improved MBean names + improved support for java5 jconsole + Major refactor to simplify Server and handler hierarchy + Moved more resources to resources + readded BoundedThreadPool shrinking (and then fixed resulting deadlock) + removed SelectBlockingChannelConnector (unmaintained) + Renamed NotFoundHandler to DefaultHandler + Reset of timer task clears expiry + Session scavenger threads from threadpool + setSendServerVersion method added to Server to control sending of Server: http header + Simplified DefaultServlet static content buffering + Thread names include URI if debug set jetty-6.0.0beta14 - 09 April 2006 + added configurability for webdefault.xml in maven plugin + Added Jasper 2.1 as jesper (jasper without JCL) + added jetty-util.jar module + Added JSP 2.1 APIs from apache + added ProxyServlet + added reset to Continuation + added support for stopping jetty using "java -jar start.jar --stop" + adding InvokerServlet + Change tmp dir of plugin to work to be in line with jetty convention + fixed forward bug (treated as include) + fixed HttpField iterator + fixed priority of port from url over host header + ignore dirs and files that don't exist in plugin scanner + implemented request.isUserInRole + improved contentType handling and test harness + Modify plugin to select JSP impl at runtime + moved test webapps to examples directory + securityHandler removed if not used. + Started readding logging to jesper using jdk logging + stop JDBCUserRealm coercing all credentials to String + Use start.config to select which JSP impl at runtime based on jdk version jetty-6.0.0beta12 - 16 March 2006 + Added JSP2.0 demos to test webapp + Added provider support to SslListener + Fixed error handling in error page + Fixed JettyPlus for root contexts + Fixed maven plugin JNDI for redeploys + Fixed tld discovery for plugin (search dependencies) + Log ERROR for runtimeExceptions + Upgraded jasper to 5.5.15 jetty-6.0.0beta11 - 14 March 2006 + Added HttpURI and improved UTF-8 parsing. + added JAAS + added missing Configurations for maven plugin + added patch to use joda-time + added webapp-specific JNDI entries + fixed ; decoding in URIs + fixed FORM authentication + moved dtd and xsd to standard javax location + refactored configuration files and start() + refactored session ID management + refactored writers and improved UTF-8 generation. jetty-6.0.0beta10 - 25 February 2006 + added getLocalPort() to connector + Added support for java:comp/env + Added support for pluggable transaction manager + Additional accessors for request logging + Fixed content-type for range requests + Fixed default servlet handling of includes + Fix for myfaces and include with close + Fix for sf1435795 30sec delay from c taylor + Fix http://jira.codehaus.org/browse/JETTY-6. hi byte reader + Fix sf1431936 don't chunk the chunk + Forward masks include attributes and vice versa + Updates javax to MR2 release jetty-6.0.0beta9 - 09 February 2006 + Added CGI servlet. + Added request log. + Added TLD tag listener handling. + Continuation cleanup + Fixed dispatch of wrapped requests. + Fixed double flush of short content. + fixed setLocale bug sf1426940 + Fixed unraw decoding of query string + Force a tempdir to be set. + Force jasper scratch dir. + PathMap for direct context mapping. + Refactored chat demo and upgraded prototype.js jetty-6.0.0beta8 - 24 January 2006 + conveniance addHandler removeHandler methods + fixed bug in overloaded write method on HttpConnection (reported against Tapestry4.0) + fixed dispatch of new session problem. sf:1407090 + Handle pipeline requests without hangs + hid org.apache.commons.logging and org.slf4j packages from webapp + improve buffer return mechanism. + improved caching of content types + maven-jetty6-plugin: ensure compile is done before invoking jetty + maven-jetty6-plugin: support all types of artifact dependencies + maven-jetty6-plugin stopped transitive inclusion of log4j and commons-logging from commons-el for jasper + patch to remove spurious ; in HttpFields + reinstated rfc2616 test harness + Removed queue from thread pool. jetty-6.0.0Beta7 + Faster header name lookup + Fixed infinite loop with chunk handling + maven-jetty6-plugin added tmpDirectory property + maven-jetty6-plugin stopped throwing an error if there is no target/classes directory + null dispatch attributes not in names + reduced info verbosity + removed singleton Container jetty-6.0.0Beta6 + Fixed issue with blocking reads + Fixed issue with unknown headers + optimizations jetty-6.0.0Beta5 + Added management module for mbeans + Fixed writer char[] creations + Moved to SVN jetty-6.0.0Beta4 + CVE-2006-2758 Fixed JSP visibility security issue. + Improved jetty-web.xml access to org.mortbay classes. + Jasper 5.5.12 + System property support in plugin jetty-6.0.0Beta3 + Fixed classloader issue with server classes + Fixed error in block read + Named dispatch. jetty-6.0.0Beta2 + Improved buffer return + Improved reuse of HttpField values and cookies. + loosely coupled with JSP servlet + loosely coupled with SLF4J + merged util jar back into jetty jar + Simpler continuation API jetty-6.0.0Beta1 + Error pages + Implemented all listeners + maven2 plugin + Multiple select sets + refactored start/stop + Servlet 2.5 API + shutdown hook + SSL connector + Virtual hosts jetty-6.0.0Beta0 + Dispatcher parameters + Fixed blocking read + Maven 2 build + UTF-8 encoding for URLs jetty-6.0.0APLPA3 + Added demo for Continuations + Jasper and associated libraries. jetty-6.0.0ALPHA2 + Continuations - way cool way to suspend a request and retry later. + Dispatchers + Security jetty-6.0.0ALPHA1 + Filters + web.xml handling jetty-6.0.0ALPHA0 + file may be sent as sent is a single operation. + Improved "dependancy injection" and "inversion of control" design of components + Improved "interceptor" design of handlers + Missing Request Dispatchers + Missing Security + Missing war support + Missing web.xml based configuration + Optional use of NIO Buffering so that efficient direct buffers and memory mapped files can be used. + Optional use of NIO gather writes, so that for example a HTTP header and a memory mapped + Optional use of NIO non-blocking scheduling so that threads are not allocated per connection. + Smart split buffer design allows large buffers to only be allocated to active connections. The resulting memory savings allow very large buffers to be used, which increases the chance of efficient asynchronous flushing and of avoiding chunking. + Totally rearchitected and rebuilt, so 10 years of cruft could be removed! jetty-5.1.11RC0 - 05 April 2006 + Added provider support to SslListener + Fixed AJP handling of ;jsessionid. + force close with shutdownOutput for win32 + improved contentType param handling + logging improvements for servlet and runtime exceptions + NPE protection if desirable client certificates + stop JDBCUserRealm forcing all credentials to be String jetty-5.1.10 - 05 January 2006 + Fixed path aliasing with // on windows. + Fix for AJP13 with encoded path + Fix for AJP13 with multiple headers + Put POST content default back to iso_8859_1. GET is UTF-8 still + Remove null dispatch attributes from getAttributeNames jetty-4.2.25 - 04 January 2006 + Fixed aliasing of // for win32 jetty-5.1.9 - 07 December 2005 + Fixed wantClientAuth(false) overriding netClientAuth(true) jetty-6.0.0betaX + See http://jetty.mortbay.org/jetty6 for 6.0 releases jetty-5.1.8 - 07 December 2005 + Fixed space in URL issued created in 5.1.6 jetty-5.1.7 - 07 December 2005 jetty-5.1.7rc0 - 06 December 2005 + better support for URI character encodings + char encoding for MultiPartRequest + fixed merging of POST params in dispatch query string. + improved server stats + JSP file servlet mappings copy JspServlet init params. + Prefix servlet context logs with org.mortbay.jetty.context + protect from NPE in dispatcher getValues + Updated to 2.6.2 xerces + use commons logging jar instead of api jar. jetty-5.1.6 - 18 November 2005 + CVE-2006-2758 Fixed JSP visibility security issue. + Improved jetty-web.xml access to org.mortbay classes. jetty-5.1.5 - 10 November 2005 + Improved mapping of JSP files. + Improved shutdown hook + Improved URL Decoding jetty-5.1.5rc2 - 07 October 2005 + ProxyHandler can handle chained proxies + public ServerMBean constructor + ReFixed merge of Dispatcher params + Response.setLocale will set locale even if getWriter called. + Reverted dispatcher params to RI rather than spec behaviour. + unsynchronized ContextLoader + UTF-8 encoding for URLs jetty-5.1.5rc1 - 23 August 2005 + Encoded full path in ResourceHandler directory listing + Fixed 100-continues with chunking and early commit + Fixed illegal state with chunks and 100 continue - Tony Seebregts + Fixed merge of Dispatcher parameters + Fixed PKCS12Import input string method + handle extra params after charset in header + Release commons logging factories when stopping context. + upgraded to commons logging 1.0.4 jetty-5.1.5rc0 - 16 August 2005 + Applied ciphersuite patch from tonyj + Authenticators use servlet sendError + CGI sets SCRIPT_FILENAME + Expect continues only sent if input is read. + Facade over commons LogFactory so that discovery may be avoided. + Fixed component remove memory leak for stop/start cycles + HttpTunnel timeout + NPE protection for double stop in ThreadedServer jetty-5.1.4 - 05 June 2005 + Change JAAS impl to be more flexible on finding roles + Fixed FTP close issue. + ModelMBean handles null signatures + NPE protection in ThreadedServer + set classloader during webapp doStop + setup MX4J with JDK1.5 in start.config jetty-5.1.4rc0 - 19 April 2005 + Allow ServletHandler in normal HttpContext again. + HttpServer delegates component handling to Container. + More protection from null classloaders. + ServletHttpContext correctly calls super.doStop. + Stop start.jar putting current directory on classpath. + Turn off web.xml validation for JBoss. jetty-5.1.3 - 07 April 2005 + Some minor code janitorial services jetty-4.2.24 - 07 April 2005 jetty-5.1.3rc4 - 31 March 2005 + Allow XmlConfiguration to start with no object. + make java:comp/env immutable for webapps as per J2EE spec + Moved servlet request wrapping to enterContextScope for geronimo security + refixed / mapping for filters + rework InitialContextFactory to use static 'default' namespace + updated to mx4j 3.0.1 jetty-5.1.3rc3 - 20 March 2005 + fixed "No getter or setter found" mbean errors + removed accidental enablement of DEBUG for JettyPlus jndi in log4j.properties jetty-5.1.3rc2 - 16 March 2005 + Fixed context to _context refactory error + Updated JSR154Filter for ERROR dispatch jetty-5.1.3rc1 - 13 March 2005 + Fixed principal naming in FormAuthenticator + Fixed typo in context-param handling. + JettyPlus updated to JOTM 2.0.5, XAPool 1.4.2 + update to demo site look and feel. jetty-4.2.24rc1 + Fixed principal naming in FormAuthenticator jetty-5.1.3rc0 - 08 March 2005 + Added logCookie and logLatency support to NCSARequestLog + Added new JAAS callback to allow extra login form fields in authentication + Added simple xpath support to XmlParser + Added SslListener for 1.4 JSSE API. + Added TagLibConfiguration to search for listeners in TLDs. + Allow system and server classes to be configured for context loader. + Fixed HTAccess crypt salt handling. + Fixed JSR154 error dispatch with explicit pass of type. + Fixed moderate load preventing ThreadPool shrinking. + Fixed rollover filename format bug + Flush filter chain caches on servlet/filter change + IOException if EOF read during chunk. jetty-4.2.24rc0 - 08 March 2005 + Added logCookie and logLatency support to NCSARequestLog + Back ported Jetty 5 ThreadedServer and ThreadPool jetty-5.1.2 - 18 January 2005 + Added id and ref support to XmlConfiguration + Apply patch #1103953 + Cleaned up AbstractSessionManager synchronization. + Fixed potential concurrent login problem with JAAS jetty-4.2.23 - 16 January 2005 + Cleaned up AbstractSessionManager synchronization. + Fixed potential concurrent login problem with JAAS jetty-5.1.2pre0 - 22 December 2004 + Added global invalidation to AbstractSessionManager + Fixed case of Cookie parameters + Fixed suffix filters + Modified useRequestedID handling to only use IDs from other contexts + Support Secure and HttpOnly in session cookies + UnavailableException handling from handle jetty-4.2.23RC0 - 17 December 2004 + Added LogStream to capture stderr and stdout to logging + Build unsealed jars + LineInput handles readers with small internal buffer + Support Secure and HttpOnly in session cookies jetty-5.1.1 - 01 December 2004 jetty-5.1.1RC1 + Allow double // within URIs + Applied patch for MD5 hashed credentials for MD5 + Fixed ordering of filters with multiple interleaved mappings. + Made more WebApplicationHandle configuration methods public. + Some minor findbugs code cleanups jetty-5.1.1RC0 - 17 November 2004 + added new contributed shell start/stop script + excluded ErrorPageHandler from standard build in extra/jdk1.2 build + fix commons logging imports to IbmJsseListener + fix for adding recognized EventListeners jetty-5.1.0 - 14 November 2004 jetty-5.1.RC1 - 24 October 2004 + Allow JSSE listener to be just confidential or just integral. + Allow multiple accepting threads + Build unsealed jars + default / mapping does not apply to Filters + Fixed NPE for null contenttype + improved clean targets + many minor cleanups suggested from figbug utility + Partially flush writers on every write so content length can be detected. + when committed setHeader is a noop rather than IllegalStateException jetty-5.1.RC0 - 11 October 2004 + Added filter chain cache + Added JSR77 servlet statistic support + Added LifeCycle events and generic container. + Added LogStream to capture stderr and stdout to logging + Fixed HTAccessHandler + Fixed many minor issues from J2EE 1.4 TCK testing See sf.net bugs 1031520 - 1032205 + JBoss 4.0.0 support + LineInput handles readers with small internal buffer + Refactored, simplified and optimized HttpOutputStream + Refactored webapp context configurations + Upgraded to ant-1.6 for jasper jetty-5.0.0 - 10 September 2004 jetty-5.0.RC4 - 05 September 2004 + Fixed configuration of URL alias checking + JettyJBoss: Use realm-name from web.xml if present, otherwise use security-domain from jboss-web.xml jetty-5.0.RC3 - 28 August 2004 + Added parameters for acceptQueueSize and lowResources level. + Always say close for HTTP/1.0 non keep alive. + Changed default URI encoding to UTF-8 + DIGEST auth handles qop, stale and maxNonceAge. + fixed deployment of ejb-link elements in web.xml with jboss + fixed jaas logout for jetty-jboss + Fixes to work with java 1.5 + JettyPlus addition of pluggable DataSources + JettyPlus upgrade to XAPool 1.3.3. and HSQLDB 1.7.2 + Less verbose warning for non validating xml parser. + Update to jasper 5.0.27 jetty-4.2.22 + Added parameters for acceptQueueSize and lowResources level. + fixed deployment of ejb-link elements in web.xml for jboss + fixed jaas logout for jetty-jboss integration jetty-5.0.RC2 - 02 July 2004 + add JMX support for JettyPlus + add listing of java:comp/env for webapp with JMX + Default servlet may use only pathInfo for resource + Error dispatchers are always GET requests. + Fixed DIGEST challenge delimiters + Fixed JAAS logout + Fixed no-role security constraint combination. + Fixed session leak in j2ee + Fix to use runas roles during servlet init and destroy + HTAccess calls UnixCrypt correctly + HttpContext sendError for authentication errors + integrated jetty-jboss with jboss-3.2.4 + make choice of override of JNDI ENC entries: config.xml or web.xml + OPTIONS works for all URLs on default servlet jetty-4.2.21 - 02 July 2004 + add JMX support for JettyPlus + add listing of java:comp/env for webapp with JMX + Fixed JAAS logout + integrated jetty-jboss with jboss-3.2.4 + make choice of override of JNDI ENC entries: config.xml or web.xml jetty-5.0.RC1 - 24 May 2004 + added extra/etc/start-plus.config to set up main.class for jettyplus + Changed to apache 2.0 license + Fixed HTTP tunnel timeout setting. + FORM auth redirects to context on a re-auth + Handle multiple virutal hosts from JBoss 3.2.4RC2 + Improved handling of exception from servlet init. + maxFormContentLength may be unlimited with <0 value jetty-4.2.20 - 22 May 2004 + Fixed HTTP tunnel timeout setting. + FORM auth redirects to context on a re-auth + Improved handling of exception from servlet init. + maxFormContentLength may be unlimited with <0 value jetty-5.0.0RC0 - 07 April 2004 + Changed dist naming convention to lowercase + Default servlet respectes servlet path + Factored out XML based config from WebApplicationContext + Fixed Default servlet for non empty servlet paths + Fixed DOS problem + Fixed j2se 1.3 problem with HttpFields + Fixed setCharacterEncoding for parameters. + Forced close of connections over stop/start + Improved RequestLog performance + ProxiedFor field support added to NCSARequestLog + ServletContext attributes wrap HttpContext attributes. + Updated jasper to 5.0.19 + Updated JettyPlus to JOTM 1.4.3 (carol-1.5.2, xapool-1.3.1) + Updated mx4j to V2 + Worked around bad jboss URL handler in XMLParser jetty-4.2.20RC0 - 07 April 2004 + Changed dist naming convention to lowercase + Fixed Default servlet for non empty servlet paths + Forced close of connections over stop/start + HttpFields protected headers + ProxiedFor field support added to NCSARequestLog + Worked around bad jboss URL handler in XMLParser jetty-4.2.19 - 19 March 2004 + Fixed DOS attack problem jetty-5.0.beta2 - 12 February 2004 + Added experimental NIO listeners again. + Added log4j context repository to jettyplus + Added skeleton JMX MBean for jetty plus + FileResource better handles non sun JVM + Fixed busy loop in threadpool run + fixed filter dispatch configuration. + Fixed HEAD with empty chunk bug. + Fixed jetty.home/work handling + fixed lazy authentication with FORMs + Fixed SessionManager init + Fixed setDate thread safety + Improved low thread handling + Monitor closes socket before exit + NPE guard for no-listener junit deployment + Reorganized ServletHolder init + RequestDispatcher uses request encoding for query params + Updated to Japser 5.0.16 jetty-4.2.18 - 01 March 2004 + Added log4j context repository to jettyplus + Default servlet respectes servlet path + Fixed j2se 1.3 problem with HttpFields + Improved log performance + NPE guard for no-listener junit deployment + Suppress some more IOExceptions jetty-4.2.17 - 01 February 2004 + Fixed busy loop in threadpool run + Reorganized ServletHolder init jetty-4.2.16 - 30 January 2004 + FileResource better handles non sun JVM + Fixed HttpTunnel for JDK 1.2 + Fixed setDate multi-cpu race + Improved low thread handling + Monitor closes socket before exit + RequestDispatcher uses request encoding for query params + Update jasper to 4.1.29 jetty-5.0.beta1 - 24 December 2003 + Added patch for JBoss realm single sign on + Env variables for CGI + Fixed UnixCrypt handling in HTAccessHandler + Removed support for old JBoss clustering + Reorganized FAQ + SecurityConstraints not reset by stop() on custom context jetty-4.2.15 - 24 December 2003 + Added patch for JBoss realm single sign on + Environment variables for CGI + Fixed UnixCrypt handling in HTAccessHandler + Removed support for old JBoss clustering + SecurityConstraints not reset by stop() on custom context jetty-5.0.beta0 - 22 November 2003 + Added MsieSslHandler to handle browsers that don't grok persistent SSL (msie 5) + Added org.mortbay.http.ErrorHandler for error pages. + Allow per listener handlers + Expire pages that contain set-cookie as per RFC2109 recommendation + Fixed init race in HttpFields cache + JBoss integration uses writer rather than stream for XML config handling + PathMap uses own Map.Entry impl for IBM JVMs + Protect ThreadPool.run() from interrupted exceptions + Removed support for HTTP trailers + Removed the CMR/CMP distributed session implementation + Respect content length when decoding form content. + Updated jasper to 5.0.14beta + Use ${jetty.home}/work or WEB-INF/work for temp directories if present jetty-4.2.15rc0 - 22 November 2003 + Added org.mortbay.http.ErrorHandler for error pages. + JsseListener checks UserAgent for browsers that can't grok persistent SSL (msie5) + PathMap uses own Map.Entry impl for IBM JVMs + Protect ThreadPool.run() from interrupted exceptions + Race in HttpFields cache + Removed the CMR/CMP distributed session implementation + Use ${jetty.home}/work or WEB-INF/work for temp directories if present jetty-4.2.14 - 04 November 2003 + Expire pages that contain set-cookie as per RFC2109 recommendation + Fixed NPE in SSO + JBoss integration uses writer rather than stream for XML config handling + respect content length when decoding form content. jetty-5.0.alpha3 - 19 October 2003 + Allow customization of HttpConnections + Failed requests excluded from duration stats + FileClassPath derived from walk of classloader hierarchy. + Fixed null pointer if no sevices configured for JettyPlus + Implemented security constraint combinations + Lazy authentication if no auth constraint. + Priority added to ThreadPool + replaced win32 service with http://wrapper.tanukisoftware.org + Restore servlet handler after dispatch + Reworked Dispatcher to better support cross context sessions. + Set TransactionManager on JettyPlus datasources and pools + Updated jasper and examples to 5.0.12 + Use File.toURI().toURL() when jdk 1.2 alternative is available. jetty-4.2.14RC1 - 19 October 2003 + Added UserRealm.logout and arrange for form auth + Allow customization of HttpConnections + Failed requests excluded from + Reworked Dispatcher to better support cross context sessions. jetty-4.2.14RC0 - 07 October 2003 + Build fileclasspath from a walk of the classloaders + cookie timestamps are in GMT + Correctly setup context classloader in cross context dispatch. + Fixed comments with embedded double dashes on jettyplus.xml file + Fixed handling of error pages for IO and Servlet exceptions + Fixed null pointer if no sevices configured for JettyPlus + Priority on ThreadedServer + Put a semi busy loop into proxy tunnels for IE problems + replaced win32 service with http://wrapper.tanukisoftware.org + Set TransactionManager on JettyPlus datasources and pools + updated extra/j2ee to jboss 3.2.1+ + Use File.toURI().toURL() when jdk 1.2 alternative is available. jetty-5.0.alpha2 - 19 September 2003 + Correctly setup context classloader in cross context dispatch. + Fixed error page handling of IO and Servlet exceptions. + Implemented ServletRequestListeners as optional filter. + Improved JMX start. + minor doco updates. + Moved error page mechanism to be webapp only. + moved mailing lists to sourceforge. + MultipartRequest supports multi value headers. + Put a semi busy loop into proxy tunnels for IE problems + Turn off validation without non-xerces errors + Update jakarta examples + Use commons logging. + Use log4j if extra is present. + XML entity resolution uses URLs not Resources jetty-5.0.alpha1 - 12 August 2003 + Implemented locale encoding mapping. + Improve combinations of Security Constraints + Server javadoc from war + Switched to mx4j + Synced with 4.2.12 + Updated to Jasper 5.0.7 jetty-5.0.alpha0 - 16 July 2003 + Compiled against 2.4 servlet spec. + Implemented Dispatcher forward attributes. + Implemented filter-mapping element + Implemented remote/local addr/port methods + Implemented setCharaterEncoding + Updated authentication so that a normal Principal is used. + updated to jasper 5.0.3 jetty-4.2.12 - 12 August 2003 + Added missing S to some OPTIONS strings + Added open method to threaded server. + Fixed MIME types for chemicals + Fixed parameter ordering for a forward request. + Fixed up HTAccessHandler + FORMAuthenticator does 403 with empty error page. + Improved error messages from ProxyHandler + Padding for IE in RootNotFoundHandler + Removed protection of org.mortbay.http attributes + Restore max inactive interval for session manager jetty-4.2.11 - 12 July 2003 + Branched for Jetty 5 development. + Cookie params all in lower case. + Fixed race in servlet initialization code. + Prevent AJP13 from reordering query. + Simplified AJP13 connection handling. + Support separate Monitor class for start jetty-4.2.10 - 07 July 2003 + Updates to JettyPlus documentation + Updates to Jetty tutorial for start.jar, jmx etc jetty-4.2.10pre2 - 04 July 2003 + Addition of mail service for JettyPlus + Allow multiple security-role-ref elements per servlet. + Cleaned up alias handling. + Confidential redirection includes query + Fixed cookie handling for old cookies and safari + handle multiple security role references + Handle Proxy-Connection better + Improvement to JettyPlus config of datasources and connection pools + Many improvements in JettyPlus java:comp handling + Move to Service-based architecture for JettyPlus features + Re-implementation of JNDI + Restricted ports in ProxyHandler. + Session statistics + URI always encodes % + XmlConfiguration can get/set fields. jetty-4.2.10pre1 - 02 June 2003 + Added SSO implementation for FORM authentication. + Added stop.jar + Deprecated forced chunking. + Fixed AJP13 protocol so that request/response header enums are correct. + Fixed form auth success redirect after retry, introduced in 4.2.9rc1 + Fixed JSP code visibility problem introduced in Jetty-4.2.10pre0 + Fixed problem with shared session for inter context dispatching. + Form authentication remembers URL over 403 + ProxyHandler has improved test for request content + Removed support of org.mortbay.http.User role. + Trace support is now optional (in AbstractHttpHandler). + WebApplicationContext does not reassign defaults descriptor value. jetty-4.2.10pre0 - 05 May 2003 + Added ability to override jetty startup class by using -Djetty.server on runline + Allow params in form auth URLs + Allow query params in error page URL. + Apply the append flag of RolloverFileOutputStream constructor. + Fixed CRLF bug in MultiPartRequest + Fixed table refs in JDBCUserRealm. + FORM Authentication is serializable for session distribution. + getAuthType maps the HttpServletRequest final strings. + getAuthType returns CLIENT_CERT instead of CLIENT-CERT. + Incorporate jetty extra and plus into build + Incorporate JettyPlus jotm etc into build. + Integrate with JAAS + Massive reorg of the CVS tree. + Merge multivalued parameters in dispatcher. + Moved Log4JLogSink into JettyPlus + New look and feel for www site. + ProxyHandler checks black and white lists for Connect. + RolloverFileOutputStream manages Rollover thread. + Updated to jasper jars from tomcat 4.1.24 + Warn if max form content size is reached. jetty-4.2.9 - 19 March 2003 + Conditional headers check after /dir to /dir/ redirection. jetty-4.2.9rc2 - 16 March 2003 + Added X-Forwarded-For header in ProxyHandler + Allow dispatch to j_security_check + Defaults descriptor has context classloader set. + Fixed build.xml for source release + Made rfc2068 PUT/POST Continues support optional. + Updated included jmx jars jetty-4.2.9rc1 - 06 March 2003 + Added requestlog to HttpContext. + Added support for client certs to AJP13. + Added trust manager support to SunJsseListener. + Allow delegated creation of WebApplication derivations. + Check Data contraints before Auth constraints + Cleaned up includes + Dump servlet can load resources for testing now. + Optional 2.4 behaviour for sessionDestroyed notification. + ProxyHandler has black and white host list. + Reduced default context cache sizes (Total 1MB file 100KB). + Removed checking for single valued headers. + Stop proxy url from doing user interaction. + Turn request log buffering off by default. + Work around URLClassloader not handling leading / jetty-4.2.8_01 - 18 February 2003 + Added a SetResponseHeadersHandler, can set P3P headers etc. + Added MBeans for Servlets and Filters + Added option to resolve remote hostnames. Defaults to off. + Default servlet can have own resourceBase. + Fixed AdminServlet to handle changed getServletPath better. + Fixed CGI servlet to handle multiple headers. + Moved ProxyHandler to the src1.4 tree + Patched first release of 4.2.8 with correct version number + ProxyHandler can handle multiple cookies. + Rolled back SocketChannelListener to 4.2.5 version jetty-4.2.7 - 04 February 2003 + Changed PathMap to conform to / getServletPath handling. + Fixed proxy tunnel for non persistent connections. + Relative sendRedirect handles trailing / correctly. + Upgraded to JSSE 1.0.3_01 to fix security problem. jetty-4.2.6 - 24 January 2003 + Added HttpContext.setHosts to restrict context by real interface. + Added MBeans for session managers + Added version to HttpServerMBean. + Allow AJP13 buffers to be resized. + ClientCertAuthentication updates request. + Fixed LineInput problem with expanded buffers. + Fixed rel sendRedirects for root context. + Improved SocketChannelListener contributed. + Improved synchronization on AbstractSessionManager. jetty-4.2.5 - 14 January 2003 + Added Log4jSink in the contrib directory. + Don't process conditional headers and ranges for includes + Fixed pathParam bug for ;jsessionid + Fixed requestedSessionId null bug. jetty-4.2.4 - 04 January 2003 + Added MBeans for handlers + Clear context attributes after stop. + Clear context listeners after stop. + Fixed stop/start handling of servlet context + HTAccessHandler checks realm as well as htpassword. + Reuse empty LogSink slots. + Upgraded jasper to 4.1.18 + Use requestedSessionId as default session ID. jetty-4.2.4rc0 - 12 December 2002 + Added gzip content encoding support to Default and ResourceHandler + Added HttpContext.flushCache + Allow empty host header. + Avoid optional 100 continues. + Better access to session manager. + Character encoding handling for GET requests. + Cheap clear for HttpFields + Cleaned up some unused listener throws. + Code logs objects rather than strings. + Configurable root context. + Dir listings in UTF8 + Fixed dir listing from jars. + Fixed isSecure and getScheme for SSL over AJP13 + Fixed setBufferSize NPE. + Handle = in param values. + Handle chunked form data. + Implemented RFC2817 CONNECT in ProxyHandler + Improved ProxyHandler to the point is works well for non SSL. + Improved setBufferSize handling + Limit form content size. + Removed container transfer encoding handling. + RootNotFoundHandler to help when no context found. + Simplified ThreadedServer + Update jasper to 4.1.16beta + Use ThreadLocals for ByteArrayPool to avoid synchronization. + Use Version to reset HttpFields jetty-4.2.3 - 02 December 2002 + Added links to Jetty Powered page + added main() to org.mortbay.http.Version + Added PKCS12Import class to import PKCS12 key directly + Check form authentication config for leading / + Cleaner servlet stop to avoid extra synchronization on handle + Clean up of ThreadedServer.stop() + Fixed some typos + org.mortbay.http.HttpContext.FileClassPathAttribute + Removed aggressive threadpool shrinkage to avoid deadlock on SMP machines. + removed old HttpContext.setDirAllowed() + Updated bat scripts jetty-4.2.2 - 20 November 2002 + Added EOFException to reduce log verbosity on closed connections. + Avoided bad buffer status after closed connection. + Fixed handling of empty headers + Fixed sendRedirect for non http URLS + Fixed URI query recycling for persistent connections jetty-4.2.1 - 18 November 2002 + Fixed bad optimization in UrlEncoding + Re-enabled UrlEncoding test harnesses jetty-4.2.0 - 16 November 2002 + Added definitions for RFC2518 WebDav response codes. + Added upload demo to dump servlet. + Fixed AJP13 buffer size. + Fixed include of Invoker servlet. + Fixed remove listener bug. + Lowercase jsessionid for URLs only. + Made NCSARequestLog easier to extend. + Many more optimizations. + Removed jasper source and just include jars from 4.1.12 + Removed remaining non portable getBytes() calls + Restrict 304 responses to seconds time resolution. + Use IE date formatting for speed. + Worked around JVM1.3 bug for JSPs jetty-4.1.4 - 16 November 2002 + Fixed ContextLoader parent delegation bug + Fixed Invoker servlet for RD.include + Fixed remove SocketListener bug. + Last modified handling uses second resolution. + Made NCSARequestLog simpler to extend. + Use IE date formatting for last-modified efficiency jetty-4.2.0rc1 - 02 November 2002 + Fixed ContextLoader parent delegation bug. + Fixed directory resource bug in JarFileResource. + Fixed firstWrite after commit. + Fixed problem setting the size of chunked buffers. + Fixed servletpath on invoker for named servlets. + Improved handling of 2 byte encoded characters within forms. + Recycling of HttpFields class. + Removed unused Servlet and Servlet-Engine headers. + Renamed Filter application methods. + Support default mime mapping defined by * jetty-4.2.0rc0 - 24 October 2002 + Added authenticator to admin.xml + Added embedded iso8859 writer to HttpOutputStream. + Fixed RolloverFileOutputStream without date. + Fixed SessionManager initialization + Fixed Session timeout NPE. + Greg's birthday release! + Removed duplicate classes from jar jetty-4.1.3 - 24 October 2002 + Added authenticator to admin.xml + Fixed RolloverFileOutputStream without date. + Fixed SessionManager initialization + Fixed Session timeout NPE. jetty-4.0.6 - 24 October 2002 + Clear interrupted status in ThreadPool + fixed forward attribute handling for jsp-file servlets + Fixed forward query string handling + Fixed handling of relative sendRedirect after forward. + Fixed setCharacterEncoding to work with getReader + Fixed virtual hosts temp directories. jetty-4.2.0beta0 - 13 October 2002 + 404 instead of 403 for WEB-INF requests + Allow %3B encoded ; in URLs + Allow anonymous realm + Build without jmx + Fixed bad log dir detection + Fixed caching of directories to avoid shared buffers. + Fix Session invalidation bug + FORM authentication sets 403 error page + getNamedDispatcher(null) returns containers default servlet. + New AJP13 implementation. + New Buffering implementation. + New ThreadPool implementation. + Removed Dispatcher dependancy on ServletHttpContext + Stop/Start filters in declaration order. + unquote charset in content type + Update jasper to 4.1.12 tag + Use "standard" names for default,jsp & invoker servlets. jetty-4.1.2 - 13 October 2002 + 404 instead of 403 for WEB-INF requests + Allow %3B encoded ; in URLs + Allow anonymous realm + Build without jmx + Fixed bad log dir detection + Fixed caching of directories to avoid shared buffers. + Fix Session invalidation bug + FORM authentication sets 403 error page + getNamedDispatcher(null) returns containers default servlet. + Some AJP13 optimizations. + Stop/Start filters in declaration order. + unquote charset in content type + Update jasper to 4.1.12 tag + Use "standard" names for default,jsp & invoker servlets. jetty-4.1.1 - 30 September 2002 + Avoid setting sotimeout for optimization. + Cache directory listings. + Deprecated maxReadTime. + Fixed client scripting vulnerability with jasper2. + Fixed infinite recursion in JDBCUserRealm + Fixed space in resource name handling for jdk1.4 + Merged LimitedNCSARequestLog into NCSARequestLog + Moved launcher/src to src/org/mortbay/start + String comparison of If-Modified-Since headers. + Touch files when expanding jars jetty-4.1.0 - 22 September 2002 + Added LimitedNCSARequestLog + ClientCertAuthenticator protected from null subjectDN + Context Initparams to control session cookie domain, path and age. + Fixed AJP13 handling of mod_jk loadbalancing. + Fixed CGI+windows security hole. + Handle unremovable tempdir. + NCSARequest log buffered default + Sorted directory listings. + Stop servlets in opposite order to start. + Use javac -target 1.2 for normal classes + WEB-INF/classes before WEB-INF/lib jetty-4.1.0RC6 - 14 September 2002 + Added logon.jsp for no cookie form authentication. + Added redirect to welcome file option. + Cleaned up old debug. + Don't URL encode FileURLS. + Encode URLs of Authentication redirections. + Extended Session API to pass request for jvmRoute handling + Fixed problem with AJP 304 responses. + FormAuthenticator uses normal redirections now. + Improved HashUserRealm doco + Improved look and feel of demo jetty-4.1.0RC5 - 08 September 2002 + Added commandPrefix init param to CGI + AJP13Listener caught up with HttpConnection changes. + Implemented security-role-ref for isUserInRole. + Improved errors for misconfigured realms. + More cleanup in ThreadPool for idle death. jetty-4.1.0RC4 - 30 August 2002 + Created statsLock sync objects to avoid deadlock when stopping. + Included IbmJsseListener in the contrib directory. + Reverted to 302 for all redirections as all clients do not understand 303 + Updated jasper2 to 4.1.10 tag. jetty-4.1.0RC3 - 28 August 2002 + Added buffering to request log + Added defaults descriptor to addWebApplications. + addWebApplications encodes paths to allow for spaces in file names. + Allow FORM auth pages to be within security constraint. + Allow WebApplicationHandler to be used with other handlers. + Created and integrated the Jetty Launcher + Fixed security problem for suffix matching with trailing "/" + Improved handling of path encoding in Resources for bad JVMs + Improved handling of PUT,DELETE & MOVE. + Made Resource canonicalize it's base path for directories jetty-4.1.0RC2 - 20 August 2002 + Added HttpListener.bufferReserve + Build ant, src and zip versions with the release + Clear interrupted status in ThreadPool + Conveninace setClassLoaderJava2Compliant method. + Fixed HttpFields cache overflow + Improved ByteArrayPool to handle multiple sizes. + Updated to Jasper2 (4_1_9 tag) + Use system line separator for log files. jetty-4.1.0RC1 - 11 August 2002 + Fixed forward query string handling + Fixed forward to jsp-file servlet + Fixed getContext to use canonical contextPathSpec + Fixed handling of relative sendRedirect after forward. + Fixed setCharacterEncoding to work with getReader + Improved the return codes for PUT + Made HttpServer serializable + Updated international URI doco + Updated jasper to CVS snapshot 200208011920 jetty-4.1.0RC0 - 31 July 2002 + Added DigestAuthenticator + Added ExpiryHandler which can set a default Expires header. + Added link to a Jetty page in Korean. + Changed URI default charset back to ISO_8859_1 + Fixed getRealPath for packed war files. + Restructured Password into Password and Credentials jetty-4.0.5 - 31 July 2002 + Fixed getRealPath for packed war files. + Fixed getRequestURI for RD.forward to return new URI. + Reversed order of ServletContextListener.contextDestroyed calls jetty-4.1.B1 - 19 July 2002 + Added 2.4 Filter dispatching support. + Added PUT,DELETE,MOVE support to webapps. + CGI Servlet, catch and report program invocation failure status. + CGI Servlet, fixed suffix mapping problem. + CGI Servlet, pass all HTTP headers through. + CGI Servlet, set working directory for exec + Moved dynamic servlet handling to Invoker servlet. + Moved webapp resource handling to Default servlet. + Reversed order of ServletContextListener.contextDestroyed calls + Sessions create attribute map lazily. + Support HTTP/0.9 requests again + Updated mini.http.jar target jetty-3.1.9 - 15 July 2002 + Allow doHead requests to be forwarded. + Fixed race in ThreadPool for minThreads <= CPUs jetty-4.1.B0 - 13 July 2002 + Added work around of JDK1.4 bug with NIO listener + Allow filter init to access servlet context methods. + close rather than disable stream after forward + Fixed close problem with load balancer. + Fixed ThreadPool bug when minThreads <= CPUs + Keep notFoundContext out of context mapping lists. + mod_jk FAQ + Moved 3rd party jars to $JETTY_HOME/ext + NCSARequestLog can log to stderr + RD.forward changes getRequestURI. + Stopped RD.includes closing response. jetty-4.1.D2 - 24 June 2002 + Added AJP13 listener for apache integration. + Allow comma separated cookies and headers + Back out Don't chunk 30x empty responses. + Better recycling of HttpRequests. + Conditional header tested against welcome file not directory. + Fixed ChunkableOutputStream close propagation + Improved ThreadedServer stopping on bad networks + Moved jmx classes from JettyExtra to here. + Protect session.getAttributeNames from concurrent modifications. + Set contextloader during webapplicationcontext.start + Support trusted external authenticators. + Use ThreadLocals to avoid unwrapping in Dispatcher. jetty-4.0.4 - 23 June 2002 + Back out change: Don't chunk 30x empty responses. + Conditional header tested against welcome file not directory. + Improved ThreadedServer stopping on bad networks jetty-4.0.3 - 20 June 2002 + Allow comma separated cookies and headers + Allow session manager to be initialized when set. + Better recycling of HttpRequests. + Fixed close propagation of on-chunked output streams + Fixed japanese locale + Force security disassociation. + Protect session.getAttributeNames from concurrent modifications. + WebapplicationContext.start sets context loader jetty-4.1.D1 - 08 June 2002 + Added simple buffer pool. + Don't chunk 30x empty responses. + Fixed /foo/../bar// bug in canonical path. + Fixed "" contextPaths in Dispatcher. + Merged ResourceBase and SecurityBase into HttpContext + Recycle servlet requests and responses + Removed race for the starting of session scavaging + Reworked output buffering to keep constant sized buffers. jetty-4.0.2 - 06 June 2002 + Added OptimizeIt plug + Don't chunk 30x empty responses. + Fixed /foo/../bar// bug in canonical path. + Fixed "" contextPaths in Dispatcher. + Fixed handler/context start order. + Fixed web.dtd references. + Removed race for the starting of session scavaging jetty-3.1.8 - 06 June 2002 + Fixed /foo/../bar// bug in canonical path. + Fixed no slash context redirection. + Fixed singled threaded dynamic servlets + Made SecurityConstraint.addRole() require authentication. jetty-4.1.D0 - 05 June 2002 + Added OptimizeIt plug. + Added TypeUtil to reduce Integer creation. + BRAND NEW WebApplicationHandler & WebApplicationContext + Experimental CLIENT-CERT Authenticator + Fixed handler/context start order. + Fixed web.dtd references. + General clean up of the API for for MBean getters/setters. + Removed the HttpMessage facade mechanism + Restructured ResourceHandler into ResourceBase + The 4.1 Series started looking for even more performance within the 2.3 specification. jetty-4.0.1 - 22 May 2002 + Fixed "null" return from getRealPath + Fixed contextclassloader on ServletContextEvents. + OutputStreamLogSink config improvements + Support graceful stopping of context and server. + Updated jasper to 16 May snapshot jetty-4.0.1RC2 - 14 May 2002 + 3DES Keylength was being reported as 0. Now reports 168 bits. + Added confidential and integral redirections to HttpListener + Better error for jre1.3 with 1.4 classes + Cleaned up RD query string regeneration. + Fixed ServletResponse.reset() to resetBuffer. + Implemented the run-as servlet tag. jetty-4.0.1RC1 - 29 April 2002 + Avoid flushes during RequestDispatcher.includes + Better handling if no realm configured. + Expand ByteBuffer full limit with capacity. + Fixed double filtering of welcome files. + Fixed FORM authentication auth of login page bug. + Fixed setTempDirectory creation bug + Improved flushing of chunked responses jetty-4.0.1RC0 - 18 April 2002 + AbstractSessionManager sets contextClassLoader for scavanging + Added extract arg to addWebApplications + DTD allows static "Get" and "Set" methods to be invoked. + Extended facade interfaces to HttpResponse.sendError + Fixed delayed response bug: Stopped HttpConnection consuming input from timedout connection. + Moved basic auth handling to HttpRequest + Pass pathParams via welcome file forward for jsessionid + Set thread context classloader for webapp load-on-startup inits + Updated Jasper to CVS snapshot from Apr 18 18:50:59 BST 2002 jetty-4.0.0 - 22 March 2002 + Added IPAddressHandler for IP restrictions + Jetty.sh cygwin support + Minor documentation updates. + Updated contributors. + Updated tutorial configure version jetty-4.0.RC3 - 20 March 2002 + Changed html attribute order for mozilla quirk. + ContextInitialized notified before load-on-startup servlets. + Fixed ZZZ offset format to +/-HHMM + JDBCUserRealm instantiates JDBC driver + Suppress WriterOutputStream warning. + Updated history jetty-4.0.RC2 - 12 March 2002 + Added experimental nio SocketChannelListener + Added skeleton load balancer + Disabled the Password EXEC mechanism by default + Dont try to extract directories + Fixed column name in JDBCUserRealm + Fixed empty referrer in NCSA log. + Fixed security constraint problem with // + Fixed version for String XmlConfigurations + Removed redundant sessionID check. + Remove last of the Class.forName calls. + Security FAQ jetty-3.1.7 - 12 March 2002 + Fixed security problem with constraints being bypassed with // in URLs jetty-4.0.RC1 - 06 March 2002 + Added ContentEncodingHandler for compression. + Call response.flushBuffer after service to flush wrappers. + contextDestroyed event sent before destruction. + Contributors list as an image to prevent SPAM! + Empty suffix for temp directory. + FileResource depends less on FilePermissions. + Fixed filter vs forward bug. + Fixed recursive DEBUG loop in Logging. + Improved efficiency of quality list handling + Minor changes to make HttpServer work on J2ME CVM + Simplified filter API to chunkable streams + Updated jetty.sh to always respect arguments. + Warn if jdk 1.4 classes used on JVM <1.4 + WebApplication will use ContextLoader even without WEB-INF directory. + XmlParser is validating by default. use o.m.x.XmlParser.NotValidating property to change. jetty-3.1.6 - 28 February 2002 + Dispatcher.forward dispatches directly to ServletHolder to avoid premature exception handling. + Empty suffix for temp directory. + Fixed HttpFields remove bug + HttpResponse.sendError makes a better attempt at finding an error page. + Implemented 2.3 clarifications to security constraint semantics PLEASE REVIEW YOUR SECURITY CONSTRAINTS (see README). + LineInput can handle any sized marks + Set Listeners default scheme jetty-4.0.B2 - 25 February 2002 + Accept jetty-web.xml or web-jetty.xml in WEB-INF + Added LoggerLogSink to direct Jetty Logs to JDK1.4 Log. + Added optional JDK 1.4 src tree + Added org.mortbay.http.JDBCUserRealm + Added String constructor to XmlConfiguration. + Adjust servlet facades for welcome redirection + Improved default jetty.xml + Improve handling of unknown URL protocols. + Init classloader for JspServlet + Minor Jasper updates + o.m.u.Frame uses JDK1.4 stack frame handling + Simplified addWebApplication + Slightly more agressive eating unused input from non persistent connection. + Start ServletHandler as part of the FilterHandler start. + User / mapping rather than /* for servlet requests to static content jetty-4.0.B1 - 13 February 2002 + Added setClassLoader and moved getFileClassPath to HttpContext + getRequestURI returns encoded path + HttpConnection always eats unused bodies + LineInput waits for LF after CF if seen CRLF before. + Merged HttpMessage and Message + Servlet request destined for static content returns paths as default servlet + Suppress error only for IOExceptions not derivitives. + Updated examples webapp from tomcat + WriterOutputStream so JSPs can include static resources. jetty-4.0.B0 - 04 February 2002 + Added AbstractSessionManager + Added Array element to XMLConfiguration + Added hack for compat tests in watchdog for old tomcat stuff + Added index links to tutorial + Allow listener schemes to be set. + Common handling of TRACE + Factor out RolloverFileOutputStream from OutputStreamLogSink + Fixed HttpFields remove bug + Handle special characters in resource file names better. + HttpContext destroy + Implemented 2.3 security constraint semantics PLEASE REVIEW YOUR SECURITY CONSTRAINTS (see README). + Reduce object count and add hash width to StringMap + Release process builds JettyExtra + Removed triggers from Code. + Remove request logSink and replace with RequestLog using RolloverFileOutputStream + Renamed getHttpServers and added setAnonymous + Stop and remove NotFound context for HttpServer + Support Random Session IDs in HashSessionManager. + Updated crimson to 1.1.3 + Updated tutorial and FAQ + Welcome file dispatch sets requestURI. + Welcome files may be relative jetty-4.0.D4 - 14 January 2002 + Added BlueRibbon campaign. + Added isAuthenticated to UserPrincipal + Extract WAR files to standard temp directory + Fixed noaccess auth demo. + FORM auth caches UserPrincipal + Handle ServletRequestWrappers for Generic Servlets + Improved handling of UnavailableException + Improved HttpResponsse.sendError error page matching. + Prevent output after forward + RequestDispatcher uses cached resources for include + URI uses UTF8 for % encodings. jetty-4.0.D3 - 31 December 2001 + cookies with maxAge==0 expire on 1 jan 1970 + Corrected name to HTTP_REFERER in CGI Servlet. + DateCache handles misses better. + Fixed cached filter wrapping. + Fixed ContextLoader lib handling. + Fixed getLocale again + Fixed UrlEncoding for % + combination. + Generalized temp file handling + HttpFields uses DateCache more. + Made Frame members private and fixed test harness + Moved admin port to 8081 to avoid JBuilder + Patch jasper to 20011229101000 + Removed limits on mark in LineInput. + setCookie always has equals jetty-3.1.5 - 11 December 2001 + Allow POSTs to static resources. + Branched at Jetty_3_1 + cookies with maxage==0 expired 1 jan 1970 + Fixed ChunableInputStream.resetStream bug. + Fixed formatting of redirectURLs for NS4.08 + Ignore IO errors when trying to persist connections. + setCookie always has equals for cookie value + stopJob/killStop in ThreadPool to improve stopping ThreadedServer on some platforms. jetty-4.0.D2 - 02 December 2001 + added addWebApplications auto discovery + Allow POSTs to static resources. + Better handling of charset in form encoding. + Disabled last forwarding by setPath() + Fixed ChunableInputStream.resetStream bug. + Fixed formatting of redirect URLs. + Ignore IO errors when trying to persist connections. + Made the root context a webapplication. + Moved demo docroot/servlets to demo directory + New event model to decouple from beans container. + Removed Demo.java (until updated). + Removed ForwardHandler. + Removed most of the old doco, which needs to be rewritten and added again. + Removed Request set methods (will be replaced) + Restructured for demo and test hierarchies + stopJob/killStop in ThreadPool to improve stopping ThreadedServer on some platforms. jetty-4.0.D1 - 14 November 2001 + Added Context and Session Event Handling + Added FilterHandler + Added FilterHolder + Changed HandlerContext to HttpContext + Fixed bug with request dispatcher parameters + Fixed ServletHandler with no servlets + New ContextLoader implementation. + New Dispatcher implementation + Removed destroy methods + Simplified MultiMap + Simplified ServletHandler jetty-4.0.D0 - 06 November 2001 + 1.2 JSP API + 2.3 Servlet API + Added examples webapp from tomcat4 + Branched at Jetty_3_1 + Branched from Jetty_3_1 == Jetty_3_1_4 + Jasper from tomcat4 + Start SessionManager abstraction. jetty-3.1.4 - 06 November 2001 + Added RequestLogFormat to allow extensible request logs. + Default PathMap separator changed to ":," + Generate session unbind events on a context.stop() + getRealPath accepts \ URI separator on platforms using \ file separator. + HTAccessHandler made stricter on misconfiguration + PathMap now ignores paths after ; or ? characters. + Remove old stuff from contrib that had been moved to extra + Support the ZZZ timezone offset format in DateCache jetty-3.1.3 - 26 October 2001 + Allow a per context UserRealm instance. + Correct dispatch to error pages with javax attributes set. + Fixed binary files in CVS + Fixed several problems with external role authentication. Role authentication in JBoss was not working correctly and there were possible object leaks. The fix required an API change to UserPrinciple and UserRealm. + Fixed Virtual hosts to case insensitive. + Fix security problem with trailing special characters. Trailing %00 enabled JSP source to be viewed or other servlets to be bypassed. + Improved FORM auth handling of role failure. + Improved Jasper debug output. + Improved ThreadedServer timeout defaults + PathMap spec separator changed from ',' to ':'. May be set with org.mortbay.http.PathMap.separators system property. + Upgraded JSSE to 1.0.2 jetty-3.1.2 - 13 October 2001 + Added run target to ant + Added ServletHandler.sessionCount() + Added short delay to shutdown hook for JVM bug. + Changed 304 responses for Opera browser. + Changed JSESSIONID to jsessionid + Changed unsatisfiable range warnings to debug. + Fixed attr handling in XmlParser.toString + Fixed authentication role handling in FORM auth. + Fixed double entry on PathMap.getMatches + Fixed FORM Authentication username. + Fixed NotFoundHandler handling of unknown methods + Fixed request log date formatting + Fixed servlet handling of non session url params. + FORM authentication passes query params. + Further improvements in handling of shutdown. + Log OK state after thread low warnings. jetty-3.1.1 - 27 September 2001 + Correctly ignore auth-constraint descriptions. + Fixed jar manifest format - patched 28 Sep 2001 + Fixed ServletRequest.getLocale(). + Handle requestdispatcher during init. + Reduced verbosity of bad URL errors from IIS virus attacks + Removed incorrect warning for WEB-INF/lib jar files. + Removed JDK 1.3 dependancy + Use lowercase tags in html package to be XHTML-like. jetty-3.1.0 - 21 September 2001 + Added HandlerContext.registerHost + Added long overdue Tutorial documentation. + Fix .. handling in URI + Fix flush on stop bug in logs. + Fix FORM authentication on exact patterns + Fix Jetty.bat for spaces. + Fix param reading on CGI servlet + Fix REFFERER in CGI + Fix ResourceHandler cache invalidate. + Fix reuse of Resource + Fix ServletResponse.setLocale() + Improved closing of listeners. + Improved some other documentation. + New simplified jetty.bat + Optimized List creation + Removed win32 service.exe jetty-3.1.rc9 - 02 September 2001 + Added bin/orgPackage.sh script to change package names. + Added handlerContext.setClassPaths + Added lowResourcePersistTimeMs for more graceful degradation when we run out of threads. + Added support for Nonblocking listener. + Changed to org.mortbay domain names. + Fixed bug with non cookie sessions. + Fixed handling of rel form authentication URLs + Format cookies in HttpFields. + Form auth login and error pages relative to context path. + Patched Jasper to 3.2.3. jetty-3.1.rc8 - 22 August 2001 + Added HttpServer statistics + Allow contextpaths without leading / + Allow per context log files. + Buffer allocation + Don't add notfound context. + Fixed handling of default mime types + ISO8859 conversion + Many major and minor optimizations: + OutputStreamLogSink replaces WriterLogSink + Removed race from dynamic servlet initialization. + Separation of URL params in HttpHandler API. + StringMap + Support WEB-INF/web-jetty.xml configuration extension for webapps + Updated sponsors page + URI canonicalPath + URI pathAdd jetty-3.1.rc7 - 09 August 2001 + Added doco for Linux port redirection. + Added FORM authentication. + Added method handling to HTAccessHandler. + Added shutdown hooks to Jetty.Server to trap Ctl-C + Added UML diagrams to Jetty architecture documentation. + Added utility methods to ServletHandler for wrapping req/res pairs. + Don't persist connections if low on threads. + Dump Servlet displays cert chains + Fix bug in sendRedirect for HTTP/1.1 + Fixed bug with session ID generation. + Fixed redirect handling by the CGI Servlet. + Fixed request.getPort for redirections from 80 + Optimized HttpField handling to reduce object creatiyon. + Remove old context path specs + ServletRequest SSL attributes in line with 2.2 and 2.3 specs. + ServletResponse.sendRedirect puts URLs into absolute format. + Use Enumerations to reduce conversions for servlet API. jetty-3.1.rc6 - 10 July 2001 + Added Client authentication to the JsseListener + Added debug and logging config example to demo.xml + Added Get element to the XmlConfiguration class. + Added getResource to HandleContext. + Added Static calls to the XmlConfiguration class. + Avoid script vulnerability in error pages. + Cleaned up destroy handling of listeners and contexts. + Cleaned up Win32 Service server creation. + Close persistent HTTP/1.0 connections on missing Content-Length + Fixed a problem with Netscape and the acrobat plugin. + Fixed bug in B64Code. Optimised B64Code. + Fixed XmlParser to handle xerces1.3 OK + Improved debug output for IOExceptions. + Improved SSL debugging information. + KeyPairTool can now load cert chains. + KeyPairTool is more robust to provider setup. + Moved gimp image files to Jetty3Extra + Moved mime types and encodings to property bundles. + Removed getConfiguration from LifeCycleThread to avoid JMX clash. + RequestDispatch.forward() uses normal HandlerContext.handle() path if possible. + Updated to JSSE-1.0.2, giving full strength crypto. + Use exec for jetty.sh run + WebApps initialize resourceBase before start. + Win32 Service uses Jetty.Server instead of HttpServer. jetty-3.1.rc5 - 01 May 2001 + Added build target for mini.jetty.jar - see README. + Added HTaccessHandler to authenitcate against apache .htaccess files. + Added query param handling to ForwardHandler + Added ServletHandler().setUsingCookies(). + Added UnixCrypt support to c.m.U.Password + Fixed EOF handling in MultiPartRequest. + Fixed forwarding to null pathInfo requests. + Fixed handling of empty responses at header commit. + Fixed handling of multiple cookies. + Fixed jetty.bat classpath problems. + Fixed ResourceHandler handling of ;JSESSIONID + Fixed sync of ThreadPool idleSet. + Major restructing of packages to separate servlet dependancies. c.m.XML - moved XML dependant classes from c.m.Util c.m.HTTP - No servlet or XML dependant classes: c.m.Jetty.Servlet - moved from c.m.HTTP.Handler.Servlet c.m.Servlet - received some servlet dependant classes from HTTP. + Optimized canonical path calculations. + Request log contains bytes actually returned. + Warn and close connections if content-length is incorrectly set. jetty-3.0.6 - 26 April 2001 + Fixed EOF handlding in MultiPartRequest. + Fixed forwarding to null pathInfo requests. + Fixed handling of empty responses at header commit. + Fixed ResourceHandler handling of ;JSESSIONID + Fixed sync of ThreadPool idleSet. + Load-on-startup the JspServlet so that precompiled servlets work. jetty-3.1.rc4 - 14 April 2001 + Added idle thread getter to ThreadPool. + Include full versions of JAXP and Crimson + Load-on-startup the JspServlet so that precompiled servlets work. + Removed stray debug println from the Frame class. jetty-3.0.5 - 14 April 2001 + Branched from 3.1 trunk to fix major errors + Created better random session ID + Don't chunk if content length is known. + fixed getLocales handling of quality params + Fixed LineInput bug EOF + Fixed session invalidation unbind notification to conform with spec + Improved flush ordering for forwarded requests. + Load-on-startup the JspServlet so that precompiled servlets work. + Resource handler strips URL params like JSESSION. + Turned off range handling by default until bugs resolved jetty-3.1.rc3 - 09 April 2001 + Added ContentHandler Observer to XmlParser. + Allow webapp XmlParser to be observed for ejb-ref tags etc. + Cleaned up handling of exceptions thrown by servlets. + Created better random session ID + Frame handles more JIT stacks. + Handle zero length POSTs + Implemented multi-part ranges so that acrobat is happy. + Improved flush ordering for forwarded requests. + Improved ThreadPool stop handling + Simplified multipart response class. + Start session scavenger if needed. jetty-3.1.rc2 - 30 March 2001 + Added MultiException to throw multiple nested exceptions. + added options to turn off ranges and chunking to support acrobat requests. + fixed getLocales handling of quality params + fixed getParameter(name) handling for multiple values. + Improved handling of Primitive classes in XmlConfig + Improved logging of nested exceptions. + Lifecycle.start() may throw Exception + Only one instance of default MIME map. + Renamed getConnection to getHttpConnection + Use reference JAXP1.1 for XML parsing.y + Version 1.1 of configuration dtd supports New objects. jetty-3.1.rc1 - 18 March 2001 + Added Jetty documentation pages from JettyWiki + Cleaned up build.xml script + Fixed problem with ServletContext.getContext(uri) + Minimal handling of Servlet.log before initialization. + Moved JMX and SASL handling to Jetty3Extra release + Resource handler strips URL params like JSESSION. + Various SSL cleanups jetty-3.1.rc0 - 23 February 2001 + Added JMX management framework. + Changed getter and setter methods that did not conform to beans API. + Dynamic servlets may be restricted to Context classloader. + Fixed init order for unnamed servlets. + Fixed session invalidation unbind notification to conform with spec + Improved handling of primitives in utilities. + Improved InetAddrPort and ThreadedServer to reduce DNS lookups. + Reoganized packages to allowed sealed Jars + Socket made available via HttpConnection. + Use Thread context classloader as default context loader parent. jetty-3.0.4 - 23 February 2001 + Fixed LineInput bug with split CRLF. jetty-3.0.3 - 03 February 2001 + Allow Log to be disabled before initialization. + Fixed handling of directories without trailing / + Fixed pipelined request buffer bug. + Handle empty form content without exception. + Implemented web.xml servlet mapping to a JSP + Included new Jetty Logo jetty-3.0.2 - 13 January 2001 + Added etc/jetty.policy as example policy file. + Allow '+' in path portion of a URL. + Context specific security permissions. + Greatly improved buffering in ChunkableOutputStream + Handle unknown status reasons in HttpResponse + Ignore included response updates rather than IllegalStateException + Improved HTML.Block efficiency + Improved jetty.bat + Improved jetty.sh + Padded error bodies for IE bug. + Removed classloading stats which were causing circular class loading problems. + Replaced ResourceHandler FIFO cache with LRU cache. + Restructured demo site pages. + Try ISO8859_1 encoding if can't find ISO-8859-1 jetty-3.0.1 - 20 December 2000 + Fixed value unbind notification for session invalidation. + Removed double null check possibility from ServletHolder jetty-3.0.0 - 17 December 2000 + Fixed rel path handling in default configurations. + Fixed rollover bug in WriterLogSink + Fixed taglib parsing + Fixed WriterLogSink init bug + Improved dtd resolution in XML parser. + Improved jetty.sh logging + Optional extract war files. + Use inner class to avoid double null check sync problems jetty-3.0.0.rc8 - 13 December 2000 + Added ForwardHandler + Change PathMap handling of /* to give precedence over suffix mapping. + Default log options changed if in debug mode. + Forward to welcome pages rather than redirect. + getSecurityHandler creates handler at position 0. + Improved exit admin handling + Jetty.Server catches init exceptions per server + Mapped *.jsp,*.jsP,*.jSp,*.jSP,*.Jsp,*.JsP,*.JSp,*.JSP + Optional alias checking added to FileResource. Turned on by default on all platforms without the "/" file separator. + Patched jasper to tomcat 3.2.1 + Protected META-INF as well as WEB-INF in web applications. + Removed security constraint on demo admin server. + Removed some unused variables. + Removed special characters from source. + SysV unix init script + Tidied handling of ".", ".." and "//" in resource paths jetty-3.0.0.rc7 - 02 December 2000 + Added Com.mortbay.HTTP.Handler.Servlet.Context.LogSink attribute to Servlet Context. If set, it is used in preference to the system log. + Added NotFoundServlet + Added range handling to ResourceHandler. + Allow dynamic servlets to be served from / + Auto add a NotFoundHandler if needed. + CGI servlet handles not found better. + Changed log options to less verbose defaults. + Conditionals apply to puts, dels and moves in ResourceHandler. + Depreciated RollOverLogSink and moved functionality to an improved WriterLogSink. + Don't set MIME-Version in response. + Double null lock checks use ThreadPool.__nullLockChecks. + Extended security constraints (see README and WebApp Demo). + Fixed security problem with lowercase WEB-INF uris on windows. + Handle multiple inits of same servlet class. + PUT, MOVE disabled in WebApplication unless defaults file is passed. + Set the AcceptRanges header. + Set thread context classloader during handler start/stop calls. + Split Debug servlet out of Admin Servlet. + ThreadedServer.forceStop() now makes a connection to itself to handle non-premptive close. + URIs accept all characters < 0xff. + WEB-INF protected by NotFoundServlet rather than security constraint. jetty-3.0.0.rc6 - 20 November 2000 + Added ServletWriter that can be disabled. + Added Win32 service support + Admin servlet uses unique links for IE. + Allow HttpMessage state to be manipulated. + Allow load-on-startup with no content. + Allow multiple set cookies. + Corrected a few of the many spelling mistakes. + don't include classes in release. + Don't set connection:close for normal HTTP/1.0 responses. + Don't start HttpServer log sink on add. + Fixed RollOverFileLogSink bug with extra log files. + Implemented customizable error pages. + Implemented resource aliases in HandlerContext - used by Servlet Context + Improved Log defaults + Javadoc improvements. + Map tablib configuration to resource aliases. + Prevent reloading dynamic servlets at different paths. + Put extra server and servlet info in header. + Reduced risk of double null check sync problem. + RequestDispatcher.forward() only resets buffer, not headers. + RequestDispatcher new queries params replace old. + Resource gets systemresources from it's own classloader. + Servlet init order may be negative. + Session cookies are given context path + Sessions try version 1 cookies in set-cookie2 header. + Simple stats in ContextLoader. + Version details in header can be suppressed with System property java.com.mortbay.HTTP.Version.paranoid + Warn for missing WEB-INF or web.xml + Webapps serve dynamics servlets by default. jetty-3.0.0.rc5 - 12 November 2000 + Added debug form to Admin servlet. + Allow null cookie values + Avoid jprobe race warnings in DateCache + Default writer encoding set by mime type if not explicitly set. + Implemented servlet load ordering. + Many javadoc cleanups. + Merged DynamicHandler into ServletHandler. + Moved JSP classpath hack to ServletHolder + Pass flush through ServletOut + Relax webapp rules, accept no web.xml or no WEB-INF + Removed Makefile build system. + RequestDispatcher can dispatch static resources. + Servlet exceptions cause 503 unavailable rather than 500 server error jetty-2.4.9 - 12 November 2000 + HtmlFilter handles non default encodings + HttpListener default max idle time = 20s + HttpListener ignore InterruptedIOExceptions + HttpRequest.write uses ISO8859_1 encoding. + Writing HttpRequests encodes path jetty-3.0.0.rc4 - 06 November 2000 + Fixed mime type mapping bug introduced in RC3 + Fixed mis-synchronization in ThreadPool.stop() + Ignore more IOExceptions (still visible with debug). + Provide default JettyIndex.properties jetty-3.0.0.rc3 - 05 November 2000 + Added bin/jetty.sh run script. + Added context class path dynamic servlet demo + Added gz tgz tar.gz .z mime mappings. + Added HandlerContext.setHttpServerAccess for trusted contexts. + Changed ThreadPool.stop for IBM 1.3 JVM + Fixed default mimemap initialization bug + Further clean up of the connection close actions + Handle mime suffixes containing dots. + Implemented mime mapping in webapplications. + Moved unused classes from com.mortbay.Util to com.mortbay.Tools in new distribution package. + Optimized persistent connections by recycling objects + Prevent servlet setAttribute calls to protected context attributes. + Removed redundant context attributes. + Set MaxReadTimeMs in all examples + Set the thread context class loader in HandlerContext.handle + Strip ./ from relative resources. + upgraded build.xml to ant v1.2 jetty-3.0.0.rc2 - 29 October 2000 + Accept HTTP/1. as HTTP/1.0 (for netscape bug). + Accept public DTD for XmlConfiguration (old style still supported). + Cleaned up non persistent connection close. + ErlEncoding treats params without values as empty rather than null. + Fixed thread name problem in ThreadPool + Pass file based classpath to JspServlet (see README). + Prevented multiple init of ServletHolder + Replaced ISO-8859-1 literals with StringUtil static jetty-3.0.0.rc1 - 22 October 2000 + Added CGI to demo + Added HashUserRealm and cleaned up security constraints + Added Multipart request and response classes from Jetty2 + Added simple admin servlet. + All attributes in javax. java. and com.mortbay. name spaces to be set. + Cleaned up exception handling. + Initialize JSP with classloader. + Moved and simplified ServletLoader to ContextLoader. + Partial handling of 0.9 requests. + removed Thread.destroy() calls. jetty-2.4.8 - 23 October 2000 + Fixed bug with 304 replies with bodies. + Fixed closing socket problem + Improved win32 make files. jetty-3.0.B05 - 18 October 2000 + Added default webapp servlet mapping /servlet/name/* + Cleaned up response committing and flushing + Fixed JarFileResource to handle jar files without directories. + Handler RFC2109 cookies (like any browser handles them!) + Implemented security-role-ref for servlets + Implemented war file support + improved ant documentation. + Improved default log format for clarity. + Improved null returns to get almost clean watchdog test. + Improved path spec interpretation by looking at 2.3 spec + Java2 style classloading + Made test harnesses work with ant. + Protected servletConfig from downcast security problems + Removed most deprecation warnings + Separated context attributes and initParams. jetty-3.0.B04 - 12 October 2000 + Added modified version of JasperB3.2 for JSP + Added webdefault.xml for web applications. + Do not try multiple servlets for a request. + Filthy hack to teach jasper JspServer Jetty classpath + Fixed problem with session ID in paths + Implemented Context.getContext(uri) + Merged and renamed third party jars. + Moved FileBase to docroot + Redirect to index files, so index.jsp works. + Restricted context mapping to simple model for servlets. jetty-3.0.B03 - 09 October 2000 + Added append mode in RolloverFileLogSink + Added release script + Catch stop and destroy exceptions in HttpServer.stop() + Expanded import package.*; lines + Expanded leading tabs to spaces + Handle ignorable spaces in WebApplication + Handle ignorable spaces in XmlConfiguration + Implemented request dispatching. + Improved Context to Handler contract. + Improved handler toString + Improved Log rollover. + Made LogSink a Lifecycle interface + Parse but not handler startup ordering in web applications. + Pass object to LogSink + Redirect context only paths. + Redo dynamic servlets handling + Remove 411 checks as IE breaks this rule after redirect. + Removed last remnants JDK 1.1 support + Send request log via a LogSink + Simplified path translation and real path calculation. + Warn about explicit sets of WebApplication jetty-2.4.7 - 06 October 2000 + Added encode methods to URI + Allow Objects to be passed to LogSink + fixes to SSL doco + Improved win32 build + Set content length on errors for keep alive. + Support key and keystore passwords + Various improvements to ServletDispatch, PropertyTree and associated classes. jetty-3.0.B02 - 24 August 2000 + Added CGI servlet + Fixed bug in TestRFC2616 + Fixed HTTP/1.0 input close bug + Fixed LineInput bug with SSL giving CR pause LF. + Improved ThreadedServer stop and destroy + Use resources in WebApplication jetty-3.0.B01 - 21 August 2000 + Implemented more webapp configuration + Partial implementation of webapp securitycontraints + SSL implemented with JsseListener + Switched to the aelfred XML parser from microstar, which is only partially validating, but small and lightweight jetty-2.4.6 - 16 August 2000 + Added passive mode methods to FTP + com.mortbay.Util.KeyPairTool added to handle openSSL SSL keys. + JsseListener & SunJsseListener added and documented + Minor changes to compile with jikes. + Turn Linger off before closing sockets, to allow restart. jetty-3.0.A99 - 10 August 2000 + Added Resource abstraction + Added Xmlconfiguration utility + Implemented jetty.xml configuration + Make it compile cleanly with jikes. + Re-added commented out imports for JDK-1.1 compile + Removed FileBase. Now use ResourceBase instead + Replaced FileHandler with ResourceHandler + ServletLoader simplied and uses ResourcePath + Use SAX XML parsing instead of DOM for space saving. jetty-3.0.A98 - 20 July 2000 + Allow HttpRequest.toString() handles bad requests. + Fixed constructor to RolloverFileLogSink + Implemented Jetty demos and Site as Web Application. + Implemented WebApplicationContext + Improved synchronization on LogSink + ServletRequest.getServerPort() returns 80 rather than 0 + Switched to JDK1.2 only jetty-3.0.A97 - 13 July 2000 + Added error handling to LifeCycleThread + Added WML mappings + Better tuned SocketListener parameters + Fixed makefiles for BSD ls + Fixed persistent commits with no content (eg redirect+keep-alive). + Formatted version in server info string. + implemented removeAttribute on requests + Implemented servlet getLocale(s). + Implemented servlet isSecure(). + Less verbose debug + Protect setContentLength from a late set in default servlet HEAD handling. + Started RequestDispatcher implementation. + Tempory request log implementation jetty-2.4.5 - 09 July 2000 + Added HtmlExpireFilter and removed response cache revention from HtmlFilter. + Don't mark a session invalid until after values unbound. + Fixed transaction handling in JDBC wrappers + Formatted version in server info. jetty-3.0.A96 - 27 June 2000 + Fixed bug with HTTP/1.1 Head reqests to servlets. + Supressed un-needed chunking EOF indicators. jetty-3.0.A95 - 24 June 2000 + Fixed getServletPath for default "/" + Handle spaces in file names in FileHandler. jetty-3.0.A94 - 19 June 2000 + Added HandlerContext to allow grouping of handlers into units with the same file, resource and class configurations. + Cleaned up commit() and added complete() to HttpResponse + Implemented Sessions. + PathMap exact matches can terminate with ; or # for URL sessions and targets. + Updated license to clarify that commercial usage IS OK! jetty-3.0.A93 - 14 June 2000 + Lots of changes and probably unstable + Major rethink! Moved to 2.2 servlet API jetty-3.0.A92 - 07 June 2000 + Added HTML classes to jar + Fixed redirection bug in FileHandler jetty-2.4.4 - 03 June 2000 + Added build-win32.mak + Added HTML.Composite.replace + Added RolloverFileLogSink + Added uk.org.gosnell.Servlets.CgiServlet to contrib + BasicAuthHandler uses getResourcePath so it can be used behind request dispatching + FileHandler implements IfModifiedSince on index files. + HttpRequest.setRequestPath does not null pathInfo. + Improved LogSink configuration + Many debug call optimizations + Support System.property expansions in PropertyTrees. jetty-3.0.A91 - 03 June 2000 + Abstracted ServletHandler + Added HTML classes from Jetty2 + Implemented realPath and getResource methods for servlets. + Improved LogSink mechanism + Simplified class loading + Simplified HttpServer configuration methods and arguments jetty-3.0.A9 - 07 May 2000 + File handler checks modified headers on directory indexes. + Fixed double chunking bug in SocketListener. + Improvided finally handling of output end game. + ServletLoader tries unix then platform separator for zip separator. jetty-3.0.A8 - 04 May 2000 + addCookie takes an int maxAge rather than a expires date. + Added LogSink extensible log architecture. + Added Tenlet class for reverse telnet. + Code.ignore only outputs when debug is verbose. + Moved Sevlet2_1 handler to com.mortbay.Servlet2_1 + Servlet2_1 class loading re-acrchitected. See README. jetty-2.4.3 - 04 May 2000 + Allow CRLF in UrlEncoded + Pass Cookies with 0 max age to browser. jetty-2.4.2 - 23 April 2000 + Added GNUJSP to JettyServer.prp file. + Added LogSink and FileLogSink classes to allow extensible Log handling. + Handle nested RequestDispatcher includes. + Modified GNUJSP to prevent close in nested requests. jetty-3.0.A7 - 15 April 2000 + Added InetGateway to help debug IE5 problems + added removeValue method to MultiMap + fixed flush problem with chunked output for IE5 + Include java 1.2 source hierarchy + removed excess ';' from source jetty-2.4.1 - 09 April 2000 + Fixed bug in HtmlFilter for tags split between writes. + Removed debug println from ServletHolder. + Set encoding before exception in FileHandler. jetty-3.0.A6 - 09 April 2000 + added bin/useJava2Collections to convert to JDK1.2 + Dates forced to use US locale + Improved portability of Frame and Debug. + Integrated skeleton 2.1 Servlet container + Removed Converter utilities and InetGateway. jetty-2.4.0 - 24 March 2000 + Absolute URIs are returned by getRequestURI (if sent by browser). + Added doc directory with a small start + Added per servlet resourceBase configuration. + Added VirtualHostHandler for virtual host handling + Fixed bug with RequestDispatcher.include() + Fixed caste problem in UrlEncoded + Fixed null pointer in ThreadedServer with stopAll + Form parameters only decoded for POSTs + Implemented full handling of cookie max age. + Improved parsing of stack trace in debug mode. + Moved SetUID native code to contrib hierarchy + RequestDispatcher handles URI parameters + Upgraded to gnujsp 1.0.0 jetty-2.3.5 - 25 January 2000 + Added configuration option to turn off Keep-Alive in HTTP/1.0 + Added contrib/com/kiwiconsulting/jetty JSSE SSL adaptor to release. + Allow configured servlets to be auto reloaded. + Allow properties to be configured for dynamic servlets. + Fixed expires bug in Cookies + Fixed nasty bug with HTTP/1.1 redirects. + Force locale of date formats to US. + ProxyHandler sends content for POSTs etc. jetty-2.3.4 - 18 January 2000 + Cookie map keyed on domain as well as name and path. + DictionaryConverter handles null values. + Fixed IllegalStateException handling in DefaultExceptionHandler + Fixed interaction with resourcePaths and proxy demo. + Improved HtmlFilter.activate header modifications. + include from linux rather than genunix for native builds + MethodTag.invoke() is now public. + Servlet properties allow objects to be stored. + URI decodes applies URL decoding to the path. jetty-3.0.A5 - 19 October 1999 + Do our own URL string encoding with 8859-1 + Replaced LF wait in LineInput with state boolean. + Use char array in UrlEncoded.decode + Use ISO8859_1 instead of UTF8 for headers etc. jetty-2.3.3 - 19 October 1999 + Do our own URL encoding with ISO-8859-1 + HTTP.HTML.EmbedUrl uses contents encoding. + Replaced UTF8 encoding with ISO-8859-1 for headers. + Use UrlEncoded for form parameters. jetty-2.3.2 - 17 October 1999 + Fixed getReader bug with HttpRequest. + Updated UrlEncoded with Jetty3 version. jetty-3.0.A4 - 16 October 1999 + Added LF wait after CR to LineInput. + Basic Authentication Handler. + Request attributes + UTF8 in UrlDecoded.decodeString. jetty-2.3.1 - 14 October 1999 + Added assert with no message to Code + Added Oracle DB adapter + Changed demo servlets to use writers in preference to outputstreams + Fixed GNUJSP 1.0 resource bug. + Force UTF8 for FTP commands + Force UTF8 for HTML + HTTP/1.0 Keep-Alive (about time!). + NullHandler/Server default name.name.PROPERTIES to load prefix/name.name.properties + Prevented thread churn on idle server. + ThreadedServer calls setSoTimeout(_maxThreadIdleMs) on accepted sockets. Idle reads will timeout. + Use UTF8 in HTTP headers jetty-3.0.A3 - 14 October 1999 + Added LifeCycle interface to Utils implemented by ThreadPool, ThreadedServer, HttpListener & HttpHandler + Added service method to HttpConnection for specialization. + MaxReadTimeMs added to ThreadedServer. + StartAll, stopAll and destroyAll methods added to HttpServer. jetty-3.0.A2 - 13 October 1999 + Added cookie support and demo. + Cleaned up Util TestHarness. + Fixed LineInput problem with repeated CRs + HEAD handling. + HTTP/1.0 Keep-alive (about time!) + NotFound Handler + OPTION * Handling. + Prevent entity content for responses 100-199,203,304 + Reduced flushing on writing response. + TRACE handling. + UTF8 handling on raw output stream. + Virtual Hosts. jetty-3.0.A1 - 12 October 1999 + Added HttpHandler interface with start/stop/destroy lifecycle + Added MultiMap for common handling of multiple valued parameters. + Added parameters to HttpRequest + Added PathMap implementing mapping as defined in the 2.2 API specification (ie. /exact, /prefix/*, *.extention & default ). + Implemented simple extension architecture in HttpServer. + LineInput uses own buffering and uses character encodings. + Quick port of FileHandler + Setup demo pages. + Updated HttpListener is start/stop/destroy lifecycle. jetty-3.0.A0 - 09 October 1999 + Added generalized HTTP Connection. + Added support for servlet 2.2 outbut buffer control. + Added support for transfer and content encoding filters. + Cleaned up chunking code to use LineInput and reduce buffering. + Cleanup and abstraction of ThreadPool. + Cleanup of HttpRequest and decoupled from Servlet API + Cleanup of HttpResponse and decoupled from Servlet API + Cleanup of LineInput, using 1.2 Collections. + Cleanup of URI, using 1.2 Collections. + Cleanup of UrlEncoded, using 1.2 Collections. + Created RFC2616 test harness. + Extended URI to handle absolute URLs + Generalized notification of outputStream events. + gzip and deflate request transfer encodings + HttpExceptions now produce error pages with specific detail of the exception. + HttpMessage supports chunked trailers. + HttpMessage supports message states. + Moved com.mortbay.Base classes to com.mortbay.Util + Moved HttpInput/OutputStream to ChunkableInput/OutputStream. + Split HttpHeader into HttpFields and HttpMessage. + Started fresh repository in CVS + TE field coding and trailer handler + ThreadedServer based on ThreadPool. jetty-2.3.0 - 05 October 1999 + Added SetUID class with native Unix call to set the effective User ID. + FTP closes files after put/get. + FTP uses InetAddress of command socket for data socket. jetty-2.3.0A - 22 September 1999 + Added "Powered by Jetty" button. + Added BuildJetty.java file. + Added GNUJSP 1.0 for the JSP 1.0 API. + Expanded tabs to spaces in source. + Made session IDs less predictable and removed race. + ServerContext available to HtmlFilters via context param + Use javax.servlet classes from JWSDK1.0 jetty-2.2.8 - 15 September 1999 + Added disableLog() to turn off logging. + Allow default table attributes to be overriden. + Fixed bug in Element.attribute with empty string values. + Improved quoting in HTML element values + Made translation of getRequestURI() optional. + Removed recursion from TranslationHandler jetty-2.2.7 - 09 September 1999 + Added default row, head and cell elements to Table. + Added GzipFilter for content encoding. + FileHandler passes POST request through if the file does not exist. + Reverted semantics of getRequestURI() to return untranslated URI. jetty-2.2.6 - 05 September 1999 + Added destroy() method on all HttpHandlers. + Added ServletRunnerHandler to the contrib directories. + Allow the handling of getPathTranslated to be configured in ServletHandler. + class StyleLink added. + Cookies always available from getCookies. + Cookies parameter renamed to CookiesAsParameters + cssClass, cssID and style methods added to element. + FileHandler does not server files ending in '/' + Fixed Cookie max age order of magnitude bug. + HttpRequest.getSession() always returns a session as per the latest API spec. + Ignore duplicate single valued headers, rather than reply with bad request, as IE4 breaks the rules. + media added to Style + New implementation of ThreadPool, avoids a thread leak problem. + Removed JRUN options from ServletHandler configuration. + ServletHandler.destroy destroys all servlets. + SPAN added to Block + Updated HTML package to better support CSS: jetty-2.2.5 - 19 August 1999 + Always close connection after a bad request. + Better default handling of ServletExceptions + Close loaded class files so Win32 can overwrite them before GC (what a silly file system!). + Don't override the cookie as parameter option. + Fixed bug with closing connections in ThreadedServer + Improved error messages from Jetty.Server. + Limited growth in MultiPartResponse boundary. + Made start and stop non final in ThreadedServer + Set Expires header in HtmlFilter. jetty-2.2.4 - 02 August 1999 + Better help on Jetty.Server + Fixed bugs in HtmlFilter parser and added TestHarness. + HtmlFilter blanks IfModifiedSince headers on construction + HttpRequests may be passed to HttpFilter constructors. + Improved cfg RCS script. + ThreadedServer can use subclasses of Thread. jetty-2.2.3 - 27 July 1999 + Added stop call to HttpServer, used by Exit Servlet. + FileHandler defaults to allowing directory access. + Fixed parser bug in HtmlFilter + Improved performance of com.mortbay.HTML.Heading + JDBC tests modified to use cloudscape as DB. + Made setInitialize public in ServletHolder + Simplified JDBC connection handling so that it works with Java1.2 - albeit less efficiently. jetty-2.2.2 - 22 July 1999 + File handler passes through not allowed options for non existant files. + Fixed bug in com.mortbay.Util.IO with thread routines. + Fixed bug in HtmlFilter that prevented single char buffers from being written. + Fixed bug with CLASSPATH in FileJarServletLoader after attempt to load from a jar. + Implemented getResourceAsStream in FileJarServletLoader + Improved com.mortbay.Base.Log handling of different JVMs + Minor fixes to README + Moved more test harnesses out of classes. + NotFoundHandler can repond with SC_METHOD_NOT_ALLOWED. jetty-2.2.1 - 18 July 1999 + Added optional resourceBase property to HttpConfiguration. This is used as a URL prefix in the getResource API and was suggested by the JSERV and Tomcat implementors. + Added TerseExceptionHandler + Comma separate header fields. + Decoupled ExceptionHandler configuration from Handler stacks. Old config style will produce warning and Default behavior. See new config file format for changes. + Handle continuation lines in HttpHeader. + HtmlFilter resets last-modified and content-length headers. + Ignore IOException in ThreadedServer.run() when closing. + Implemented com.mortbay.Util.IO as a ThreadPool + Less verbose debug in PropertyTree + Limit maximum line length in HttpInputStream. + Protect against duplicate single valued headers. + Response with SC_BAD_REQUEST rather than close in more circumstances jetty-2.2.0 - 01 July 1999 + Added Protekt SSL HttpListener + Exit servlet improved (a little). + Fixed some of the javadoc formatting. + Improved feature description page. + Moved GNUJSP and Protekt listener to a contrib hierarchy. + ThreadedServer.stop() closes socket before interrupting threads. jetty-2.2.Beta4 - 29 June 1999 + Added comments to configuration files. + Added getGlobalProperty to Jetty.Server and used this to configure default page type. + Added JettyMinimalDemo.prp as an example of an abbreviated configuration. + Added property handling to ServletHandler to read JRUN servlet configuration files. + Altered meaning of * in PropertyTree to assist in abbreviated configuration files. + Expanded Mime.prp file + FileHandler flushes files from cache in DELETE method. + Made ServerSocket and accept call generic in ThreadedServer for SSL listeners. + Options "allowDir" added to FileHandler. + Restructured com.mortbay.Jetty.Server for better clarity and documentation. + ThreadedServer.stop() now waits until all threads are stopped. + Updated README.txt jetty-2.2.Beta3 - 22 June 1999 + Added alternate constructors to HTML.Include for InputStream. + Added file cache to FileHandler + Applied contributed patch of spelling and typo corrections + Fixed bug in HttpResponse flush. + Fixed file and socket leaks in Include and Embed tags. + Implemented efficient version of ServletContext.getResourceAsStream() that does not open a new socket connection (as does getResource()). + Improved Block.write. + LookAndFeelServlet uses getResourceAsStream to get the file to wrap. This allows it to benefit from any caching done and to wrap arbitrary content (not just files). + Ran dos2unix on all text files + Re-implemented ThreadedServer to improve and balance performance. + Restructure demo so that LookAndFeel content comes from simple handler stack. + Server.shutdown() clears configuration so that server may be restarted in same virtual machine. jetty-2.2.Beta2 - 12 June 1999 + Added all write methods to HttpOutputStream$SwitchOutputStream + Added com.mortbay.Jetty.Server.shutdown() for gentler shutdown of server. Called from Exit servlet + Handle path info of a dynamic loaded servlets and correctly set the servlet path. + HttpRequest.getParameterNames() no longer alters the order returned by getQueryString(). + Standardized date format in persistent cookies. jetty-2.2.Beta1 - 07 June 1999 + Allow configuration of MinListenerThreads, MaxListenerThreads, MaxListenerThreadIdleMs + Close files after use to avoid "file leak" under heavy load. + Defined abstract ServletLoader, derivations of which can be specified in HttpConfiguration properties. + Destroy requests and responses to help garbage collector. + Don't warn about IOExceptions unless Debug is on. + Fixed cache in FileJarServletLoader + Fixed incorrect version numbers in a few places. + Fixed missing copyright messages from some contributions + HtmlFilter optimized for being called by a buffered writer. + Implemented all HttpServer attribute methods by mapping to the HttpConfiguration properties. Dynamic reconfiguration is NOT supported by these methods (but we are thinking about it). + Improved ThreadPool synchronization and added minThreads. + Included GNUJSP 0.9.9 + Limit the job queue only grow to the max number of threads. + Optional use of DateCache in log file format + Restructure ThreadedServer to reduce object creation. jetty-2.2.Beta0 - 31 May 1999 + Added "Initialize" attribute to servlet configuration to allow servlet to be initialized when loaded. + Added HttpResponse.requestHandled() method to avoid bug with servlet doHead method. + Added Page.rewind() method to allow a page to be written multiple times + Handle malformed % characters in URLs. + HttpRequest.getCookies returns empty array rather than null for no cookies. + Included and improved version of ThreadPool for significant performance improvement under high load. + Included contributed com.mortbay.Jetty.StressTester class + LogHandler changed to support only a single outfile and optional append. + Removed support for STF + Servlet loader handles jar files with different files separator. + ThreadedServer gently shuts down. + Token effort to keep test files out of the jar jetty-2.2.Alpha1 - 07 May 1999 + Call destroy on old servlets when reloading. + Dynamic servlets can have autoReload configured + Fixed bug in SessionDump + Made capitalization of config file more consistent(ish) + ServletHolder can auto reload servlets + Wait for requests to complete before reloading. jetty-2.2.Alpha0 - 06 May 1999 + Added reload method to ServletHolder, but no way to call it yet. + Added ServletLoader implementation if ClassLoader. + Changed options for FileServer + Dynamic loading of servlets. + Fixed date overflow in Cookies + HttpHandlers given setProperties method to configure via Properties. + HttpListener class can be configured + HttpResponse.sendError avoids IllegalStateException + Implemented ServletServer + Improved PropertyTree implementation + Improved SessionDump servlet + Mime suffix mapping can be configured. + New Server class using PropertyTree for configuration + Old Jetty.Server class renamed to Jetty.Server21 + Removed historic API from sessions + Removed SimpleServletServer jetty-2.1.7 - 22 April 1999 + Fixed showstopper bug with getReader and getWriter in requests and responses. + HttpFilter uses package interface to get HttpOutputStream jetty-2.1.6 - 21 April 1999 + Added additional date formats for HttpHeader.getDateHeader + New simpler version of PropertyTree + Reduced initial size of most hashtables to reduce default memory overheads. + Return EOF from HttpInputStream that has a content length. + Throw IllegalStateException as required from gets of input/output/reader/writer in requests/responses. + Updated PropertyTreeEditor jetty-2.1.5 - 15 April 1999 + Added setType methods to com.mortbay.FTP.Ftp + Fixed alignment bug in TableForm + Fixed bug in ServletDispatch for null pathInfo + Fixed bugs with invalid sessions + Form parameters protected against multiple decodes when redirected. + HtmlFilter now expands to the URL encoded session if required. + Implemented HttpRequest.getReader() + Instrumented most of the demo to support URL session encoding. + Moved SessionHandler to front of stacks + Page factory requires response for session encoding + Reduced session memory overhead of sessions + Removed RFCs from package + Servlet log has been diverted to com.mortbay.Base.Log.event() Thus debug does not need to be turned on to see servlet logs. + Session URL encoding fixed for relative URLs. jetty-2.1.4 - 26 March 1999 + fixed bug in getRealPath + Fixed problem compiling PathMap under some JDKs. + getPathTranslated now call getRealPath with pathInfo (as per spec). + HttpRequest attributes implemented. + pathInfo returns null for zero length pathInfo (as per spec). Sorry if this breaks your servlets - it is a pain! + Reduced HTML dependence in HTTP package to allow minimal configuration + Session max idle time implemented. + Tightened license agreement so that binary distributions are required to include the license file. jetty-2.1.3 - 19 March 1999 + Added support for suffixes to PathMap + Included GNUJSP implementation of Java Server Pages + Use Java2 javadoc jetty-2.1.2 - 09 March 1999 + API documentation for JSDK 2.1.1 + Cascading style sheet HTML element added. + Converted most servlets to HttpServlets using do Methods. + Fixed trailing / bug in FileHandler (again!). + JSDK 2.1.1 jetty-2.1.1 - 05 March 1999 + com.mortbay.Base.DateCache class added and used to speed date handling. + Fast char buffer handling in HttpInputStream + Faster version of HttpHeader.read() + Faster version of HttpInputStream.readLine(). + Faster version of HttpRequest + Handle '.' in configured paths (temp fix until PropertyTrees) + Reduced number of calls to getRemoteHost for optimization + Size all StringBuffers jetty-2.1.0 - 22 February 1999 + Deprecated com.mortbay.Util.STF + getServlet methods return null. + image/jpg -> image/jpeg + PropertyTrees (see new Demo page) + ServletDispatch (see new Demo page) + Session URL Encoding jetty-2.1.B1 - 13 February 1999 + Added video/quicktime to default MIME types. + Fixed bug with if-modified-since in FileHandler + Fixed bug with MultipartRequest. + Implemented getResource and getResourceAsStream (NOT Tested!). + Implemented Handler translations and getRealPath. + Implemented RequestDispatcher (NOT Tested!). + Improved handling of File.separator in FileHandler. + Replace package com.mortbay.Util.Gateway with class com.mortbay.Util.InetGateway + Updated DefaultExceptionHandler. + Updated InetAddrPort. + Updated URI. jetty-2.1.B0 - 30 January 1999 + Added plug gateway classes com.mortbay.Util.Gateway + Added support for PUT, MOVE, DELETE in FileHandler + FileHandler now sets content length. + Fixed command line bug with SimpleServletConfig + Minor changes to support MS J++ and its non standard language extensions - MMMmmm should have left it unchanged! + Uses JSDK2.1 API, but not all methods implemented. jetty-2.0.5 - 15 December 1998 + added getHeaderNoParams + Temp fix to getCharacterEncoding jetty-2.0.4 - 10 December 1998 + Implement getCharacterEncoding + Improved default Makefile behaviour + Improved error code returns + Portability issues solved for Apple's + Removed MORTBAY_HOME support from Makefiles + Use real release of JSDK2.0 (rather than beta). jetty-2.0.3 - 13 November 1998 + Fix bug with index files for Jetty.Server. Previously servers configured with com.mortbay.Jetty.Server would not handle index.html files. Need to make this configurable in the prp file. + Fixed errors in README file: com.mortbay.Jetty.Server was called com.mortbay.HTTP.Server + Limit threads in ThreadedServer and low priority listener option greatly improve performance under worse case loads. jetty-2.0.2 - 01 November 1998 + Add thread pool to threaded server for significant performance improvement. + Buffer files during configuration + Buffer HTTP Response headers. + Use JETTY_HOME rather than MORTBAY_HOME for build environment jetty-2.0.1 - 27 October 1998 + Released under an Open Source license. jetty-2.0.0 - 25 October 1998 + Added multipart/form-data demo. + Fixed Code.formatObject handling of null objects. + Removed Chat demo (too many netscape dependencies). + Removed exceptional case from FileHandler redirect. jetty-2.0.Beta3 - 29 September 1998 + Added com.mortbay.HTTP.MultiPartRequest to handle file uploads + Added com.mortbay.Jetty.Server (see README.Jetty) + Demo converted to an instance of com.mortbay.Jetty.Server + Fixed Log Handler again. + Ignore exception from HttpListener + Properly implemented multiple listening addresses + Send 301 for directories without trailing / in FileHandler jetty-2.0Beta2 - 01 July 1998 + Fixed Log Handler for HTTP/1.1 + Slight improvement in READMEEs jetty-2.0Beta1 - 01 June 1998 + Fixed bug with calls to service during initialization of servlet + Handle full URLs in HTTP requests (to some extent) + Improved performance of Code.debug() calls, significantly in the case of non matching debug patterns. + Improved performance with special asciiToLowerCase + Provided addSection on com.mortbay.HTML.Page + Provided reset on com.mortbay.HTML.Composite. + Proxy demo in different server instance + Warn if MSIE used for multi part MIME. jetty-2.0Alpha2 - 01 May 1998 + Added date format to Log + Added timezone to Log + Handle params in getIntHeader and getDateHeader + Handle Single Threaded servlets with servlet pool + JDK1.2 javax.servlet API + Removed HttpRequest.getByteContent + Use javax.servlet.http.Cookie + Use javax.servlet.http.HttpSession + Use javax.servlet.http.HttpUtils.parsePostData jetty-1.3.5 - 01 May 1998 + Added date format to Log + Correct handling of multiple parameters + Debug triggers added to com.mortbay.Base.Code + Fixed socket inet bug in FTP jetty-2.0Alpha1 - 08 April 1998 + accept chunked data + Add HTTP/1.1 Date: header + Correct formatting of Date HTTP headers + Debug triggers added to com.mortbay.Base.Code + Fixed forward bug with no port number + handle extra spaces in HTTP headers + Handle file requests with If-Modified-Since: or If-Unmodified-Since: + Handle HEAD properly + Handle HTTP/1.1 Host: header + HttpTests test harness + persistent connections + Really fixed handling of multiple parameters + Removed HttpRequestHeader class + Requires Host: header for 1.1 requests + Send 100 Continue for HTTP/1.1 requests (concerned about push???) + Send Connection: close + Sends chunked data for 1.1 responses of unknown length. jetty-1.3.4 - 15 March 1998 + Dump servlet enhanced to exercise these changes. + Fixed handling of multiple parameters in query and form content. "?A=1%2C2&A=C%2CD" now returns two values ("1,2" & "C,D") rather than 4. + ServletHandler now takes an optional file base directory name which is used to set the translated path for pathInfo in servlet requests. jetty-1.3.3 + Closed exception window in HttpListener.java + Fixed TableForm.addButtonArea bug. + TableForm.extendRow() uses existing cell jetty-1.3.2 + Added per Table cell composite factories + Fixed proxy bug with no port number jetty-1.3.1 + Better handling of InvocationTargetException in debug + ForwardHandler only forwards as http/1.0 (from Tobias.Miller) + Improved parsing of stack traces + Minor fixes in SmtpMail + Minor release adjustments for Tracker jetty-1.3.0 + Added DbAdaptor to JDBC wrappers + Beta release of Tracker jetty-1.2.0 + Alternate look and feel for Jetty + Better Debug configuration + DebugServlet + Fixed install bug for nested classes + Reintroduced STF jetty-1.1.1 + Improved documentation jetty-1.1 + Improved connection caching in java.mortbay.JDBC + Moved HttpCode to com.mortbay.Util jetty-1.0.1 + Bug fixes jetty-1.0 + First release in com.mortbay package structure + Included Util, JDBC, HTML, HTTP, Jetty jetty-9.2.14.v20151106/advisories/000077500000000000000000000000001261716203600162065ustar00rootroot00000000000000jetty-9.2.14.v20151106/advisories/2015-02-24-httpparser-error-buffer-bleed.md000066400000000000000000000067361261716203600254360ustar00rootroot00000000000000HttpParser Error Buffer Bleed Vulnerability =========================================== Published Date: --------------- 2015, Feb 24 CVE: ---- CVE-2015-2080 Discovered and Reported By: --------------------------- [Gotham Digital Science](http://www.gdssecurity.com/) and Stephen Komal. [JetLeak Vulnerability Remote Leakage of Shared Buffers in Jetty / blogs.gdsecurity.com](http://blog.gdssecurity.com/labs/2015/2/25/jetleak-vulnerability-remote-leakage-of-shared-buffers-in-je.html) Affected Versions of Jetty: --------------------------- * 9.2.3.v20140905 * 9.2.4.v20141103 * 9.2.5.v20141112 * 9.2.6.v20141205 * 9.2.7.v20150116 * 9.2.8.v20150217 * 9.3.0.M0 * 9.3.0.M1 Versions of Jetty Containing Fix: -------------------------------- * 9.2.9.v20150224 Patched version of jetty-http.jar: ---------------------------------- Patched version of the affected jetty-http jars are available as attachments on https://bugs.eclipse.org/460642 Statement: ---------- Jetty versions 9.2.3.v20140905 through 9.2.8.v20150217 have a ByteBuffer reuse and information bleed vulnerability surrounding bad HTTP request header parsing error responses. History: -------- Back in Jetty 9.2.3, a feature requesting more detailed logging messages surrounding problems parsing bad HTTP request headers ( https://bugs.eclipse.org/443049 ) was implemented. The feature request was to include better debug information in the Jetty logs (at WARN level) to help diagnose and resolve HTTP parsing errors. However, the implementation incorrectly exposes this debug information back on the HTTP 400 response reason phrase, potentially exposing parts of server side buffers used from prior request processing on the same server. The following bash shell script demonstrates the problem using netcat on linux against the Jetty Distribution's demo-base. ``` #!/bin/bash RESOURCEPATH="/test/dump/info" BAD=$'\a' function normalRequest { echo "-- Normal Request --" nc localhost 8080 << NORMREQ POST $RESOURCEPATH HTTP/1.1 Host: localhost Content-Type: application/x-www-form-urlencoded;charset=utf-8 Connection: close Content-Length: 16 Username=Joakim NORMREQ } function badCookie { echo "-- Bad Cookie --" nc localhost 8080 << BADCOOKIE GET $RESOURCEPATH HTTP/1.1 Host: localhost Coo${BAD}kie: ${BAD} BADCOOKIE } normalRequest echo "" echo "" badCookie ``` The results are often seen in the HTTP response such as ... ``` HTTP/1.1 400 Illegal character 0x7 in state=HEADER_IN_NAME in 'GET /dummy/ HTTP/... localhost\nCoo\x07<<>>e: application/x-...\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' Content-Length: 0 Connection: close Server: Jetty(9.2.8.v20150217) ``` What you are seeing is a http response phrase that includes raw ByteBuffer details on what happened during the parsing failure. The parts of the output are in the general form `{what_has_been_parsed}<<<{left_to_parse}>>>{old_buffer_seen_past_limit}` The part at `{old_buffer_seen_past_limit}` is where this exposure of past buffers comes from. It is this information where an exploit could be made to present random prior buffers from the server buffer pool. This information can contain anything seen in a past handled request. We have this problem already patched in Jetty 9.2.9.v20150224, and the same test as above results in ... ``` HTTP/1.1 400 Illegal character 0x7 Content-Length: 0 Connection: close Server: Jetty(9.2.9.v20150224) ``` Everyone is strongly encouraged to upgrade to Jetty 9.2.9.v20150224 immediately. jetty-9.2.14.v20151106/aggregates/000077500000000000000000000000001261716203600161475ustar00rootroot00000000000000jetty-9.2.14.v20151106/aggregates/jetty-all/000077500000000000000000000000001261716203600200545ustar00rootroot00000000000000jetty-9.2.14.v20151106/aggregates/jetty-all/pom.xml000066400000000000000000000177411261716203600214030ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 ../../pom.xml 4.0.0 org.eclipse.jetty.aggregate jetty-all Jetty :: Aggregate :: All core Jetty http://www.eclipse.org/jetty ${project.build.directory}/sources org.apache.maven.plugins maven-dependency-plugin unpack-dependencies unpack-dependencies **/MANIFEST.MF,javax/** javax javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm ${project.build.directory}/classes false true unpack-source generate-sources unpack-dependencies sources **/* META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation* org.eclipse.jetty,org.eclipse.jetty.websocket javax javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm ${project.build.directory}/sources true true org.apache.maven.plugins maven-jar-plugin package package jar development http://eclipse.org/jetty ${user.name} org.eclipse.jetty http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/NOTICE.txt Jetty org.apache.maven.plugins maven-javadoc-plugin javadoc-jar compile jar org.apache.maven.plugins maven-pmd-plugin true org.eclipse.jetty jetty-client ${project.version} provided org.eclipse.jetty jetty-deploy ${project.version} provided org.eclipse.jetty.websocket websocket-servlet ${project.version} provided org.eclipse.jetty.websocket javax-websocket-server-impl ${project.version} provided org.eclipse.jetty.websocket websocket-client ${project.version} provided org.eclipse.jetty.spdy spdy-http-server ${project.version} provided org.eclipse.jetty jetty-jmx ${project.version} provided org.eclipse.jetty jetty-plus ${project.version} provided org.eclipse.jetty jetty-annotations ${project.version} provided org.eclipse.jetty jetty-util ${project.version} provided org.eclipse.jetty jetty-jaspi ${project.version} provided org.eclipse.jetty jetty-jndi ${project.version} provided org.eclipse.jetty jetty-rewrite ${project.version} provided org.eclipse.jetty jetty-servlets ${project.version} provided org.eclipse.jetty jetty-quickstart ${project.version} provided javax.websocket javax.websocket-api compile javax.servlet javax.servlet-api compile javax.transaction javax.transaction-api compile true org.eclipse.jetty.orbit javax.mail.glassfish compile true org.slf4j slf4j-api compile true jetty-9.2.14.v20151106/aggregates/jetty-websocket-all/000077500000000000000000000000001261716203600220405ustar00rootroot00000000000000jetty-9.2.14.v20151106/aggregates/jetty-websocket-all/pom.xml000066400000000000000000000134371261716203600233650ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.1.0-SNAPSHOT ../../pom.xml 4.0.0 org.eclipse.jetty.aggregate jetty-websocket-all Jetty :: Aggregate :: All WebSocket Server + Client Classes http://www.eclipse.org/jetty ${project.build.directory}/sources org.apache.maven.plugins maven-dependency-plugin unpack-dependencies unpack-dependencies **/MANIFEST.MF org.slf4j,org.eclipse.jetty.orbit,org.mortbay.jetty.npn ${project.build.directory}/classes false true unpack-source generate-sources unpack-dependencies sources **/* META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation* org.eclipse.jetty,org.eclipse.jetty.websocket javax javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn ${project.build.directory}/sources true true org.apache.maven.plugins maven-jar-plugin package package jar development http://eclipse.org/jetty ${user.name} org.eclipse.jetty http://git.eclipse.org/c/jetty/org.eclipse.jetty.project.git/tree/NOTICE.txt Jetty org.apache.maven.plugins maven-javadoc-plugin javadoc-jar compile jar org.apache.maven.plugins maven-pmd-plugin true org.eclipse.jetty.websocket websocket-servlet ${project.version} provided org.eclipse.jetty.websocket javax-websocket-server-impl ${project.version} provided org.eclipse.jetty.websocket websocket-client ${project.version} provided org.eclipse.jetty jetty-plus ${project.version} provided org.eclipse.jetty jetty-annotations ${project.version} provided org.eclipse.jetty jetty-util ${project.version} provided javax.websocket javax.websocket-api compile javax.servlet javax.servlet-api compile org.slf4j slf4j-api runtime true jetty-9.2.14.v20151106/apache-jsp/000077500000000000000000000000001261716203600160515ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/pom.xml000066400000000000000000000101441261716203600173660ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 apache-jsp Jetty :: Apache JSP Implementation http://www.eclipse.org/jetty jar ${project.groupId}.${project.artifactId} org.apache.felix maven-bundle-plugin true generate-manifest manifest Jetty-specific ServletContainerInitializer for Jasper org.eclipse.jetty.apache.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}", org.eclipse.jetty.jsp.*;version="${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}" osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" osgi.serviceloader; osgi.serviceloader=javax.servlet.ServletContainerInitializer <_nouses>true org.apache.maven.plugins maven-jar-plugin artifact-jar jar test-jar test-jar ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.apache.maven.plugins maven-assembly-plugin package single config org.eclipse.jetty jetty-util ${project.version} org.eclipse.jetty jetty-server ${project.version} org.eclipse.jetty.toolchain jetty-schemas javax.servlet javax.servlet-api org.mortbay.jasper apache-jsp org.eclipse.jetty.orbit org.eclipse.jdt.core jetty-9.2.14.v20151106/apache-jsp/src/000077500000000000000000000000001261716203600166405ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/000077500000000000000000000000001261716203600175645ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/config/000077500000000000000000000000001261716203600210315ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/config/modules/000077500000000000000000000000001261716203600225015ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/config/modules/jsp-impl/000077500000000000000000000000001261716203600242345ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/config/modules/jsp-impl/apache-jsp.mod000066400000000000000000000001061261716203600267450ustar00rootroot00000000000000# # Apache JSP Module # [name] jsp-impl [lib] lib/apache-jsp/*.jar jetty-9.2.14.v20151106/apache-jsp/src/main/java/000077500000000000000000000000001261716203600205055ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/000077500000000000000000000000001261716203600212745ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/000077500000000000000000000000001261716203600227205ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600240575ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/apache/000077500000000000000000000000001261716203600253005ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/000077500000000000000000000000001261716203600260745ustar00rootroot00000000000000JettyJasperInitializer.java000066400000000000000000000075711261716203600333420ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.apache.jsp; import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.servlet.ServletContext; import org.apache.jasper.servlet.JasperInitializer; import org.apache.jasper.servlet.TldPreScanned; import org.apache.jasper.servlet.TldScanner; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.xml.sax.SAXException; /** * JettyJasperInitializer * */ public class JettyJasperInitializer extends JasperInitializer { private static final Logger LOG = Log.getLogger(JettyJasperInitializer.class); /** * NullTldScanner * * Does nothing. Used when we can tell that all jsps have been precompiled, in which case * the tlds are not needed. */ private final class NullTldScanner extends TldScanner { /** * @param context * @param namespaceAware * @param validation * @param blockExternal */ private NullTldScanner(ServletContext context, boolean namespaceAware, boolean validation, boolean blockExternal) { super(context, namespaceAware, validation, blockExternal); } /** * @see org.apache.jasper.servlet.TldScanner#scan() */ @Override public void scan() throws IOException, SAXException { return; //do nothing } /** * @see org.apache.jasper.servlet.TldScanner#getListeners() */ @Override public List getListeners() { return Collections.emptyList(); } /** * @see org.apache.jasper.servlet.TldScanner#scanJars() */ @Override public void scanJars() { return; //do nothing } } /** * Make a TldScanner, and prefeed it the tlds that have already been discovered in jar files * by the MetaInfConfiguration. * * @see org.apache.jasper.servlet.JasperInitializer#prepareScanner(javax.servlet.ServletContext, boolean, boolean, boolean) */ @Override public TldScanner newTldScanner(ServletContext context, boolean namespaceAware, boolean validate, boolean blockExternal) { String tmp = context.getInitParameter("org.eclipse.jetty.jsp.precompiled"); if (tmp!=null && !tmp.equals("") && Boolean.valueOf(tmp)) { if (LOG.isDebugEnabled()) LOG.debug("Jsp precompilation detected"); return new NullTldScanner(context, namespaceAware, validate, blockExternal); } Collection tldUrls = (Collection)context.getAttribute("org.eclipse.jetty.tlds"); if (tldUrls != null) { if (LOG.isDebugEnabled()) LOG.debug("Tld pre-scan detected"); return new TldPreScanned(context,namespaceAware,validate,blockExternal,tldUrls); } if (LOG.isDebugEnabled()) LOG.debug("Defaulting to jasper tld scanning"); return super.newTldScanner(context, namespaceAware, validate, blockExternal); } } jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/apache/jsp/JuliLog.java000066400000000000000000000117551261716203600303150ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.apache.jsp; public class JuliLog implements org.apache.juli.logging.Log { public static org.apache.juli.logging.Log getInstance(String name) { return new JuliLog(name); } private final org.eclipse.jetty.util.log.Logger _logger; private final org.eclipse.jetty.util.log.StdErrLog _stdErrLog; public JuliLog() { _logger=org.eclipse.jetty.util.log.Log.getRootLogger(); _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null; } public JuliLog(String name) { _logger=org.eclipse.jetty.util.log.Log.getLogger(name); _stdErrLog=(_logger instanceof org.eclipse.jetty.util.log.StdErrLog) ? (org.eclipse.jetty.util.log.StdErrLog)_logger:null; } @Override public boolean isDebugEnabled() { return _logger.isDebugEnabled(); } @Override public boolean isErrorEnabled() { return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; } @Override public boolean isFatalEnabled() { return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; } @Override public boolean isInfoEnabled() { return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_INFO; } @Override public boolean isTraceEnabled() { return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_DEBUG; } @Override public boolean isWarnEnabled() { return _stdErrLog==null?true:_stdErrLog.getLevel()<=org.eclipse.jetty.util.log.StdErrLog.LEVEL_WARN; } @Override public void trace(Object message) { if (message instanceof String) _logger.debug((String)message); else _logger.debug("{}",message); } @Override public void trace(Object message, Throwable t) { if (message instanceof String) _logger.debug((String)message,t); else _logger.debug("{}",message,t); } @Override public void debug(Object message) { if (message instanceof String) _logger.debug((String)message); else _logger.debug("{}",message); } @Override public void debug(Object message, Throwable t) { if (message instanceof String) _logger.debug((String)message,t); else _logger.debug("{}",message,t); } @Override public void info(Object message) { if (message instanceof String) _logger.info((String)message); else _logger.info("{}",message); } @Override public void info(Object message, Throwable t) { if (message instanceof String) _logger.info((String)message,t); else _logger.info("{}",message,t); } @Override public void warn(Object message) { if (message instanceof String) _logger.warn((String)message); else _logger.warn("{}",message); } @Override public void warn(Object message, Throwable t) { if (message instanceof String) _logger.warn((String)message,t); else _logger.warn("{}",message,t); } @Override public void error(Object message) { if (message instanceof String) _logger.warn((String)message); else _logger.warn("{}",message); } @Override public void error(Object message, Throwable t) { if (message instanceof String) _logger.warn((String)message,t); else _logger.warn("{}",message,t); } @Override public void fatal(Object message) { if (message instanceof String) _logger.warn((String)message); else _logger.warn("{}",message); } @Override public void fatal(Object message, Throwable t) { if (message instanceof String) _logger.warn((String)message,t); else _logger.warn("{}",message,t); } } jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/jsp/000077500000000000000000000000001261716203600246535ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/java/org/eclipse/jetty/jsp/JettyJspServlet.java000066400000000000000000000073541261716203600306500ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.jsp; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.jasper.servlet.JspServlet; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; /** * JettyJspServlet * * Wrapper for the jsp servlet that handles receiving requests mapped from * jsp-property-groups. Mappings could be wildcard urls like "/*", which would * include welcome files, but we need those to be handled by the DefaultServlet. */ public class JettyJspServlet extends JspServlet { /** * */ private static final long serialVersionUID = -5387857473125086791L; @Override public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpServletRequest request = null; if (req instanceof HttpServletRequest) request = (HttpServletRequest)req; else throw new ServletException("Request not HttpServletRequest"); String servletPath=null; String pathInfo=null; if (request.getAttribute("javax.servlet.include.request_uri")!=null) { servletPath=(String)request.getAttribute("javax.servlet.include.servlet_path"); pathInfo=(String)request.getAttribute("javax.servlet.include.path_info"); if (servletPath==null) { servletPath=request.getServletPath(); pathInfo=request.getPathInfo(); } } else { servletPath = request.getServletPath(); pathInfo = request.getPathInfo(); } String pathInContext = URIUtil.addPaths(servletPath,pathInfo); String jspFile = getInitParameter("jspFile"); //if this is a forced-path from a jsp-file, we want the jsp servlet to handle it, //otherwise the default servlet might handle it if (jspFile == null) { if (pathInContext.endsWith("/")) { //dispatch via forward to the default servlet getServletContext().getNamedDispatcher("default").forward(req, resp); return; } else { //check if it resolves to a directory Resource resource = ((ContextHandler.Context)getServletContext()).getContextHandler().getResource(pathInContext); if (resource!=null && resource.isDirectory()) { //dispatch via forward to the default servlet getServletContext().getNamedDispatcher("default").forward(req, resp); return; } } } //fall through to the normal jsp servlet handling super.service(req, resp); } } jetty-9.2.14.v20151106/apache-jsp/src/main/resources/000077500000000000000000000000001261716203600215765ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/resources/META-INF/000077500000000000000000000000001261716203600227365ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/resources/META-INF/services/000077500000000000000000000000001261716203600245615ustar00rootroot00000000000000javax.servlet.ServletContainerInitializer000066400000000000000000000000641261716203600347130ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jsp/src/main/resources/META-INF/servicesorg.eclipse.jetty.apache.jsp.JettyJasperInitializer jetty-9.2.14.v20151106/apache-jsp/src/main/resources/META-INF/services/org.apache.juli.logging.Log000066400000000000000000000000451261716203600316210ustar00rootroot00000000000000org.eclipse.jetty.apache.jsp.JuliLog jetty-9.2.14.v20151106/apache-jstl/000077500000000000000000000000001261716203600162315ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/pom.xml000066400000000000000000000026761261716203600175610ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 apache-jstl Apache :: JSTL module http://tomcat.apache.org/taglibs/standard/ jar org.apache.maven.plugins maven-assembly-plugin package single config org.apache.taglibs taglibs-standard-spec org.apache.taglibs taglibs-standard-impl jetty-9.2.14.v20151106/apache-jstl/src/000077500000000000000000000000001261716203600170205ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/000077500000000000000000000000001261716203600177445ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/config/000077500000000000000000000000001261716203600212115ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/config/modules/000077500000000000000000000000001261716203600226615ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/config/modules/jsp-impl/000077500000000000000000000000001261716203600244145ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/config/modules/jsp-impl/apache-jstl.mod000066400000000000000000000001011261716203600273000ustar00rootroot00000000000000# # Apache JSTL # [name] jstl-impl [lib] lib/apache-jstl/*.jar jetty-9.2.14.v20151106/apache-jstl/src/main/resources/000077500000000000000000000000001261716203600217565ustar00rootroot00000000000000jetty-9.2.14.v20151106/apache-jstl/src/main/resources/readme.txt000066400000000000000000000005741261716203600237620ustar00rootroot00000000000000This empty jar file is purely to work around a problem with the Maven Dependency plugin. Several modules in jetty use the Dependency plugin to copy or unpack the dependencies of other modules. However, the Dependency plugin is not capable of unpacking or copying a dependency of type 'pom', which this module is, as it consists purely of external dependencies needed to run jsp. jetty-9.2.14.v20151106/dists/000077500000000000000000000000001261716203600151645ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/000077500000000000000000000000001261716203600170535ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/pom.xml000066400000000000000000000107711261716203600203760ustar00rootroot00000000000000 4.0.0 org.eclipse.jetty.dist dist-parent 9.0.0-SNAPSHOT jetty-deb Jetty :: Unix Distributions :: Debian deb org.mortbay.jetty.toolchain unix-maven-plugin 1.0-alpha-6.1 true Jetty Project jetty-dev@eclipse.org Core Jetty ${project.version} Distribution Jetty provides an Web server and javax.servlet container, plus support for Web Sockets, OSGi, JMX, JNDI, JASPI, AJP and many other integrations. These components are open source and available for commercial use and distribution. false optional
java
jetty-server org.eclipse.jetty:jetty-distribution:zip /usr/share/jetty9 /jetty-distribution-${project.version}(.*) $1 jetty-distribution-*/javadoc jetty-distribution-*/javadoc/** jetty-distribution-*/logs/** jetty-distribution-*/bin/** jetty-distribution-*/etc/** jetty-distribution-*/webapps/** jetty-distribution-*/*.html jetty-distribution-*/*.txt org.eclipse.jetty:jetty-distribution:zip /usr/share/doc/jetty9 /jetty-distribution-${project.version}(.*) $1 jetty-distribution-*/*.html jetty-distribution-*/*.txt jetty-distribution-*/** org.eclipse.jetty:jetty-distribution:zip /etc/jetty9 /jetty-distribution-${project.version}(.*) $1 jetty-distribution-*/etc/** jetty-test-webapp test-webapp org.eclipse.jetty:jetty-distribution:zip /var/lib/jetty9/webapps /jetty-distribution-${project.version}(.*) $1 jetty-distribution-*/webapps/**
org.eclipse.jetty jetty-distribution ${project.version} zip
jetty-9.2.14.v20151106/dists/jetty-deb/src/000077500000000000000000000000001261716203600176425ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/src/main/000077500000000000000000000000001261716203600205665ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/src/main/unix/000077500000000000000000000000001261716203600215515ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/src/main/unix/scripts/000077500000000000000000000000001261716203600232405ustar00rootroot00000000000000jetty-9.2.14.v20151106/dists/jetty-deb/src/main/unix/scripts/postinst000066400000000000000000000006331261716203600250500ustar00rootroot00000000000000#!/bin/bash LOG_DIR=/var/lib/jetty9/logs WEBAPP_DIR=/var/lib/jetty9/webapps # copy the jetty start script into place cp /usr/share/jetty9/bin/jetty.sh /etc/init.d/jetty # make it generally executable chmod 755 /etc/init.d/jetty # ensure we have a logging directory if [ ! -d "$LOG_DIR" ]; then mkdir $LOG_DIR fi # ensure we have a webapps directory if [ ! -d "$WEBAPP_DIR" ]; then mkdir $WEBAPP_DIR fi jetty-9.2.14.v20151106/dists/jetty-deb/src/main/unix/scripts/postrm000066400000000000000000000027101261716203600245070ustar00rootroot00000000000000#!/bin/bash #rm -f /etc/init.d/jetty case "$1" in purge) [...] # find first and last SYSTEM_UID numbers for LINE in `grep SYSTEM_UID /etc/adduser.conf | grep -v "^#"`; do case $LINE in FIRST_SYSTEM_UID*) FIST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='` ;; LAST_SYSTEM_UID*) LAST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='` ;; *) ;; esac done # Remove system account if necessary CREATEDUSER="jetty" if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then if USERID=`getent passwd $CREATEDUSER | cut -f 3 -d ':'`; then if [ -n "$USERID" ]; then if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \ [ "$USERID" -le "$LAST_SYSTEM_UID" ]; then echo -n "Removing $CREATEDUSER system user.." deluser --quiet $CREATEDUSER || true echo "..done" fi fi fi fi # Remove system group if necessary CREATEDGROUP="jetty" FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='` if [ -n "$FIST_USER_GID" ] then if GROUPGID=`getent group $CREATEDGROUP | cut -f 3 -d ':'`; then if [ -n "$GROUPGID" ]; then if [ "$FIST_USER_GID" -gt "$GROUPGID" ]; then echo -n "Removing $CREATEDGROUP group.." delgroup --only-if-empty $CREATEDGROUP || true echo "..done" fi fi fi fijetty-9.2.14.v20151106/dists/jetty-deb/src/main/unix/scripts/preinst000066400000000000000000000035371261716203600246570ustar00rootroot00000000000000#!/bin/bash case "$1" in install|upgrade) # If the package has default file it could be sourced, so that # the local admin can overwrite the defaults [ -f "/etc/default/jetty9" ] && . /etc/default/jetty9 # Sane defaults: [ -z "$SERVER_HOME" ] && SERVER_HOME=/usr/share/jetty9 [ -z "$SERVER_USER" ] && SERVER_USER=jetty [ -z "$SERVER_NAME" ] && SERVER_NAME="Jetty-9 Http and Servlet Engine" [ -z "$SERVER_GROUP" ] && SERVER_GROUP=jetty # Groups that the user will be added to, if undefined, then none. ADDGROUP="" # create user to avoid running server as root # 1. create group if not existing if ! getent group | grep -q "^$SERVER_GROUP:" ; then echo -n "Adding group $SERVER_GROUP.." addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true echo "..done" fi # 2. create homedir if not existing test -d $SERVER_HOME || mkdir $SERVER_HOME # 3. create user if not existing if ! getent passwd | grep -q "^$SERVER_USER:"; then echo -n "Adding system user $SERVER_USER.." adduser --quiet \ --system \ --ingroup $SERVER_GROUP \ --no-create-home \ --disabled-password \ $SERVER_USER 2>/dev/null || true echo "..done" fi # 4. adjust passwd entry usermod -c "$SERVER_NAME" \ -d $SERVER_HOME \ -g $SERVER_GROUP \ $SERVER_USER # 5. adjust file and directory permissions if ! dpkg-statoverride --list $SERVER_HOME >/dev/null then chown -R $SERVER_USER:$SERVER_GROUP $SERVER_HOME chmod u=rwx,g=rxs,o= $SERVER_HOME fi # 6. Add the user to the ADDGROUP group if test -n $ADDGROUP then if ! groups $SERVER_USER | cut -d: -f2 | \ grep -qw $ADDGROUP; then adduser $SERVER_USER $ADDGROUP fi fi ;; configure)jetty-9.2.14.v20151106/dists/pom.xml000066400000000000000000000014731261716203600165060ustar00rootroot00000000000000 jetty-project org.eclipse.jetty 9.0.0-SNAPSHOT 4.0.0 org.eclipse.jetty.dist dist-parent pom Jetty :: Distribution :: Parent linux-packaging jetty-deb jetty-9.2.14.v20151106/examples/000077500000000000000000000000001261716203600156545ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/000077500000000000000000000000001261716203600177445ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/000077500000000000000000000000001261716203600226065ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/pom.xml000066400000000000000000000021731261716203600241260ustar00rootroot00000000000000 org.eclipse.jetty example-async-rest 9.2.14.v20151106 4.0.0 org.eclipse.jetty.example-async-rest example-async-rest-jar jar Example Async Rest :: Jar http://www.eclipse.org/jetty org.eclipse.jetty jetty-client ${project.version} org.eclipse.jetty jetty-util-ajax ${project.version} javax.servlet javax.servlet-api provided jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/000077500000000000000000000000001261716203600233755ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/000077500000000000000000000000001261716203600243215ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/000077500000000000000000000000001261716203600252425ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/000077500000000000000000000000001261716203600260315ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/000077500000000000000000000000001261716203600274555ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600306145ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/000077500000000000000000000000001261716203600322475ustar00rootroot00000000000000asyncrest/000077500000000000000000000000001261716203600342035ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/exampleAbstractRestServlet.java000066400000000000000000000102171261716203600410150ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.example.asyncrest; import java.io.IOException; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.URLEncoder; import java.util.Map; import java.util.Queue; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * Abstract Servlet implementation class AsyncRESTServlet. * Enquires ebay REST service for auctions by key word. * May be configured with init parameters:
*
appid
The eBay application ID to use
*
* Each request examines the following request parameters:
*
items
The keyword to search for
*
*/ public class AbstractRestServlet extends HttpServlet { protected final static String __DEFAULT_APPID = "Webtide81-adf4-4f0a-ad58-d91e41bbe85"; protected final static String STYLE = ""; protected final static String ITEMS_PARAM = "items"; protected final static String APPID_PARAM = "appid"; protected String _appid; @Override public void init(ServletConfig servletConfig) throws ServletException { if (servletConfig.getInitParameter(APPID_PARAM) == null) _appid = __DEFAULT_APPID; else _appid = servletConfig.getInitParameter(APPID_PARAM); } public static String sanitize(String s) { if (s==null) return null; return s.replace("<","?").replace("&","?").replace("\n","?"); } protected String restURL(String item) { try { return ("http://open.api.ebay.com/shopping?MaxEntries=3&appid=" + _appid + "&version=573&siteid=0&callname=FindItems&responseencoding=JSON&QueryKeywords=" + URLEncoder.encode(item,"UTF-8")); } catch(Exception e) { throw new RuntimeException(e); } } protected String generateThumbs(Queue> results) { StringBuilder thumbs = new StringBuilder(); for (Map m : results) { if (!m.containsKey("GalleryURL")) continue; thumbs.append(""); thumbs.append(""); thumbs.append(" "); } return thumbs.toString(); } protected String ms(long nano) { BigDecimal dec = new BigDecimal(nano); return dec.divide(new BigDecimal(1000000L)).setScale(1,RoundingMode.UP).toString(); } protected int width(long nano) { int w=(int)((nano+999999L)/5000000L); if (w==0) w=2; return w; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } AsyncRestServlet.java000066400000000000000000000162141261716203600403320ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.example.asyncrest; import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteBuffer; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.AsyncContext; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.ajax.JSON; /** * Servlet implementation class AsyncRESTServlet. * Enquires ebay REST service for auctions by key word. * May be configured with init parameters:
*
appid
The eBay application ID to use
*
* Each request examines the following request parameters:
*
items
The keyword to search for
*
*/ public class AsyncRestServlet extends AbstractRestServlet { final static String RESULTS_ATTR = "org.eclipse.jetty.demo.client"; final static String DURATION_ATTR = "org.eclipse.jetty.demo.duration"; final static String START_ATTR = "org.eclispe.jetty.demo.start"; HttpClient _client; @Override public void init(ServletConfig servletConfig) throws ServletException { super.init(servletConfig); _client = new HttpClient(); try { _client.start(); } catch (Exception e) { throw new ServletException(e); } } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { Long start=System.nanoTime(); // Do we have results yet? Queue> results = (Queue>) request.getAttribute(RESULTS_ATTR); // If no results, this must be the first dispatch, so send the REST request(s) if (results==null) { // define results data structures final Queue> resultsQueue = new ConcurrentLinkedQueue<>(); request.setAttribute(RESULTS_ATTR, results=resultsQueue); // suspend the request // This is done before scheduling async handling to avoid race of // dispatch before startAsync! final AsyncContext async = request.startAsync(); async.setTimeout(30000); // extract keywords to search for String[] keywords=sanitize(request.getParameter(ITEMS_PARAM)).split(","); final AtomicInteger outstanding=new AtomicInteger(keywords.length); // Send request each keyword for (final String item:keywords) { _client.newRequest(restURL(item)).method(HttpMethod.GET).send( new AsyncRestRequest() { @Override void onAuctionFound(Map auction) { resultsQueue.add(auction); } @Override void onComplete() { if (outstanding.decrementAndGet()<=0) async.dispatch(); } }); } // save timing info and return request.setAttribute(START_ATTR, start); request.setAttribute(DURATION_ATTR, System.nanoTime() - start); return; } // We have results! // Generate the response String thumbs = generateThumbs(results); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println(""); out.println(STYLE); out.println(""); long initial = (Long) request.getAttribute(DURATION_ATTR); long start0 = (Long) request.getAttribute(START_ATTR); long now = System.nanoTime(); long total=now-start0; long generate=now-start; long thread=initial+generate; out.print("Asynchronous: "+sanitize(request.getParameter(ITEMS_PARAM))+"
"); out.print("Total Time: "+ms(total)+"ms
"); out.print("Thread held (red): "+ms(thread)+"ms (" + ms(initial) + " initial + " + ms(generate) + " generate )
"); out.print("Async wait (green): "+ms(total-thread)+"ms
"); out.println(""+ ""+ ""); out.println("
"); out.println(thumbs); out.println("
"); out.println(""); out.close(); } private abstract class AsyncRestRequest extends Response.Listener.Adapter { final Utf8StringBuilder _content = new Utf8StringBuilder(); AsyncRestRequest() { } @Override public void onContent(Response response, ByteBuffer content) { byte[] bytes = BufferUtil.toArray(content); _content.append(bytes,0,bytes.length); } @Override public void onComplete(Result result) { // extract auctions from the results Map query = (Map) JSON.parse(_content.toString()); Object[] auctions = (Object[]) query.get("Item"); if (auctions != null) { for (Object o : auctions) onAuctionFound((Map)o); } onComplete(); } abstract void onAuctionFound(Map details); abstract void onComplete(); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } SerialRestServlet.java000066400000000000000000000070301261716203600404700ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.example.asyncrest; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.URL; import java.util.LinkedList; import java.util.Map; import java.util.Queue; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.util.ajax.JSON; /** * Servlet implementation class SerialRestServlet */ public class SerialRestServlet extends AbstractRestServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long start = System.nanoTime(); String[] keywords=sanitize(request.getParameter(ITEMS_PARAM)).split(","); Queue> results = new LinkedList>(); // make all requests serially for (String itemName : keywords) { URL url = new URL(restURL(itemName)); HttpURLConnection connection = (HttpURLConnection)url.openConnection(); connection.setRequestMethod("GET"); Map query = (Map)JSON.parse(new BufferedReader(new InputStreamReader(connection.getInputStream()))); Object[] auctions = (Object[]) query.get("Item"); if (auctions != null) { for (Object o : auctions) results.add((Map) o); } } // Generate the response String thumbs=generateThumbs(results); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println(""); out.println(STYLE); out.println(""); long now = System.nanoTime(); long total=now-start; out.print("Blocking: "+sanitize(request.getParameter(ITEMS_PARAM))+"
"); out.print("Total Time: "+ms(total)+"ms
"); out.print("Thread held (red): "+ms(total)+"ms
"); out.println(""); out.println("
"); out.println(thumbs); out.println("
"); out.println(""); out.close(); } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } } jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/000077500000000000000000000000001261716203600263335ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/000077500000000000000000000000001261716203600274735ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/resources/000077500000000000000000000000001261716203600315055ustar00rootroot00000000000000asyncrest.html000066400000000000000000000015311261716203600343270ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/resources

Blocking vs Asynchronous REST

This demo calls the EBay WS API both synchronously and asynchronously, to obtain items matching each of the keywords passed on the query string. The time the request thread is head is displayed for both.

asyncrest/000077500000000000000000000000001261716203600334415ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/resourcesgreen.png000066400000000000000000000002461261716203600352510ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrestPNG  IHDRKm)sRGB pHYs  tIME1)sX+ztEXtCommentCreated with GIMPWIDATcdπ010 % BIENDB`red.png000066400000000000000000000002441261716203600347210ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF/resources/asyncrestPNG  IHDRKm)sRGB pHYs  tIME1?1;$tEXtCommentCreated with GIMPWIDATcπ01 ) AֆIENDB`web-fragment.xml000066400000000000000000000017751261716203600325260ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-jar/src/main/resources/META-INF SerialRestServlet SerialRestServlet org.eclipse.jetty.example.asyncrest.SerialRestServlet SerialRestServlet /testSerial AsyncRestServlet AsyncRestServlet org.eclipse.jetty.example.asyncrest.AsyncRestServlet true AsyncRestServlet /testAsync jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/000077500000000000000000000000001261716203600233105ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/pom.xml000066400000000000000000000023241261716203600246260ustar00rootroot00000000000000 org.eclipse.jetty example-async-rest 9.2.14.v20151106 4.0.0 org.eclipse.jetty.example-async-rest example-async-rest-webapp war Example Async Rest :: Webapp async-rest org.eclipse.jetty.example-async-rest example-async-rest-jar ${project.version} org.eclipse.jetty jetty-webapp ${project.version} test javax.servlet javax.servlet-api provided jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/000077500000000000000000000000001261716203600240775ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/000077500000000000000000000000001261716203600250235ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/000077500000000000000000000000001261716203600263015ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/META-INF/000077500000000000000000000000001261716203600274415ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/META-INF/MANIFEST.MF000066400000000000000000000000471261716203600310740ustar00rootroot00000000000000Manifest-Version: 1.0 Class-Path: jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/000077500000000000000000000000001261716203600273305ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/jetty-web.xml000066400000000000000000000013161261716203600317650ustar00rootroot00000000000000 async-rest webapp is deployed. DO NOT USE IN PRODUCTION! jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/WEB-INF/web.xml000066400000000000000000000005161261716203600306310ustar00rootroot00000000000000 Async REST Webservice Example jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/main/webapp/index.html000066400000000000000000000024631261716203600303030ustar00rootroot00000000000000

Blocking vs Asynchronous REST

This demo calls the EBay WS API both synchronously and asynchronously, to obtain items matching each of the keywords passed on the query string. The time the request thread is held by the servlet is displayed in red for both.

By the use of Asynchronous Servlets and the Jetty Asychronous client, the server is able to release the thread (green) while waiting for the response from Ebay. This thread goes back into the thread pool and can service many other requests during the wait. This greatly reduces the number of threads needed, which in turn greatly reduces the memory requirements of the server.

Press reload to see even better results after JIT and TCP/IP warmup! jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/000077500000000000000000000000001261716203600250565ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/000077500000000000000000000000001261716203600257775ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/000077500000000000000000000000001261716203600265665ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/000077500000000000000000000000001261716203600302125ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600313515ustar00rootroot00000000000000example/000077500000000000000000000000001261716203600327255ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jettyasyncrest/000077500000000000000000000000001261716203600347405ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/exampleDemoServer.java000066400000000000000000000030021261716203600376510ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/async-rest/async-rest-webapp/src/test/java/org/eclipse/jetty/example/asyncrest// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.example.asyncrest; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; public class DemoServer { public static void main(String[] args) throws Exception { String jetty_home = System.getProperty("jetty.home","."); Server server = new Server(Integer.getInteger("jetty.port",8080).intValue()); WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); webapp.setWar(jetty_home+"/target/async-rest/"); webapp.setParentLoaderPriority(true); webapp.setServerClasses(new String[]{}); server.setHandler(webapp); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/async-rest/pom.xml000066400000000000000000000012761261716203600212670ustar00rootroot00000000000000 org.eclipse.jetty.examples examples-parent 9.2.14.v20151106 ../pom.xml 4.0.0 org.eclipse.jetty example-async-rest pom Example Async Rest async-rest-jar async-rest-webapp jetty-9.2.14.v20151106/examples/embedded/000077500000000000000000000000001261716203600174055ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/pom.xml000066400000000000000000000074331261716203600207310ustar00rootroot00000000000000 org.eclipse.jetty.examples examples-parent 9.2.14.v20151106 ../pom.xml 4.0.0 org.eclipse.jetty example-jetty-embedded Example :: Jetty Embedded Jetty Embedded Examples http://www.eclipse.org/jetty org.eclipse.jetty jetty-util-ajax ${project.version} org.eclipse.jetty jetty-webapp ${project.version} org.eclipse.jetty jetty-security ${project.version} org.eclipse.jetty jetty-servlets ${project.version} org.eclipse.jetty jetty-deploy ${project.version} org.eclipse.jetty jetty-jmx ${project.version} org.eclipse.jetty.websocket javax-websocket-server-impl ${project.version} org.eclipse.jetty.websocket websocket-server ${project.version} org.eclipse.jetty.spdy spdy-http-server ${project.version} org.eclipse.jetty jetty-annotations ${project.version} org.eclipse.jetty.tests test-mock-resources ${project.version} javax.transaction javax.transaction-api org.eclipse.jetty jetty-proxy ${project.version} org.eclipse.jetty jetty-jaas ${project.version} org.eclipse.jetty jetty-plus ${project.version} org.eclipse.jetty apache-jsp ${project.version} org.eclipse.jetty apache-jstl ${project.version} org.eclipse.jetty.orbit javax.mail.glassfish org.eclipse.jetty.toolchain jetty-test-helper jetty-9.2.14.v20151106/examples/embedded/prodDb.properties000066400000000000000000000006431261716203600227400ustar00rootroot00000000000000#HSQL Database Engine 1.8.0.10 #Mon Nov 08 13:35:35 EST 2010 hsqldb.script_format=0 runtime.gc_interval=0 sql.enforce_strict_size=false hsqldb.cache_size_scale=8 readonly=false hsqldb.nio_data_file=true hsqldb.cache_scale=14 version=1.8.0 hsqldb.default_table_type=memory hsqldb.cache_file_scale=1 hsqldb.log_size=200 modified=no hsqldb.cache_version=1.7.0 hsqldb.original_version=1.8.0 hsqldb.compatible_version=1.8.0 jetty-9.2.14.v20151106/examples/embedded/prodDb.script000066400000000000000000000001451261716203600220450ustar00rootroot00000000000000CREATE SCHEMA PUBLIC AUTHORIZATION DBA CREATE USER SA PASSWORD "" GRANT DBA TO SA SET WRITE_DELAY 10 jetty-9.2.14.v20151106/examples/embedded/src/000077500000000000000000000000001261716203600201745ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/000077500000000000000000000000001261716203600211205ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/000077500000000000000000000000001261716203600220415ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/HelloWorld.java000066400000000000000000000040221261716203600247550ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; public class HelloWorld extends AbstractHandler { @Override public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { // Declare response encoding and types response.setContentType("text/html; charset=utf-8"); // Declare response status code response.setStatus(HttpServletResponse.SC_OK); // Write back response response.getWriter().println("

Hello World

"); // Inform jetty that this request has now been handled baseRequest.setHandled(true); } public static void main( String[] args ) throws Exception { Server server = new Server(8080); server.setHandler(new HelloWorld()); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/000077500000000000000000000000001261716203600226305ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/000077500000000000000000000000001261716203600242545ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600254135ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/000077500000000000000000000000001261716203600271445ustar00rootroot00000000000000AsyncEchoServlet.java000066400000000000000000000072701261716203600331570ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.IOException; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.AsyncContext; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.WriteListener; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class AsyncEchoServlet extends HttpServlet { @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { AsyncContext asyncContext = request.startAsync(request, response); asyncContext.setTimeout(0); Echoer echoer = new Echoer(asyncContext); request.getInputStream().setReadListener(echoer); response.getOutputStream().setWriteListener(echoer); } private class Echoer implements ReadListener, WriteListener { private final byte[] buffer = new byte[4096]; private final AsyncContext asyncContext; private final ServletInputStream input; private final ServletOutputStream output; private final AtomicBoolean complete = new AtomicBoolean(false); private Echoer(AsyncContext asyncContext) throws IOException { this.asyncContext = asyncContext; this.input = asyncContext.getRequest().getInputStream(); this.output = asyncContext.getResponse().getOutputStream(); } @Override public void onDataAvailable() throws IOException { onWritePossible(); } @Override public void onAllDataRead() throws IOException { onWritePossible(); } @Override public void onWritePossible() throws IOException { // This method is called: // 1) after first registering a WriteListener (ready for first write) // 2) after first registering a ReadListener iff write is ready // 3) when a previous write completes after an output.isReady() returns false // 4) from an input callback // We should try to read, only if we are able to write! while (output.isReady() && input.isReady()) { int read = input.read(buffer); if (read<0) { if (complete.compareAndSet(false,true)) asyncContext.complete(); break; } else if (read>0) { output.write(buffer, 0, read); } } } @Override public void onError(Throwable failure) { failure.printStackTrace(); asyncContext.complete(); } } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/DumpServlet.java000066400000000000000000000041411261716203600322610ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class DumpServlet extends HttpServlet { @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter out = response.getWriter(); out.println("

DumpServlet

"); out.println("
");
        out.println("requestURI=" + request.getRequestURI());
        out.println("contextPath=" + request.getContextPath());
        out.println("servletPath=" + request.getServletPath());
        out.println("pathInfo=" + request.getPathInfo());
        out.println("session=" + request.getSession(true).getId());

        String r = request.getParameter("resource");
        if (r != null)
        {
            out.println("resource(" + r + ")="
                    + getServletContext().getResource(r));
        }

        out.println("
"); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ExampleServer.java000066400000000000000000000036171261716203600326000ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.servlet.ServletContextHandler; public class ExampleServer { public static void main( String[] args ) throws Exception { Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(8080); server.setConnectors(new Connector[] { connector }); ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/"); context.addServlet(HelloServlet.class, "/hello"); context.addServlet(AsyncEchoServlet.class, "/echo/*"); HandlerCollection handlers = new HandlerCollection(); handlers.setHandlers(new Handler[] { context, new DefaultHandler() }); server.setHandler(handlers); server.start(); server.join(); } } ExampleServerXml.java000066400000000000000000000027551261716203600332040ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; /** * Configures and Starts a Jetty server from an XML declaration. *

* See exampleserver.xml *

*/ public class ExampleServerXml { public static void main( String[] args ) throws Exception { // Find Jetty XML (in classpath) that configures and starts Server. Resource serverXml = Resource.newSystemResource("exampleserver.xml"); XmlConfiguration.main(serverXml.getFile().getAbsolutePath()); } } FastFileServer.java000066400000000000000000000164321261716203600326220ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileChannel.MapMode; import java.nio.file.StandardOpenOption; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpOutput; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; /** * Fast FileServer. *

* This example shows how to use the Jetty APIs for sending static as fast as * possible using various strategies for small, medium and large content. *

*

* The Jetty {@link DefaultServlet} does all this and more, and to a lesser * extent so does the {@link ResourceHandler}, so unless you have exceptional * circumstances it is best to use those classes for static content *

*/ public class FastFileServer { public static void main( String[] args ) throws Exception { Server server = new Server(8080); HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { new FastFileHandler(new File(System.getProperty("user.dir"))), new DefaultHandler() }); server.setHandler(handlers); server.start(); server.join(); } static class FastFileHandler extends AbstractHandler { private final MimeTypes mimeTypes = new MimeTypes(); private final File dir; private FastFileHandler( File dir ) { this.dir = dir; } @Override public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { // define small medium and large. // This should be turned for your content, JVM and OS, but we will // huge HTTP response buffer size as a measure final int SMALL = response.getBufferSize(); final int MEDIUM = 8 * SMALL; // What file to serve? final File file = new File(this.dir, request.getPathInfo()); // Only handle existing files if (!file.exists()) return; // we will handle this request baseRequest.setHandled(true); // Handle directories if (file.isDirectory()) { if (!request.getPathInfo().endsWith(URIUtil.SLASH)) { response.sendRedirect(response.encodeRedirectURL(URIUtil .addPaths(request.getRequestURI(), URIUtil.SLASH))); return; } String listing = Resource.newResource(file).getListHTML( request.getRequestURI(), request.getPathInfo().lastIndexOf("/") > 0); response.setContentType("text/html; charset=utf-8"); response.getWriter().println(listing); return; } // Set some content headers. // Jetty DefaultServlet will cache formatted date strings, but we // will reformat for each request here response.setDateHeader("Last-Modified", file.lastModified()); response.setDateHeader("Content-Length", file.length()); response.setContentType(mimeTypes.getMimeByExtension(file.getName())); // send "small" files blocking directly from an input stream if (file.length() < SMALL) { // need to caste to Jetty output stream for best API ((HttpOutput) response.getOutputStream()) .sendContent(FileChannel.open(file.toPath(), StandardOpenOption.READ)); return; } // send not "small" files asynchronously so we don't hold threads if // the client is slow final AsyncContext async = request.startAsync(); Callback completionCB = new Callback() { @Override public void succeeded() { // Async content write succeeded, so complete async response async.complete(); } @Override public void failed( Throwable x ) { // log error and complete async response; x.printStackTrace(); async.complete(); } }; // send "medium" files from an input stream if (file.length() < MEDIUM) { // the file channel is closed by the async send ((HttpOutput) response.getOutputStream()) .sendContent(FileChannel.open(file.toPath(), StandardOpenOption.READ), completionCB); return; } // for "large" files get the file mapped buffer to send Typically // the resulting buffer should be cached as allocating kernel memory // can be hard to GC on some JVMs. But for this example we will // create a new buffer per file ByteBuffer buffer; try ( RandomAccessFile raf = new RandomAccessFile(file, "r"); ) { buffer = raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()); } // Assuming the file buffer might be shared cached version, so lets // take our own view of it buffer = buffer.asReadOnlyBuffer(); // send the content as a buffer with a callback to complete the // async request need to caste to Jetty output stream for best API ((HttpOutput) response.getOutputStream()).sendContent(buffer, completionCB); } } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServer.java000066400000000000000000000054031261716203600320570ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.ResourceHandler; /** * Simple Jetty FileServer. * This is a simple example of Jetty configured as a FileServer. */ public class FileServer { public static void main(String[] args) throws Exception { // Create a basic Jetty server object that will listen on port 8080. Note that if you set this to port 0 // then a randomly available port will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. Server server = new Server(8080); // Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is // a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples. ResourceHandler resource_handler = new ResourceHandler(); // Configure the ResourceHandler. Setting the resource base indicates where the files should be served out of. // In this example it is the current directory but it can be configured to anything that the jvm has access to. resource_handler.setDirectoriesListed(true); resource_handler.setWelcomeFiles(new String[]{ "index.html" }); resource_handler.setResourceBase("."); // Add the ResourceHandler to the server. HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() }); server.setHandler(handlers); // Start things up! By using the server.join() the server thread will join with the current thread. // See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details. server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/FileServerXml.java000066400000000000000000000033301261716203600325350ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; /** * A Jetty FileServer. *

* This server is identical to {@link FileServer}, except that it is configured * via an {@link XmlConfiguration} config file that does the identical work. *

*

* See fileserver.xml *

*/ public class FileServerXml { public static void main( String[] args ) throws Exception { Resource fileserverXml = Resource.newSystemResource("fileserver.xml"); XmlConfiguration configuration = new XmlConfiguration( fileserverXml.getInputStream()); Server server = (Server) configuration.configure(); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloHandler.java000066400000000000000000000041271261716203600323540ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; public class HelloHandler extends AbstractHandler { final String greeting; final String body; public HelloHandler() { this("Hello World"); } public HelloHandler( String greeting ) { this(greeting, null); } public HelloHandler( String greeting, String body ) { this.greeting = greeting; this.body = body; } public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { response.setContentType("text/html; charset=utf-8"); response.setStatus(HttpServletResponse.SC_OK); PrintWriter out = response.getWriter(); out.println("

" + greeting + "

"); if (body != null) { out.println(body); } baseRequest.setHandled(true); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/HelloServlet.java000066400000000000000000000032741261716203600324250ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial") public class HelloServlet extends HttpServlet { final String greeting; public HelloServlet() { this("Hello"); } public HelloServlet( String greeting ) { this.greeting = greeting; } @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println( "

" + greeting + " from HelloServlet

"); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/JarServer.java000066400000000000000000000035741261716203600317230ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.resource.Resource; /** */ public class JarServer { public static void main(String[] args) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler(); Resource.setDefaultUseCaches(true); Resource base = Resource.newResource("jar:file:src/main/resources/content.jar!/"); context.setBaseResource(base); context.addServlet(new ServletHolder(new DefaultServlet()), "/"); HandlerList handlers = new HandlerList(); handlers.setHandlers(new Handler[] { context, new DefaultHandler() }); server.setHandler(handlers); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java000066400000000000000000000216461261716203600324050ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.FileNotFoundException; import java.lang.management.ManagementFactory; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.PropertiesConfigurationManager; import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.LowResourceMonitor; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.webapp.Configuration; /** * Starts the Jetty Distribution's demo-base directory using entirely * embedded jetty techniques. */ public class LikeJettyXml { public static void main( String[] args ) throws Exception { // Path to as-built jetty-distribution directory String jettyHomeBuild = "../../jetty-distribution/target/distribution"; // Find jetty home and base directories String homePath = System.getProperty("jetty.home", jettyHomeBuild); File homeDir = new File(homePath); if (!homeDir.exists()) { throw new FileNotFoundException(homeDir.getAbsolutePath()); } String basePath = System.getProperty("jetty.base", homeDir + "/demo-base"); File baseDir = new File(basePath); if(!baseDir.exists()) { throw new FileNotFoundException(baseDir.getAbsolutePath()); } // Configure jetty.home and jetty.base system properties String jetty_home = homeDir.getAbsolutePath(); String jetty_base = baseDir.getAbsolutePath(); System.setProperty("jetty.home", jetty_home); System.setProperty("jetty.base", jetty_base); // === jetty.xml === // Setup Threadpool QueuedThreadPool threadPool = new QueuedThreadPool(); threadPool.setMaxThreads(500); // Server Server server = new Server(threadPool); // Scheduler server.addBean(new ScheduledExecutorScheduler()); // HTTP Configuration HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); http_config.setSecurePort(8443); http_config.setOutputBufferSize(32768); http_config.setRequestHeaderSize(8192); http_config.setResponseHeaderSize(8192); http_config.setSendServerVersion(true); http_config.setSendDateHeader(false); // httpConfig.addCustomizer(new ForwardedRequestCustomizer()); // Handler Structure HandlerCollection handlers = new HandlerCollection(); ContextHandlerCollection contexts = new ContextHandlerCollection(); handlers.setHandlers(new Handler[] { contexts, new DefaultHandler() }); server.setHandler(handlers); // Extra options server.setDumpAfterStart(false); server.setDumpBeforeStop(false); server.setStopAtShutdown(true); // === jetty-jmx.xml === MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); server.addBean(mbContainer); // === jetty-http.xml === ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(http_config)); http.setPort(8080); http.setIdleTimeout(30000); server.addConnector(http); // === jetty-https.xml === // SSL Context Factory SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setExcludeCipherSuites("SSL_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); // SSL HTTP Configuration HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); // SSL Connector ServerConnector sslConnector = new ServerConnector(server, new SslConnectionFactory(sslContextFactory,"http/1.1"), new HttpConnectionFactory(https_config)); sslConnector.setPort(8443); server.addConnector(sslConnector); // === jetty-deploy.xml === DeploymentManager deployer = new DeploymentManager(); deployer.setContexts(contexts); deployer.setContextAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/servlet-api-[^/]*\\.jar$"); WebAppProvider webapp_provider = new WebAppProvider(); webapp_provider.setMonitoredDirName(jetty_base + "/webapps"); webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml"); webapp_provider.setScanInterval(1); webapp_provider.setExtractWars(true); webapp_provider.setConfigurationManager(new PropertiesConfigurationManager()); deployer.addAppProvider(webapp_provider); server.addBean(deployer); // === setup jetty plus == Configuration.ClassList.setServerDefault(server).addAfter( "org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration"); // === jetty-stats.xml === StatisticsHandler stats = new StatisticsHandler(); stats.setHandler(server.getHandler()); server.setHandler(stats); // === jetty-requestlog.xml === NCSARequestLog requestLog = new NCSARequestLog(); requestLog.setFilename(jetty_home + "/logs/yyyy_mm_dd.request.log"); requestLog.setFilenameDateFormat("yyyy_MM_dd"); requestLog.setRetainDays(90); requestLog.setAppend(true); requestLog.setExtended(true); requestLog.setLogCookies(false); requestLog.setLogTimeZone("GMT"); RequestLogHandler requestLogHandler = new RequestLogHandler(); requestLogHandler.setRequestLog(requestLog); handlers.addHandler(requestLogHandler); // === jetty-lowresources.xml === LowResourceMonitor lowResourcesMonitor=new LowResourceMonitor(server); lowResourcesMonitor.setPeriod(1000); lowResourcesMonitor.setLowResourcesIdleTimeout(200); lowResourcesMonitor.setMonitorThreads(true); lowResourcesMonitor.setMaxConnections(0); lowResourcesMonitor.setMaxMemory(0); lowResourcesMonitor.setMaxLowResourcesTime(5000); server.addBean(lowResourcesMonitor); // === test-realm.xml === HashLoginService login = new HashLoginService(); login.setName("Test Realm"); login.setConfig(jetty_base + "/etc/realm.properties"); login.setRefreshInterval(0); server.addBean(login); // Start the server server.start(); server.join(); } } ManyConnectors.java000066400000000000000000000133221261716203600326730ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.FileNotFoundException; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.util.ssl.SslContextFactory; /** * A Jetty server with multiple connectors. */ public class ManyConnectors { public static void main( String[] args ) throws Exception { // Since this example shows off SSL configuration, we need a keystore // with the appropriate key. These lookup of jetty.home is purely a hack // to get access to a keystore that we use in many unit tests and should // probably be a direct path to your own keystore. String jettyDistKeystore = "../../jetty-distribution/target/distribution/etc/keystore"; String keystorePath = System.getProperty( "example.keystore", jettyDistKeystore); File keystoreFile = new File(keystorePath); if (!keystoreFile.exists()) { throw new FileNotFoundException(keystoreFile.getAbsolutePath()); } // Create a basic jetty server object without declaring the port. Since // we are configuring connectors directly we'll be setting ports on // those connectors. Server server = new Server(); // HTTP Configuration // HttpConfiguration is a collection of configuration information // appropriate for http and https. The default scheme for http is // http of course, as the default for secured http is // https but we show setting the scheme to show it can be // done. The port for secured communication is also set here. HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); http_config.setSecurePort(8443); http_config.setOutputBufferSize(32768); // HTTP connector // The first server connector we create is the one for http, passing in // the http configuration we configured above so it can get things like // the output buffer size, etc. We also set the port (8080) and // configure an idle timeout. ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(http_config)); http.setPort(8080); http.setIdleTimeout(30000); // SSL Context Factory for HTTPS and SPDY // SSL requires a certificate so we configure a factory for ssl contents // with information pointing to what keystore the ssl connection needs // to know about. Much more configuration is available the ssl context, // including things like choosing the particular certificate out of a // keystore to be used. SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(keystoreFile.getAbsolutePath()); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); // HTTPS Configuration // A new HttpConfiguration object is needed for the next connector and // you can pass the old one as an argument to effectively clone the // contents. On this HttpConfiguration object we add a // SecureRequestCustomizer which is how a new connector is able to // resolve the https connection before handing control over to the Jetty // Server. HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); // HTTPS connector // We create a second ServerConnector, passing in the http configuration // we just made along with the previously created ssl context factory. // Next we set the port and a longer idle timeout. ServerConnector https = new ServerConnector(server, new SslConnectionFactory(sslContextFactory, "http/1.1"), new HttpConnectionFactory(https_config)); https.setPort(8443); https.setIdleTimeout(500000); // Here you see the server having multiple connectors registered with // it, now requests can flow into the server from both http and https // urls to their respective ports and be processed accordingly by jetty. // A simple handler is also registered with the server so the example // has something to pass requests off to. // Set the connectors server.setConnectors(new Connector[] { http, https }); // Set a handler server.setHandler(new HelloHandler()); // Start the server server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyContexts.java000066400000000000000000000037701261716203600324520ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; public class ManyContexts { public static void main( String[] args ) throws Exception { Server server = new Server(8080); ContextHandler context = new ContextHandler("/"); context.setContextPath("/"); context.setHandler(new HelloHandler("Root Hello")); ContextHandler contextFR = new ContextHandler("/fr"); contextFR.setHandler(new HelloHandler("Bonjoir")); ContextHandler contextIT = new ContextHandler("/it"); contextIT.setHandler(new HelloHandler("Bongiorno")); ContextHandler contextV = new ContextHandler("/"); contextV.setVirtualHosts(new String[] { "127.0.0.2" }); contextV.setHandler(new HelloHandler("Virtual Hello")); ContextHandlerCollection contexts = new ContextHandlerCollection(); contexts.setHandlers(new Handler[] { context, contextFR, contextIT, contextV }); server.setHandler(contexts); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ManyHandlers.java000066400000000000000000000131501261716203600323740ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.util.ajax.JSON; /** * Frequently many handlers are combined together to handle different aspects of * a request. A handler may: *
    *
  • handle the request and completely generate the response *
  • partially handle the request, but defer response generation to another * handler. *
  • select another handler to pass the request to. *
  • use business logic to decide to do one of the above. *
* Multiple handlers may be combined with: *
    *
  • {@link HandlerWrapper} which will nest one handler inside another. In * this example, the HelloHandler is nested inside a HandlerWrapper that sets * the greeting as a request attribute. *
  • {@link HandlerList} which will call a collection of handlers until the * request is marked as handled. In this example, a list is used to combine the * param handler (which only handles the request if there are parameters) and * the wrapper handler. Frequently handler lists are terminated with the * {@link DefaultHandler}, which will generate a suitable 404 response if the * request has not been handled. *
  • {@link HandlerCollection} which will call each handler regardless if the * request has been handled or not. Typically this is used to always pass a * request to the logging handler. *
*/ public class ManyHandlers { /** * Produce output that lists all of the request parameters */ public static class ParamHandler extends AbstractHandler { public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { Map params = request.getParameterMap(); if (params.size() > 0) { response.setContentType("text/plain"); response.getWriter().println(JSON.toString(params)); baseRequest.setHandled(true); } } } /** * Add a request attribute, but produce no output. */ public static class WelcomeWrapHandler extends HandlerWrapper { @Override public void handle( String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response ) throws IOException, ServletException { request.setAttribute("welcome", "Hello"); super.handle(target, baseRequest, request, response); } } public static void main( String[] args ) throws Exception { Server server = new Server(8080); // create the handlers Handler param = new ParamHandler(); HandlerWrapper wrapper = new WelcomeWrapHandler(); Handler hello = new HelloHandler(); Handler dft = new DefaultHandler(); RequestLogHandler requestLog = new RequestLogHandler(); // configure request logging File requestLogFile = File.createTempFile("demo", "log"); NCSARequestLog ncsaLog = new NCSARequestLog( requestLogFile.getAbsolutePath()); requestLog.setRequestLog(ncsaLog); // create the handler collections HandlerCollection handlers = new HandlerCollection(); HandlerList list = new HandlerList(); // link them all together wrapper.setHandler(hello); list.setHandlers(new Handler[] { param, wrapper, dft }); handlers.setHandlers(new Handler[] { list, requestLog }); // Handler tree looks like the following //
        // Server
        // + HandlerCollection
        // . + HandlerList
        // . | + param (ParamHandler)
        // . | + wrapper (WelcomeWrapHandler)
        // . | | \ hello (HelloHandler)
        // . | \ dft (DefaultHandler)
        // . \ requestLog (RequestLogHandler)
        // 
server.setHandler(handlers); server.start(); server.join(); } } ManyServletContexts.java000066400000000000000000000050031261716203600337270ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; public class ManyServletContexts { public static void main( String[] args ) throws Exception { Server server = new Server(8080); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); server.addBean(mbContainer, true); // Declare server handler collection ContextHandlerCollection contexts = new ContextHandlerCollection(); server.setHandler(contexts); // Configure context "/" (root) for servlets ServletContextHandler root = new ServletContextHandler(contexts, "/", ServletContextHandler.SESSIONS); // Add servlets to root context root.addServlet(new ServletHolder(new HelloServlet("Hello")), "/"); root.addServlet(new ServletHolder(new HelloServlet("Ciao")), "/it/*"); root.addServlet(new ServletHolder(new HelloServlet("Bonjoir")), "/fr/*"); // Configure context "/other" for servlets ServletContextHandler other = new ServletContextHandler(contexts, "/other", ServletContextHandler.SESSIONS); // Add servlets to /other context other.addServlet(DefaultServlet.class.getCanonicalName(), "/"); other.addServlet(new ServletHolder(new HelloServlet("YO!")), "*.yo"); server.start(); server.join(); } } MinimalServlets.java000066400000000000000000000060431261716203600330510ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; public class MinimalServlets { public static void main( String[] args ) throws Exception { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. Server server = new Server(8080); // The ServletHandler is a dead simple way to create a context handler // that is backed by an instance of a Servlet. // This handler then needs to be registered with the Server object. ServletHandler handler = new ServletHandler(); server.setHandler(handler); // Passing in the class for the Servlet allows jetty to instantiate an // instance of that Servlet and mount it on a given context path. // IMPORTANT: // This is a raw Servlet, not a Servlet that has been configured // through a web.xml @WebServlet annotation, or anything similar. handler.addServletWithMapping(HelloServlet.class, "/*"); // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. // See // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } @SuppressWarnings("serial") public static class HelloServlet extends HttpServlet { @Override protected void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { response.setContentType("text/html"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println("

Hello from HelloServlet

"); } } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneConnector.java000066400000000000000000000027751261716203600324160ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; /** * A Jetty server with one connectors. */ public class OneConnector { public static void main( String[] args ) throws Exception { // The Server Server server = new Server(); // HTTP connector ServerConnector http = new ServerConnector(server); http.setHost("localhost"); http.setPort(8080); http.setIdleTimeout(30000); // Set the connector server.addConnector(http); // Set a handler server.setHandler(new HelloHandler()); // Start the server server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneContext.java000066400000000000000000000026561261716203600321060ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandler; public class OneContext { public static void main( String[] args ) throws Exception { Server server = new Server( 8080 ); // Add a single handler on context "/hello" ContextHandler context = new ContextHandler(); context.setContextPath( "/hello" ); context.setHandler( new HelloHandler() ); // Can be accessed using http://localhost:8080/hello server.setHandler( context ); // Start the server server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneHandler.java000066400000000000000000000021301261716203600320220ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; public class OneHandler { public static void main( String[] args ) throws Exception { Server server = new Server(8080); server.setHandler(new HelloHandler()); server.start(); server.join(); } } OneServletContext.java000066400000000000000000000031131261716203600333610ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; public class OneServletContext { public static void main( String[] args ) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); context.setResourceBase(System.getProperty("java.io.tmpdir")); server.setHandler(context); // Add dump servlet context.addServlet(DumpServlet.class, "/dump/*"); // Add default servlet context.addServlet(DefaultServlet.class, "/"); server.start(); server.join(); } } OneServletContextJmxStats.java000066400000000000000000000035521261716203600350660ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.ConnectorStatistics; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.ServletContextHandler; public class OneServletContextJmxStats { public static void main( String[] args ) throws Exception { Server server = new Server(8080); // Add JMX tracking to Server server.addBean(new MBeanContainer(ManagementFactory .getPlatformMBeanServer())); ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); context.addServlet(DumpServlet.class, "/dump/*"); context.addServlet(DefaultServlet.class, "/"); // Add Connector Statistics tracking to all connectors ConnectorStatistics.addToAllConnectors(server); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/OneWebApp.java000066400000000000000000000072311261716203600316320ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.lang.management.ManagementFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebApp { public static void main( String[] args ) throws Exception { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. Server server = new Server(8080); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); server.addBean(mbContainer); // The WebAppContext is the entity that controls the environment in // which a web application lives and breathes. In this example the // context path is being set to "/" so it is suitable for serving root // context requests and then we see it setting the location of the war. // A whole host of other configurations are available, ranging from // configuring to support annotation scanning in the webapp (through // PlusConfiguration) to choosing where the webapp will unpack itself. WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); File warFile = new File( "../../jetty-distribution/target/distribution/demo-base/webapps/test.war"); webapp.setWar(warFile.getAbsolutePath()); // A WebAppContext is a ContextHandler as well so it needs to be set to // the server so it is aware of where to send the appropriate requests. server.setHandler(webapp); // Configure a LoginService // Since this example is for our test webapp, we need to setup a // LoginService so this shows how to create a very simple hashmap based // one. The name of the LoginService needs to correspond to what is // configured in the webapp's web.xml and since it has a lifecycle of // its own we register it as a bean with the Jetty server object so it // can be started and stopped according to the lifecycle of the server // itself. HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); loginService.setConfig("src/test/resources/realm.properties"); server.addBean(loginService); // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } OneWebAppWithJsp.java000066400000000000000000000115221261716203600330620ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.lang.management.ManagementFactory; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; public class OneWebAppWithJsp { public static void main( String[] args ) throws Exception { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then // a randomly available port will be assigned that you can either look // in the logs for the port, // or programmatically obtain it for use in test cases. Server server = new Server( 8080 ); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer() ); server.addBean( mbContainer ); // The WebAppContext is the entity that controls the environment in // which a web application lives and // breathes. In this example the context path is being set to "/" so it // is suitable for serving root context // requests and then we see it setting the location of the war. A whole // host of other configurations are // available, ranging from configuring to support annotation scanning in // the webapp (through // PlusConfiguration) to choosing where the webapp will unpack itself. WebAppContext webapp = new WebAppContext(); webapp.setContextPath( "/" ); File warFile = new File( "../../jetty-distribution/target/distribution/demo-base/webapps/test.war" ); if (!warFile.exists()) { throw new RuntimeException( "Unable to find WAR File: " + warFile.getAbsolutePath() ); } webapp.setWar( warFile.getAbsolutePath() ); // This webapp will use jsps and jstl. We need to enable the // AnnotationConfiguration in order to correctly // set up the jsp container Configuration.ClassList classlist = Configuration.ClassList .setServerDefault( server ); classlist.addBefore( "org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration" ); // Set the ContainerIncludeJarPattern so that jetty examines these // container-path jars for tlds, web-fragments etc. // If you omit the jar that contains the jstl .tlds, the jsp engine will // scan for them instead. webapp.setAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/[^/]*servlet-api-[^/]*\\.jar$|.*/javax.servlet.jsp.jstl-.*\\.jar$|.*/[^/]*taglibs.*\\.jar$" ); // A WebAppContext is a ContextHandler as well so it needs to be set to // the server so it is aware of where to // send the appropriate requests. server.setHandler( webapp ); // Configure a LoginService. // Since this example is for our test webapp, we need to setup a // LoginService so this shows how to create a very simple hashmap based // one. The name of the LoginService needs to correspond to what is // configured in the webapp's web.xml and since it has a lifecycle of // its own we register it as a bean with the Jetty server object so it // can be started and stopped according to the lifecycle of the server // itself. HashLoginService loginService = new HashLoginService(); loginService.setName( "Test Realm" ); loginService.setConfig( "src/test/resources/realm.properties" ); server.addBean( loginService ); // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ProxyServer.java000066400000000000000000000036021261716203600323200ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.proxy.ConnectHandler; import org.eclipse.jetty.proxy.ProxyServlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; public class ProxyServer { public static void main( String[] args ) throws Exception { Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(8888); server.addConnector(connector); // Setup proxy handler to handle CONNECT methods ConnectHandler proxy = new ConnectHandler(); server.setHandler(proxy); // Setup proxy servlet ServletContextHandler context = new ServletContextHandler(proxy, "/", ServletContextHandler.SESSIONS); ServletHolder proxyServlet = new ServletHolder(ProxyServlet.class); proxyServlet.setInitParameter("blackList", "www.eclipse.org"); context.addServlet(proxyServlet, "/*"); server.start(); } } SecuredHelloHandler.java000066400000000000000000000116231261716203600336070ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.util.Collections; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.security.Constraint; public class SecuredHelloHandler { public static void main( String[] args ) throws Exception { // Create a basic jetty server object that will listen on port 8080. // Note that if you set this to port 0 then a randomly available port // will be assigned that you can either look in the logs for the port, // or programmatically obtain it for use in test cases. Server server = new Server(8080); // Since this example is for our test webapp, we need to setup a // LoginService so this shows how to create a very simple hashmap based // one. The name of the LoginService needs to correspond to what is // configured a webapp's web.xml and since it has a lifecycle of its own // we register it as a bean with the Jetty server object so it can be // started and stopped according to the lifecycle of the server itself. // In this example the name can be whatever you like since we are not // dealing with webapp realms. LoginService loginService = new HashLoginService("MyRealm", "src/test/resources/realm.properties"); server.addBean(loginService); // A security handler is a jetty handler that secures content behind a // particular portion of a url space. The ConstraintSecurityHandler is a // more specialized handler that allows matching of urls to different // constraints. The server sets this as the first handler in the chain, // effectively applying these constraints to all subsequent handlers in // the chain. ConstraintSecurityHandler security = new ConstraintSecurityHandler(); server.setHandler(security); // This constraint requires authentication and in addition that an // authenticated user be a member of a given set of roles for // authorization purposes. Constraint constraint = new Constraint(); constraint.setName("auth"); constraint.setAuthenticate(true); constraint.setRoles(new String[] { "user", "admin" }); // Binds a url pattern with the previously created constraint. The roles // for this constraing mapping are mined from the Constraint itself // although methods exist to declare and bind roles separately as well. ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/*"); mapping.setConstraint(constraint); // First you see the constraint mapping being applied to the handler as // a singleton list, however you can passing in as many security // constraint mappings as you like so long as they follow the mapping // requirements of the servlet api. Next we set a BasicAuthenticator // instance which is the object that actually checks the credentials // followed by the LoginService which is the store of known users, etc. security.setConstraintMappings(Collections.singletonList(mapping)); security.setAuthenticator(new BasicAuthenticator()); security.setLoginService(loginService); // The Hello Handler is the handler we are securing so we create one, // and then set it as the handler on the // security handler to complain the simple handler chain. HelloHandler hh = new HelloHandler(); // chain the hello handler into the security handler security.setHandler(hh); // Start things up! server.start(); // The use of server.join() the will make the current thread join and // wait until the server is done executing. // See // http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } ServerWithAnnotations.java000066400000000000000000000062461261716203600342600ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import org.eclipse.jetty.plus.jndi.EnvEntry; import org.eclipse.jetty.plus.jndi.Resource; import org.eclipse.jetty.plus.jndi.Transaction; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; /** * ServerWithAnnotations */ public class ServerWithAnnotations { public static final void main( String args[] ) throws Exception { // Create the server Server server = new Server(8080); // Enable parsing of jndi-related parts of web.xml and jetty-env.xml Configuration.ClassList classlist = Configuration.ClassList .setServerDefault(server); classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration"); classlist.addBefore( "org.eclipse.jetty.webapp.JettyWebXmlConfiguration", "org.eclipse.jetty.annotations.AnnotationConfiguration"); // Create a WebApp WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); File warFile = new File( "../../jetty-distribution/target/distribution/demo-base/webapps/test.war"); webapp.setWar(warFile.getAbsolutePath()); webapp.setAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/javax.servlet-[^/]*\\.jar$|.*/servlet-api-[^/]*\\.jar$"); server.setHandler(webapp); // Register new transaction manager in JNDI // At runtime, the webapp accesses this as java:comp/UserTransaction new Transaction(new com.acme.MockUserTransaction()); // Define an env entry with webapp scope. new EnvEntry(webapp, "maxAmount", new Double(100), true); // Register a mock DataSource scoped to the webapp new Resource(webapp, "jdbc/mydatasource", new com.acme.MockDataSource()); // Configure a LoginService HashLoginService loginService = new HashLoginService(); loginService.setName("Test Realm"); loginService.setConfig("src/test/resources/realm.properties"); server.addBean(loginService); server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/ServerWithJMX.java000066400000000000000000000035361261716203600324770ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.lang.management.ManagementFactory; import javax.management.remote.JMXServiceURL; import org.eclipse.jetty.jmx.ConnectorServer; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.Server; /** * The simplest possible Jetty server. */ public class ServerWithJMX { public static void main( String[] args ) throws Exception { // === jetty-jmx.xml === MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); Server server = new Server(8080); server.addBean(mbContainer); ConnectorServer jmx = new ConnectorServer( new JMXServiceURL( "rmi", null, 1999, "/jndi/rmi://localhost:1999/jmxrmi"), "org.eclipse.jetty.jmx:name=rmiconnectorserver"); server.addBean(jmx); server.start(); server.dumpStdErr(); server.join(); } } ServerWithJNDI.java000066400000000000000000000114601261716203600325010ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.util.Properties; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; /** * ServerWithJNDI */ public class ServerWithJNDI { public static void main( String[] args ) throws Exception { // Create the server Server server = new Server(8080); // Enable parsing of jndi-related parts of web.xml and jetty-env.xml Configuration.ClassList classlist = Configuration.ClassList .setServerDefault(server); classlist.addAfter("org.eclipse.jetty.webapp.FragmentConfiguration", "org.eclipse.jetty.plus.webapp.EnvConfiguration", "org.eclipse.jetty.plus.webapp.PlusConfiguration"); // Create a WebApp WebAppContext webapp = new WebAppContext(); webapp.setContextPath("/"); File warFile = new File( "../../jetty-distribution/target/distribution/demo-base/webapps/test.war"); webapp.setWar(warFile.getAbsolutePath()); server.setHandler(webapp); // Register new transaction manager in JNDI // At runtime, the webapp accesses this as java:comp/UserTransaction new org.eclipse.jetty.plus.jndi.Transaction( new com.acme.MockUserTransaction()); // Define an env entry with Server scope. // At runtime, the webapp accesses this as java:comp/env/woggle // This is equivalent to putting an env-entry in web.xml: // // woggle // java.lang.Integer // 4000 // new org.eclipse.jetty.plus.jndi.EnvEntry(server, "woggle", new Integer(4000), false); // Define an env entry with webapp scope. // At runtime, the webapp accesses this as java:comp/env/wiggle // This is equivalent to putting a web.xml entry in web.xml: // // wiggle // 100 // java.lang.Double // // Note that the last arg of "true" means that this definition for // "wiggle" would override an entry of the // same name in web.xml new org.eclipse.jetty.plus.jndi.EnvEntry(webapp, "wiggle", new Double(100), true); // Register a reference to a mail service scoped to the webapp. // This must be linked to the webapp by an entry in web.xml: // // mail/Session // javax.mail.Session // Container // // At runtime the webapp accesses this as java:comp/env/mail/Session org.eclipse.jetty.jndi.factories.MailSessionReference mailref = new org.eclipse.jetty.jndi.factories.MailSessionReference(); mailref.setUser("CHANGE-ME"); mailref.setPassword("CHANGE-ME"); Properties props = new Properties(); props.put("mail.smtp.auth", "false"); props.put("mail.smtp.host", "CHANGE-ME"); props.put("mail.from", "CHANGE-ME"); props.put("mail.debug", "false"); mailref.setProperties(props); new org.eclipse.jetty.plus.jndi.Resource(webapp, "mail/Session", mailref); // Register a mock DataSource scoped to the webapp // This must be linked to the webapp via an entry in web.xml: // // jdbc/mydatasource // javax.sql.DataSource // Container // // At runtime the webapp accesses this as // java:comp/env/jdbc/mydatasource new org.eclipse.jetty.plus.jndi.Resource( webapp, "jdbc/mydatasource", new com.acme.MockDataSource()); server.start(); server.join(); } } SimplestServer.java000066400000000000000000000021701261716203600327170ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; /** * The simplest possible Jetty server. */ public class SimplestServer { public static void main( String[] args ) throws Exception { Server server = new Server(8080); server.start(); server.dumpStdErr(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyConnector.java000066400000000000000000000105571261716203600326110ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.FileNotFoundException; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory; import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory; import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory; import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy; import org.eclipse.jetty.util.ssl.SslContextFactory; /** * A Jetty server with HTTP and SPDY connectors. */ public class SpdyConnector { public static void main(String[] args) throws Exception { // Path to as-built jetty-distribution directory String jettyHomeBuild = "../../jetty-distribution/target/distribution"; // Find jetty home directories String homePath = System.getProperty("jetty.home", jettyHomeBuild); File homeDir = new File(homePath); if (!homeDir.exists()) { throw new FileNotFoundException(homeDir.getAbsolutePath()); } String jetty_home = homeDir.getAbsolutePath(); System.setProperty("jetty.home", jetty_home); // The Server Server server = new Server(); // HTTP Configuration HttpConfiguration http_config = new HttpConfiguration(); http_config.setSecureScheme("https"); http_config.setSecurePort(8443); // HTTP connector ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(http_config)); http.setPort(8080); server.addConnector(http); // SSL Context Factory for HTTPS and SPDY SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); // HTTPS Configuration HttpConfiguration https_config = new HttpConfiguration(http_config); https_config.addCustomizer(new SecureRequestCustomizer()); // SPDY versions HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2, https_config); HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3, https_config, new ReferrerPushStrategy()); // NPN Factory SPDYServerConnectionFactory.checkProtocolNegotiationAvailable(); NPNServerConnectionFactory npn = new NPNServerConnectionFactory( spdy3.getProtocol(), spdy2.getProtocol(), http.getDefaultProtocol()); npn.setDefaultProtocol(http.getDefaultProtocol()); // SSL Factory SslConnectionFactory ssl = new SslConnectionFactory( sslContextFactory, npn.getProtocol()); // SPDY Connector ServerConnector spdyConnector = new ServerConnector(server, ssl, npn, spdy3, spdy2, new HttpConnectionFactory(https_config)); spdyConnector.setPort(8443); server.addConnector(spdyConnector); // Set a handler server.setHandler(new HelloHandler()); // Start the server server.start(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded/SpdyServer.java000066400000000000000000000214411261716203600321170ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import java.io.FileNotFoundException; import java.lang.management.ManagementFactory; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.providers.WebAppProvider; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.server.AsyncNCSARequestLog; import org.eclipse.jetty.server.ForwardedRequestCustomizer; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NCSARequestLog; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.server.handler.StatisticsHandler; import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory; import org.eclipse.jetty.spdy.server.SPDYServerConnectionFactory; import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory; import org.eclipse.jetty.spdy.server.http.PushStrategy; import org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; public class SpdyServer { public static void main( String[] args ) throws Exception { // Path to as-built jetty-distribution directory String jettyHomeBuild = "../../jetty-distribution/target/distribution"; // Find jetty home directories String homePath = System.getProperty("jetty.home", jettyHomeBuild); File homeDir = new File(homePath); if (!homeDir.exists()) { throw new FileNotFoundException(homeDir.getAbsolutePath()); } String jetty_home = homeDir.getAbsolutePath(); System.setProperty("jetty.home", jetty_home); // Setup Threadpool QueuedThreadPool threadPool = new QueuedThreadPool(512); // Setup Jetty Server instance Server server = new Server(threadPool); server.manage(threadPool); server.setDumpAfterStart(false); server.setDumpBeforeStop(false); // Setup JMX MBeanContainer mbContainer = new MBeanContainer( ManagementFactory.getPlatformMBeanServer()); server.addBean(mbContainer); // Common HTTP configuration HttpConfiguration config = new HttpConfiguration(); config.setSecurePort(8443); config.addCustomizer(new ForwardedRequestCustomizer()); config.addCustomizer(new SecureRequestCustomizer()); config.setSendServerVersion(true); // Http Connector Setup // A plain HTTP connector listening on port 8080. Note that it's also // possible to have port 8080 configured as a non SSL SPDY connector. // But the specification and most browsers do not allow to use SPDY // without SSL encryption. However some browsers allow it to be // configured. HttpConnectionFactory http = new HttpConnectionFactory(config); ServerConnector httpConnector = new ServerConnector(server, http); httpConnector.setPort(8080); httpConnector.setIdleTimeout(10000); server.addConnector(httpConnector); // SSL configurations // We need a SSLContextFactory for the SSL encryption. That // SSLContextFactory will be used by the SPDY // connector. SslContextFactory sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setKeyStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setKeyManagerPassword("OBF:1u2u1wml1z7s1z7a1wnl1u2g"); sslContextFactory.setTrustStorePath(jetty_home + "/etc/keystore"); sslContextFactory.setTrustStorePassword("OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4"); sslContextFactory.setExcludeCipherSuites( "SSL_RSA_WITH_DES_CBC_SHA", "SSL_DHE_RSA_WITH_DES_CBC_SHA", "SSL_DHE_DSS_WITH_DES_CBC_SHA", "SSL_RSA_EXPORT_WITH_RC4_40_MD5", "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", "SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA"); // Spdy Connector // Make sure that the required NPN implementations are available. SPDYServerConnectionFactory.checkProtocolNegotiationAvailable(); // A ReferrerPushStrategy is being initialized. // See: // http://www.eclipse.org/jetty/documentation/current/spdy-configuring-push.html // for more details. PushStrategy push = new ReferrerPushStrategy(); HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2, config, push); spdy2.setInputBufferSize(8192); spdy2.setInitialWindowSize(32768); // We need a connection factory per protocol that our server is supposed // to support on the NPN port. We then // create a ServerConnector and pass in the supported factories. NPN // will then be used to negotiate the // protocol with the client. HTTPSPDYServerConnectionFactory spdy3 = new HTTPSPDYServerConnectionFactory(3, config, push); spdy3.setInputBufferSize(8192); NPNServerConnectionFactory npn = new NPNServerConnectionFactory( spdy3.getProtocol(), spdy2.getProtocol(), http.getProtocol()); npn.setDefaultProtocol(http.getProtocol()); npn.setInputBufferSize(1024); SslConnectionFactory ssl = new SslConnectionFactory(sslContextFactory, npn.getProtocol()); // Setup the npn connector on port 8443 ServerConnector spdyConnector = new ServerConnector(server, ssl, npn, spdy3, spdy2, http); spdyConnector.setPort(8443); server.addConnector(spdyConnector); // The following section adds some handlers, deployers and webapp // providers. See // http://www.eclipse.org/jetty/documentation/current/advanced-embedding.html // for details. // Setup handlers HandlerCollection handlers = new HandlerCollection(); ContextHandlerCollection contexts = new ContextHandlerCollection(); RequestLogHandler requestLogHandler = new RequestLogHandler(); handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler }); StatisticsHandler stats = new StatisticsHandler(); stats.setHandler(handlers); server.setHandler(stats); // Setup deployers DeploymentManager deployer = new DeploymentManager(); deployer.setContexts(contexts); server.addBean(deployer); WebAppProvider webapp_provider = new WebAppProvider(); webapp_provider.setMonitoredDirName(jetty_home + "/webapps"); webapp_provider.setParentLoaderPriority(false); webapp_provider.setExtractWars(true); webapp_provider.setScanInterval(2); webapp_provider.setDefaultsDescriptor(jetty_home + "/etc/webdefault.xml"); deployer.addAppProvider(webapp_provider); HashLoginService login = new HashLoginService(); login.setName("Test Realm"); login.setConfig(jetty_home + "/etc/realm.properties"); server.addBean(login); NCSARequestLog requestLog = new AsyncNCSARequestLog(); requestLog.setFilename(jetty_home + "/logs/jetty-yyyy_mm_dd.log"); requestLog.setExtended(false); requestLogHandler.setRequestLog(requestLog); server.setStopAtShutdown(true); server.start(); server.dumpStdErr(); server.join(); } } SplitFileServer.java000066400000000000000000000077661261716203600330320ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import java.io.File; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.ResourceHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.resource.Resource; /** * A {@link ContextHandlerCollection} handler may be used to direct a request to * a specific Context. The URI path prefix and optional virtual host is used to * select the context. */ public class SplitFileServer { public static void main( String[] args ) throws Exception { // Create the Server object and a corresponding ServerConnector and then // set the port for the connector. In this example the server will // listen on port 8090. If you set this to port 0 then when the server // has been started you can called connector.getLocalPort() to // programmatically get the port the server started on. Server server = new Server(); ServerConnector connector = new ServerConnector(server); connector.setPort(8090); server.setConnectors(new Connector[] { connector }); // Create a Context Handler and ResourceHandler. The ContextHandler is // getting set to "/" path but this could be anything you like for // builing out your url. Note how we are setting the ResourceBase using // our jetty maven testing utilities to get the proper resource // directory, you needn't use these, you simply need to supply the paths // you are looking to serve content from. ContextHandler context0 = new ContextHandler(); context0.setContextPath("/"); ResourceHandler rh0 = new ResourceHandler(); File dir0 = MavenTestingUtils.getTestResourceDir("dir0"); rh0.setBaseResource(Resource.newResource(dir0)); context0.setHandler(rh0); // Rinse and repeat the previous item, only specifying a different // resource base. ContextHandler context1 = new ContextHandler(); context1.setContextPath("/"); ResourceHandler rh1 = new ResourceHandler(); File dir1 = MavenTestingUtils.getTestResourceDir("dir1"); rh1.setBaseResource(Resource.newResource(dir1)); context1.setHandler(rh1); // Create a ContextHandlerCollection and set the context handlers to it. // This will let jetty process urls against the declared contexts in // order to match up content. ContextHandlerCollection contexts = new ContextHandlerCollection(); contexts.setHandlers(new Handler[] { context0, context1 }); server.setHandler(contexts); // Start things up! server.start(); // Dump the server state System.out.println(server.dump()); // The use of server.join() the will make the current thread join and // wait until the server is done executing. // See http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join() server.join(); } } WebSocketJsrServer.java000066400000000000000000000043511261716203600334670ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.jsr356.server.ServerContainer; import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer; /** * Example of setting up a javax.websocket server with Jetty embedded */ public class WebSocketJsrServer { /** * A server socket endpoint */ @ServerEndpoint(value = "/echo") public static class EchoJsrSocket { @OnMessage public void onMessage( Session session, String message ) { session.getAsyncRemote().sendText(message); } } public static void main( String[] args ) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); // Enable javax.websocket configuration for the context ServerContainer wsContainer = WebSocketServerContainerInitializer .configureContext(context); // Add your websockets to the container wsContainer.addEndpoint(EchoJsrSocket.class); server.start(); context.dumpStdErr(); server.join(); } } WebSocketServer.java000066400000000000000000000051351261716203600330110ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage; import org.eclipse.jetty.websocket.api.annotations.WebSocket; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; /** * Example of setting up a Jetty WebSocket server *

* Note: this uses the Jetty WebSocket API, not the javax.websocket API. */ public class WebSocketServer { /** * Example of a Jetty API WebSocket Echo Socket */ @WebSocket public static class EchoSocket { @OnWebSocketMessage public void onMessage( Session session, String message ) { session.getRemote().sendStringByFuture(message); } } /** * Servlet layer */ @SuppressWarnings("serial") public static class EchoServlet extends WebSocketServlet { @Override public void configure( WebSocketServletFactory factory ) { // Register the echo websocket with the basic WebSocketCreator factory.register(EchoSocket.class); } } public static void main( String[] args ) throws Exception { Server server = new Server(8080); ServletContextHandler context = new ServletContextHandler( ServletContextHandler.SESSIONS); context.setContextPath("/"); server.setHandler(context); // Add the echo socket servlet to the /echo path map context.addServlet(new ServletHolder(EchoServlet.class), "/echo"); server.start(); context.dumpStdErr(); server.join(); } } jetty-9.2.14.v20151106/examples/embedded/src/main/resources/000077500000000000000000000000001261716203600231325ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/main/resources/content.jar000066400000000000000000000013771261716203600253120ustar00rootroot00000000000000PK*[7F META-INF/PKPK*[7FMETA-INF/MANIFEST.MFMLK-. K-*ϳR03r.JM,IMu X(h%&*8%krrPK'kCDPK SBdir0/PKSBdir0/test0.txt+I-.1PKtPK SBdir1/PKSBdir1/test1.txt+I-.1PKܲPK*[7F META-INF/PK*[7F'kCD=META-INF/MANIFEST.MFPK SBdir0/PKSBtdir0/test0.txtPK SB(dir1/PKSBܲKdir1/test1.txtPK[jetty-9.2.14.v20151106/examples/embedded/src/main/resources/exampleserver.xml000066400000000000000000000022101261716203600265310ustar00rootroot00000000000000 8080 /hello org.eclipse.jetty.embedded.HelloServlet / jetty-9.2.14.v20151106/examples/embedded/src/main/resources/fileserver.xml000066400000000000000000000022571261716203600260300ustar00rootroot00000000000000 8080 true index.html . jetty-9.2.14.v20151106/examples/embedded/src/main/resources/jetty-logging.properties000066400000000000000000000007131261716203600300340ustar00rootroot00000000000000org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.LEVEL=INFO org.eclipse.jetty.STACKS=true org.eclipse.jetty.SOURCE=false #org.eclipse.jetty.STACKS=false #org.eclipse.jetty.spdy.LEVEL=DEBUG #org.eclipse.jetty.server.LEVEL=DEBUG #org.eclipse.jetty.io.LEVEL=DEBUG #org.eclipse.jetty.io.ssl.LEVEL=DEBUG #org.eclipse.jetty.spdy.server.LEVEL=DEBUG #org.eclipse.jetty.server.LEVEL=DEBUG #org.eclipse.jetty.servlets.LEVEL=DEBUG jetty-9.2.14.v20151106/examples/embedded/src/main/resources/jetty-otherserver.xml000066400000000000000000000034271261716203600273670ustar00rootroot00000000000000 8888 /other-webapps /etc/webdefault.xml true jetty-9.2.14.v20151106/examples/embedded/src/test/000077500000000000000000000000001261716203600211535ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/000077500000000000000000000000001261716203600220745ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/000077500000000000000000000000001261716203600226635ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/eclipse/000077500000000000000000000000001261716203600243075ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600254465ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/eclipse/jetty/embedded/000077500000000000000000000000001261716203600271775ustar00rootroot00000000000000GzipHandlerTest.java000066400000000000000000000113201261716203600330270ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/eclipse/jetty/embedded// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.util.zip.GZIPInputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.servlets.gzip.GzipHandler; import org.eclipse.jetty.util.IO; import org.junit.After; import org.junit.Before; import org.junit.Test; public class GzipHandlerTest { private static String __content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis felis nunc. "+ "Quisque suscipit mauris et ante auctor ornare rhoncus lacus aliquet. Pellentesque "+ "habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. "+ "Vestibulum sit amet felis augue, vel convallis dolor. Cras accumsan vehicula diam "+ "at faucibus. Etiam in urna turpis, sed congue mi. Morbi et lorem eros. Donec vulputate "+ "velit in risus suscipit lobortis. Aliquam id urna orci, nec sollicitudin ipsum. "+ "Cras a orci turpis. Donec suscipit vulputate cursus. Mauris nunc tellus, fermentum "+ "eu auctor ut, mollis at diam. Quisque porttitor ultrices metus, vitae tincidunt massa "+ "sollicitudin a. Vivamus porttitor libero eget purus hendrerit cursus. Integer aliquam "+ "consequat mauris quis luctus. Cras enim nibh, dignissim eu faucibus ac, mollis nec neque. "+ "Aliquam purus mauris, consectetur nec convallis lacinia, porta sed ante. Suspendisse "+ "et cursus magna. Donec orci enim, molestie a lobortis eu, imperdiet vitae neque."; private Server _server; private LocalConnector _connector; @Before public void init() throws Exception { _server = new Server(); _connector = new LocalConnector(_server); _server.addConnector(_connector); Handler testHandler = new AbstractHandler() { public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter writer = response.getWriter(); writer.write(__content); writer.close(); baseRequest.setHandled(true); } }; GzipHandler gzipHandler = new GzipHandler(); gzipHandler.setHandler(testHandler); _server.setHandler(gzipHandler); _server.start(); } @After public void destroy() throws Exception { _server.stop(); _server.join(); } @Test public void testGzipHandler() throws Exception { // generated and parsed test HttpTester.Request request = HttpTester.newRequest(); HttpTester.Response response; request.setMethod("GET"); request.setVersion("HTTP/1.0"); request.setHeader("Host","tester"); request.setHeader("accept-encoding","gzip"); request.setURI("/"); response = HttpTester.parseResponse(_connector.getResponses(request.generate())); assertTrue(response.get("Content-Encoding").equalsIgnoreCase("gzip")); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); InputStream testIn = new GZIPInputStream(new ByteArrayInputStream(response.getContentBytes())); ByteArrayOutputStream testOut = new ByteArrayOutputStream(); IO.copy(testIn,testOut); assertEquals(__content, testOut.toString("UTF8")); } } jetty-9.2.14.v20151106/examples/embedded/src/test/java/org/eclipse/jetty/embedded/TestXml.java000066400000000000000000000025121261716203600314420ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.embedded; import org.eclipse.jetty.xml.XmlConfiguration; public class TestXml { public static void main(String[] args) throws Exception { System.setProperty("jetty.home","../jetty-distribution/target/distribution"); XmlConfiguration.main(new String[] { "../jetty-jmx/src/main/config/etc/jetty-jmx.xml", "../jetty-server/src/main/config/etc/jetty.xml", "../jetty-spdy/spdy-jetty-http-webapp/src/main/config/etc/jetty-spdy.xml" } ); } } jetty-9.2.14.v20151106/examples/embedded/src/test/resources/000077500000000000000000000000001261716203600231655ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/resources/dir0/000077500000000000000000000000001261716203600240235ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/resources/dir0/test0.txt000066400000000000000000000000051261716203600256160ustar00rootroot00000000000000test0jetty-9.2.14.v20151106/examples/embedded/src/test/resources/dir1/000077500000000000000000000000001261716203600240245ustar00rootroot00000000000000jetty-9.2.14.v20151106/examples/embedded/src/test/resources/dir1/test1.txt000066400000000000000000000000051261716203600256200ustar00rootroot00000000000000test1jetty-9.2.14.v20151106/examples/embedded/src/test/resources/realm.properties000066400000000000000000000013761261716203600264120ustar00rootroot00000000000000# # This file defines users passwords and roles for a HashUserRealm # # The format is # : [, ...] # # Passwords may be clear text, obfuscated or checksummed. The class # org.eclipse.util.Password should be used to generate obfuscated # passwords or password checksums # # If DIGEST Authentication is used, the password must be in a recoverable # format, either plain text or OBF:. # jetty: MD5:164c88b302622e17050af52c89945d44,user admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin,user other: OBF:1xmk1w261u9r1w1c1xmq,user plain: plain,user user: password,user # This entry is for digest auth. The credential is a MD5 hash of username:realmname:password digest: MD5:6e120743ad67abfbc385bc2bb754e297,user jetty-9.2.14.v20151106/examples/pom.xml000066400000000000000000000035371261716203600172010ustar00rootroot00000000000000 4.0.0 org.eclipse.jetty jetty-project 9.2.14.v20151106 ../pom.xml org.eclipse.jetty.examples examples-parent Jetty Examples :: Parent pom org.codehaus.mojo findbugs-maven-plugin true async-rest embedded jetty-9.2.14.v20151106/header-template.txt000066400000000000000000000013621261716203600176420ustar00rootroot00000000000000 ======================================================================== Copyright (c) ${copyright-range} Mort Bay Consulting Pty. Ltd. ------------------------------------------------------------------------ All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 and Apache License v2.0 which accompanies this distribution. The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html The Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php You may elect to redistribute this code under either of these licenses. ========================================================================jetty-9.2.14.v20151106/icon.jpg000066400000000000000000000043621261716203600154750ustar00rootroot00000000000000JFIFHHC   C    @@ 8 !1"2ABQeq#Ra$6bcrs< !"12ABQaqr#RbCc ?DQDEDQDE`./Q ^u%Ŧs +PiH%J8R:Rm"w·6j mt%)H'`+).ֶ 4oaLߚ#Ԧ6dڲK^[:l6Z[eJRIP' ÝiiU6T߆1K Ҥ-^@4uQHnLSyX*&y^p}A4bűQC` C5LYWL1\d7,Sl,OjN4_Uidqx|:llG_Y\zqsK˚y ~52O=6𢿛69jް vwrG-Fi_kOYG?u? K-CYV uS~!f+@ 1!qkϣ$穵 >:xB7~iWǵ߭M}*Ò\K)D+XY$6ar~+4s@xs$N"1SEM/B!G*H} lrGN%o-9. %ij}9NUsSV--zmlRõB,6s?UWO(R+.ϵܬCK6)6ggx(3- C8C.]깎;}D<)ȝȟ:^*ےNnWyC-8)m%dHJֺVҘ>6.`ʹxX$iX8 *y e_Lh %7 ,ɓ4dsϵKCn3(mP !s)9kfÄ#_hۛrSH jJO+pamlM%w}ic1DKe rDo]#h9A ʢ,.:;b+;3=W'ҙ ZDd7mEe >'asɎR ڟ%.6-oҨҼR:&hcf~f썅FҎq{lB{1E 8j/9Iߜ~TSum1o'rsߧ˽3qYK-$% QBo]$K"YЊE˘um!#9mITJlxPO}2~֖Drx#sL(d,GS'idYg֏+$:-a [=j"zԩEDQDEDQjetty-9.2.14.v20151106/jetty-alpn/000077500000000000000000000000001261716203600161255ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/000077500000000000000000000000001261716203600214705ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/pom.xml000066400000000000000000000051351261716203600230110ustar00rootroot00000000000000 org.eclipse.jetty jetty-alpn-parent 9.2.14.v20151106 4.0.0 jetty-alpn-client Jetty :: ALPN Client Jetty ALPN client services http://www.eclipse.org/jetty ${project.groupId}.alpn.client org.apache.felix maven-bundle-plugin true manifest org.eclipse.jetty.alpn;resolution:=optional org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.apache.maven.plugins maven-source-plugin org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.alpn.* org.eclipse.jetty jetty-io ${project.version} org.eclipse.jetty.alpn alpn-api ${alpn.api.version} provided org.eclipse.jetty.toolchain jetty-test-helper test jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/000077500000000000000000000000001261716203600222575ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/000077500000000000000000000000001261716203600232035ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/000077500000000000000000000000001261716203600241245ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/000077500000000000000000000000001261716203600247135ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/000077500000000000000000000000001261716203600263375ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600274765ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/000077500000000000000000000000001261716203600304305ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/000077500000000000000000000000001261716203600317065ustar00rootroot00000000000000ALPNClientConnection.java000066400000000000000000000046641261716203600364150ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.alpn.client; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; import javax.net.ssl.SSLEngine; import org.eclipse.jetty.alpn.ALPN; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.NegotiatingClientConnection; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class ALPNClientConnection extends NegotiatingClientConnection implements ALPN.ClientProvider { private static final Logger LOG = Log.getLogger(ALPNClientConnection.class); private final String protocol; public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map context, String protocol) { super(endPoint, executor, sslEngine, connectionFactory, context); this.protocol = protocol; ALPN.put(sslEngine, this); } @Override public void unsupported() { ALPN.remove(getSSLEngine()); completed(); } @Override public List protocols() { return Arrays.asList(protocol); } @Override public void selected(String protocol) { if (this.protocol.equals(protocol)) { ALPN.remove(getSSLEngine()); completed(); } else { LOG.info("Could not negotiate protocol: server {} - client {}", protocol, this.protocol); close(); } } @Override public void close() { ALPN.remove(getSSLEngine()); super.close(); } } ALPNClientConnectionFactory.java000066400000000000000000000036211261716203600377350ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.alpn.client; import java.io.IOException; import java.util.Map; import java.util.concurrent.Executor; import javax.net.ssl.SSLEngine; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.NegotiatingClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFactory { private final Executor executor; private final String protocol; public ALPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol) { super(connectionFactory); this.executor = executor; this.protocol = protocol; } @Override public Connection newConnection(EndPoint endPoint, Map context) throws IOException { return new ALPNClientConnection(endPoint, executor, getClientConnectionFactory(), (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol); } } jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/000077500000000000000000000000001261716203600215205ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/pom.xml000066400000000000000000000061631261716203600230430ustar00rootroot00000000000000 org.eclipse.jetty jetty-alpn-parent 9.2.14.v20151106 4.0.0 jetty-alpn-server Jetty :: ALPN Server Jetty ALPN server services http://www.eclipse.org/jetty ${project.groupId}.alpn.server org.apache.felix maven-bundle-plugin true manifest org.eclipse.jetty.alpn,* <_nouses>true org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.apache.maven.plugins maven-assembly-plugin package single config org.apache.maven.plugins maven-source-plugin org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.alpn.* org.eclipse.jetty jetty-server ${project.version} org.eclipse.jetty.alpn alpn-api ${alpn.api.version} provided org.eclipse.jetty.toolchain jetty-test-helper test jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/000077500000000000000000000000001261716203600223075ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/000077500000000000000000000000001261716203600232335ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/000077500000000000000000000000001261716203600245005ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/etc/000077500000000000000000000000001261716203600252535ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/etc/protonego-alpn.xml000066400000000000000000000011571261716203600307450ustar00rootroot00000000000000 spdy/3 spdy/2 http/1.1 http/1.1 jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/000077500000000000000000000000001261716203600261505ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/000077500000000000000000000000001261716203600311235ustar00rootroot00000000000000alpn-1.7.0_40.mod000066400000000000000000000003661261716203600334500ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_45.mod000066400000000000000000000003661261716203600334550ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_51.mod000066400000000000000000000003661261716203600334520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_55.mod000066400000000000000000000003661261716203600334560ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_60.mod000066400000000000000000000003661261716203600334520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_65.mod000066400000000000000000000003661261716203600334570ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_67.mod000066400000000000000000000003661261716203600334610ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.0.v20141016/alpn-boot-7.1.0.v20141016.jar|lib/alpn/alpn-boot-7.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.0.v20141016.jar alpn-1.7.0_71.mod000066400000000000000000000003661261716203600334540ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar alpn-1.7.0_72.mod000066400000000000000000000003661261716203600334550ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.2.v20141202/alpn-boot-7.1.2.v20141202.jar|lib/alpn/alpn-boot-7.1.2.v20141202.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.2.v20141202.jar alpn-1.7.0_75.mod000066400000000000000000000003661261716203600334600ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar alpn-1.7.0_76.mod000066400000000000000000000003661261716203600334610ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar alpn-1.7.0_79.mod000066400000000000000000000003661261716203600334640ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar alpn-1.7.0_80.mod000066400000000000000000000003661261716203600334540ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/7.1.3.v20150130/alpn-boot-7.1.3.v20150130.jar|lib/alpn/alpn-boot-7.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-7.1.3.v20150130.jar alpn-1.8.0.mod000066400000000000000000000003661261716203600331460ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar alpn-1.8.0_05.mod000066400000000000000000000003661261716203600334520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar alpn-1.8.0_11.mod000066400000000000000000000003661261716203600334470ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar alpn-1.8.0_20.mod000066400000000000000000000003661261716203600334470ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.0.v20141016/alpn-boot-8.1.0.v20141016.jar|lib/alpn/alpn-boot-8.1.0.v20141016.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.0.v20141016.jar alpn-1.8.0_25.mod000066400000000000000000000003661261716203600334540ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.2.v20141202/alpn-boot-8.1.2.v20141202.jar|lib/alpn/alpn-boot-8.1.2.v20141202.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.2.v20141202.jar alpn-1.8.0_31.mod000066400000000000000000000003661261716203600334510ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.3.v20150130/alpn-boot-8.1.3.v20150130.jar|lib/alpn/alpn-boot-8.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar alpn-1.8.0_40.mod000066400000000000000000000003661261716203600334510ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.3.v20150130/alpn-boot-8.1.3.v20150130.jar|lib/alpn/alpn-boot-8.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar alpn-1.8.0_45.mod000066400000000000000000000003661261716203600334560ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.3.v20150130/alpn-boot-8.1.3.v20150130.jar|lib/alpn/alpn-boot-8.1.3.v20150130.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.3.v20150130.jar alpn-1.8.0_51.mod000066400000000000000000000003661261716203600334530ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.4.v20150727/alpn-boot-8.1.4.v20150727.jar|lib/alpn/alpn-boot-8.1.4.v20150727.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.4.v20150727.jar alpn-1.8.0_60.mod000066400000000000000000000003661261716203600334530ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.5.v20150921/alpn-boot-8.1.5.v20150921.jar|lib/alpn/alpn-boot-8.1.5.v20150921.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.5.v20150921.jar alpn-1.8.0_65.mod000066400000000000000000000003661261716203600334600ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.6.v20151105/alpn-boot-8.1.6.v20151105.jar|lib/alpn/alpn-boot-8.1.6.v20151105.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.6.v20151105.jar alpn-1.8.0_66.mod000066400000000000000000000003661261716203600334610ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl[name] protonego-boot [files] http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/8.1.6.v20151105/alpn-boot-8.1.6.v20151105.jar|lib/alpn/alpn-boot-8.1.6.v20151105.jar [exec] -Xbootclasspath/p:lib/alpn/alpn-boot-8.1.6.v20151105.jar jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/config/modules/protonego-impl/alpn.mod000066400000000000000000000024421261716203600325600ustar00rootroot00000000000000# ALPN is provided via a -Xbootclasspath that modifies the secure connections # in java to support the ALPN layer needed for SPDY (and eventually HTTP/2) # # This modification has a tight dependency on specific recent updates of # Java 1.7 and Java 1.8 # (Java versions prior to 1.7u40 are not supported) # # The alpn protonego module will use an appropriate alpn-boot jar for your # specific version of Java. # # IMPORTANT: Versions of Java that exist after this module was created are # not guaranteed to work with existing alpn-boot jars, and might # need a new alpn-boot to be created / tested / deployed by the # Jetty project in order to provide support for these future # Java versions. # # All versions of alpn-boot can be found at # http://central.maven.org/maven2/org/mortbay/jetty/alpn/alpn-boot/ [name] protonego-impl [depend] protonego-impl/alpn-${java.version} [lib] lib/jetty-alpn-client-${jetty.version}.jar lib/jetty-alpn-server-${jetty.version}.jar [xml] etc/protonego-alpn.xml [files] lib/ lib/alpn/ [license] ALPN is a hosted at github under the GPL v2 with ClassPath Exception. ALPN replaces/modifies OpenJDK classes in the java.sun.security.ssl package. http://github.com/jetty-project/jetty-alpn http://openjdk.java.net/legal/gplv2+ce.html jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/000077500000000000000000000000001261716203600241545ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/000077500000000000000000000000001261716203600247435ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/000077500000000000000000000000001261716203600263675ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600275265ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/000077500000000000000000000000001261716203600304605ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server/000077500000000000000000000000001261716203600317665ustar00rootroot00000000000000ALPNServerConnection.java000066400000000000000000000051511261716203600365150ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.alpn.server; import java.util.Collections; import java.util.List; import javax.net.ssl.SSLEngine; import org.eclipse.jetty.alpn.ALPN; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.NegotiatingServerConnection; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider { private static final Logger LOG = Log.getLogger(ALPNServerConnection.class); public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol) { super(connector, endPoint, engine, protocols, defaultProtocol); ALPN.put(engine, this); } @Override public void unsupported() { select(Collections.emptyList()); } @Override public String select(List clientProtocols) { List serverProtocols = getProtocols(); String negotiated = null; // RFC 7301 states that the server picks the protocol // that it prefers that is also supported by the client. for (String serverProtocol : serverProtocols) { if (clientProtocols.contains(serverProtocol)) { negotiated = serverProtocol; break; } } if (negotiated == null) { negotiated = getDefaultProtocol(); } if (LOG.isDebugEnabled()) LOG.debug("{} protocol selected {}", this, negotiated); setProtocol(negotiated); ALPN.remove(getSSLEngine()); return negotiated; } @Override public void close() { ALPN.remove(getSSLEngine()); super.close(); } } ALPNServerConnectionFactory.java000066400000000000000000000044671261716203600400560ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-alpn/jetty-alpn-server/src/main/java/org/eclipse/jetty/alpn/server// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.alpn.server; import java.util.List; import javax.net.ssl.SSLEngine; import org.eclipse.jetty.alpn.ALPN; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.NegotiatingServerConnectionFactory; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory { private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class); public ALPNServerConnectionFactory(@Name("protocols") String... protocols) { super("alpn", protocols); try { ClassLoader alpnClassLoader = ALPN.class.getClassLoader(); if (alpnClassLoader != null) { LOG.warn("ALPN must be in the boot classloader, not in: " + alpnClassLoader); throw new IllegalStateException("ALPN must be in the boot classloader"); } } catch (Throwable x) { LOG.warn("ALPN not available", x); throw new IllegalStateException("ALPN not available", x); } } @Override protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List protocols, String defaultProtocol) { return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol); } } jetty-9.2.14.v20151106/jetty-alpn/pom.xml000066400000000000000000000013071261716203600174430ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-alpn-parent pom Jetty :: ALPN :: Parent Jetty ALPN services parent http://www.eclipse.org/jetty jetty-alpn-server jetty-alpn-client jetty-9.2.14.v20151106/jetty-annotations/000077500000000000000000000000001261716203600175305ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/pom.xml000066400000000000000000000073741261716203600210600ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-annotations Jetty :: Servlet Annotations Annotation support for deploying servlets in jetty. http://www.eclipse.org/jetty ${project.groupId}.annotations org.apache.maven.plugins maven-assembly-plugin package single config org.apache.felix maven-bundle-plugin true generate-manifest manifest javax.servlet.*;version="[2.6.0,3.2)",org.objectweb.asm.*;version=5,* osgi.serviceloader; filter:="(osgi.serviceloader=javax.servlet.ServletContainerInitializer)";resolution:=optional;cardinality:=multiple, osgi.extender; filter:="(osgi.extender=osgi.serviceloader.processor)" org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.annotations.* org.eclipse.jetty.toolchain jetty-test-helper test org.eclipse.jetty jetty-jndi ${project.version} test org.eclipse.jetty jetty-plus ${project.version} org.eclipse.jetty jetty-webapp ${project.version} javax.annotation javax.annotation-api org.ow2.asm asm org.ow2.asm asm-commons jetty-9.2.14.v20151106/jetty-annotations/src/000077500000000000000000000000001261716203600203175ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/000077500000000000000000000000001261716203600212435ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/config/000077500000000000000000000000001261716203600225105ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/config/etc/000077500000000000000000000000001261716203600232635ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/config/etc/jetty-annotations.xml000066400000000000000000000015041261716203600274770ustar00rootroot00000000000000 org.eclipse.jetty.webapp.JettyWebXmlConfiguration org.eclipse.jetty.annotations.AnnotationConfiguration jetty-9.2.14.v20151106/jetty-annotations/src/main/config/modules/000077500000000000000000000000001261716203600241605ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/config/modules/annotations.mod000066400000000000000000000005161261716203600272200ustar00rootroot00000000000000# # Jetty Annotation Scanning Module # [depend] # Annotations needs plus, and jndi features plus [lib] # Annotations needs jetty annotation jars lib/jetty-annotations-${jetty.version}.jar # Need annotation processing jars too lib/annotations/*.jar [xml] # Enable annotation scanning webapp configurations etc/jetty-annotations.xml jetty-9.2.14.v20151106/jetty-annotations/src/main/java/000077500000000000000000000000001261716203600221645ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/000077500000000000000000000000001261716203600227535ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/000077500000000000000000000000001261716203600243775ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600255365ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/000077500000000000000000000000001261716203600300735ustar00rootroot00000000000000AbstractDiscoverableAnnotationHandler.java000066400000000000000000000027221261716203600403010ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.WebAppContext; /** * DiscoverableAnnotationHandler * * Base class for handling the discovery of an annotation. * */ public abstract class AbstractDiscoverableAnnotationHandler extends AbstractHandler { protected WebAppContext _context; public AbstractDiscoverableAnnotationHandler(WebAppContext context) { _context = context; } public void addAnnotation (DiscoveredAnnotation a) { _context.getMetaData().addDiscoveredAnnotation(a); } } AnnotationConfiguration.java000066400000000000000000001234511261716203600355270ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import javax.servlet.ServletContainerInitializer; import javax.servlet.annotation.HandlesTypes; import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.plus.annotation.ContainerInitializer; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.statistic.CounterStatistic; import org.eclipse.jetty.webapp.AbstractConfiguration; import org.eclipse.jetty.webapp.FragmentDescriptor; import org.eclipse.jetty.webapp.MetaDataComplete; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebDescriptor; /** * Configuration for Annotations * * */ public class AnnotationConfiguration extends AbstractConfiguration { private static final Logger LOG = Log.getLogger(AnnotationConfiguration.class); public static final String SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN = "org.eclipse.jetty.containerInitializerExclusionPattern"; public static final String SERVLET_CONTAINER_INITIALIZER_ORDER = "org.eclipse.jetty.containerInitializerOrder"; public static final String CLASS_INHERITANCE_MAP = "org.eclipse.jetty.classInheritanceMap"; public static final String CONTAINER_INITIALIZERS = "org.eclipse.jetty.containerInitializers"; public static final String CONTAINER_INITIALIZER_STARTER = "org.eclipse.jetty.containerInitializerStarter"; public static final String MULTI_THREADED = "org.eclipse.jetty.annotations.multiThreaded"; public static final String MAX_SCAN_WAIT = "org.eclipse.jetty.annotations.maxWait"; public static final int DEFAULT_MAX_SCAN_WAIT = 60; /* time in sec */ public static final boolean DEFAULT_MULTI_THREADED = true; protected List _discoverableAnnotationHandlers = new ArrayList(); protected ClassInheritanceHandler _classInheritanceHandler; protected List _containerInitializerAnnotationHandlers = new ArrayList(); protected List _parserTasks; protected WebAppClassNameResolver _webAppClassNameResolver; protected ContainerClassNameResolver _containerClassNameResolver; protected CounterStatistic _containerPathStats; protected CounterStatistic _webInfLibStats; protected CounterStatistic _webInfClassesStats; protected Pattern _sciExcludePattern; /** * TimeStatistic * * Simple class to capture elapsed time of an operation. * */ public class TimeStatistic { public long _start = 0; public long _end = 0; public void start () { _start = System.nanoTime(); } public void end () { _end = System.nanoTime(); } public long getStart() { return _start; } public long getEnd () { return _end; } public long getElapsed () { return (_end > _start?(_end-_start):0); } } /** * ParserTask * * Task to executing scanning of a resource for annotations. * */ public class ParserTask implements Callable { protected Exception _exception; protected final AnnotationParser _parser; protected final Set _handlers; protected final ClassNameResolver _resolver; protected final Resource _resource; protected TimeStatistic _stat; public ParserTask (AnnotationParser parser, Sethandlers, Resource resource, ClassNameResolver resolver) { _parser = parser; _handlers = handlers; _resolver = resolver; _resource = resource; } public void setStatistic(TimeStatistic stat) { _stat = stat; } public Void call() throws Exception { if (_stat != null) _stat.start(); if (_parser != null) _parser.parse(_handlers, _resource, _resolver); if (_stat != null) _stat.end(); return null; } public TimeStatistic getStatistic() { return _stat; } public Resource getResource() { return _resource; } } /** * WebAppClassNameResolver * * Checks to see if a classname belongs to hidden or visible packages when scanning, * and whether a classname that is a duplicate should override a previously * scanned classname. * * This is analogous to the management of classes that the WebAppClassLoader is doing, * however we don't want to load the classes at this point so we are doing it on * the name only. * */ public class WebAppClassNameResolver implements ClassNameResolver { private WebAppContext _context; public WebAppClassNameResolver (WebAppContext context) { _context = context; } public boolean isExcluded (String name) { if (_context.isSystemClass(name)) return true; if (_context.isServerClass(name)) return false; return false; } public boolean shouldOverride (String name) { //looking at webapp classpath, found already-parsed class //of same name - did it come from system or duplicate in webapp? if (_context.isParentLoaderPriority()) return false; return true; } } /** * ContainerClassNameResolver * * Checks to see if a classname belongs to a hidden or visible package * when scanning for annotations and thus whether it should be excluded from * consideration or not. * * This is analogous to the management of classes that the WebAppClassLoader is doing, * however we don't want to load the classes at this point so we are doing it on * the name only. * */ public class ContainerClassNameResolver implements ClassNameResolver { private WebAppContext _context; public ContainerClassNameResolver (WebAppContext context) { _context = context; } public boolean isExcluded (String name) { if (_context.isSystemClass(name)) return false; if (_context.isServerClass(name)) return true; return false; } public boolean shouldOverride (String name) { //visiting the container classpath, if (_context.isParentLoaderPriority()) return true; return false; } } /** * ServletContainerInitializerOrdering * * A list of classnames of ServletContainerInitializers in the order in which * they are to be called back. One name only in the list can be "*", which is a * wildcard which matches any other ServletContainerInitializer name not already * matched. */ public class ServletContainerInitializerOrdering { private Map _indexMap = new HashMap(); private Integer _star = null; private String _ordering = null; public ServletContainerInitializerOrdering (String ordering) { if (ordering != null) { _ordering = ordering; String[] tmp = ordering.split(","); for (int i=0; i { private ServletContainerInitializerOrdering _ordering; public ServletContainerInitializerComparator (ServletContainerInitializerOrdering ordering) { _ordering = ordering; } @Override public int compare(ServletContainerInitializer sci1, ServletContainerInitializer sci2) { String c1 = (sci1 != null? sci1.getClass().getName() : null); String c2 = (sci2 != null? sci2.getClass().getName() : null); if (c1 == null && c2 == null) return 0; int i1 = _ordering.getIndexOf(c1); if (i1 < 0 && _ordering.hasWildcard()) i1 = _ordering.getWildcardIndex(); int i2 = _ordering.getIndexOf(c2); if (i2 < 0 && _ordering.hasWildcard()) i2 = _ordering.getWildcardIndex(); return Integer.compare(i1, i2); } } @Override public void preConfigure(final WebAppContext context) throws Exception { _webAppClassNameResolver = new WebAppClassNameResolver(context); _containerClassNameResolver = new ContainerClassNameResolver(context); String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_EXCLUSION_PATTERN); _sciExcludePattern = (tmp==null?null:Pattern.compile(tmp)); } public void addDiscoverableAnnotationHandler(AbstractDiscoverableAnnotationHandler handler) { _discoverableAnnotationHandlers.add(handler); } @Override public void deconfigure(WebAppContext context) throws Exception { context.removeAttribute(CLASS_INHERITANCE_MAP); context.removeAttribute(CONTAINER_INITIALIZERS); ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER); if (starter != null) { context.removeBean(starter); context.removeAttribute(CONTAINER_INITIALIZER_STARTER); } } /** * @see org.eclipse.jetty.webapp.AbstractConfiguration#configure(org.eclipse.jetty.webapp.WebAppContext) */ @Override public void configure(WebAppContext context) throws Exception { context.addDecorator(new AnnotationDecorator(context)); //Even if metadata is complete, we still need to scan for ServletContainerInitializers - if there are any if (!context.getMetaData().isMetaDataComplete()) { //If metadata isn't complete, if this is a servlet 3 webapp or isConfigDiscovered is true, we need to search for annotations if (context.getServletContext().getEffectiveMajorVersion() >= 3 || context.isConfigurationDiscovered()) { _discoverableAnnotationHandlers.add(new WebServletAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebFilterAnnotationHandler(context)); _discoverableAnnotationHandlers.add(new WebListenerAnnotationHandler(context)); } } //Regardless of metadata, if there are any ServletContainerInitializers with @HandlesTypes, then we need to scan all the //classes so we can call their onStartup() methods correctly createServletContainerInitializerAnnotationHandlers(context, getNonExcludedInitializers(context)); if (!_discoverableAnnotationHandlers.isEmpty() || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) scanForAnnotations(context); // Resolve container initializers List initializers = (List)context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); if (initializers != null && initializers.size()>0) { Map> map = ( Map>) context.getAttribute(AnnotationConfiguration.CLASS_INHERITANCE_MAP); if (map == null) LOG.warn ("ServletContainerInitializers: detected. Class hierarchy: empty"); for (ContainerInitializer i : initializers) i.resolveClasses(context,map); } } /** * @see org.eclipse.jetty.webapp.AbstractConfiguration#postConfigure(org.eclipse.jetty.webapp.WebAppContext) */ @Override public void postConfigure(WebAppContext context) throws Exception { ConcurrentHashMap> classMap = (ClassInheritanceMap)context.getAttribute(CLASS_INHERITANCE_MAP); List initializers = (List)context.getAttribute(CONTAINER_INITIALIZERS); context.removeAttribute(CLASS_INHERITANCE_MAP); if (classMap != null) classMap.clear(); context.removeAttribute(CONTAINER_INITIALIZERS); if (initializers != null) initializers.clear(); if (_discoverableAnnotationHandlers != null) _discoverableAnnotationHandlers.clear(); _classInheritanceHandler = null; if (_containerInitializerAnnotationHandlers != null) _containerInitializerAnnotationHandlers.clear(); if (_parserTasks != null) { _parserTasks.clear(); _parserTasks = null; } super.postConfigure(context); } /** * Perform scanning of classes for annotations * * @param context * @throws Exception */ protected void scanForAnnotations (WebAppContext context) throws Exception { AnnotationParser parser = createAnnotationParser(); _parserTasks = new ArrayList(); long start = 0; if (LOG.isDebugEnabled()) LOG.debug("Annotation scanning commencing: webxml={}, metadatacomplete={}, configurationDiscovered={}, multiThreaded={}, maxScanWait={}", context.getServletContext().getEffectiveMajorVersion(), context.getMetaData().isMetaDataComplete(), context.isConfigurationDiscovered(), isUseMultiThreading(context), getMaxScanWait(context)); parseContainerPath(context, parser); //email from Rajiv Mordani jsrs 315 7 April 2010 // If there is a then the ordering should be // WEB-INF/classes the order of the declared elements + others. // In case there is no others then it is // WEB-INF/classes + order of the elements. parseWebInfClasses(context, parser); parseWebInfLib (context, parser); start = System.nanoTime(); //execute scan, either effectively synchronously (1 thread only), or asynchronously (limited by number of processors available) final Semaphore task_limit = (isUseMultiThreading(context)? new Semaphore(Runtime.getRuntime().availableProcessors()):new Semaphore(1)); final CountDownLatch latch = new CountDownLatch(_parserTasks.size()); final MultiException me = new MultiException(); for (final ParserTask p:_parserTasks) { task_limit.acquire(); context.getServer().getThreadPool().execute(new Runnable() { @Override public void run() { try { p.call(); } catch (Exception e) { me.add(e); } finally { task_limit.release(); latch.countDown(); } } }); } boolean timeout = !latch.await(getMaxScanWait(context), TimeUnit.SECONDS); if (LOG.isDebugEnabled()) { for (ParserTask p:_parserTasks) LOG.debug("Scanned {} in {}ms", p.getResource(), TimeUnit.MILLISECONDS.convert(p.getStatistic().getElapsed(), TimeUnit.NANOSECONDS)); LOG.debug("Scanned {} container path jars, {} WEB-INF/lib jars, {} WEB-INF/classes dirs in {}ms for context {}", _containerPathStats.getTotal(), _webInfLibStats.getTotal(), _webInfClassesStats.getTotal(), (TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS)), context); } if (timeout) me.add(new Exception("Timeout scanning annotations")); me.ifExceptionThrow(); } /** * @return a new AnnotationParser. This method can be overridden to use a different implementation of * the AnnotationParser. Note that this is considered internal API. */ protected AnnotationParser createAnnotationParser() { return new AnnotationParser(); } /** * Check if we should use multiple threads to scan for annotations or not * @param context * @return */ protected boolean isUseMultiThreading(WebAppContext context) { //try context attribute to see if we should use multithreading Object o = context.getAttribute(MULTI_THREADED); if (o instanceof Boolean) { return ((Boolean)o).booleanValue(); } //try server attribute to see if we should use multithreading o = context.getServer().getAttribute(MULTI_THREADED); if (o instanceof Boolean) { return ((Boolean)o).booleanValue(); } //try system property to see if we should use multithreading return Boolean.valueOf(System.getProperty(MULTI_THREADED, Boolean.toString(DEFAULT_MULTI_THREADED))); } /** * Work out how long we should wait for the async scanning to occur. * * @param context * @return */ protected int getMaxScanWait (WebAppContext context) { //try context attribute to get max time in sec to wait for scan completion Object o = context.getAttribute(MAX_SCAN_WAIT); if (o != null && o instanceof Number) { return ((Number)o).intValue(); } //try server attribute to get max time in sec to wait for scan completion o = context.getServer().getAttribute(MAX_SCAN_WAIT); if (o != null && o instanceof Number) { return ((Number)o).intValue(); } //try system property to get max time in sec to wait for scan completion return Integer.getInteger(MAX_SCAN_WAIT, DEFAULT_MAX_SCAN_WAIT).intValue(); } /** * @see org.eclipse.jetty.webapp.AbstractConfiguration#cloneConfigure(org.eclipse.jetty.webapp.WebAppContext, org.eclipse.jetty.webapp.WebAppContext) */ @Override public void cloneConfigure(WebAppContext template, WebAppContext context) throws Exception { context.addDecorator(new AnnotationDecorator(context)); } /** * @param context * @param scis * @throws Exception */ public void createServletContainerInitializerAnnotationHandlers (WebAppContext context, List scis) throws Exception { if (scis == null || scis.isEmpty()) return; // nothing to do List initializers = new ArrayList(); context.setAttribute(CONTAINER_INITIALIZERS, initializers); for (ServletContainerInitializer service : scis) { HandlesTypes annotation = service.getClass().getAnnotation(HandlesTypes.class); ContainerInitializer initializer = null; if (annotation != null) { //There is a HandlesTypes annotation on the on the ServletContainerInitializer Class[] classes = annotation.value(); if (classes != null) { initializer = new ContainerInitializer(service, classes); //If we haven't already done so, we need to register a handler that will //process the whole class hierarchy to satisfy the ServletContainerInitializer if (context.getAttribute(CLASS_INHERITANCE_MAP) == null) { //MultiMap map = new MultiMap<>(); ConcurrentHashMap> map = new ClassInheritanceMap(); context.setAttribute(CLASS_INHERITANCE_MAP, map); _classInheritanceHandler = new ClassInheritanceHandler(map); } for (Class c: classes) { //The value of one of the HandlesTypes classes is actually an Annotation itself so //register a handler for it if (c.isAnnotation()) { if (LOG.isDebugEnabled()) LOG.debug("Registering annotation handler for "+c.getName()); _containerInitializerAnnotationHandlers.add(new ContainerInitializerAnnotationHandler(initializer, c)); } } } else { initializer = new ContainerInitializer(service, null); if (LOG.isDebugEnabled()) LOG.debug("No classes in HandlesTypes on initializer "+service.getClass()); } } else { initializer = new ContainerInitializer(service, null); if (LOG.isDebugEnabled()) LOG.debug("No annotation on initializer "+service.getClass()); } initializers.add(initializer); } //add a bean to the context which will call the servletcontainerinitializers when appropriate ServletContainerInitializersStarter starter = (ServletContainerInitializersStarter)context.getAttribute(CONTAINER_INITIALIZER_STARTER); if (starter != null) throw new IllegalStateException("ServletContainerInitializersStarter already exists"); starter = new ServletContainerInitializersStarter(context); context.setAttribute(CONTAINER_INITIALIZER_STARTER, starter); context.addBean(starter, true); } public Resource getJarFor (ServletContainerInitializer service) throws MalformedURLException, IOException { //try the thread context classloader to get the jar that loaded the class URL jarURL = Thread.currentThread().getContextClassLoader().getResource(service.getClass().getName().replace('.','/')+".class"); //if for some reason that failed (eg we're in osgi and the TCCL does not know about the service) try the classloader that //loaded the class if (jarURL == null) jarURL = service.getClass().getClassLoader().getResource(service.getClass().getName().replace('.','/')+".class"); String loadingJarName = jarURL.toString(); int i = loadingJarName.indexOf(".jar"); if (i < 0) return null; //not from a jar loadingJarName = loadingJarName.substring(0,i+4); loadingJarName = (loadingJarName.startsWith("jar:")?loadingJarName.substring(4):loadingJarName); return Resource.newResource(loadingJarName); } /** * Check to see if the ServletContainerIntializer loaded via the ServiceLoader came * from a jar that is excluded by the fragment ordering. See ServletSpec 3.0 p.85. * @param context * @param sci * @return true if excluded */ public boolean isFromExcludedJar (WebAppContext context, ServletContainerInitializer sci, Resource sciResource) throws Exception { if (sci == null) throw new IllegalArgumentException("ServletContainerInitializer null"); if (context == null) throw new IllegalArgumentException("WebAppContext null"); if (LOG.isDebugEnabled()) LOG.debug("Checking {} for jar exclusion", sci); //A ServletContainerInitializer that came from the container's classpath cannot be excluded by an ordering //of WEB-INF/lib jars if (isFromContainerClassPath(context, sci)) return false; //If no ordering, nothing is excluded if (context.getMetaData().getOrdering() == null) return false; List orderedJars = context.getMetaData().getOrderedWebInfJars(); //there is an ordering, but there are no jars resulting from the ordering, everything excluded if (orderedJars.isEmpty()) return true; if (sciResource == null) return false; //not from a jar therefore not from WEB-INF so not excludable URI loadingJarURI = sciResource.getURI(); boolean found = false; Iterator itor = orderedJars.iterator(); while (!found && itor.hasNext()) { Resource r = itor.next(); found = r.getURI().equals(loadingJarURI); } return !found; } /** * Test if the ServletContainerIntializer is excluded by the * o.e.j.containerInitializerExclusionPattern * @param context * @param sci * @return */ public boolean matchesExclusionPattern(ServletContainerInitializer sci) { //no exclusion pattern, no SCI is excluded by it if (_sciExcludePattern == null) return false; //test if name of class matches the regex if (LOG.isDebugEnabled()) LOG.debug("Checking {} against containerInitializerExclusionPattern",sci.getClass().getName()); return _sciExcludePattern.matcher(sci.getClass().getName()).matches(); } /** * Test if the ServletContainerInitializer is from the container classpath * * @param context * @param sci * @return */ public boolean isFromContainerClassPath (WebAppContext context, ServletContainerInitializer sci) { if (sci == null) return false; return sci.getClass().getClassLoader()==context.getClassLoader().getParent(); } /** * @param context * @return list of non-excluded {@link ServletContainerInitializer}s * @throws Exception */ public List getNonExcludedInitializers (WebAppContext context) throws Exception { ArrayList nonExcludedInitializers = new ArrayList(); //We use the ServiceLoader mechanism to find the ServletContainerInitializer classes to inspect long start = 0; ClassLoader old = Thread.currentThread().getContextClassLoader(); ServiceLoader loadedInitializers = null; try { if (LOG.isDebugEnabled()) start = System.nanoTime(); Thread.currentThread().setContextClassLoader(context.getClassLoader()); loadedInitializers = ServiceLoader.load(ServletContainerInitializer.class); } finally { Thread.currentThread().setContextClassLoader(old); } if (LOG.isDebugEnabled()) LOG.debug("Service loaders found in {}ms", (TimeUnit.MILLISECONDS.convert((System.nanoTime()-start), TimeUnit.NANOSECONDS))); Map sciResourceMap = new HashMap(); ServletContainerInitializerOrdering initializerOrdering = getInitializerOrdering(context); //Get initial set of SCIs that aren't from excluded jars or excluded by the containerExclusionPattern, or excluded //because containerInitializerOrdering omits it for (ServletContainerInitializer sci:loadedInitializers) { if (matchesExclusionPattern(sci)) { if (LOG.isDebugEnabled()) LOG.debug("{} excluded by pattern", sci); continue; } Resource sciResource = getJarFor(sci); if (isFromExcludedJar(context, sci, sciResource)) { if (LOG.isDebugEnabled()) LOG.debug("{} is from excluded jar", sci); continue; } //check containerInitializerOrdering doesn't exclude it String name = sci.getClass().getName(); if (initializerOrdering != null && (!initializerOrdering.hasWildcard() && initializerOrdering.getIndexOf(name) < 0)) { if (LOG.isDebugEnabled()) LOG.debug("{} is excluded by ordering", sci); continue; } sciResourceMap.put(sci, sciResource); } //Order the SCIs that are included if (initializerOrdering != null && !initializerOrdering.isDefaultOrder()) { if (LOG.isDebugEnabled()) LOG.debug("Ordering ServletContainerInitializers with "+initializerOrdering); //There is an ordering that is not just "*". //Arrange ServletContainerInitializers according to the ordering of classnames given, irrespective of coming from container or webapp classpaths nonExcludedInitializers.addAll(sciResourceMap.keySet()); Collections.sort(nonExcludedInitializers, new ServletContainerInitializerComparator(initializerOrdering)); } else { //No jetty-specific ordering specified, or just the wildcard value "*" specified. //Fallback to ordering the ServletContainerInitializers according to: //container classpath first, WEB-INF/classes then WEB-INF/lib (obeying any web.xml jar ordering) //no web.xml ordering defined, add SCIs in any order if (context.getMetaData().getOrdering() == null) { if (LOG.isDebugEnabled()) LOG.debug("No web.xml ordering, ServletContainerInitializers in random order"); nonExcludedInitializers.addAll(sciResourceMap.keySet()); } else { if (LOG.isDebugEnabled()) LOG.debug("Ordering ServletContainerInitializers with ordering {}",context.getMetaData().getOrdering()); for (Map.Entry entry:sciResourceMap.entrySet()) { //add in SCIs from the container classpath if (entry.getKey().getClass().getClassLoader()==context.getClassLoader().getParent()) nonExcludedInitializers.add(entry.getKey()); else if (entry.getValue() == null) //add in SCIs not in a jar, as they must be from WEB-INF/classes and can't be ordered nonExcludedInitializers.add(entry.getKey()); } //add SCIs according to the ordering of its containing jar for (Resource webInfJar:context.getMetaData().getOrderedWebInfJars()) { for (Map.Entry entry:sciResourceMap.entrySet()) { if (webInfJar.equals(entry.getValue())) nonExcludedInitializers.add(entry.getKey()); } } } } if (LOG.isDebugEnabled()) { int i=0; for (ServletContainerInitializer sci:nonExcludedInitializers) LOG.debug("ServletContainerInitializer: {} {}",(++i), sci.getClass().getName()); } return nonExcludedInitializers; } /** * Jetty-specific extension that allows an ordering to be applied across ALL ServletContainerInitializers. * * @return */ public ServletContainerInitializerOrdering getInitializerOrdering (WebAppContext context) { if (context == null) return null; String tmp = (String)context.getAttribute(SERVLET_CONTAINER_INITIALIZER_ORDER); if (tmp == null || "".equals(tmp.trim())) return null; return new ServletContainerInitializerOrdering(tmp); } /** * Scan jars on container path. * * @param context * @param parser * @throws Exception */ public void parseContainerPath (final WebAppContext context, final AnnotationParser parser) throws Exception { //always parse for discoverable annotations as well as class hierarchy and servletcontainerinitializer related annotations final Set handlers = new HashSet(); handlers.addAll(_discoverableAnnotationHandlers); handlers.addAll(_containerInitializerAnnotationHandlers); if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); _containerPathStats = new CounterStatistic(); for (Resource r : context.getMetaData().getContainerResources()) { //queue it up for scanning if using multithreaded mode if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers, r, _containerClassNameResolver); _parserTasks.add(task); _containerPathStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } } /** * Scan jars in WEB-INF/lib * * @param context * @param parser * @throws Exception */ public void parseWebInfLib (final WebAppContext context, final AnnotationParser parser) throws Exception { List frags = context.getMetaData().getFragments(); //email from Rajiv Mordani jsrs 315 7 April 2010 //jars that do not have a web-fragment.xml are still considered fragments //they have to participate in the ordering ArrayList webInfUris = new ArrayList(); List jars = null; if (context.getMetaData().getOrdering() != null) jars = context.getMetaData().getOrderedWebInfJars(); else //No ordering just use the jars in any order jars = context.getMetaData().getWebInfJars(); _webInfLibStats = new CounterStatistic(); for (Resource r : jars) { //for each jar, we decide which set of annotations we need to parse for final Set handlers = new HashSet(); FragmentDescriptor f = getFragmentFromJar(r, frags); //if its from a fragment jar that is metadata complete, we should skip scanning for @webservlet etc // but yet we still need to do the scanning for the classes on behalf of the servletcontainerinitializers //if a jar has no web-fragment.xml we scan it (because it is not excluded by the ordering) //or if it has a fragment we scan it if it is not metadata complete if (f == null || !isMetaDataComplete(f) || _classInheritanceHandler != null || !_containerInitializerAnnotationHandlers.isEmpty()) { //register the classinheritance handler if there is one if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); //register the handlers for the @HandlesTypes values that are themselves annotations if there are any handlers.addAll(_containerInitializerAnnotationHandlers); //only register the discoverable annotation handlers if this fragment is not metadata complete, or has no fragment descriptor if (f == null || !isMetaDataComplete(f)) handlers.addAll(_discoverableAnnotationHandlers); if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers,r, _webAppClassNameResolver); _parserTasks.add (task); _webInfLibStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } } } /** * Scan classes in WEB-INF/classes * * @param context * @param parser * @throws Exception */ public void parseWebInfClasses (final WebAppContext context, final AnnotationParser parser) throws Exception { Set handlers = new HashSet(); handlers.addAll(_discoverableAnnotationHandlers); if (_classInheritanceHandler != null) handlers.add(_classInheritanceHandler); handlers.addAll(_containerInitializerAnnotationHandlers); _webInfClassesStats = new CounterStatistic(); for (Resource dir : context.getMetaData().getWebInfClassesDirs()) { if (_parserTasks != null) { ParserTask task = new ParserTask(parser, handlers, dir, _webAppClassNameResolver); _parserTasks.add(task); _webInfClassesStats.increment(); if (LOG.isDebugEnabled()) task.setStatistic(new TimeStatistic()); } } } /** * Get the web-fragment.xml from a jar * * @param jar * @param frags * @return the fragment if found, or null of not found * @throws Exception */ public FragmentDescriptor getFragmentFromJar (Resource jar, List frags) throws Exception { //check if the jar has a web-fragment.xml FragmentDescriptor d = null; for (FragmentDescriptor frag: frags) { Resource fragResource = frag.getResource(); //eg jar:file:///a/b/c/foo.jar!/META-INF/web-fragment.xml if (Resource.isContainedIn(fragResource,jar)) { d = frag; break; } } return d; } public boolean isMetaDataComplete (WebDescriptor d) { return (d!=null && d.getMetaDataComplete() == MetaDataComplete.True); } public static class ClassInheritanceMap extends ConcurrentHashMap> { @Override public String toString() { return String.format("ClassInheritanceMap@%x{size=%d}",hashCode(),size()); } } } AnnotationDecorator.java000066400000000000000000000047271261716203600346460ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.servlet.ServletContextHandler.Decorator; import org.eclipse.jetty.webapp.WebAppContext; /** * AnnotationDecorator * * */ public class AnnotationDecorator implements Decorator { protected AnnotationIntrospector _introspector = new AnnotationIntrospector(); /** * @param context */ public AnnotationDecorator(WebAppContext context) { registerHandlers(context); } public void registerHandlers (WebAppContext context) { _introspector.registerHandler(new ResourceAnnotationHandler(context)); _introspector.registerHandler(new ResourcesAnnotationHandler(context)); _introspector.registerHandler(new RunAsAnnotationHandler(context)); _introspector.registerHandler(new PostConstructAnnotationHandler(context)); _introspector.registerHandler(new PreDestroyAnnotationHandler(context)); _introspector.registerHandler(new DeclareRolesAnnotationHandler(context)); _introspector.registerHandler(new MultiPartConfigAnnotationHandler(context)); _introspector.registerHandler(new ServletSecurityAnnotationHandler(context)); } /** * Look for annotations that can be discovered with introspection: *

    *
  • Resource *
  • Resources *
  • PostConstruct *
  • PreDestroy *
  • ServletSecurity? *
* @param o */ protected void introspect (Object o) { _introspector.introspect(o.getClass()); } @Override public Object decorate(Object o) { introspect(o); return o; } @Override public void destroy(Object o) { } } AnnotationIntrospector.java000066400000000000000000000060601261716203600354070ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.util.ArrayList; import java.util.List; /** * AnnotationIntrospector * * */ public class AnnotationIntrospector { protected List _handlers = new ArrayList(); /** * IntrospectableAnnotationHandler * * Interface for all handlers that wish to introspect a class to find a particular annotation */ public interface IntrospectableAnnotationHandler { public void handle(Class clazz); } /** * AbstractIntrospectableAnnotationHandler * * Base class for handlers that introspect a class to find a particular annotation. * A handler can optionally introspect the parent hierarchy of a class. */ public static abstract class AbstractIntrospectableAnnotationHandler implements IntrospectableAnnotationHandler { private boolean _introspectAncestors; public abstract void doHandle(Class clazz); public AbstractIntrospectableAnnotationHandler(boolean introspectAncestors) { _introspectAncestors = introspectAncestors; } public void handle(Class clazz) { Class c = clazz; //process the whole inheritance hierarchy for the class while (c!=null && (!c.equals(Object.class))) { doHandle(c); if (!_introspectAncestors) break; c = c.getSuperclass(); } } } public void registerHandler (IntrospectableAnnotationHandler handler) { _handlers.add(handler); } public void introspect (Class clazz) { if (_handlers == null) return; if (clazz == null) return; for (IntrospectableAnnotationHandler handler:_handlers) { try { handler.handle(clazz); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } } } } AnnotationParser.java000066400000000000000000000731341261716203600341560ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.JarScanner; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * AnnotationParser * * Use asm to scan classes for annotations. A SAX-style parsing is done. * Handlers are registered which will be called back when various types of * entity are encountered, eg a class, a method, a field. * * Handlers are not called back in any particular order and are assumed * to be order-independent. * * As a registered Handler will be called back for each annotation discovered * on a class, a method, a field, the Handler should test to see if the annotation * is one that it is interested in. * * For the servlet spec, we are only interested in annotations on classes, methods and fields, * so the callbacks for handling finding a class, a method a field are themselves * not fully implemented. */ public class AnnotationParser { private static final Logger LOG = Log.getLogger(AnnotationParser.class); protected Set _parsedClassNames = new ConcurrentHashSet(); protected static int ASM_OPCODE_VERSION = Opcodes.ASM5; //compatibility of api /** * Convert internal name to simple name * * @param name * @return */ public static String normalize (String name) { if (name==null) return null; if (name.startsWith("L") && name.endsWith(";")) name = name.substring(1, name.length()-1); if (name.endsWith(".class")) name = name.substring(0, name.length()-".class".length()); return name.replace('/', '.'); } /** * Convert internal names to simple names. * * @param list * @return */ public static String[] normalize (String[] list) { if (list == null) return null; String[] normalList = new String[list.length]; int i=0; for (String s : list) normalList[i++] = normalize(s); return normalList; } /** * ClassInfo * * Immutable information gathered by parsing class header. * */ public class ClassInfo { final Resource _containingResource; final String _className; final int _version; final int _access; final String _signature; final String _superName; final String[] _interfaces; public ClassInfo(Resource resource, String className, int version, int access, String signature, String superName, String[] interfaces) { super(); _containingResource = resource; _className = className; _version = version; _access = access; _signature = signature; _superName = superName; _interfaces = interfaces; } public String getClassName() { return _className; } public int getVersion() { return _version; } public int getAccess() { return _access; } public String getSignature() { return _signature; } public String getSuperName() { return _superName; } public String[] getInterfaces() { return _interfaces; } public Resource getContainingResource() { return _containingResource; } } /** * MethodInfo * * Immutable information gathered by parsing a method on a class. */ public class MethodInfo { final ClassInfo _classInfo; final String _methodName; final int _access; final String _desc; final String _signature; final String[] _exceptions; public MethodInfo(ClassInfo classInfo, String methodName, int access, String desc, String signature, String[] exceptions) { super(); _classInfo = classInfo; _methodName = methodName; _access = access; _desc = desc; _signature = signature; _exceptions = exceptions; } public ClassInfo getClassInfo() { return _classInfo; } public String getMethodName() { return _methodName; } public int getAccess() { return _access; } public String getDesc() { return _desc; } public String getSignature() { return _signature; } public String[] getExceptions() { return _exceptions; } } /** * FieldInfo * * Immutable information gathered by parsing a field on a class. * */ public class FieldInfo { final ClassInfo _classInfo; final String _fieldName; final int _access; final String _fieldType; final String _signature; final Object _value; public FieldInfo(ClassInfo classInfo, String fieldName, int access, String fieldType, String signature, Object value) { super(); _classInfo = classInfo; _fieldName = fieldName; _access = access; _fieldType = fieldType; _signature = signature; _value = value; } public ClassInfo getClassInfo() { return _classInfo; } public String getFieldName() { return _fieldName; } public int getAccess() { return _access; } public String getFieldType() { return _fieldType; } public String getSignature() { return _signature; } public Object getValue() { return _value; } } /** * Handler * * Signature for all handlers that respond to parsing class files. */ public static interface Handler { public void handle(ClassInfo classInfo); public void handle(MethodInfo methodInfo); public void handle (FieldInfo fieldInfo); public void handle (ClassInfo info, String annotationName); public void handle (MethodInfo info, String annotationName); public void handle (FieldInfo info, String annotationName); } /** * AbstractHandler * * Convenience base class to provide no-ops for all Handler methods. * */ public static abstract class AbstractHandler implements Handler { @Override public void handle(ClassInfo classInfo) { //no-op } @Override public void handle(MethodInfo methodInfo) { // no-op } @Override public void handle(FieldInfo fieldInfo) { // no-op } @Override public void handle(ClassInfo info, String annotationName) { // no-op } @Override public void handle(MethodInfo info, String annotationName) { // no-op } @Override public void handle(FieldInfo info, String annotationName) { // no-op } } /** * MyMethodVisitor * * ASM Visitor for parsing a method. We are only interested in the annotations on methods. */ public class MyMethodVisitor extends MethodVisitor { final MethodInfo _mi; final Set _handlers; public MyMethodVisitor(final Set handlers, final ClassInfo classInfo, final int access, final String name, final String methodDesc, final String signature, final String[] exceptions) { super(ASM_OPCODE_VERSION); _handlers = handlers; _mi = new MethodInfo(classInfo, name, access, methodDesc,signature, exceptions); } /** * We are only interested in finding the annotations on methods. * * @see org.objectweb.asm.MethodVisitor#visitAnnotation(java.lang.String, boolean) */ @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { String annotationName = normalize(desc); for (Handler h:_handlers) h.handle(_mi, annotationName); return null; } } /** * MyFieldVisitor * * An ASM visitor for parsing Fields. * We are only interested in visiting annotations on Fields. * */ public class MyFieldVisitor extends FieldVisitor { final FieldInfo _fieldInfo; final Set _handlers; public MyFieldVisitor(final Set handlers, final ClassInfo classInfo, final int access, final String fieldName, final String fieldType, final String signature, final Object value) { super(ASM_OPCODE_VERSION); _handlers = handlers; _fieldInfo = new FieldInfo(classInfo, fieldName, access, fieldType, signature, value); } /** * Parse an annotation found on a Field. * * @see org.objectweb.asm.FieldVisitor#visitAnnotation(java.lang.String, boolean) */ @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { String annotationName = normalize(desc); for (Handler h : _handlers) h.handle(_fieldInfo, annotationName); return null; } } /** * MyClassVisitor * * ASM visitor for a class. */ public class MyClassVisitor extends ClassVisitor { final Resource _containingResource; final Set _handlers; ClassInfo _ci; public MyClassVisitor(Set handlers, Resource containingResource) { super(ASM_OPCODE_VERSION); _handlers = handlers; _containingResource = containingResource; } @Override public void visit (final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { _ci = new ClassInfo(_containingResource, normalize(name), version, access, signature, normalize(superName), normalize(interfaces)); _parsedClassNames.add(_ci.getClassName()); for (Handler h:_handlers) h.handle(_ci); } /** * Visit an annotation on a Class * * @see org.objectweb.asm.ClassVisitor#visitAnnotation(java.lang.String, boolean) */ @Override public AnnotationVisitor visitAnnotation (String desc, boolean visible) { String annotationName = normalize(desc); for (Handler h : _handlers) h.handle(_ci, annotationName); return null; } /** * Visit a method to extract its annotations * * @see org.objectweb.asm.ClassVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) */ @Override public MethodVisitor visitMethod (final int access, final String name, final String methodDesc, final String signature, final String[] exceptions) { return new MyMethodVisitor(_handlers, _ci, access, name, methodDesc, signature, exceptions); } /** * Visit a field to extract its annotations * * @see org.objectweb.asm.ClassVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object) */ @Override public FieldVisitor visitField (final int access, final String fieldName, final String fieldType, final String signature, final Object value) { return new MyFieldVisitor(_handlers, _ci, access, fieldName, fieldType, signature, value); } } /** * True if the class has already been processed, false otherwise * @param className */ public boolean isParsed (String className) { return _parsedClassNames.contains(className); } /** * Parse a given class * * @param className * @param resolver * @throws Exception */ public void parse (Set handlers, String className, ClassNameResolver resolver) throws Exception { if (className == null) return; if (!resolver.isExcluded(className)) { if (!isParsed(className) || resolver.shouldOverride(className)) { className = className.replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), className); if (resource!= null) { Resource r = Resource.newResource(resource); scanClass(handlers, null, r.getInputStream()); } } } } /** * Parse the given class, optionally walking its inheritance hierarchy * * @param clazz * @param resolver * @param visitSuperClasses * @throws Exception */ public void parse (Set handlers, Class clazz, ClassNameResolver resolver, boolean visitSuperClasses) throws Exception { Class cz = clazz; while (cz != null) { if (!resolver.isExcluded(cz.getName())) { if (!isParsed(cz.getName()) || resolver.shouldOverride(cz.getName())) { String nameAsResource = cz.getName().replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), nameAsResource); if (resource!= null) { Resource r = Resource.newResource(resource); scanClass(handlers, null, r.getInputStream()); } } } if (visitSuperClasses) cz = cz.getSuperclass(); else cz = null; } } /** * Parse the given classes * * @param classNames * @param resolver * @throws Exception */ public void parse (Set handlers, String[] classNames, ClassNameResolver resolver) throws Exception { if (classNames == null) return; parse(handlers, Arrays.asList(classNames), resolver); } /** * Parse the given classes * * @param classNames * @param resolver * @throws Exception */ public void parse (Set handlers, List classNames, ClassNameResolver resolver) throws Exception { MultiException me = new MultiException(); for (String s:classNames) { try { if ((resolver == null) || (!resolver.isExcluded(s) && (!isParsed(s) || resolver.shouldOverride(s)))) { s = s.replace('.', '/')+".class"; URL resource = Loader.getResource(this.getClass(), s); if (resource!= null) { Resource r = Resource.newResource(resource); scanClass(handlers, null, r.getInputStream()); } } } catch (Exception e) { me.add(new RuntimeException("Error scanning class "+s, e)); } } me.ifExceptionThrow(); } /** * Parse all classes in a directory * * @param dir * @param resolver * @throws Exception */ protected void parseDir (Set handlers, Resource dir, ClassNameResolver resolver) throws Exception { //skip dirs whose name start with . (ie hidden) if (!dir.isDirectory() || !dir.exists() || dir.getName().startsWith(".")) return; if (LOG.isDebugEnabled()) {LOG.debug("Scanning dir {}", dir);}; MultiException me = new MultiException(); String[] files=dir.list(); for (int f=0;files!=null && f handlers, ClassLoader loader, boolean visitParents, boolean nullInclusive, final ClassNameResolver resolver) throws Exception { if (loader==null) return; if (!(loader instanceof URLClassLoader)) return; //can't extract classes? final MultiException me = new MultiException(); JarScanner scanner = new JarScanner() { @Override public void processEntry(URI jarUri, JarEntry entry) { try { parseJarEntry(handlers, Resource.newResource(jarUri), entry, resolver); } catch (Exception e) { me.add(new RuntimeException("Error parsing entry "+entry.getName()+" from jar "+ jarUri, e)); } } }; scanner.scan(null, loader, nullInclusive, visitParents); me.ifExceptionThrow(); } /** * Parse classes in the supplied uris. * * @param uris * @param resolver * @throws Exception */ public void parse (final Set handlers, final URI[] uris, final ClassNameResolver resolver) throws Exception { if (uris==null) return; MultiException me = new MultiException(); for (URI uri:uris) { try { parse(handlers, uri, resolver); } catch (Exception e) { me.add(new RuntimeException("Problem parsing classes from "+ uri, e)); } } me.ifExceptionThrow(); } /** * Parse a particular uri * @param uri * @param resolver * @throws Exception */ public void parse (final Set handlers, URI uri, final ClassNameResolver resolver) throws Exception { if (uri == null) return; parse (handlers, Resource.newResource(uri), resolver); } /** * Parse a resource * @param r * @param resolver * @throws Exception */ public void parse (final Set handlers, Resource r, final ClassNameResolver resolver) throws Exception { if (r == null) return; if (r.exists() && r.isDirectory()) { parseDir(handlers, r, resolver); return; } String fullname = r.toString(); if (fullname.endsWith(".jar")) { parseJar(handlers, r, resolver); return; } if (fullname.endsWith(".class")) { scanClass(handlers, null, r.getInputStream()); return; } if (LOG.isDebugEnabled()) LOG.warn("Resource not scannable for classes: {}", r); } /** * Parse a resource that is a jar file. * * @param jarResource * @param resolver * @throws Exception */ protected void parseJar (Set handlers, Resource jarResource, final ClassNameResolver resolver) throws Exception { if (jarResource == null) return; if (jarResource.toString().endsWith(".jar")) { if (LOG.isDebugEnabled()) {LOG.debug("Scanning jar {}", jarResource);}; //treat it as a jar that we need to open and scan all entries from //TODO alternative impl /* long start = System.nanoTime(); Collection resources = Resource.newResource("jar:"+jarResource+"!/").getAllResources(); System.err.println(jarResource+String.valueOf(resources.size())+" resources listed in "+ ((TimeUnit.MILLISECONDS.convert(System.nanoTime()-start, TimeUnit.NANOSECONDS)))); for (Resource r:resources) { //skip directories if (r.isDirectory()) continue; String name = r.getName(); name = name.substring(name.indexOf("!/")+2); //check file is a valid class file name if (isValidClassFileName(name) && isValidClassFilePath(name)) { String shortName = name.replace('/', '.').substring(0,name.length()-6); if ((resolver == null) || (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName)))) { if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", r);}; scanClass(handlers, jarResource, r.getInputStream()); } } } */ InputStream in = jarResource.getInputStream(); if (in==null) return; MultiException me = new MultiException(); JarInputStream jar_in = new JarInputStream(in); try { JarEntry entry = jar_in.getNextJarEntry(); while (entry!=null) { try { parseJarEntry(handlers, jarResource, entry, resolver); } catch (Exception e) { me.add(new RuntimeException("Error scanning entry "+entry.getName()+" from jar "+jarResource, e)); } entry = jar_in.getNextJarEntry(); } } finally { jar_in.close(); } me.ifExceptionThrow(); } } /** * Parse a single entry in a jar file * @param jar * @param entry * @param resolver * @throws Exception */ protected void parseJarEntry (Set handlers, Resource jar, JarEntry entry, final ClassNameResolver resolver) throws Exception { if (jar == null || entry == null) return; //skip directories if (entry.isDirectory()) return; String name = entry.getName(); //check file is a valid class file name if (isValidClassFileName(name) && isValidClassFilePath(name)) { String shortName = name.replace('/', '.').substring(0,name.length()-6); if ((resolver == null) || (!resolver.isExcluded(shortName) && (!isParsed(shortName) || resolver.shouldOverride(shortName)))) { Resource clazz = Resource.newResource("jar:"+jar.getURI()+"!/"+name); if (LOG.isDebugEnabled()) {LOG.debug("Scanning class from jar {}", clazz);}; scanClass(handlers, jar, clazz.getInputStream()); } } } /** * Use ASM on a class * * @param containingResource the dir or jar that the class is contained within, can be null if not known * @param is * @throws IOException */ protected void scanClass (Set handlers, Resource containingResource, InputStream is) throws IOException { ClassReader reader = new ClassReader(is); reader.accept(new MyClassVisitor(handlers, containingResource), ClassReader.SKIP_CODE|ClassReader.SKIP_DEBUG|ClassReader.SKIP_FRAMES); } /** * Check that the given path represents a valid class file name. * The check is fairly cursory, checking that: *
    *
  • the name ends with .class
  • *
  • it isn't a dot file or in a hidden directory
  • *
  • the name of the class at least begins with a valid identifier for a class name
  • *
* @param name * @return */ private boolean isValidClassFileName (String name) { //no name cannot be valid if (name == null || name.length()==0) return false; //skip anything that is not a class file if (!name.toLowerCase(Locale.ENGLISH).endsWith(".class")) { if (LOG.isDebugEnabled()) LOG.debug("Not a class: {}",name); return false; } //skip any classfiles that are not a valid java identifier int c0 = 0; int ldir = name.lastIndexOf('/', name.length()-6); c0 = (ldir > -1 ? ldir+1 : c0); if (!Character.isJavaIdentifierStart(name.charAt(c0))) { if (LOG.isDebugEnabled()) LOG.debug("Not a java identifier: {}"+name); return false; } return true; } /** * Check that the given path does not contain hidden directories * * @param path * @return */ private boolean isValidClassFilePath (String path) { //no path is not valid if (path == null || path.length()==0) return false; //skip any classfiles that are in a hidden directory if (path.startsWith(".") || path.contains("/.")) { if (LOG.isDebugEnabled()) LOG.debug("Contains hidden dirs: {}"+path); return false; } return true; } } ClassInheritanceHandler.java000066400000000000000000000064361261716203600354050ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.util.ConcurrentHashSet; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * ClassInheritanceHandler * * As asm scans for classes, remember the type hierarchy. */ public class ClassInheritanceHandler extends AbstractHandler { private static final Logger LOG = Log.getLogger(ClassInheritanceHandler.class); ConcurrentHashMap> _inheritanceMap; public ClassInheritanceHandler(ConcurrentHashMap> map) { _inheritanceMap = map; } public void handle(ClassInfo classInfo) { try { for (int i=0; classInfo.getInterfaces() != null && i < classInfo.getInterfaces().length;i++) { addToInheritanceMap(classInfo.getInterfaces()[i], classInfo.getClassName()); //_inheritanceMap.add (classInfo.getInterfaces()[i], classInfo.getClassName()); } //To save memory, we don't record classes that only extend Object, as that can be assumed if (!"java.lang.Object".equals(classInfo.getSuperName())) { addToInheritanceMap(classInfo.getSuperName(), classInfo.getClassName()); //_inheritanceMap.add(classInfo.getSuperName(), classInfo.getClassName()); } } catch (Exception e) { LOG.warn(e); } } private void addToInheritanceMap (String interfaceOrSuperClassName, String implementingOrExtendingClassName) { //As it is likely that the interfaceOrSuperClassName is already in the map, try getting it first ConcurrentHashSet implementingClasses = _inheritanceMap.get(interfaceOrSuperClassName); //If it isn't in the map, then add it in, but test to make sure that someone else didn't get in //first and add it if (implementingClasses == null) { implementingClasses = new ConcurrentHashSet(); ConcurrentHashSet tmp = _inheritanceMap.putIfAbsent(interfaceOrSuperClassName, implementingClasses); if (tmp != null) implementingClasses = tmp; } implementingClasses.add(implementingOrExtendingClassName); } } ClassNameResolver.java000066400000000000000000000026111261716203600342470ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; public interface ClassNameResolver { /** * Based on the execution context, should the class represented * by "name" be excluded from consideration? * @param name * @return true if classname is excluded */ public boolean isExcluded (String name); /** * Based on the execution context, if a duplicate class * represented by "name" is detected, should the existing * one be overridden or not? * @param name * @return true if name should be overridden */ public boolean shouldOverride (String name); } ContainerInitializerAnnotationHandler.java000066400000000000000000000063561261716203600403500ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.plus.annotation.ContainerInitializer; /** * ContainerInitializerAnnotationHandler * * Discovers classes that contain the specified annotation, either at class or * method level. The specified annotation is derived from an @HandlesTypes on * a ServletContainerInitializer class. */ /** * @author janb * */ public class ContainerInitializerAnnotationHandler extends AbstractHandler { final ContainerInitializer _initializer; final Class _annotation; public ContainerInitializerAnnotationHandler (ContainerInitializer initializer, Class annotation) { _initializer = initializer; _annotation = annotation; } /** * Handle finding a class that is annotated with the annotation we were constructed with. * * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String) */ public void handle(ClassInfo info, String annotationName) { if (annotationName == null || !_annotation.getName().equals(annotationName)) return; _initializer.addAnnotatedTypeName(info.getClassName()); } /** * Handle finding a field that is annotated with the annotation we were constructed with. * * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(FieldInfo, String) */ public void handle(FieldInfo info, String annotationName) { if (annotationName == null || !_annotation.getName().equals(annotationName)) return; _initializer.addAnnotatedTypeName(info.getClassInfo().getClassName()); } /** * Handle finding a method that is annotated with the annotation we were constructed with. * * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(MethodInfo, String) */ public void handle(MethodInfo info, String annotationName) { if (annotationName == null || !_annotation.getName().equals(annotationName)) return; _initializer.addAnnotatedTypeName(info.getClassInfo().getClassName()); } public ContainerInitializer getContainerInitializer() { return _initializer; } } DeclareRolesAnnotationHandler.java000066400000000000000000000042041261716203600365540ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import javax.annotation.security.DeclareRoles; import javax.servlet.Servlet; import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.webapp.WebAppContext; /** * DeclaresRolesAnnotationHandler * * */ public class DeclareRolesAnnotationHandler extends AbstractIntrospectableAnnotationHandler { protected WebAppContext _context; /** * @param context */ public DeclareRolesAnnotationHandler(WebAppContext context) { super(false); _context = context; } /** * @see org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler#doHandle(java.lang.Class) */ public void doHandle(Class clazz) { if (!Servlet.class.isAssignableFrom(clazz)) return; //only applicable on javax.servlet.Servlet derivatives DeclareRoles declareRoles = (DeclareRoles) clazz.getAnnotation(DeclareRoles.class); if (declareRoles == null) return; String[] roles = declareRoles.value(); if (roles != null && roles.length > 0) { for (String r:roles) ((ConstraintSecurityHandler)_context.getSecurityHandler()).addRole(r); } } } MultiPartConfigAnnotationHandler.java000066400000000000000000000067501261716203600372670ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import javax.servlet.MultipartConfigElement; import javax.servlet.Servlet; import javax.servlet.annotation.MultipartConfig; import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.webapp.Descriptor; import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.WebAppContext; /** * MultiPartConfigAnnotationHandler * * */ public class MultiPartConfigAnnotationHandler extends AbstractIntrospectableAnnotationHandler { protected WebAppContext _context; public MultiPartConfigAnnotationHandler(WebAppContext context) { //TODO verify that MultipartConfig is not inheritable super(false); _context = context; } /** * @see org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler#doHandle(java.lang.Class) */ public void doHandle(Class clazz) { if (!Servlet.class.isAssignableFrom(clazz)) return; MultipartConfig multi = (MultipartConfig) clazz.getAnnotation(MultipartConfig.class); if (multi == null) return; MetaData metaData = _context.getMetaData(); //TODO: The MultipartConfigElement needs to be set on the ServletHolder's Registration. //How to identify the correct Servlet? If the Servlet has no WebServlet annotation on it, does it mean that this MultipartConfig //annotation applies to all declared instances in web.xml/programmatically? //Assuming TRUE for now. ServletHolder holder = getServletHolderForClass(clazz); if (holder != null) { Descriptor d = metaData.getOriginDescriptor(holder.getName()+".servlet.multipart-config"); //if a descriptor has already set the value for multipart config, do not //let the annotation override it if (d == null) { metaData.setOrigin(holder.getName()+".servlet.multipart-config",multi,clazz); holder.getRegistration().setMultipartConfig(new MultipartConfigElement(multi)); } } } private ServletHolder getServletHolderForClass (Class clazz) { ServletHolder holder = null; ServletHolder[] holders = _context.getServletHandler().getServlets(); if (holders != null) { for (ServletHolder h : holders) { if (h.getClassName() != null && h.getClassName().equals(clazz.getName())) { holder = h; } } } return holder; } } PostConstructAnnotationHandler.java000066400000000000000000000073561261716203600370550ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import javax.annotation.PostConstruct; import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.plus.annotation.LifeCycleCallbackCollection; import org.eclipse.jetty.plus.annotation.PostConstructCallback; import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.Origin; import org.eclipse.jetty.webapp.WebAppContext; public class PostConstructAnnotationHandler extends AbstractIntrospectableAnnotationHandler { protected WebAppContext _context; public PostConstructAnnotationHandler (WebAppContext wac) { super(true); _context = wac; } public void doHandle(Class clazz) { //Check that the PostConstruct is on a class that we're interested in if (Util.supportsPostConstructPreDestroy(clazz)) { Method[] methods = clazz.getDeclaredMethods(); for (int i=0; i clazz) { if (Util.supportsResourceInjection(clazz)) { handleClass(clazz); Method[] methods = clazz.getDeclaredMethods(); for (int i=0; i clazz) { Resource resource = (Resource)clazz.getAnnotation(Resource.class); if (resource != null) { String name = resource.name(); String mappedName = resource.mappedName(); if (name==null || name.trim().equals("")) throw new IllegalStateException ("Class level Resource annotations must contain a name (Common Annotations Spec Section 2.3)"); try { if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name,mappedName)) if (!org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name,mappedName)) throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName)); } catch (NamingException e) { LOG.warn(e); } } } public void handleField(Class clazz, Field field) { Resource resource = (Resource)field.getAnnotation(Resource.class); if (resource != null) { //JavaEE Spec 5.2.3: Field cannot be static if (Modifier.isStatic(field.getModifiers())) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+field.getName()+": cannot be static"); return; } //JavaEE Spec 5.2.3: Field cannot be final if (Modifier.isFinal(field.getModifiers())) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+field.getName()+": cannot be final"); return; } //work out default name String name = clazz.getCanonicalName()+"/"+field.getName(); //allow @Resource name= to override the field name name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name); String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null); //get the type of the Field Class type = field.getType(); //Servlet Spec 3.0 p. 76 //If a descriptor has specified at least 1 injection target for this //resource, then it overrides this annotation MetaData metaData = _context.getMetaData(); if (metaData.getOriginDescriptor("resource-ref."+name+".injection") != null) { //at least 1 injection was specified for this resource by a descriptor, so //it overrides this annotation return; } //No injections for this resource in any descriptors, so we can add it //Does the injection already exist? InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); if (injections == null) { injections = new InjectionCollection(); _context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); } Injection injection = injections.getInjection(name, clazz, field); if (injection == null) { //No injection has been specified, add it try { boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName); if (!bound) bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); if (!bound) bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(null, name, mappedName); if (!bound) { //see if there is an env-entry value been bound try { InitialContext ic = new InitialContext(); String nameInEnvironment = (mappedName!=null?mappedName:name); ic.lookup("java:comp/env/"+nameInEnvironment); bound = true; } catch (NameNotFoundException e) { bound = false; } } //Check there is a JNDI entry for this annotation if (bound) { LOG.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it if the binding succeeded injection = new Injection(); injection.setTarget(clazz, field, type); injection.setJndiName(name); injection.setMappingName(mappedName); injections.add(injection); //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination metaData.setOrigin("resource-ref."+name+".injection",resource,clazz); } else if (!Util.isEnvEntryType(type)) { //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName)); } } catch (NamingException e) { //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 if (!Util.isEnvEntryType(type)) throw new IllegalStateException(e); } } } } /** * Process a Resource annotation on a Method. * * This will generate a JNDI entry, and an Injection to be * processed when an instance of the class is created. */ public void handleMethod(Class clazz, Method method) { Resource resource = (Resource)method.getAnnotation(Resource.class); if (resource != null) { /* * Commons Annotations Spec 2.3 * " The Resource annotation is used to declare a reference to a resource. * It can be specified on a class, methods or on fields. When the * annotation is applied on a field or method, the container will * inject an instance of the requested resource into the application * when the application is initialized... Even though this annotation * is not marked Inherited, if used all superclasses MUST be examined * to discover all uses of this annotation. All such annotation instances * specify resources that are needed by the application. Note that this * annotation may appear on private fields and methods of the superclasses. * Injection of the declared resources needs to happen in these cases as * well, even if a method with such an annotation is overridden by a subclass." * * Which IMHO, put more succinctly means "If you find a @Resource on any method * or field, inject it!". */ //JavaEE Spec 5.2.3: Method cannot be static if (Modifier.isStatic(method.getModifiers())) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": cannot be static"); return; } // Check it is a valid javabean: must be void return type, the name must start with "set" and it must have // only 1 parameter if (!method.getName().startsWith("set")) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, does not start with 'set'"); return; } if (method.getParameterTypes().length != 1) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, not single argument to method"); return; } if (Void.TYPE != method.getReturnType()) { LOG.warn("Skipping Resource annotation on "+clazz.getName()+"."+method.getName()+": invalid java bean, not void"); return; } //default name is the javabean property name String name = method.getName().substring(3); name = name.substring(0,1).toLowerCase(Locale.ENGLISH)+name.substring(1); name = clazz.getCanonicalName()+"/"+name; name = (resource.name()!=null && !resource.name().trim().equals("")? resource.name(): name); String mappedName = (resource.mappedName()!=null && !resource.mappedName().trim().equals("")?resource.mappedName():null); Class paramType = method.getParameterTypes()[0]; Class resourceType = resource.type(); //Servlet Spec 3.0 p. 76 //If a descriptor has specified at least 1 injection target for this //resource, then it overrides this annotation MetaData metaData = _context.getMetaData(); if (metaData.getOriginDescriptor("resource-ref."+name+".injection") != null) { //at least 1 injection was specified for this resource by a descriptor, so //it overrides this annotation return; } //check if an injection has already been setup for this target by web.xml InjectionCollection injections = (InjectionCollection)_context.getAttribute(InjectionCollection.INJECTION_COLLECTION); if (injections == null) { injections = new InjectionCollection(); _context.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); } Injection injection = injections.getInjection(name, clazz, method, paramType); if (injection == null) { try { //try binding name to environment //try the webapp's environment first boolean bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context, name, mappedName); //try the server's environment if (!bound) bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(_context.getServer(), name, mappedName); //try the jvm's environment if (!bound) bound = org.eclipse.jetty.plus.jndi.NamingEntryUtil.bindToENC(null, name, mappedName); //TODO if it is an env-entry from web.xml it can be injected, in which case there will be no //NamingEntry, just a value bound in java:comp/env if (!bound) { try { InitialContext ic = new InitialContext(); String nameInEnvironment = (mappedName!=null?mappedName:name); ic.lookup("java:comp/env/"+nameInEnvironment); bound = true; } catch (NameNotFoundException e) { bound = false; } } if (bound) { LOG.debug("Bound "+(mappedName==null?name:mappedName) + " as "+ name); // Make the Injection for it injection = new Injection(); injection.setTarget(clazz, method,paramType,resourceType); injection.setJndiName(name); injection.setMappingName(mappedName); injections.add(injection); //TODO - an @Resource is equivalent to a resource-ref, resource-env-ref, message-destination metaData.setOrigin("resource-ref."+name+".injection",resource,clazz); } else if (!Util.isEnvEntryType(paramType)) { //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 throw new IllegalStateException("No resource at "+(mappedName==null?name:mappedName)); } } catch (NamingException e) { //if this is an env-entry type resource and there is no value bound for it, it isn't //an error, it just means that perhaps the code will use a default value instead // JavaEE Spec. sec 5.4.1.3 if (!Util.isEnvEntryType(paramType)) throw new IllegalStateException(e); } } } } } ResourcesAnnotationHandler.java000066400000000000000000000056521261716203600361720ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import javax.annotation.Resource; import javax.annotation.Resources; import javax.naming.NamingException; import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; public class ResourcesAnnotationHandler extends AbstractIntrospectableAnnotationHandler { private static final Logger LOG = Log.getLogger(ResourcesAnnotationHandler.class); protected WebAppContext _wac; public ResourcesAnnotationHandler (WebAppContext wac) { super(true); _wac = wac; } public void doHandle (Class clazz) { Resources resources = (Resources)clazz.getAnnotation(Resources.class); if (resources != null) { Resource[] resArray = resources.value(); if (resArray==null||resArray.length==0) { LOG.warn ("Skipping empty or incorrect Resources annotation on "+clazz.getName()); return; } for (int j=0;j initializers = (List)_context.getAttribute(AnnotationConfiguration.CONTAINER_INITIALIZERS); if (initializers == null) return; for (ContainerInitializer i : initializers) { try { if (LOG.isDebugEnabled()) LOG.debug("Calling ServletContainerInitializer "+i.getTarget().getClass().getName()); i.callStartup(_context); } catch (Exception e) { LOG.warn(e); throw new RuntimeException(e); } } } } ServletSecurityAnnotationHandler.java000066400000000000000000000165061261716203600373740ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletSecurityElement; import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic; import javax.servlet.annotation.ServletSecurity.TransportGuarantee; import org.eclipse.jetty.annotations.AnnotationIntrospector.AbstractIntrospectableAnnotationHandler; import org.eclipse.jetty.security.ConstraintAware; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.webapp.WebAppContext; /** * ServletSecurityAnnotationHandler * * Inspect a class to see if it has an @ServletSecurity annotation on it, * setting up the s. * * A servlet can be defined in: *
    *
  • web.xml *
  • web-fragment.xml *
  • @WebServlet annotation discovered *
  • ServletContext.createServlet *
* * The ServletSecurity annotation for a servlet should only be processed * iff metadata-complete == false. */ public class ServletSecurityAnnotationHandler extends AbstractIntrospectableAnnotationHandler { private static final Logger LOG = Log.getLogger(ServletSecurityAnnotationHandler.class); private WebAppContext _context; public ServletSecurityAnnotationHandler(WebAppContext wac) { super(false); _context = wac; } /** * @see org.eclipse.jetty.annotations.AnnotationIntrospector.IntrospectableAnnotationHandler#handle(java.lang.Class) */ public void doHandle(Class clazz) { if (!(_context.getSecurityHandler() instanceof ConstraintAware)) { LOG.warn("SecurityHandler not ConstraintAware, skipping security annotation processing"); return; } ServletSecurity servletSecurity = (ServletSecurity)clazz.getAnnotation(ServletSecurity.class); if (servletSecurity == null) return; //If there are already constraints defined (ie from web.xml) that match any //of the url patterns defined for this servlet, then skip the security annotation. List servletMappings = getServletMappings(clazz.getCanonicalName()); List constraintMappings = ((ConstraintAware)_context.getSecurityHandler()).getConstraintMappings(); if (constraintsExist(servletMappings, constraintMappings)) { LOG.warn("Constraints already defined for "+clazz.getName()+", skipping ServletSecurity annotation"); return; } //Make a fresh list constraintMappings = new ArrayList(); ServletSecurityElement securityElement = new ServletSecurityElement(servletSecurity); for (ServletMapping sm : servletMappings) { for (String url : sm.getPathSpecs()) { _context.getMetaData().setOrigin("constraint.url."+url,servletSecurity,clazz); constraintMappings.addAll(ConstraintSecurityHandler.createConstraintsWithMappingsForPath(clazz.getName(), url, securityElement)); } } //set up the security constraints produced by the annotation ConstraintAware securityHandler = (ConstraintAware)_context.getSecurityHandler(); for (ConstraintMapping m:constraintMappings) securityHandler.addConstraintMapping(m); //Servlet Spec 3.1 requires paths with uncovered http methods to be reported securityHandler.checkPathsWithUncoveredHttpMethods(); } /** * Make a jetty Constraint object, which represents the and * elements, based on the security annotation. * @param servlet * @param rolesAllowed * @param permitOrDeny * @param transport */ protected Constraint makeConstraint (Class servlet, String[] rolesAllowed, EmptyRoleSemantic permitOrDeny, TransportGuarantee transport) { return ConstraintSecurityHandler.createConstraint(servlet.getName(), rolesAllowed, permitOrDeny, transport); } /** * Get the ServletMappings for the servlet's class. * @param className */ protected List getServletMappings(String className) { List results = new ArrayList(); ServletMapping[] mappings = _context.getServletHandler().getServletMappings(); for (ServletMapping mapping : mappings) { //Check the name of the servlet that this mapping applies to, and then find the ServletHolder for it to find it's class ServletHolder holder = _context.getServletHandler().getServlet(mapping.getServletName()); if (holder.getClassName() != null && holder.getClassName().equals(className)) results.add(mapping); } return results; } /** * Check if there are already elements defined that match the url-patterns for * the servlet. * @param servletMappings */ protected boolean constraintsExist (List servletMappings, List constraintMappings) { boolean exists = false; //Check to see if the path spec on each constraint mapping matches a pathSpec in the servlet mappings. //If it does, then we should ignore the security annotations. for (ServletMapping mapping : servletMappings) { //Get its url mappings String[] pathSpecs = mapping.getPathSpecs(); if (pathSpecs == null) continue; //Check through the constraints to see if there are any whose pathSpecs (url mappings) //match the servlet. If so, then we already have constraints defined for this servlet, //and we will not be processing the annotation (ie web.xml or programmatic override). for (int i=0; constraintMappings != null && i < constraintMappings.size() && !exists; i++) { for (int j=0; j < pathSpecs.length; j++) { //TODO decide if we need to check the origin if (pathSpecs[j].equals(constraintMappings.get(i).getPathSpec())) { exists = true; break; } } } } return exists; } } jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/Util.java000066400000000000000000000222411261716203600316540ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.lang.reflect.Array; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.TypeUtil; import org.objectweb.asm.Type; /** * Util */ public class Util { private static Class[] __envEntryClassTypes = new Class[] {String.class, Character.class, Integer.class, Boolean.class, Double.class, Byte.class, Short.class, Long.class, Float.class}; private static String[] __envEntryTypes = new String[] { Type.getDescriptor(String.class), Type.getDescriptor(Character.class), Type.getDescriptor(Integer.class), Type.getDescriptor(Boolean.class), Type.getDescriptor(Double.class), Type.getDescriptor(Byte.class), Type.getDescriptor(Short.class), Type.getDescriptor(Long.class), Type.getDescriptor(Float.class)}; /** * Check if the presented method belongs to a class that is one * of the classes with which a servlet container should be concerned. * @param c * @return true if class is a type of one of the following: * ({@link javax.servlet.Servlet}, * {@link javax.servlet.Filter}, * {@link javax.servlet.ServletContextListener}, * {@link javax.servlet.ServletContextAttributeListener}, * {@link javax.servlet.ServletRequestListener}, * {@link javax.servlet.ServletRequestAttributeListener}, * {@link javax.servlet.http.HttpSessionListener}, * {@link javax.servlet.http.HttpSessionAttributeListener}) */ public static boolean isServletType (Class c) { boolean isServlet = false; if (javax.servlet.Servlet.class.isAssignableFrom(c) || javax.servlet.Filter.class.isAssignableFrom(c) || javax.servlet.ServletContextListener.class.isAssignableFrom(c) || javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) || javax.servlet.AsyncListener.class.isAssignableFrom(c)) isServlet=true; return isServlet; } public static boolean supportsResourceInjection (Class c) { if (javax.servlet.Servlet.class.isAssignableFrom(c) || javax.servlet.Filter.class.isAssignableFrom(c) || javax.servlet.ServletContextListener.class.isAssignableFrom(c) || javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) || javax.servlet.AsyncListener.class.isAssignableFrom(c) || javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c)) return true; return false; } public static boolean supportsPostConstructPreDestroy (Class c) { if (javax.servlet.Servlet.class.isAssignableFrom(c) || javax.servlet.Filter.class.isAssignableFrom(c) || javax.servlet.ServletContextListener.class.isAssignableFrom(c) || javax.servlet.ServletContextAttributeListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestListener.class.isAssignableFrom(c) || javax.servlet.ServletRequestAttributeListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionAttributeListener.class.isAssignableFrom(c) || javax.servlet.http.HttpSessionIdListener.class.isAssignableFrom(c) || javax.servlet.AsyncListener.class.isAssignableFrom(c) || javax.servlet.http.HttpUpgradeHandler.class.isAssignableFrom(c)) return true; return false; } public static boolean isEnvEntryType (Class type) { boolean result = false; for (int i=0;i<__envEntryClassTypes.length && !result;i++) { result = (type.equals(__envEntryClassTypes[i])); } return result; } public static boolean isEnvEntryType (String desc) { boolean result = false; for (int i=0;i<__envEntryTypes.length && !result;i++) { result = (desc.equals(__envEntryTypes[i])); } return result; } public static String normalizePattern(String p) { if (p!=null && p.length()>0 && !p.startsWith("/") && !p.startsWith("*")) return "/"+p; return p; } public static Class[] convertTypes (String params) throws Exception { return convertTypes(Type.getArgumentTypes(params)); } public static Class[] convertTypes (Type[] types) throws Exception { if (types==null) return new Class[0]; Class[] classArray = new Class[types.length]; for (int i=0; i 0 && filterAnnotation.urlPatterns().length > 0) { LOG.warn(clazz.getName()+" defines both @WebFilter.value and @WebFilter.urlPatterns"); return; } String name = (filterAnnotation.filterName().equals("")?clazz.getName():filterAnnotation.filterName()); String[] urlPatterns = filterAnnotation.value(); if (urlPatterns.length == 0) urlPatterns = filterAnnotation.urlPatterns(); FilterHolder holder = _context.getServletHandler().getFilter(name); if (holder == null) { //Filter with this name does not already exist, so add it holder = _context.getServletHandler().newFilterHolder(Holder.Source.ANNOTATION); holder.setName(name); holder.setHeldClass(clazz); metaData.setOrigin(name+".filter.filter-class",filterAnnotation,clazz); holder.setDisplayName(filterAnnotation.displayName()); metaData.setOrigin(name+".filter.display-name",filterAnnotation,clazz); for (WebInitParam ip: filterAnnotation.initParams()) { holder.setInitParameter(ip.name(), ip.value()); metaData.setOrigin(name+".filter.init-param."+ip.name(),ip,clazz); } FilterMapping mapping = new FilterMapping(); mapping.setFilterName(holder.getName()); if (urlPatterns.length > 0) { ArrayList paths = new ArrayList(); for (String s:urlPatterns) { paths.add(Util.normalizePattern(s)); } mapping.setPathSpecs(paths.toArray(new String[paths.size()])); } if (filterAnnotation.servletNames().length > 0) { ArrayList names = new ArrayList(); for (String s : filterAnnotation.servletNames()) { names.add(s); } mapping.setServletNames(names.toArray(new String[names.size()])); } EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); for (DispatcherType d : filterAnnotation.dispatcherTypes()) { dispatcherSet.add(d); } mapping.setDispatcherTypes(dispatcherSet); metaData.setOrigin(name+".filter.mappings",filterAnnotation,clazz); holder.setAsyncSupported(filterAnnotation.asyncSupported()); metaData.setOrigin(name+".filter.async-supported",filterAnnotation,clazz); _context.getServletHandler().addFilter(holder); _context.getServletHandler().addFilterMapping(mapping); } else { //A Filter definition for the same name already exists from web.xml //ServletSpec 3.0 p81 if the Filter is already defined and has mappings, //they override the annotation. If it already has DispatcherType set, that //also overrides the annotation. Init-params are additive, but web.xml overrides //init-params of the same name. for (WebInitParam ip: filterAnnotation.initParams()) { //if (holder.getInitParameter(ip.name()) == null) if (metaData.getOrigin(name+".filter.init-param."+ip.name())==Origin.NotSet) { holder.setInitParameter(ip.name(), ip.value()); metaData.setOrigin(name+".filter.init-param."+ip.name(),ip,clazz); } } FilterMapping[] mappings = _context.getServletHandler().getFilterMappings(); boolean mappingExists = false; if (mappings != null) { for (FilterMapping m:mappings) { if (m.getFilterName().equals(name)) { mappingExists = true; break; } } } //if a descriptor didn't specify at least one mapping, use the mappings from the annotation and the DispatcherTypes //from the annotation if (!mappingExists) { FilterMapping mapping = new FilterMapping(); mapping.setFilterName(holder.getName()); if (urlPatterns.length > 0) { ArrayList paths = new ArrayList(); for (String s:urlPatterns) { paths.add(Util.normalizePattern(s)); } mapping.setPathSpecs(paths.toArray(new String[paths.size()])); } if (filterAnnotation.servletNames().length > 0) { ArrayList names = new ArrayList(); for (String s : filterAnnotation.servletNames()) { names.add(s); } mapping.setServletNames(names.toArray(new String[names.size()])); } EnumSet dispatcherSet = EnumSet.noneOf(DispatcherType.class); for (DispatcherType d : filterAnnotation.dispatcherTypes()) { dispatcherSet.add(d); } mapping.setDispatcherTypes(dispatcherSet); _context.getServletHandler().addFilterMapping(mapping); metaData.setOrigin(name+".filter.mappings",filterAnnotation,clazz); } } } } WebFilterAnnotationHandler.java000066400000000000000000000050011261716203600360670ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; /** * WebFilterAnnotationHandler * * */ public class WebFilterAnnotationHandler extends AbstractDiscoverableAnnotationHandler { private static final Logger LOG = Log.getLogger(WebFilterAnnotationHandler.class); public WebFilterAnnotationHandler (WebAppContext context) { super(context); } @Override public void handle(ClassInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebFilter".equals(annotationName)) return; WebFilterAnnotation wfAnnotation = new WebFilterAnnotation(_context, info.getClassName(), info.getContainingResource()); addAnnotation(wfAnnotation); } @Override public void handle(FieldInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebFilter".equals(annotationName)) return; LOG.warn ("@WebFilter not applicable for fields: "+info.getClassInfo().getClassName()+"."+info.getFieldName()); } @Override public void handle(MethodInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebFilter".equals(annotationName)) return; LOG.warn ("@WebFilter not applicable for methods: "+info.getClassInfo().getClassName()+"."+info.getMethodName()+" "+info.getSignature()); } } WebListenerAnnotation.java000066400000000000000000000074431261716203600351450ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.util.EventListener; import javax.servlet.ServletContextAttributeListener; import javax.servlet.ServletContextListener; import javax.servlet.ServletRequestAttributeListener; import javax.servlet.ServletRequestListener; import javax.servlet.http.HttpSessionAttributeListener; import javax.servlet.http.HttpSessionIdListener; import javax.servlet.http.HttpSessionListener; import org.eclipse.jetty.servlet.BaseHolder.Source; import org.eclipse.jetty.servlet.ListenerHolder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.Origin; import org.eclipse.jetty.webapp.WebAppContext; /** * WebListenerAnnotation * * */ public class WebListenerAnnotation extends DiscoveredAnnotation { private static final Logger LOG = Log.getLogger(WebListenerAnnotation.class); /** * @param context * @param className */ public WebListenerAnnotation(WebAppContext context, String className) { super(context, className); } public WebListenerAnnotation(WebAppContext context, String className, Resource resource) { super(context, className, resource); } /** * @see DiscoveredAnnotation#apply() */ public void apply() { Class clazz = (Class)getTargetClass(); if (clazz == null) { LOG.warn(_className+" cannot be loaded"); return; } try { if (ServletContextListener.class.isAssignableFrom(clazz) || ServletContextAttributeListener.class.isAssignableFrom(clazz) || ServletRequestListener.class.isAssignableFrom(clazz) || ServletRequestAttributeListener.class.isAssignableFrom(clazz) || HttpSessionListener.class.isAssignableFrom(clazz) || HttpSessionAttributeListener.class.isAssignableFrom(clazz) || HttpSessionIdListener.class.isAssignableFrom(clazz)) { java.util.EventListener listener = (java.util.EventListener)_context.getServletContext().createInstance(clazz); MetaData metaData = _context.getMetaData(); if (metaData.getOrigin(clazz.getName()+".listener") == Origin.NotSet) { ListenerHolder h = _context.getServletHandler().newListenerHolder(Source.ANNOTATION); h.setListener(listener); _context.getServletHandler().addListener(h); _context.addEventListener(listener); } } else LOG.warn(clazz.getName()+" does not implement one of the servlet listener interfaces"); } catch (Exception e) { LOG.warn(e); } } } WebListenerAnnotationHandler.java000066400000000000000000000050531261716203600364360ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; public class WebListenerAnnotationHandler extends AbstractDiscoverableAnnotationHandler { private static final Logger LOG = Log.getLogger(WebListenerAnnotationHandler.class); public WebListenerAnnotationHandler (WebAppContext context) { super(context); } /** * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String) */ public void handle(ClassInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName)) return; WebListenerAnnotation wlAnnotation = new WebListenerAnnotation(_context, info.getClassName(), info.getContainingResource()); addAnnotation(wlAnnotation); } public void handle(FieldInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName)) return; LOG.warn ("@WebListener is not applicable to fields: "+info.getClassInfo().getClassName()+"."+info.getFieldName()); } public void handle(MethodInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebListener".equals(annotationName)) return; LOG.warn ("@WebListener is not applicable to methods: "+info.getClassInfo().getClassName()+"."+info.getMethodName()+" "+info.getSignature()); } } WebServletAnnotation.java000066400000000000000000000211111261716203600347700ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.util.ArrayList; import javax.servlet.Servlet; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import org.eclipse.jetty.servlet.Holder; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.MetaData; import org.eclipse.jetty.webapp.Origin; import org.eclipse.jetty.webapp.WebAppContext; /** * WebServletAnnotation * * */ public class WebServletAnnotation extends DiscoveredAnnotation { private static final Logger LOG = Log.getLogger(WebServletAnnotation.class); public WebServletAnnotation (WebAppContext context, String className) { super(context, className); } public WebServletAnnotation (WebAppContext context, String className, Resource resource) { super(context, className, resource); } /** * @see DiscoveredAnnotation#apply() */ public void apply() { //TODO check this algorithm with new rules for applying descriptors and annotations in order Class clazz = (Class)getTargetClass(); if (clazz == null) { LOG.warn(_className+" cannot be loaded"); return; } //Servlet Spec 8.1.1 if (!HttpServlet.class.isAssignableFrom(clazz)) { LOG.warn(clazz.getName()+" is not assignable from javax.servlet.http.HttpServlet"); return; } WebServlet annotation = (WebServlet)clazz.getAnnotation(WebServlet.class); if (annotation.urlPatterns().length > 0 && annotation.value().length > 0) { LOG.warn(clazz.getName()+ " defines both @WebServlet.value and @WebServlet.urlPatterns"); return; } String[] urlPatterns = annotation.value(); if (urlPatterns.length == 0) urlPatterns = annotation.urlPatterns(); if (urlPatterns.length == 0) { LOG.warn(clazz.getName()+ " defines neither @WebServlet.value nor @WebServlet.urlPatterns"); return; } //canonicalize the patterns ArrayList urlPatternList = new ArrayList(); for (String p : urlPatterns) urlPatternList.add(Util.normalizePattern(p)); String servletName = (annotation.name().equals("")?clazz.getName():annotation.name()); MetaData metaData = _context.getMetaData(); //Find out if a already exists with this name ServletHolder[] holders = _context.getServletHandler().getServlets(); boolean isNew = true; ServletHolder holder = null; if (holders != null) { for (ServletHolder h : holders) { if (h.getName() != null && servletName.equals(h.getName())) { holder = h; isNew = false; break; } } } if (isNew) { //No servlet of this name has already been defined, either by a descriptor //or another annotation (which would be impossible). holder = _context.getServletHandler().newServletHolder(Holder.Source.ANNOTATION); holder.setHeldClass(clazz); metaData.setOrigin(servletName+".servlet.servlet-class",annotation,clazz); holder.setName(servletName); holder.setDisplayName(annotation.displayName()); metaData.setOrigin(servletName+".servlet.display-name",annotation,clazz); holder.setInitOrder(annotation.loadOnStartup()); metaData.setOrigin(servletName+".servlet.load-on-startup",annotation,clazz); holder.setAsyncSupported(annotation.asyncSupported()); metaData.setOrigin(servletName+".servlet.async-supported",annotation,clazz); for (WebInitParam ip:annotation.initParams()) { holder.setInitParameter(ip.name(), ip.value()); metaData.setOrigin(servletName+".servlet.init-param."+ip.name(),ip,clazz); } _context.getServletHandler().addServlet(holder); ServletMapping mapping = new ServletMapping(); mapping.setServletName(holder.getName()); mapping.setPathSpecs( LazyList.toStringArray(urlPatternList)); _context.getServletHandler().addServletMapping(mapping); metaData.setOrigin(servletName+".servlet.mappings",annotation,clazz); } else { //set the class according to the servlet that is annotated, if it wasn't already //NOTE: this may be considered as "completing" an incomplete servlet registration, and it is //not clear from servlet 3.0 spec whether this is intended, or if only a ServletContext.addServlet() call //can complete it, see http://java.net/jira/browse/SERVLET_SPEC-42 if (holder.getClassName() == null) holder.setClassName(clazz.getName()); if (holder.getHeldClass() == null) holder.setHeldClass(clazz); //check if the existing servlet has each init-param from the annotation //if not, add it for (WebInitParam ip:annotation.initParams()) { if (metaData.getOrigin(servletName+".servlet.init-param."+ip.name())==Origin.NotSet) { holder.setInitParameter(ip.name(), ip.value()); metaData.setOrigin(servletName+".servlet.init-param."+ip.name(),ip,clazz); } } //check the url-patterns //ServletSpec 3.0 p81 If a servlet already has url mappings from a //webxml or fragment descriptor the annotation is ignored. However, we want to be able to //replace mappings that were given in webdefault.xml boolean mappingsExist = false; boolean anyNonDefaults = false; ServletMapping[] allMappings = _context.getServletHandler().getServletMappings(); if (allMappings != null) { for (ServletMapping m:allMappings) { if (m.getServletName() != null && servletName.equals(m.getServletName())) { mappingsExist = true; if (!m.isDefault()) { anyNonDefaults = true; break; } } } } if (anyNonDefaults) return; //if any mappings already set by a descriptor that is not webdefault.xml, we're done boolean clash = false; if (mappingsExist) { for (String p:urlPatternList) { ServletMapping m = _context.getServletHandler().getServletMapping(p); if (m != null && !m.isDefault()) { //trying to override a servlet-mapping that was added not by webdefault.xml clash = true; break; } } } if (!mappingsExist || !clash) { ServletMapping m = new ServletMapping(); m.setServletName(servletName); m.setPathSpecs(LazyList.toStringArray(urlPatternList)); _context.getServletHandler().addServletMapping(m); } } } } WebServletAnnotationHandler.java000066400000000000000000000051471261716203600363010ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; /** * WebServletAnnotationHandler * * Process a WebServlet annotation on a class. * */ public class WebServletAnnotationHandler extends AbstractDiscoverableAnnotationHandler { private static final Logger LOG = Log.getLogger(WebServletAnnotationHandler.class); public WebServletAnnotationHandler (WebAppContext context) { super(context); } /** * Handle discovering a WebServlet annotation. * * @see org.eclipse.jetty.annotations.AnnotationParser.Handler#handle(ClassInfo, String) */ @Override public void handle(ClassInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebServlet".equals(annotationName)) return; WebServletAnnotation annotation = new WebServletAnnotation (_context, info.getClassName(), info.getContainingResource()); addAnnotation(annotation); } @Override public void handle(FieldInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebServlet".equals(annotationName)) return; LOG.warn ("@WebServlet annotation not supported for fields"); } @Override public void handle(MethodInfo info, String annotationName) { if (annotationName == null || !"javax.servlet.annotation.WebServlet".equals(annotationName)) return; LOG.warn ("@WebServlet annotation not supported for methods"); } } package-info.java000066400000000000000000000016021261716203600332020ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/main/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Annotations : Support for Servlet Annotations */ package org.eclipse.jetty.annotations; jetty-9.2.14.v20151106/jetty-annotations/src/test/000077500000000000000000000000001261716203600212765ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/000077500000000000000000000000001261716203600222175ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/000077500000000000000000000000001261716203600230065ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/000077500000000000000000000000001261716203600244325ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600255715ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/000077500000000000000000000000001261716203600301265ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ClassA.java000066400000000000000000000037231261716203600321440ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; /** * ClassA * * */ @Sample(1) public class ClassA { private Integer e; private Integer f; private Integer g; private Integer h; private Integer j; private Integer k; public static class Foo { } @Sample(7) private Integer m; @Sample(2) public void a (Integer[] x) { System.err.println("ClassA.public"); } @Sample(3) protected void b(Foo[] f) { System.err.println("ClassA.protected"); } @Sample(4) void c(int[] x) { System.err.println("ClassA.package"); } @Sample(5) private void d(int x, String y) { System.err.println("ClassA.private"); } @Sample(6) protected void l() { System.err.println("ClassA.protected method l"); } public Integer getE() { return this.e; } public Integer getF() { return this.f; } public Integer getG() { return this.g; } public Integer getJ() { return this.j; } public void x() { System.err.println("ClassA.x"); } } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ClassB.java000066400000000000000000000027301261716203600321420ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; /** * ClassB * * */ @Sample(value=50) @Multi({"do", "re", "mi"}) public class ClassB extends ClassA implements InterfaceD { //test override of public scope method @Sample(value=51) @Multi({"fa", "so", "la"}) public void a() { System.err.println("ClassB.public"); } //test override of package scope method @Sample(value=52) void c() { System.err.println("ClassB.package"); } public void l() { System.err.println("Overridden method l has no annotation"); } //test no annotation public void z() { System.err.println("ClassB.z"); } } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/FilterC.java000066400000000000000000000046671261716203600323360ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.io.IOException; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import javax.annotation.security.RunAs; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebFilter(filterName="CFilter", dispatcherTypes={DispatcherType.REQUEST}, urlPatterns = {"/*"}, initParams={@WebInitParam(name="a", value="99")}, asyncSupported=false) @RunAs("admin") public class FilterC implements Filter { @Resource (mappedName="foo") private Double foo; @PreDestroy public void pre () { } @PostConstruct public void post() { } public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)arg0; HttpServletResponse response = (HttpServletResponse)arg1; HttpSession session = request.getSession(true); String val = request.getParameter("action"); if (val!=null) session.setAttribute("action", val); arg2.doFilter(request, response); } public void destroy() { } public void init(FilterConfig arg0) throws ServletException { } } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/InterfaceD.java000066400000000000000000000016001261716203600327720ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; /** * InterfaceD * * */ public interface InterfaceD { } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ListenerC.java000066400000000000000000000022651261716203600326660ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener; @WebListener public class ListenerC implements ServletContextListener { public void contextDestroyed(ServletContextEvent arg0) { } public void contextInitialized(ServletContextEvent arg0) { } } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/Multi.java000066400000000000000000000022011261716203600320560ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) public @interface Multi { String[] value(); } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/Sample.java000066400000000000000000000021741261716203600322160ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) public @interface Sample { int value(); } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/ServletC.java000066400000000000000000000047271261716203600325320ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import java.io.IOException; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import javax.annotation.security.DeclareRoles; import javax.annotation.security.RunAs; import javax.servlet.ServletException; import javax.servlet.annotation.HttpConstraint; import javax.servlet.annotation.HttpMethodConstraint; import javax.servlet.annotation.MultipartConfig; import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.WebInitParam; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @DeclareRoles({"alice"}) @WebServlet(urlPatterns = { "/foo/*", "/bah/*" }, name="CServlet", initParams={@WebInitParam(name="x", value="y")}, loadOnStartup=2, asyncSupported=false) @MultipartConfig(fileSizeThreshold=1000, maxFileSize=2000, maxRequestSize=3000) @RunAs("admin") @ServletSecurity(value=@HttpConstraint(rolesAllowed={"fred", "bill", "dorothy"}), httpMethodConstraints={@HttpMethodConstraint(value="GET", rolesAllowed={"bob", "carol", "ted"})}) public class ServletC extends HttpServlet { @Resource (mappedName="foo", type=Double.class) private Double foo; @PreDestroy public void pre () { } @PostConstruct public void post() { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); response.getWriter().println("

Annotated Servlet

"); response.getWriter().println("An annotated Servlet."); } } TestAnnotationConfiguration.java000066400000000000000000000037411261716203600364210ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import static org.junit.Assert.assertNotNull; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.FragmentDescriptor; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; /** * TestAnnotationConfiguration * * */ public class TestAnnotationConfiguration { @Test public void testGetFragmentFromJar() throws Exception { String dir = System.getProperty("basedir", "."); File file = new File(dir); file=new File(file.getCanonicalPath()); URL url=file.toURL(); Resource jar1 = Resource.newResource(url+"file.jar"); AnnotationConfiguration config = new AnnotationConfiguration(); WebAppContext wac = new WebAppContext(); List frags = new ArrayList(); frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file.jar!/fooa.props"))); frags.add(new FragmentDescriptor(Resource.newResource("jar:"+url+"file2.jar!/foob.props"))); assertNotNull(config.getFragmentFromJar(jar1, frags)); } } TestAnnotationInheritance.java000066400000000000000000000220041261716203600360340ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import javax.naming.Context; import javax.naming.InitialContext; import org.eclipse.jetty.annotations.AnnotationParser.AbstractHandler; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.util.ConcurrentHashSet; import org.junit.After; import org.junit.Test; /** * */ public class TestAnnotationInheritance { List classNames = new ArrayList(); class SampleHandler extends AbstractHandler { public final List annotatedClassNames = new ArrayList(); public final List annotatedMethods = new ArrayList(); public final List annotatedFields = new ArrayList(); public void handle(ClassInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; annotatedClassNames.add(info.getClassName()); } public void handle(FieldInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; annotatedFields.add(info.getClassInfo().getClassName()+"."+info.getFieldName()); } public void handle(MethodInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; annotatedMethods.add(info.getClassInfo().getClassName()+"."+info.getMethodName()); } } @After public void destroy() throws Exception { classNames.clear(); InitialContext ic = new InitialContext(); Context comp = (Context)ic.lookup("java:comp"); comp.destroySubcontext("env"); } @Test public void testParseClassNames() throws Exception { classNames.add(ClassA.class.getName()); classNames.add(ClassB.class.getName()); SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); parser.parse(Collections.singleton(handler), classNames, new ClassNameResolver () { public boolean isExcluded(String name) { return false; } public boolean shouldOverride(String name) { return false; } }); //check we got 2 class annotations assertEquals(2, handler.annotatedClassNames.size()); //check we got all annotated methods on each class assertEquals (7, handler.annotatedMethods.size()); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.a")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.b")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.c")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.d")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.l")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.a")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.c")); //check we got all annotated fields on each class assertEquals(1, handler.annotatedFields.size()); assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0)); } @Test public void testParseClass() throws Exception { SampleHandler handler = new SampleHandler(); AnnotationParser parser = new AnnotationParser(); parser.parse(Collections.singleton(handler), ClassB.class, new ClassNameResolver () { public boolean isExcluded(String name) { return false; } public boolean shouldOverride(String name) { return false; } }, true); //check we got 2 class annotations assertEquals(2, handler.annotatedClassNames.size()); //check we got all annotated methods on each class assertEquals (7, handler.annotatedMethods.size()); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.a")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.b")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.c")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.d")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassA.l")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.a")); assertTrue (handler.annotatedMethods.contains("org.eclipse.jetty.annotations.ClassB.c")); //check we got all annotated fields on each class assertEquals(1, handler.annotatedFields.size()); assertEquals("org.eclipse.jetty.annotations.ClassA.m", handler.annotatedFields.get(0)); } @Test public void testExclusions() throws Exception { AnnotationParser parser = new AnnotationParser(); SampleHandler handler = new SampleHandler(); parser.parse(Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() { public boolean isExcluded(String name) { return true; } public boolean shouldOverride(String name) { return false; } }); assertEquals (0, handler.annotatedClassNames.size()); assertEquals (0, handler.annotatedFields.size()); assertEquals (0, handler.annotatedMethods.size()); handler.annotatedClassNames.clear(); handler.annotatedFields.clear(); handler.annotatedMethods.clear(); parser.parse (Collections.singleton(handler), ClassA.class.getName(), new ClassNameResolver() { public boolean isExcluded(String name) { return false; } public boolean shouldOverride(String name) { return false; } }); assertEquals (1, handler.annotatedClassNames.size()); } @Test public void testTypeInheritanceHandling() throws Exception { ConcurrentHashMap> map = new ConcurrentHashMap>(); AnnotationParser parser = new AnnotationParser(); ClassInheritanceHandler handler = new ClassInheritanceHandler(map); class Foo implements InterfaceD { } classNames.clear(); classNames.add(ClassA.class.getName()); classNames.add(ClassB.class.getName()); classNames.add(InterfaceD.class.getName()); classNames.add(Foo.class.getName()); parser.parse(Collections.singleton(handler), classNames, null); assertNotNull(map); assertFalse(map.isEmpty()); assertEquals(2, map.size()); assertTrue (map.keySet().contains("org.eclipse.jetty.annotations.ClassA")); assertTrue (map.keySet().contains("org.eclipse.jetty.annotations.InterfaceD")); ConcurrentHashSet classes = map.get("org.eclipse.jetty.annotations.ClassA"); assertEquals(1, classes.size()); assertEquals ("org.eclipse.jetty.annotations.ClassB", classes.iterator().next()); classes = map.get("org.eclipse.jetty.annotations.InterfaceD"); assertEquals(2, classes.size()); assertTrue(classes.contains("org.eclipse.jetty.annotations.ClassB")); assertTrue(classes.contains(Foo.class.getName())); } } TestAnnotationParser.java000066400000000000000000000206471261716203600350520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jetty.annotations.AnnotationParser.ClassInfo; import org.eclipse.jetty.annotations.AnnotationParser.FieldInfo; import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.annotations.AnnotationParser.MethodInfo; import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; public class TestAnnotationParser { public static class TrackingAnnotationHandler extends AnnotationParser.AbstractHandler { private final String annotationName; public final Set foundClasses; public TrackingAnnotationHandler(String annotationName) { this.annotationName = annotationName; this.foundClasses = new HashSet<>(); } @Override public void handle(ClassInfo info, String annotation) { if (annotation == null || !annotationName.equals(annotation)) return; foundClasses.add(info.getClassName()); } } @Rule public TestingDir testdir = new TestingDir(); @Test public void testSampleAnnotation() throws Exception { String[] classNames = new String[] { "org.eclipse.jetty.annotations.ClassA" }; AnnotationParser parser = new AnnotationParser(); class SampleAnnotationHandler extends AnnotationParser.AbstractHandler { private List methods = Arrays.asList("a","b","c","d","l"); public void handle(ClassInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; assertEquals("org.eclipse.jetty.annotations.ClassA",info.getClassName()); } public void handle(FieldInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; assertEquals("m",info.getFieldName()); assertEquals(org.objectweb.asm.Type.OBJECT,org.objectweb.asm.Type.getType(info.getFieldType()).getSort()); } public void handle(MethodInfo info, String annotation) { if (annotation == null || !"org.eclipse.jetty.annotations.Sample".equals(annotation)) return; assertEquals("org.eclipse.jetty.annotations.ClassA",info.getClassInfo().getClassName()); assertTrue(methods.contains(info.getMethodName())); assertEquals("org.eclipse.jetty.annotations.Sample",annotation); } } //long start = System.currentTimeMillis(); parser.parse(Collections.singleton(new SampleAnnotationHandler()), classNames,new ClassNameResolver() { public boolean isExcluded(String name) { return false; } public boolean shouldOverride(String name) { return false; } }); //long end = System.currentTimeMillis(); //System.err.println("Time to parse class: " + ((end - start))); } @Test public void testMultiAnnotation() throws Exception { String[] classNames = new String[] { "org.eclipse.jetty.annotations.ClassB" }; AnnotationParser parser = new AnnotationParser(); class MultiAnnotationHandler extends AnnotationParser.AbstractHandler { public void handle(ClassInfo info, String annotation) { if (annotation == null || ! "org.eclipse.jetty.annotations.Multi".equals(annotation)) return; assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassName())); } public void handle(FieldInfo info, String annotation) { if (annotation == null || ! "org.eclipse.jetty.annotations.Multi".equals(annotation)) return; // there should not be any fail(); } public void handle(MethodInfo info, String annotation) { if (annotation == null || ! "org.eclipse.jetty.annotations.Multi".equals(annotation)) return; assertTrue("org.eclipse.jetty.annotations.ClassB".equals(info.getClassInfo().getClassName())); assertTrue("a".equals(info.getMethodName())); } } parser.parse(Collections.singleton(new MultiAnnotationHandler()), classNames,null); } @Test public void testHiddenFilesInJar() throws Exception { File badClassesJar = MavenTestingUtils.getTestResourceFile("bad-classes.jar"); AnnotationParser parser = new AnnotationParser(); Set emptySet = Collections.emptySet(); parser.parse(emptySet, badClassesJar.toURI(),null); // only the valid classes inside bad-classes.jar should be parsed. If any invalid classes are parsed and exception would be thrown here } @Test public void testBasedirExclusion() throws Exception { // Build up basedir, which itself has a path segment that violates java package and classnaming. // The basedir should have no effect on annotation scanning. // Intentionally using a base director name that starts with a "." // This mimics what you see in jenkins, hudson, hadoop, solr, camel, and selenium for their // installed and/or managed webapps File basedir = testdir.getFile(".base/workspace/classes"); FS.ensureEmpty(basedir); // Copy in class that is known to have annotations. copyClass(ClassA.class,basedir); // Setup Tracker TrackingAnnotationHandler tracker = new TrackingAnnotationHandler(Sample.class.getName()); // Setup annotation scanning AnnotationParser parser = new AnnotationParser(); // Parse parser.parse(Collections.singleton(tracker), basedir.toURI(),null); // Validate Assert.assertThat("Found Class", tracker.foundClasses, contains(ClassA.class.getName())); } private void copyClass(Class clazz, File basedir) throws IOException { String classname = clazz.getName().replace('.',File.separatorChar) + ".class"; URL url = this.getClass().getResource('/'+classname); Assert.assertThat("URL for: " + classname,url,notNullValue()); String classpath = classname.substring(0,classname.lastIndexOf(File.separatorChar)); FS.ensureDirExists(new File(basedir,classpath)); InputStream in = null; OutputStream out = null; try { in = url.openStream(); out = new FileOutputStream(new File(basedir,classname)); IO.copy(in,out); } finally { IO.close(out); IO.close(in); } } } TestSecurityAnnotationConversions.java000066400000000000000000000347671261716203600376660ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.Arrays; import java.util.List; import javax.servlet.annotation.HttpConstraint; import javax.servlet.annotation.HttpMethodConstraint; import javax.servlet.annotation.ServletSecurity; import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic; import javax.servlet.annotation.ServletSecurity.TransportGuarantee; import javax.servlet.http.HttpServlet; import org.eclipse.jetty.security.ConstraintAware; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; public class TestSecurityAnnotationConversions { @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.DENY)) public static class DenyServlet extends HttpServlet {} @ServletSecurity public static class PermitServlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"})) public static class RolesServlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}), httpMethodConstraints={@HttpMethodConstraint(value="GET")}) public static class Method1Servlet extends HttpServlet {} @ServletSecurity(value=@HttpConstraint(value=EmptyRoleSemantic.PERMIT, transportGuarantee=TransportGuarantee.CONFIDENTIAL, rolesAllowed={"tom", "dick", "harry"}), httpMethodConstraints={@HttpMethodConstraint(value="GET", transportGuarantee=TransportGuarantee.CONFIDENTIAL)}) public static class Method2Servlet extends HttpServlet {} public void setUp() { } @Test public void testDenyAllOnClass() throws Exception { WebAppContext wac = makeWebAppContext(DenyServlet.class.getCanonicalName(), "denyServlet", new String[]{"/foo/*", "*.foo"}); //Assume we found 1 servlet with a @HttpConstraint with value=EmptyRoleSemantic.DENY security annotation ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes: //1 ConstraintMapping per ServletMapping pathSpec Constraint expectedConstraint = new Constraint(); expectedConstraint.setAuthenticate(true); expectedConstraint.setDataConstraint(Constraint.DC_NONE); ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint); expectedMappings[1].setPathSpec("*.foo"); introspector.introspect(DenyServlet.class); compareResults(expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testPermitAll() throws Exception { //Assume we found 1 servlet with a @ServletSecurity security annotation WebAppContext wac = makeWebAppContext(PermitServlet.class.getCanonicalName(), "permitServlet", new String[]{"/foo/*", "*.foo"}); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes - no constraints at all as per Servlet Spec 3.1 pg 129 //1 ConstraintMapping per ServletMapping pathSpec ConstraintMapping[] expectedMappings = new ConstraintMapping[]{}; introspector.introspect(PermitServlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testRolesAllowedWithTransportGuarantee() throws Exception { //Assume we found 1 servlet with annotation with roles defined and //and a TransportGuarantee WebAppContext wac = makeWebAppContext(RolesServlet.class.getCanonicalName(), "rolesServlet", new String[]{"/foo/*", "*.foo"}); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); AnnotationIntrospector introspector = new AnnotationIntrospector(); introspector.registerHandler(annotationHandler); //set up the expected outcomes:compareResults //1 ConstraintMapping per ServletMapping Constraint expectedConstraint = new Constraint(); expectedConstraint.setAuthenticate(true); expectedConstraint.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint.setDataConstraint(Constraint.DC_CONFIDENTIAL); ConstraintMapping[] expectedMappings = new ConstraintMapping[2]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint); expectedMappings[1].setPathSpec("*.foo"); introspector.introspect(RolesServlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testMethodAnnotation() throws Exception { //ServletSecurity annotation with HttpConstraint of TransportGuarantee.CONFIDENTIAL, and a list of rolesAllowed, and //a HttpMethodConstraint for GET method that permits all and has TransportGuarantee.NONE (ie is default) WebAppContext wac = makeWebAppContext(Method1Servlet.class.getCanonicalName(), "method1Servlet", new String[]{"/foo/*", "*.foo"}); //set up the expected outcomes: - a Constraint for the RolesAllowed on the class //with userdata constraint of DC_CONFIDENTIAL //and mappings for each of the pathSpecs Constraint expectedConstraint1 = new Constraint(); expectedConstraint1.setAuthenticate(true); expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); //a Constraint for the PermitAll on the doGet method with a userdata //constraint of DC_CONFIDENTIAL inherited from the class Constraint expectedConstraint2 = new Constraint(); expectedConstraint2.setDataConstraint(Constraint.DC_NONE); ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint1); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[0].setMethodOmissions(new String[]{"GET"}); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint1); expectedMappings[1].setPathSpec("*.foo"); expectedMappings[1].setMethodOmissions(new String[]{"GET"}); expectedMappings[2] = new ConstraintMapping(); expectedMappings[2].setConstraint(expectedConstraint2); expectedMappings[2].setPathSpec("/foo/*"); expectedMappings[2].setMethod("GET"); expectedMappings[3] = new ConstraintMapping(); expectedMappings[3].setConstraint(expectedConstraint2); expectedMappings[3].setPathSpec("*.foo"); expectedMappings[3].setMethod("GET"); AnnotationIntrospector introspector = new AnnotationIntrospector(); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); introspector.registerHandler(annotationHandler); introspector.introspect(Method1Servlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } @Test public void testMethodAnnotation2() throws Exception { //A ServletSecurity annotation that has HttpConstraint of CONFIDENTIAL with defined roles, but a //HttpMethodConstraint for GET that permits all, but also requires CONFIDENTIAL WebAppContext wac = makeWebAppContext(Method2Servlet.class.getCanonicalName(), "method2Servlet", new String[]{"/foo/*", "*.foo"}); AnnotationIntrospector introspector = new AnnotationIntrospector(); ServletSecurityAnnotationHandler annotationHandler = new ServletSecurityAnnotationHandler(wac); introspector.registerHandler(annotationHandler); //set up the expected outcomes: - a Constraint for the RolesAllowed on the class //with userdata constraint of DC_CONFIDENTIAL //and mappings for each of the pathSpecs Constraint expectedConstraint1 = new Constraint(); expectedConstraint1.setAuthenticate(true); expectedConstraint1.setRoles(new String[]{"tom", "dick", "harry"}); expectedConstraint1.setDataConstraint(Constraint.DC_CONFIDENTIAL); //a Constraint for the Permit on the GET method with a userdata //constraint of DC_CONFIDENTIAL Constraint expectedConstraint2 = new Constraint(); expectedConstraint2.setDataConstraint(Constraint.DC_CONFIDENTIAL); ConstraintMapping[] expectedMappings = new ConstraintMapping[4]; expectedMappings[0] = new ConstraintMapping(); expectedMappings[0].setConstraint(expectedConstraint1); expectedMappings[0].setPathSpec("/foo/*"); expectedMappings[0].setMethodOmissions(new String[]{"GET"}); expectedMappings[1] = new ConstraintMapping(); expectedMappings[1].setConstraint(expectedConstraint1); expectedMappings[1].setPathSpec("*.foo"); expectedMappings[1].setMethodOmissions(new String[]{"GET"}); expectedMappings[2] = new ConstraintMapping(); expectedMappings[2].setConstraint(expectedConstraint2); expectedMappings[2].setPathSpec("/foo/*"); expectedMappings[2].setMethod("GET"); expectedMappings[3] = new ConstraintMapping(); expectedMappings[3].setConstraint(expectedConstraint2); expectedMappings[3].setPathSpec("*.foo"); expectedMappings[3].setMethod("GET"); introspector.introspect(Method2Servlet.class); compareResults (expectedMappings, ((ConstraintAware)wac.getSecurityHandler()).getConstraintMappings()); } private void compareResults (ConstraintMapping[] expectedMappings, List actualMappings) { assertNotNull(actualMappings); assertEquals(expectedMappings.length, actualMappings.size()); for (int k=0; k < actualMappings.size(); k++) { ConstraintMapping am = actualMappings.get(k); boolean matched = false; for (int i=0; i< expectedMappings.length && !matched; i++) { ConstraintMapping em = expectedMappings[i]; if (em.getPathSpec().equals(am.getPathSpec())) { if ((em.getMethod()==null && am.getMethod() == null) || em.getMethod() != null && em.getMethod().equals(am.getMethod())) { matched = true; assertEquals(em.getConstraint().getAuthenticate(), am.getConstraint().getAuthenticate()); assertEquals(em.getConstraint().getDataConstraint(), am.getConstraint().getDataConstraint()); if (em.getMethodOmissions() == null) { assertNull(am.getMethodOmissions()); } else { assertTrue(Arrays.equals(am.getMethodOmissions(), em.getMethodOmissions())); } if (em.getConstraint().getRoles() == null) { assertNull(am.getConstraint().getRoles()); } else { assertTrue(Arrays.equals(em.getConstraint().getRoles(), am.getConstraint().getRoles())); } } } } if (!matched) fail("No expected ConstraintMapping matching method:"+am.getMethod()+" pathSpec: "+am.getPathSpec()); } } private WebAppContext makeWebAppContext (String className, String servletName, String[] paths) { WebAppContext wac = new WebAppContext(); ServletHolder[] holders = new ServletHolder[1]; holders[0] = new ServletHolder(); holders[0].setClassName(className); holders[0].setName(servletName); holders[0].setServletHandler(wac.getServletHandler()); wac.getServletHandler().setServlets(holders); wac.setSecurityHandler(new ConstraintSecurityHandler()); ServletMapping[] servletMappings = new ServletMapping[1]; servletMappings[0] = new ServletMapping(); servletMappings[0].setPathSpecs(paths); servletMappings[0].setServletName(servletName); wac.getServletHandler().setServletMappings(servletMappings); return wac; } } TestServletAnnotations.java000066400000000000000000000103511261716203600354140ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.webapp.DiscoveredAnnotation; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.Test; /** * TestServletAnnotations */ public class TestServletAnnotations { public class TestWebServletAnnotationHandler extends WebServletAnnotationHandler { List _list = null; public TestWebServletAnnotationHandler(WebAppContext context, List list) { super(context); _list = list; } @Override public void addAnnotation(DiscoveredAnnotation a) { super.addAnnotation(a); _list.add(a); } } @Test public void testServletAnnotation() throws Exception { List classes = new ArrayList(); classes.add("org.eclipse.jetty.annotations.ServletC"); AnnotationParser parser = new AnnotationParser(); WebAppContext wac = new WebAppContext(); List results = new ArrayList(); TestWebServletAnnotationHandler handler = new TestWebServletAnnotationHandler(wac, results); parser.parse(Collections.singleton(handler), classes, new ClassNameResolver () { public boolean isExcluded(String name) { return false; } public boolean shouldOverride(String name) { return false; } }); assertEquals(1, results.size()); assertTrue(results.get(0) instanceof WebServletAnnotation); results.get(0).apply(); ServletHolder[] holders = wac.getServletHandler().getServlets(); assertNotNull(holders); assertEquals(1, holders.length); // Verify servlet annotations ServletHolder cholder = holders[0]; assertThat("Servlet Name", cholder.getName(), is("CServlet")); assertThat("InitParameter[x]", cholder.getInitParameter("x"), is("y")); assertThat("Init Order", cholder.getInitOrder(), is(2)); assertThat("Async Supported", cholder.isAsyncSupported(), is(false)); // Verify mappings ServletMapping[] mappings = wac.getServletHandler().getServletMappings(); assertNotNull(mappings); assertEquals(1, mappings.length); String[] paths = mappings[0].getPathSpecs(); assertNotNull(paths); assertEquals(2, paths.length); } public void testDeclareRoles () throws Exception { WebAppContext wac = new WebAppContext(); ConstraintSecurityHandler sh = new ConstraintSecurityHandler(); wac.setSecurityHandler(sh); sh.setRoles(new HashSet(Arrays.asList(new String[]{"humpty", "dumpty"}))); DeclareRolesAnnotationHandler handler = new DeclareRolesAnnotationHandler(wac); handler.doHandle(ServletC.class); assertTrue(sh.getRoles().contains("alice")); assertTrue(sh.getRoles().contains("humpty")); assertTrue(sh.getRoles().contains("dumpty")); } } jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources/000077500000000000000000000000001261716203600321405ustar00rootroot00000000000000ResourceA.java000066400000000000000000000056031261716203600346200ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations.resources; import java.io.IOException; import javax.annotation.Resource; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * ResourceA * * */ public class ResourceA implements javax.servlet.Servlet { private Integer e; private Integer h; private Integer k; @Resource(name="myf", mappedName="resB") //test giving both a name and mapped name from the environment private Integer f;//test a non inherited field that needs injection @Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment private Integer g; @Resource(name="resA") //test using the given name as the name from the environment private Integer j; @Resource(mappedName="resB") //test using the default name on an inherited field protected Integer n; //TODO - if it's inherited, is it supposed to use the classname of the class it is inherited by? @Resource(name="mye", mappedName="resA", type=Integer.class) public void setE(Integer e) { this.e=e; } public Integer getE() { return this.e; } public Integer getF() { return this.f; } public Integer getG() { return this.g; } public Integer getJ() { return this.j; } @Resource(mappedName="resA") public void setH(Integer h) { this.h=h; } @Resource(name="resA") public void setK(Integer k) { this.k=k; } public void x() { System.err.println("ResourceA.x"); } @Override public void destroy() { } @Override public ServletConfig getServletConfig() { return null; } @Override public String getServletInfo() { return null; } @Override public void init(ServletConfig arg0) throws ServletException { } @Override public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException { } } ResourceB.java000066400000000000000000000025601261716203600346200ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations.resources; import javax.annotation.Resource; import javax.annotation.Resources; /** * ResourceB * * */ @Resources({ @Resource(name="peach", mappedName="resA"), @Resource(name="pear", mappedName="resB") }) public class ResourceB extends ResourceA { @Resource(mappedName="resB") private Integer f;//test no inheritance of private fields @Resource private Integer p = new Integer(8); //test no injection because no value //test no annotation public void z() { System.err.println("ResourceB.z"); } } TestResourceAnnotations.java000066400000000000000000000144461261716203600376020ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/resources// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.annotations.resources; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import java.lang.reflect.Field; import java.util.List; import javax.naming.Context; import javax.naming.InitialContext; import org.eclipse.jetty.annotations.AnnotationIntrospector; import org.eclipse.jetty.annotations.ResourceAnnotationHandler; import org.eclipse.jetty.annotations.ResourcesAnnotationHandler; import org.eclipse.jetty.plus.annotation.Injection; import org.eclipse.jetty.plus.annotation.InjectionCollection; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TestResourceAnnotations { private Server server; private WebAppContext wac; private InjectionCollection injections; private Context comp; private Context env; private Object objA = 1000; private Object objB = 2000; @Before public void init() throws Exception { server = new Server(); wac = new WebAppContext(); wac.setServer(server); injections = new InjectionCollection(); wac.setAttribute(InjectionCollection.INJECTION_COLLECTION, injections); InitialContext ic = new InitialContext(); comp = (Context)ic.lookup("java:comp"); env = comp.createSubcontext("env"); } @After public void destroy() throws Exception { comp.destroySubcontext("env"); } @Test public void testResourceAnnotations () throws Exception { new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false); new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false); AnnotationIntrospector parser = new AnnotationIntrospector(); ResourceAnnotationHandler handler = new ResourceAnnotationHandler(wac); parser.registerHandler(handler); parser.introspect(ResourceA.class); parser.introspect(ResourceB.class); //processing classA should give us these jndi name bindings: // java:comp/env/myf // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/g // java:comp/env/mye // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/h // java:comp/env/resA // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceB/f // java:comp/env/org.eclipse.jetty.annotations.resources.ResourceA/n // assertEquals(objB, env.lookup("myf")); assertEquals(objA, env.lookup("mye")); assertEquals(objA, env.lookup("resA")); assertEquals(objA, env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/g")); assertEquals(objA, env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/h")); assertEquals(objB, env.lookup("org.eclipse.jetty.annotations.resources.ResourceB/f")); assertEquals(objB, env.lookup("org.eclipse.jetty.annotations.resources.ResourceA/n")); //we should have Injections assertNotNull(injections); List resBInjections = injections.getInjections(ResourceB.class.getCanonicalName()); assertNotNull(resBInjections); //only 1 field injection because the other has no Resource mapping assertEquals(1, resBInjections.size()); Injection fi = resBInjections.get(0); assertEquals ("f", fi.getTarget().getName()); //3 method injections on class ResourceA, 4 field injections List resAInjections = injections.getInjections(ResourceA.class.getCanonicalName()); assertNotNull(resAInjections); assertEquals(7, resAInjections.size()); int fieldCount = 0; int methodCount = 0; for (Injection x : resAInjections) { if (x.isField()) fieldCount++; else methodCount++; } assertEquals(4, fieldCount); assertEquals(3, methodCount); //test injection ResourceB binst = new ResourceB(); injections.inject(binst); //check injected values Field f = ResourceB.class.getDeclaredField ("f"); f.setAccessible(true); assertEquals(objB , f.get(binst)); //@Resource(mappedName="resA") //test the default naming scheme but using a mapped name from the environment f = ResourceA.class.getDeclaredField("g"); f.setAccessible(true); assertEquals(objA, f.get(binst)); //@Resource(name="resA") //test using the given name as the name from the environment f = ResourceA.class.getDeclaredField("j"); f.setAccessible(true); assertEquals(objA, f.get(binst)); //@Resource(mappedName="resB") //test using the default name on an inherited field f = ResourceA.class.getDeclaredField("n"); f.setAccessible(true); assertEquals(objB, f.get(binst)); } @Test public void testResourcesAnnotation () throws Exception { new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resA", objA, false); new org.eclipse.jetty.plus.jndi.EnvEntry(server, "resB", objB, false); AnnotationIntrospector introspector = new AnnotationIntrospector(); ResourcesAnnotationHandler handler = new ResourcesAnnotationHandler(wac); introspector.registerHandler(handler); introspector.introspect(ResourceA.class); introspector.introspect(ResourceB.class); assertEquals(objA, env.lookup("peach")); assertEquals(objB, env.lookup("pear")); } } jetty-9.2.14.v20151106/jetty-annotations/src/test/resources/000077500000000000000000000000001261716203600233105ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-annotations/src/test/resources/bad-classes.jar000066400000000000000000000036711261716203600261760ustar00rootroot00000000000000PK]C META-INF/PKPK]CMETA-INF/MANIFEST.MFMLK-. K-*ϳR03r.JM,IMu *h%&*8%krrPKGCDPK ]Ccom/PK ]C com/acme/PKe]Ccom/acme/Foo.classeN@511> ;0b#-K@tCGګ/ *PK@(;PK]Ccom/acme/.Blah.class+(MLV(LRPP/,V<̼Ĝb̜TZ.PKp33PK ]C com/.shadow/PK]Ccom/.shadow/.Blah.class+(MLV(LQpIR/,V<̼dZfN*W-PK|/0PK]Ccom/.shadow/Blah.class+(MLVpIRP/,V<̼Ĝ̜TZ.PK'g^11PKO]C Top.classe @g5&1V]RD %^$DJ|(q/n1;|x塌&)9vk$RZ/PfX'4WQx3 zEr yd:,T6/ Ŀ* jN+;a Ѕ1^qPKFџPK]C .Blah.class+HLNLOUЫ*(MLVpIRW(,V<̼Ĝb̜TZ.PKҴ k@@PK]C META-INF/PK]CGCD=META-INF/MANIFEST.MFPK ]Ccom/PK ]C com/acme/PKe]C@(; com/acme/Foo.classPK]Cp33com/acme/.Blah.classPK ]C com/.shadow/PK]C|/0com/.shadow/.Blah.classPK]C'g^11%com/.shadow/Blah.classPKO]CFџ Top.classPK]CҴ k@@ .Blah.classPK jetty-9.2.14.v20151106/jetty-ant/000077500000000000000000000000001261716203600157555ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/pom.xml000066400000000000000000000047471261716203600173060ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-ant jar Jetty :: Ant Plugin maven-dependency-plugin copy-lib-deps generate-resources copy-dependencies org.eclipse.jetty org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.drafts jetty-all,jetty-start,jetty-monitor,jetty-jsp jar ${project.build.directory}/test-lib org.eclipse.jetty.toolchain jetty-test-helper test ant ant 1.6.5 ant ant-launcher 1.6.5 org.eclipse.jetty jetty-security ${project.version} org.eclipse.jetty jetty-plus ${project.version} org.eclipse.jetty jetty-webapp ${project.version} org.eclipse.jetty jetty-annotations ${project.version} jetty-9.2.14.v20151106/jetty-ant/src/000077500000000000000000000000001261716203600165445ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/000077500000000000000000000000001261716203600174705ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/000077500000000000000000000000001261716203600204115ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/000077500000000000000000000000001261716203600212005ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/000077500000000000000000000000001261716203600226245ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600237635ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/000077500000000000000000000000001261716203600245455ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebAppContext.java000066400000000000000000000532571261716203600306120ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; import java.security.PermissionCollection; import java.util.ArrayList; import java.util.Enumeration; import java.util.EventListener; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.jar.Manifest; import javax.servlet.Servlet; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.FileSet; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.ant.types.Attribute; import org.eclipse.jetty.ant.types.Attributes; import org.eclipse.jetty.ant.types.FileMatchingConfiguration; import org.eclipse.jetty.ant.utils.TaskLog; import org.eclipse.jetty.plus.webapp.EnvConfiguration; import org.eclipse.jetty.plus.webapp.PlusConfiguration; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.FilterMapping; import org.eclipse.jetty.servlet.Holder; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletMapping; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.FragmentConfiguration; import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; import org.eclipse.jetty.webapp.MetaInfConfiguration; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebXmlConfiguration; import org.eclipse.jetty.xml.XmlConfiguration; /** * AntWebAppContext * * Extension of WebAppContext to allow configuration via Ant environment. * */ public class AntWebAppContext extends WebAppContext { private static final Logger LOG = Log.getLogger(WebAppContext.class); public final AntWebInfConfiguration antWebInfConfiguration = new AntWebInfConfiguration(); public final WebXmlConfiguration webXmlConfiguration = new WebXmlConfiguration(); public final MetaInfConfiguration metaInfConfiguration = new MetaInfConfiguration(); public final FragmentConfiguration fragmentConfiguration = new FragmentConfiguration(); public final EnvConfiguration envConfiguration = new EnvConfiguration(); public final PlusConfiguration plusConfiguration = new PlusConfiguration(); public final AnnotationConfiguration annotationConfiguration = new AnnotationConfiguration(); public final JettyWebXmlConfiguration jettyWebXmlConfiguration = new JettyWebXmlConfiguration(); public final Configuration[] DEFAULT_CONFIGURATIONS = { antWebInfConfiguration, webXmlConfiguration, metaInfConfiguration, fragmentConfiguration, envConfiguration, plusConfiguration, annotationConfiguration, jettyWebXmlConfiguration }; public final static String DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN = ".*/.*jsp-api-[^/]*\\.jar$|.*/.*jsp-[^/]*\\.jar$|.*/.*taglibs[^/]*\\.jar$|.*/.*jstl[^/]*\\.jar$|.*/.*jsf-impl-[^/]*\\.jar$|.*/.*javax.faces-[^/]*\\.jar$|.*/.*myfaces-impl-[^/]*\\.jar$"; /** Location of jetty-env.xml file. */ private File jettyEnvXml; /** List of web application libraries. */ private List libraries = new ArrayList(); /** List of web application class directories. */ private List classes = new ArrayList(); /** context xml file to apply to the webapp */ private File contextXml; /** List of extra scan targets for this web application. */ private FileSet scanTargets; /** context attributes to set **/ private Attributes attributes; private Project project; private List scanFiles; /** Extra scan targets. */ private FileMatchingConfiguration extraScanTargetsConfiguration; private FileMatchingConfiguration librariesConfiguration; public static void dump(ClassLoader loader) { while (loader != null) { System.err.println(loader); if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader)loader).getURLs(); if (urls != null) { for (URL u:urls) System.err.println("\t"+u+"\n"); } } loader = loader.getParent(); } } /** * AntURLClassLoader * * Adapt the AntClassLoader which is not a URLClassLoader - this is needed for * jsp to be able to search the classpath. */ public static class AntURLClassLoader extends URLClassLoader { private AntClassLoader antLoader; public AntURLClassLoader(AntClassLoader antLoader) { super(new URL[] {}, antLoader); this.antLoader = antLoader; } @Override public InputStream getResourceAsStream(String name) { return super.getResourceAsStream(name); } @Override public void close() throws IOException { super.close(); } @Override protected void addURL(URL url) { super.addURL(url); } @Override public URL[] getURLs() { Set urls = new HashSet(); //convert urls from antLoader String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar})); if (paths != null) { for (String p:paths) { File f = new File(p); try { urls.add(f.toURI().toURL()); } catch (Exception e) { LOG.ignore(e); } } } //add in any that may have been added to us as a URL directly URL[] ourURLS = super.getURLs(); if (ourURLS != null) { for (URL u:ourURLS) urls.add(u); } return urls.toArray(new URL[urls.size()]); } @Override protected Class findClass(String name) throws ClassNotFoundException { return super.findClass(name); } @Override protected Package definePackage(String name, Manifest man, URL url) throws IllegalArgumentException { return super.definePackage(name, man, url); } @Override public URL findResource(String name) { return super.findResource(name); } @Override public Enumeration findResources(String name) throws IOException { return super.findResources(name); } @Override protected PermissionCollection getPermissions(CodeSource codesource) { return super.getPermissions(codesource); } @Override public Class loadClass(String name) throws ClassNotFoundException { return super.loadClass(name); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { return super.loadClass(name, resolve); } @Override protected Object getClassLoadingLock(String className) { return super.getClassLoadingLock(className); } @Override public URL getResource(String name) { return super.getResource(name); } @Override public Enumeration getResources(String name) throws IOException { return super.getResources(name); } @Override protected Package definePackage(String name, String specTitle, String specVersion, String specVendor, String implTitle, String implVersion, String implVendor, URL sealBase) throws IllegalArgumentException { return super.definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase); } @Override protected Package getPackage(String name) { return super.getPackage(name); } @Override protected Package[] getPackages() { return super.getPackages(); } @Override protected String findLibrary(String libname) { return super.findLibrary(libname); } @Override public void setDefaultAssertionStatus(boolean enabled) { super.setDefaultAssertionStatus(enabled); } @Override public void setPackageAssertionStatus(String packageName, boolean enabled) { super.setPackageAssertionStatus(packageName, enabled); } @Override public void setClassAssertionStatus(String className, boolean enabled) { super.setClassAssertionStatus(className, enabled); } @Override public void clearAssertionStatus() { super.clearAssertionStatus(); } } /** * AntServletHolder * * */ public static class AntServletHolder extends ServletHolder { public AntServletHolder() { super(); } public AntServletHolder(Class servlet) { super(servlet); } public AntServletHolder(Servlet servlet) { super(servlet); } public AntServletHolder(String name, Class servlet) { super(name, servlet); } public AntServletHolder(String name, Servlet servlet) { super(name, servlet); } protected String getSystemClassPath (ClassLoader loader) throws Exception { StringBuilder classpath=new StringBuilder(); while (loader != null) { if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader)loader).getURLs(); if (urls != null) { for (int i=0;i0) classpath.append(File.pathSeparatorChar); classpath.append(file.getAbsolutePath()); } } } } else if (loader instanceof AntClassLoader) { classpath.append(((AntClassLoader)loader).getClasspath()); } loader = loader.getParent(); } return classpath.toString(); } } /** * AntServletHandler * * */ public static class AntServletHandler extends ServletHandler { @Override public ServletHolder newServletHolder(Holder.Source source) { return new AntServletHolder(); } } /** * Default constructor. Takes project as an argument * * @param project the project. */ public AntWebAppContext(Project project) throws Exception { super(); this.project = project; setConfigurations(DEFAULT_CONFIGURATIONS); setAttribute(WebInfConfiguration.CONTAINER_JAR_PATTERN, DEFAULT_CONTAINER_INCLUDE_JAR_PATTERN); setParentLoaderPriority(true); } /** * Adds a new Ant's attributes tag object if it have not been created yet. */ public void addAttributes(Attributes atts) { if (this.attributes != null) { throw new BuildException("Only one tag is allowed!"); } this.attributes = atts; } public void addLib(FileSet lib) { libraries.add(lib); } public void addClasses(FileSet classes) { this.classes.add(classes); } @Override protected ServletHandler newServletHandler() { return new AntServletHandler(); } public void setJettyEnvXml(File jettyEnvXml) { this.jettyEnvXml = jettyEnvXml; TaskLog.log("jetty-env.xml file: = " + (jettyEnvXml == null ? null : jettyEnvXml.getAbsolutePath())); } public File getJettyEnvXml () { return this.jettyEnvXml; } public List getLibraries() { return librariesConfiguration.getBaseDirectories(); } public void addScanTargets(FileSet scanTargets) { if (this.scanTargets != null) { throw new BuildException("Only one tag is allowed!"); } this.scanTargets = scanTargets; } public List getScanTargetFiles () { if (this.scanTargets == null) return null; FileMatchingConfiguration configuration = new FileMatchingConfiguration(); configuration.addDirectoryScanner(scanTargets.getDirectoryScanner(project)); return configuration.getBaseDirectories(); } public List getScanFiles() { if (scanFiles == null) scanFiles = initScanFiles(); return scanFiles; } public boolean isScanned (File file) { List files = getScanFiles(); if (files == null || files.isEmpty()) return false; return files.contains(file); } public List initScanFiles () { List scanList = new ArrayList(); if (getDescriptor() != null) { try (Resource r = Resource.newResource(getDescriptor());) { scanList.add(r.getFile()); } catch (IOException e) { throw new BuildException(e); } } if (getJettyEnvXml() != null) { try (Resource r = Resource.newResource(getJettyEnvXml());) { scanList.add(r.getFile()); } catch (IOException e) { throw new BuildException("Problem configuring scanner for jetty-env.xml", e); } } if (getDefaultsDescriptor() != null) { try (Resource r = Resource.newResource(getDefaultsDescriptor());) { if (!WebAppContext.WEB_DEFAULTS_XML.equals(getDefaultsDescriptor())) { scanList.add(r.getFile()); } } catch (IOException e) { throw new BuildException("Problem configuring scanner for webdefaults.xml", e); } } if (getOverrideDescriptor() != null) { try { Resource r = Resource.newResource(getOverrideDescriptor()); scanList.add(r.getFile()); } catch (IOException e) { throw new BuildException("Problem configuring scanner for webdefaults.xml", e); } } //add any extra classpath and libs List cpFiles = getClassPathFiles(); if (cpFiles != null) scanList.addAll(cpFiles); //any extra scan targets @SuppressWarnings("unchecked") List scanFiles = (List)getScanTargetFiles(); if (scanFiles != null) scanList.addAll(scanFiles); return scanList; } @Override public void setWar(String path) { super.setWar(path); try { Resource war = Resource.newResource(path); if (war.exists() && war.isDirectory() && getDescriptor() == null) { Resource webXml = war.addPath("WEB-INF/web.xml"); setDescriptor(webXml.toString()); } } catch (IOException e) { throw new BuildException(e); } } /** * */ public void doStart() { try { TaskLog.logWithTimestamp("Starting web application "+this.getDescriptor()); if (jettyEnvXml != null && jettyEnvXml.exists()) envConfiguration.setJettyEnvXml(Resource.toURL(jettyEnvXml)); ClassLoader parentLoader = this.getClass().getClassLoader(); if (parentLoader instanceof AntClassLoader) parentLoader = new AntURLClassLoader((AntClassLoader)parentLoader); setClassLoader(new WebAppClassLoader(parentLoader, this)); if (attributes != null && attributes.getAttributes() != null) { for (Attribute a:attributes.getAttributes()) setAttribute(a.getName(), a.getValue()); } //apply a context xml file if one was supplied if (contextXml != null) { XmlConfiguration xmlConfiguration = new XmlConfiguration(Resource.toURL(contextXml)); TaskLog.log("Applying context xml file "+contextXml); xmlConfiguration.configure(this); } super.doStart(); } catch (Exception e) { TaskLog.log(e.toString()); } } public void doStop() { try { scanFiles = null; TaskLog.logWithTimestamp("Stopping web application "+this); Thread.currentThread().sleep(500L); super.doStop(); //remove all filters, servlets and listeners. They will be recreated //either via application of a context xml file or web.xml or annotation or servlet api setEventListeners(new EventListener[0]); getServletHandler().setFilters(new FilterHolder[0]); getServletHandler().setFilterMappings(new FilterMapping[0]); getServletHandler().setServlets(new ServletHolder[0]); getServletHandler().setServletMappings(new ServletMapping[0]); } catch (InterruptedException e) { TaskLog.log(e.toString()); } catch (Exception e) { TaskLog.log(e.toString()); } } /** * @return a list of classpath files (libraries and class directories). */ public List getClassPathFiles() { List classPathFiles = new ArrayList(); Iterator classesIterator = classes.iterator(); while (classesIterator.hasNext()) { FileSet clazz = (FileSet) classesIterator.next(); classPathFiles.add(clazz.getDirectoryScanner(project).getBasedir()); } Iterator iterator = libraries.iterator(); while (iterator.hasNext()) { FileSet library = (FileSet) iterator.next(); String[] includedFiles = library.getDirectoryScanner(project).getIncludedFiles(); File baseDir = library.getDirectoryScanner(project).getBasedir(); for (int i = 0; i < includedFiles.length; i++) { classPathFiles.add(new File(baseDir, includedFiles[i])); } } return classPathFiles; } /** * @return a FileMatchingConfiguration object describing the * configuration of all libraries added to this particular web app * (both classes and libraries). */ public FileMatchingConfiguration getLibrariesConfiguration() { FileMatchingConfiguration config = new FileMatchingConfiguration(); Iterator classesIterator = classes.iterator(); while (classesIterator.hasNext()) { FileSet clazz = (FileSet) classesIterator.next(); config.addDirectoryScanner(clazz.getDirectoryScanner(project)); } Iterator librariesIterator = libraries.iterator(); while (librariesIterator.hasNext()) { FileSet library = (FileSet) librariesIterator.next(); config.addDirectoryScanner(library.getDirectoryScanner(project)); } return config; } public File getContextXml() { return contextXml; } public void setContextXml(File contextXml) { this.contextXml = contextXml; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebInfConfiguration.java000066400000000000000000000141671261716203600317660ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.File; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.regex.Pattern; import org.apache.tools.ant.AntClassLoader; import org.eclipse.jetty.util.PatternMatcher; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebInfConfiguration; import org.eclipse.jetty.webapp.WebXmlConfiguration; public class AntWebInfConfiguration extends WebInfConfiguration { @Override public void preConfigure(final WebAppContext context) throws Exception { //Make a temp directory for the webapp if one is not already set resolveTempDirectory(context); //Extract webapp if necessary unpack (context); //Apply an initial ordering to the jars which governs which will be scanned for META-INF //info and annotations. The ordering is based on inclusion patterns. String tmp = (String)context.getAttribute(WEBINF_JAR_PATTERN); Pattern webInfPattern = (tmp==null?null:Pattern.compile(tmp)); tmp = (String)context.getAttribute(CONTAINER_JAR_PATTERN); Pattern containerPattern = (tmp==null?null:Pattern.compile(tmp)); //Apply ordering to container jars - if no pattern is specified, we won't //match any of the container jars PatternMatcher containerJarNameMatcher = new PatternMatcher () { public void matched(URI uri) throws Exception { context.getMetaData().addContainerResource(Resource.newResource(uri)); } }; ClassLoader loader = context.getClassLoader(); if (loader != null) { loader = loader.getParent(); if (loader != null) { URI[] containerUris = null; if (loader instanceof URLClassLoader) { URL[] urls = ((URLClassLoader)loader).getURLs(); if (urls != null) { containerUris = new URI[urls.length]; int i=0; for (URL u : urls) { try { containerUris[i] = u.toURI(); } catch (URISyntaxException e) { containerUris[i] = new URI(u.toString().replaceAll(" ", "%20")); } i++; } } } else if (loader instanceof AntClassLoader) { AntClassLoader antLoader = (AntClassLoader)loader; String[] paths = antLoader.getClasspath().split(new String(new char[]{File.pathSeparatorChar})); if (paths != null) { containerUris = new URI[paths.length]; int i=0; for (String p:paths) { File f = new File(p); containerUris[i] = f.toURI(); i++; } } } containerJarNameMatcher.match(containerPattern, containerUris, false); } } //Apply ordering to WEB-INF/lib jars PatternMatcher webInfJarNameMatcher = new PatternMatcher () { @Override public void matched(URI uri) throws Exception { context.getMetaData().addWebInfJar(Resource.newResource(uri)); } }; List jars = findJars(context); //Convert to uris for matching URI[] uris = null; if (jars != null) { uris = new URI[jars.size()]; int i=0; for (Resource r: jars) { uris[i++] = r.getURI(); } } webInfJarNameMatcher.match(webInfPattern, uris, true); //null is inclusive, no pattern == all jars match //No pattern to appy to classes, just add to metadata context.getMetaData().setWebInfClassesDirs(findClassDirs(context)); } /** * Adds classpath files into web application classloader, and * sets web.xml and base directory for the configured web application. * * @see WebXmlConfiguration#configure(WebAppContext) */ public void configure(WebAppContext context) throws Exception { if (context instanceof AntWebAppContext) { List classPathFiles = ((AntWebAppContext)context).getClassPathFiles(); if (classPathFiles != null) { for (File cpFile:classPathFiles) { if (cpFile.exists()) { ((WebAppClassLoader) context.getClassLoader()).addClassPath(cpFile.getCanonicalPath()); } } } } super.configure(context); } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebXmlConfiguration.java000066400000000000000000000034511261716203600320040ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.File; import java.util.List; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebXmlConfiguration; /** * This configuration object provides additional way to inject application * properties into the configured web application. The list of classpath files, * the application base directory and web.xml file could be specified in this * way. */ public class AntWebXmlConfiguration extends WebXmlConfiguration { private static final Logger LOG = Log.getLogger(WebXmlConfiguration.class); /** List of classpath files. */ private List classPathFiles; /** Web application root directory. */ private File webAppBaseDir; public AntWebXmlConfiguration() { super(); } public void setClassPathFiles(List classPathFiles) { this.classPathFiles = classPathFiles; } public void setWebAppBaseDir(File webAppBaseDir) { this.webAppBaseDir = webAppBaseDir; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyRunTask.java000066400000000000000000000202211261716203600300140ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.apache.tools.ant.taskdefs.Property; import org.eclipse.jetty.ant.types.Connector; import org.eclipse.jetty.ant.types.Connectors; import org.eclipse.jetty.ant.types.ContextHandlers; import org.eclipse.jetty.ant.types.LoginServices; import org.eclipse.jetty.ant.types.SystemProperties; import org.eclipse.jetty.ant.utils.TaskLog; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.webapp.WebAppContext; /** * Ant task for running a Jetty server. */ public class JettyRunTask extends Task { private int scanIntervalSeconds; /** Temporary files directory. */ private File tempDirectory; /** List of web applications to be deployed. */ private List webapps = new ArrayList(); /** Location of jetty.xml file. */ private File jettyXml; /** List of server connectors. */ private Connectors connectors = null; /** Server request logger object. */ private RequestLog requestLog; /** List of login services. */ private LoginServices loginServices; /** List of system properties to be set. */ private SystemProperties systemProperties; /** List of other contexts to deploy */ private ContextHandlers contextHandlers; /** Port Jetty will use for the default connector */ private int jettyPort = 8080; private int stopPort; private String stopKey; private boolean daemon; public JettyRunTask() { TaskLog.setTask(this); } /** * Creates a new WebApp Ant object. * */ public void addWebApp(AntWebAppContext webapp) { webapps.add(webapp); } /** * Adds a new Ant's connector tag object if it have not been created yet. */ public void addConnectors(Connectors connectors) { if (this.connectors != null) throw new BuildException("Only one tag is allowed!"); this.connectors = connectors; } /** * @param services */ public void addLoginServices(LoginServices services) { if (this.loginServices != null ) throw new BuildException("Only one tag is allowed!"); this.loginServices = services; } public void addSystemProperties(SystemProperties systemProperties) { if (this.systemProperties != null) throw new BuildException("Only one tag is allowed!"); this.systemProperties = systemProperties; } /** * @param handlers */ public void addContextHandlers (ContextHandlers handlers) { if (this.contextHandlers != null) throw new BuildException("Only one tag is allowed!"); this.contextHandlers = handlers; } public File getTempDirectory() { return tempDirectory; } /** * @param tempDirectory */ public void setTempDirectory(File tempDirectory) { this.tempDirectory = tempDirectory; } public File getJettyXml() { return jettyXml; } /** * @param jettyXml */ public void setJettyXml(File jettyXml) { this.jettyXml = jettyXml; } /** * @param className */ public void setRequestLog(String className) { try { this.requestLog = (RequestLog) Class.forName(className).newInstance(); } catch (InstantiationException e) { throw new BuildException("Request logger instantiation exception: " + e); } catch (IllegalAccessException e) { throw new BuildException("Request logger instantiation exception: " + e); } catch (ClassNotFoundException e) { throw new BuildException("Unknown request logger class: " + className); } } public String getRequestLog() { if (requestLog != null) { return requestLog.getClass().getName(); } return ""; } /** * Sets the port Jetty uses for the default connector. * * @param jettyPort The port Jetty will use for the default connector */ public void setJettyPort(final int jettyPort) { this.jettyPort = jettyPort; } /** * Executes this Ant task. The build flow is being stopped until Jetty * server stops. * * @throws BuildException */ public void execute() throws BuildException { TaskLog.log("Configuring Jetty for project: " + getProject().getName()); setSystemProperties(); List connectorsList = null; if (connectors != null) connectorsList = connectors.getConnectors(); else connectorsList = new Connectors(jettyPort,30000).getDefaultConnectors(); List loginServicesList = (loginServices != null?loginServices.getLoginServices():new ArrayList()); ServerProxyImpl server = new ServerProxyImpl(); server.setConnectors(connectorsList); server.setLoginServices(loginServicesList); server.setRequestLog(requestLog); server.setJettyXml(jettyXml); server.setDaemon(daemon); server.setStopPort(stopPort); server.setStopKey(stopKey); server.setContextHandlers(contextHandlers); server.setTempDirectory(tempDirectory); server.setScanIntervalSecs(scanIntervalSeconds); try { for (WebAppContext webapp: webapps) { server.addWebApplication((AntWebAppContext)webapp); } } catch (Exception e) { throw new BuildException(e); } server.start(); } public int getStopPort() { return stopPort; } public void setStopPort(int stopPort) { this.stopPort = stopPort; TaskLog.log("stopPort="+stopPort); } public String getStopKey() { return stopKey; } public void setStopKey(String stopKey) { this.stopKey = stopKey; TaskLog.log("stopKey="+stopKey); } /** * @return the daemon */ public boolean isDaemon() { return daemon; } /** * @param daemon the daemon to set */ public void setDaemon(boolean daemon) { this.daemon = daemon; TaskLog.log("Daemon="+daemon); } public int getScanIntervalSeconds() { return scanIntervalSeconds; } /** * @param secs */ public void setScanIntervalSeconds(int secs) { scanIntervalSeconds = secs; TaskLog.log("scanIntervalSecs="+secs); } /** * Sets the system properties. */ private void setSystemProperties() { if (systemProperties != null) { Iterator propertiesIterator = systemProperties.getSystemProperties().iterator(); while (propertiesIterator.hasNext()) { Property property = ((Property) propertiesIterator.next()); SystemProperties.setIfNotSetAlready(property); } } } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/JettyStopTask.java000066400000000000000000000060631261716203600302050ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.InetAddress; import java.net.Socket; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.eclipse.jetty.ant.utils.TaskLog; /** * JettyStopTask * * */ public class JettyStopTask extends Task { private int stopPort; private String stopKey; private int stopWait; /** * */ public JettyStopTask() { TaskLog.setTask(this); } /** * @see org.apache.tools.ant.Task#execute() */ public void execute() throws BuildException { try { Socket s = new Socket(InetAddress.getByName("127.0.0.1"),stopPort); if (stopWait > 0) s.setSoTimeout(stopWait*1000); try { OutputStream out = s.getOutputStream(); out.write((stopKey + "\r\nstop\r\n").getBytes()); out.flush(); if (stopWait > 0) { TaskLog.log("Waiting"+(stopWait > 0 ? (" "+stopWait+"sec") : "")+" for jetty to stop"); LineNumberReader lin = new LineNumberReader(new InputStreamReader(s.getInputStream())); String response=lin.readLine(); if ("Stopped".equals(response)) System.err.println("Stopped"); } } finally { s.close(); } } catch (ConnectException e) { TaskLog.log("Jetty not running!"); } catch (Exception e) { TaskLog.log(e.getMessage()); } } public int getStopPort() { return stopPort; } public void setStopPort(int stopPort) { this.stopPort = stopPort; } public String getStopKey() { return stopKey; } public void setStopKey(String stopKey) { this.stopKey = stopKey; } public int getStopWait() { return stopWait; } public void setStopWait(int stopWait) { this.stopWait = stopWait; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/ServerProxyImpl.java000066400000000000000000000313571261716203600305530ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.eclipse.jetty.ant.types.Connector; import org.eclipse.jetty.ant.types.ContextHandlers; import org.eclipse.jetty.ant.utils.ServerProxy; import org.eclipse.jetty.ant.utils.TaskLog; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.RequestLog; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.ShutdownMonitor; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.server.handler.RequestLogHandler; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.xml.XmlConfiguration; import org.xml.sax.SAXException; /** * A proxy class for interaction with Jetty server object. Used to have some * level of abstraction over standard Jetty classes. */ public class ServerProxyImpl implements ServerProxy { /** Proxied Jetty server object. */ private Server server; /** Temporary files directory. */ private File tempDirectory; /** Collection of context handlers (web application contexts). */ private ContextHandlerCollection contexts; /** Location of jetty.xml file. */ private File jettyXml; /** List of connectors. */ private List connectors; /** Request logger. */ private RequestLog requestLog; /** User realms. */ private List loginServices; /** List of added web applications. */ private List webApplications = new ArrayList(); /** other contexts to deploy */ private ContextHandlers contextHandlers; /** scan interval for changed files */ private int scanIntervalSecs; /** port to listen for stop command */ private int stopPort; /** security key for stop command */ private String stopKey; /** wait for all jetty threads to exit or continue */ private boolean daemon; private boolean configured = false; /** * WebAppScannerListener * * Handle notifications that files we are interested in have changed * during execution. * */ public static class WebAppScannerListener implements Scanner.BulkListener { AntWebAppContext awc; public WebAppScannerListener (AntWebAppContext awc) { this.awc = awc; } public void filesChanged(List changedFileNames) { boolean isScanned = false; try { Iterator itor = changedFileNames.iterator(); while (!isScanned && itor.hasNext()) { isScanned = awc.isScanned(Resource.newResource(itor.next()).getFile()); } if (isScanned) { awc.stop(); awc.start(); } } catch (Exception e) { TaskLog.log(e.getMessage()); } } } /** * Default constructor. Creates a new Jetty server with a standard connector * listening on a given port. */ public ServerProxyImpl () { server = new Server(); server.setStopAtShutdown(true); } public void addWebApplication(AntWebAppContext webApp) { webApplications.add(webApp); } public int getStopPort() { return stopPort; } public void setStopPort(int stopPort) { this.stopPort = stopPort; } public String getStopKey() { return stopKey; } public void setStopKey(String stopKey) { this.stopKey = stopKey; } public File getJettyXml() { return jettyXml; } public void setJettyXml(File jettyXml) { this.jettyXml = jettyXml; } public List getConnectors() { return connectors; } public void setConnectors(List connectors) { this.connectors = connectors; } public RequestLog getRequestLog() { return requestLog; } public void setRequestLog(RequestLog requestLog) { this.requestLog = requestLog; } public List getLoginServices() { return loginServices; } public void setLoginServices(List loginServices) { this.loginServices = loginServices; } public List getWebApplications() { return webApplications; } public void setWebApplications(List webApplications) { this.webApplications = webApplications; } public File getTempDirectory() { return tempDirectory; } public void setTempDirectory(File tempDirectory) { this.tempDirectory = tempDirectory; } /** * @see org.eclipse.jetty.ant.utils.ServerProxy#start() */ public void start() { try { configure(); configureWebApps(); server.start(); System.setProperty("jetty.ant.server.port","" + ((ServerConnector)server.getConnectors()[0]).getLocalPort()); String host = ((ServerConnector)server.getConnectors()[0]).getHost(); if (host == null) { System.setProperty("jetty.ant.server.host", "localhost"); } else { System.setProperty("jetty.ant.server.host", host); } startScanners(); TaskLog.log("Jetty AntTask Started"); if (!daemon) server.join(); } catch (InterruptedException e) { new RuntimeException(e); } catch (Exception e) { e.printStackTrace(); new RuntimeException(e); } } /** * @see org.eclipse.jetty.ant.utils.ServerProxy#getProxiedObject() */ public Object getProxiedObject() { return server; } /** * @return the daemon */ public boolean isDaemon() { return daemon; } /** * @param daemon the daemon to set */ public void setDaemon(boolean daemon) { this.daemon = daemon; } /** * @return the contextHandlers */ public ContextHandlers getContextHandlers() { return contextHandlers; } /** * @param contextHandlers the contextHandlers to set */ public void setContextHandlers (ContextHandlers contextHandlers) { this.contextHandlers = contextHandlers; } public int getScanIntervalSecs() { return scanIntervalSecs; } public void setScanIntervalSecs(int scanIntervalSecs) { this.scanIntervalSecs = scanIntervalSecs; } /** * Configures Jetty server before adding any web applications to it. */ private void configure() { if (configured) return; configured = true; if(stopPort>0 && stopKey!=null) { ShutdownMonitor monitor = ShutdownMonitor.getInstance(); monitor.setPort(stopPort); monitor.setKey(stopKey); monitor.setExitVm(false); } if (tempDirectory != null && !tempDirectory.exists()) tempDirectory.mkdirs(); // Applies external configuration via jetty.xml applyJettyXml(); // Configures connectors for this server instance. if (connectors != null) { for (Connector c:connectors) { ServerConnector jc = new ServerConnector(server); jc.setPort(c.getPort()); jc.setIdleTimeout(c.getMaxIdleTime()); server.addConnector(jc); } } // Configures login services if (loginServices != null) { for (LoginService ls:loginServices) { server.addBean(ls); } } // Does not cache resources, to prevent Windows from locking files Resource.setDefaultUseCaches(false); // Set default server handlers configureHandlers(); } /** * */ private void configureHandlers() { RequestLogHandler requestLogHandler = new RequestLogHandler(); if (requestLog != null) requestLogHandler.setRequestLog(requestLog); contexts = (ContextHandlerCollection) server .getChildHandlerByClass(ContextHandlerCollection.class); if (contexts == null) { contexts = new ContextHandlerCollection(); HandlerCollection handlers = (HandlerCollection) server .getChildHandlerByClass(HandlerCollection.class); if (handlers == null) { handlers = new HandlerCollection(); server.setHandler(handlers); handlers.setHandlers(new Handler[] { contexts, new DefaultHandler(), requestLogHandler }); } else { handlers.addHandler(contexts); } } //if there are any extra contexts to deploy if (contextHandlers != null && contextHandlers.getContextHandlers() != null) { for (ContextHandler c:contextHandlers.getContextHandlers()) contexts.addHandler(c); } } /** * Applies jetty.xml configuration to the Jetty server instance. */ private void applyJettyXml() { if (jettyXml != null && jettyXml.exists()) { TaskLog.log("Configuring jetty from xml configuration file = " + jettyXml.getAbsolutePath()); XmlConfiguration configuration; try { configuration = new XmlConfiguration(Resource.toURL(jettyXml)); configuration.configure(server); } catch (MalformedURLException e) { throw new RuntimeException(e); } catch (SAXException e) { throw new RuntimeException(e); } catch (IOException e) { throw new RuntimeException(e); } catch (Exception e) { throw new RuntimeException(e); } } } /** * Starts web applications' scanners. */ private void startScanners() throws Exception { for (AntWebAppContext awc:webApplications) { if (scanIntervalSecs <= 0) return; List scanList = awc.getScanFiles(); TaskLog.log("Web application '" + awc + "': starting scanner at interval of " + scanIntervalSecs + " seconds."); Scanner.Listener changeListener = new WebAppScannerListener(awc); Scanner scanner = new Scanner(); scanner.setScanInterval(scanIntervalSecs); scanner.addListener(changeListener); scanner.setScanDirs(scanList); scanner.setReportExistingFilesOnStartup(false); scanner.start(); } } /** * */ private void configureWebApps() { for (AntWebAppContext awc:webApplications) { awc.setAttribute(AntWebAppContext.BASETEMPDIR, tempDirectory); if (contexts != null) contexts.addHandler(awc); } } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/package-info.java000066400000000000000000000015561261716203600277430ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Ant : Ant Tasks and Configuration */ package org.eclipse.jetty.ant; jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/000077500000000000000000000000001261716203600257115ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Attribute.java000066400000000000000000000022571261716203600305250ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; public class Attribute { String name; String value; public void setName(String name) { this.name = name; } public void setValue(String value) { this.value = value; } public String getName() { return name; } public String getValue() { return value; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Attributes.java000066400000000000000000000022061261716203600307020ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.util.ArrayList; import java.util.List; public class Attributes { List _attributes = new ArrayList(); public void addAttribute(Attribute attr ) { _attributes.add(attr); } public List getAttributes() { return _attributes; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connector.java000066400000000000000000000026011261716203600305050ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; /** * Connector * * */ public class Connector { private int port; private int maxIdleTime; public Connector() { } public Connector(int port, int maxIdleTime) { this.port = port; this.maxIdleTime = maxIdleTime; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getMaxIdleTime() { return maxIdleTime; } public void setMaxIdleTime(int maxIdleTime) { this.maxIdleTime = maxIdleTime; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/Connectors.java000066400000000000000000000043571261716203600307020ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.util.ArrayList; import java.util.List; /** * * Connectors * * Specifies a jetty configuration element for Ant build file. * */ public class Connectors { private List connectors = new ArrayList(); private List defaultConnectors = new ArrayList(); /** * Default constructor. */ public Connectors() { this(8080, 30000); } /** * Constructor. * * @param port The port that the default connector will listen on * @param maxIdleTime The maximum idle time for the default connector */ public Connectors(int port, int maxIdleTime) { defaultConnectors.add(new Connector(port, maxIdleTime)); } /** * Adds a connector to the list of connectors to deploy. * * @param connector A connector to add to the list */ public void add(Connector connector) { connectors.add(connector); } /** * Returns the list of known connectors to deploy. * * @return The list of known connectors */ public List getConnectors() { return connectors; } /** * Gets the default list of connectors to deploy when no connectors * were explicitly added to the list. * * @return The list of default connectors */ public List getDefaultConnectors() { return defaultConnectors; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/ContextHandlers.java000066400000000000000000000024461261716203600316670ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.server.handler.ContextHandler; /** * Specifies element in web app configuration. * */ public class ContextHandlers { private List contextHandlers = new ArrayList(); public void add(ContextHandler handler) { contextHandlers.add(handler); } public List getContextHandlers() { return contextHandlers; } } FileMatchingConfiguration.java000066400000000000000000000056171261716203600335700ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.io.File; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.tools.ant.DirectoryScanner; /** * Describes set of files matched by elements in ant configuration * file. It is used to group application classes, libraries, and scannedTargets * elements. * */ public class FileMatchingConfiguration { private List directoryScanners; public FileMatchingConfiguration() { this.directoryScanners = new ArrayList(); } /** * @param directoryScanner new directory scanner retrieved from the * element. */ public void addDirectoryScanner(DirectoryScanner directoryScanner) { this.directoryScanners.add(directoryScanner); } /** * @return a list of base directories denoted by a list of directory * scanners. */ public List getBaseDirectories() { List baseDirs = new ArrayList(); Iterator scanners = directoryScanners.iterator(); while (scanners.hasNext()) { DirectoryScanner scanner = (DirectoryScanner) scanners.next(); baseDirs.add(scanner.getBasedir()); } return baseDirs; } /** * Checks if passed file is scanned by any of the directory scanners. * * @param pathToFile a fully qualified path to tested file. * @return true if so, false otherwise. */ public boolean isIncluded(String pathToFile) { Iterator scanners = directoryScanners.iterator(); while (scanners.hasNext()) { DirectoryScanner scanner = (DirectoryScanner) scanners.next(); scanner.scan(); String[] includedFiles = scanner.getIncludedFiles(); for (int i = 0; i < includedFiles.length; i++) { File includedFile = new File(scanner.getBasedir(), includedFiles[i]); if (pathToFile.equalsIgnoreCase(includedFile.getAbsolutePath())) { return true; } } } return false; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/LoginServices.java000066400000000000000000000024561261716203600313370ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.security.LoginService; /** * LoginServices * * Specifies a jetty configuration element for Ant build file. * */ public class LoginServices { private List loginServices = new ArrayList(); public void add(LoginService service) { loginServices.add(service); } public List getLoginServices() { return loginServices; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java000066400000000000000000000036131261716203600321200ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.types; import java.util.ArrayList; import java.util.List; import org.apache.tools.ant.taskdefs.Property; import org.eclipse.jetty.ant.utils.TaskLog; /** * SystemProperties * * Ant tag definition. * */ public class SystemProperties { private List systemProperties = new ArrayList(); public List getSystemProperties() { return systemProperties; } public void addSystemProperty(Property property) { systemProperties.add(property); } /** * Set a System.property with this value if it is not already set. * * @returns true if property has been set */ public static boolean setIfNotSetAlready(Property property) { if (System.getProperty(property.getName()) == null) { System.setProperty(property.getName(), (property.getValue() == null ? "" : property .getValue())); TaskLog.log("Setting property '" + property.getName() + "' to value '" + property.getValue() + "'"); return true; } return false; } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/package-info.java000066400000000000000000000015701261716203600311030ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Ant : Ant Wrappers of Jetty Internals */ package org.eclipse.jetty.ant.types; jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/utils/000077500000000000000000000000001261716203600257055ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/utils/ServerProxy.java000066400000000000000000000022541261716203600310630ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.utils; import org.eclipse.jetty.ant.AntWebAppContext; public interface ServerProxy { /** * Adds a new web application to this server. * * @param awc a AntWebAppContext object. */ public void addWebApplication(AntWebAppContext awc); /** * Starts this server. */ public void start(); public Object getProxiedObject(); } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/utils/TaskLog.java000066400000000000000000000030561261716203600301200ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2012 Sabre Holdings. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant.utils; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.tools.ant.Task; /** * Provides logging functionality for classes without access to the Ant project * variable. * */ public class TaskLog { private static Task task; private static final SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS"); public static void setTask(Task task) { TaskLog.task = task; } public static void log(String message) { task.log(message); } public static void logWithTimestamp(String message) { String date; synchronized (format) { date = format.format(new Date()); } task.log(date + ": " + message); } } jetty-9.2.14.v20151106/jetty-ant/src/main/java/org/eclipse/jetty/ant/utils/package-info.java000066400000000000000000000015501261716203600310750ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Ant : Utility Classes */ package org.eclipse.jetty.ant.utils; jetty-9.2.14.v20151106/jetty-ant/src/main/resources/000077500000000000000000000000001261716203600215025ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/main/resources/tasks.properties000066400000000000000000000001331261716203600247420ustar00rootroot00000000000000jetty.run=org.eclipse.jetty.ant.JettyRunTask jetty.stop=org.eclipse.jetty.ant.JettyStopTaskjetty-9.2.14.v20151106/jetty-ant/src/test/000077500000000000000000000000001261716203600175235ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/config/000077500000000000000000000000001261716203600207705ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/config/build.xml000066400000000000000000000047701261716203600226210ustar00rootroot00000000000000 jetty-9.2.14.v20151106/jetty-ant/src/test/java/000077500000000000000000000000001261716203600204445ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/000077500000000000000000000000001261716203600212335ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/eclipse/000077500000000000000000000000001261716203600226575ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600240165ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/eclipse/jetty/ant/000077500000000000000000000000001261716203600246005ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/eclipse/jetty/ant/AntBuild.java000066400000000000000000000204421261716203600271470ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.tools.ant.DefaultLogger; import org.apache.tools.ant.Project; import org.apache.tools.ant.ProjectHelper; import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; public class AntBuild { private Thread _process; private String _ant; private int _port; private String _host; public AntBuild(String ant) { _ant = ant; } private class AntBuildProcess implements Runnable { List connList; @Override public void run() { File buildFile = new File(_ant); Project antProject = new Project(); try { antProject.setBaseDir(MavenTestingUtils.getBasedir()); antProject.setUserProperty("ant.file",buildFile.getAbsolutePath()); DefaultLogger logger = new DefaultLogger(); ConsoleParser parser = new ConsoleParser(); //connList = parser.newPattern(".*([0-9]+\\.[0-9]*\\.[0-9]*\\.[0-9]*):([0-9]*)",1); connList = parser.newPattern("Jetty AntTask Started",1); PipedOutputStream pos = new PipedOutputStream(); PipedInputStream pis = new PipedInputStream(pos); PipedOutputStream pose = new PipedOutputStream(); PipedInputStream pise = new PipedInputStream(pose); startPump("STDOUT",parser,pis); startPump("STDERR",parser,pise); logger.setErrorPrintStream(new PrintStream(pos)); logger.setOutputPrintStream(new PrintStream(pose)); logger.setMessageOutputLevel(Project.MSG_VERBOSE); antProject.addBuildListener(logger); antProject.fireBuildStarted(); antProject.init(); ProjectHelper helper = ProjectHelper.getProjectHelper(); antProject.addReference("ant.projectHelper",helper); helper.parse(antProject,buildFile); antProject.executeTarget("jetty.run"); parser.waitForDone(10000,TimeUnit.MILLISECONDS); } catch (Exception e) { antProject.fireBuildFinished(e); } } public void waitForStarted() throws Exception { while (connList == null || connList.isEmpty()) { Thread.sleep(10); } } } public void start() throws Exception { System.out.println("Starting Ant Build ..."); AntBuildProcess abp = new AntBuildProcess(); _process = new Thread(abp); _process.start(); abp.waitForStarted(); // once this has returned we should have the connection info we need //_host = abp.getConnectionList().get(0)[0]; //_port = Integer.parseInt(abp.getConnectionList().get(0)[1]); } public int getJettyPort() { return Integer.parseInt(System.getProperty("jetty.ant.server.port")); } public String getJettyHost() { return System.getProperty("jetty.ant.server.host"); } /** * Stop the jetty server */ public void stop() { System.out.println("Stopping Ant Build ..."); _process.interrupt(); } private static class ConsoleParser { private List patterns = new ArrayList(); private CountDownLatch latch; private int count; public List newPattern(String exp, int cnt) { ConsolePattern pat = new ConsolePattern(exp,cnt); patterns.add(pat); count += cnt; return pat.getMatches(); } public void parse(String line) { for (ConsolePattern pat : patterns) { Matcher mat = pat.getMatcher(line); if (mat.find()) { int num = 0, count = mat.groupCount(); String[] match = new String[count]; while (num++ < count) { match[num - 1] = mat.group(num); } pat.getMatches().add(match); if (pat.getCount() > 0) { getLatch().countDown(); } } } } public void waitForDone(long timeout, TimeUnit unit) throws InterruptedException { getLatch().await(timeout,unit); } private CountDownLatch getLatch() { synchronized (this) { if (latch == null) { latch = new CountDownLatch(count); } } return latch; } } private static class ConsolePattern { private Pattern pattern; private List matches; private int count; ConsolePattern(String exp, int cnt) { pattern = Pattern.compile(exp); matches = new ArrayList(); count = cnt; } public Matcher getMatcher(String line) { return pattern.matcher(line); } public List getMatches() { return matches; } public int getCount() { return count; } } private void startPump(String mode, ConsoleParser parser, InputStream inputStream) { ConsoleStreamer pump = new ConsoleStreamer(mode,inputStream); pump.setParser(parser); Thread thread = new Thread(pump,"ConsoleStreamer/" + mode); thread.start(); } /** * Simple streamer for the console output from a Process */ private static class ConsoleStreamer implements Runnable { private String mode; private BufferedReader reader; private ConsoleParser parser; public ConsoleStreamer(String mode, InputStream is) { this.mode = mode; this.reader = new BufferedReader(new InputStreamReader(is)); } public void setParser(ConsoleParser connector) { this.parser = connector; } public void run() { String line; //System.out.printf("ConsoleStreamer/%s initiated%n",mode); try { while ((line = reader.readLine()) != (null)) { if (parser != null) { parser.parse(line); } System.out.println("[" + mode + "] " + line); } } catch (IOException ignore) { /* ignore */ } finally { IO.close(reader); } //System.out.printf("ConsoleStreamer/%s finished%n",mode); } } } jetty-9.2.14.v20151106/jetty-ant/src/test/java/org/eclipse/jetty/ant/JettyAntTaskTest.java000066400000000000000000000042751261716203600307000ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.ant; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import java.net.HttpURLConnection; import java.net.URI; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.junit.Test; public class JettyAntTaskTest { @Test public void testConnectorTask() throws Exception { AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("connector-test.xml").getAbsolutePath()); build.start(); URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort()); HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection(); connection.connect(); assertThat("response code is 404", connection.getResponseCode(), is(404)); build.stop(); } @Test public void testWebApp () throws Exception { AntBuild build = new AntBuild(MavenTestingUtils.getTestResourceFile("webapp-test.xml").getAbsolutePath()); build.start(); URI uri = new URI("http://" + build.getJettyHost() + ":" + build.getJettyPort() + "/"); HttpURLConnection connection = (HttpURLConnection)uri.toURL().openConnection(); connection.connect(); assertThat("response code is 200", connection.getResponseCode(), is(200)); System.err.println("Stop build!"); build.stop(); } } jetty-9.2.14.v20151106/jetty-ant/src/test/resources/000077500000000000000000000000001261716203600215355ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/connector-test.xml000066400000000000000000000011341261716203600252250ustar00rootroot00000000000000 jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/000077500000000000000000000000001261716203600223205ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/000077500000000000000000000000001261716203600233475ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/acme-taglib.tld000066400000000000000000000013501261716203600262200ustar00rootroot00000000000000 1.0 1.2 acme http://www.acme.com/taglib taglib example com.acme.TagListener date com.acme.DateTag TAGDEPENDENT Display Date tz false jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/acme-taglib2.tld000066400000000000000000000022461261716203600263070ustar00rootroot00000000000000 Acme JSP2 tags 1.0 acme2 http://www.acme.com/taglib2 Simple Date formatting date2 com.acme.Date2Tag scriptless Day of the Month day Month of the Year month Year year format true true jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/tags/000077500000000000000000000000001261716203600243055ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/tags/panel.tag000066400000000000000000000005621261716203600261040ustar00rootroot00000000000000<%-- - Copyright (c) 2002 The Apache Software Foundation. All rights - reserved. --%> <%@ attribute name="color" %> <%@ attribute name="bgcolor" %> <%@ attribute name="title" %>
${title}
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/WEB-INF/web.xml000066400000000000000000000014311261716203600246450ustar00rootroot00000000000000 Test WebApp org.eclipse.jetty.server.context.ManagedAttributes QoSFilter,TransparentProxy.ThreadPool,TransparentProxy.HttpClient foo.jsp /jsp/foo/foo.jsp foo.jsp /jsp/foo/ jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/index.html000066400000000000000000000000651261716203600243160ustar00rootroot00000000000000

INDEX!

jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/000077500000000000000000000000001261716203600231145ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/bean1.jsp000066400000000000000000000007221261716203600246210ustar00rootroot00000000000000 <%@ page session="true"%>

JSP1.2 Beans: 1

Counter accessed times.
Counter last accessed by
Goto bean2.jsp jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/bean2.jsp000066400000000000000000000007221261716203600246220ustar00rootroot00000000000000 <%@ page session="true"%>

JSP1.2 Beans: 2

Counter accessed times.
Counter last accessed by
Goto bean1.jsp jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/dump.jsp000066400000000000000000000011001261716203600245670ustar00rootroot00000000000000 <%@ page import="java.util.Enumeration" %>

JSP Dump

<% Enumeration e =request.getParameterNames(); while(e.hasMoreElements()) { String name = (String)e.nextElement(); %> <% } %>
Request URI:<%= request.getRequestURI() %>
ServletPath:<%= request.getServletPath() %>
PathInfo:<%= request.getPathInfo() %>
getParameter("<%= name %>") <%= request.getParameter(name) %>
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/expr.jsp000066400000000000000000000007601261716203600246130ustar00rootroot00000000000000

JSP2.0 Expressions

ExpressionResult
\${param["A"]} ${param["A"]} 
\${header["host"]} ${header["host"]}
\${header["user-agent"]} ${header["user-agent"]}
\${1+1} ${1+1}
\${param["A"] * 2} ${param["A"] * 2} 
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/foo/000077500000000000000000000000001261716203600236775ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/foo/foo.jsp000066400000000000000000000004561261716203600252050ustar00rootroot00000000000000<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

FOO Example


A trivial FOO example



jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/index.html000066400000000000000000000010151261716203600251060ustar00rootroot00000000000000

JSP Examples

Main Menu jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/jstl.jsp000066400000000000000000000004601261716203600246060ustar00rootroot00000000000000<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

JSTL Example


A trivial jstl example



jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/tag.jsp000066400000000000000000000006461261716203600244130ustar00rootroot00000000000000 <%@ taglib uri="http://www.acme.com/taglib" prefix="acme" %> <acme:date tz="GMT">EEE, dd/MMM/yyyy HH:mm:ss ZZZ</acme:date> ==> EEE, dd/MMM/yyyy HH:mm:ss ZZZ
<acme:date tz="EST">EEE, dd-MMM-yyyy HH:mm:ss ZZZ</acme:date> ==> EEE, dd-MMM-yyyy HH:mm:ss ZZZ
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/tag2.jsp000066400000000000000000000004131261716203600244650ustar00rootroot00000000000000 <%@ taglib uri="http://www.acme.com/taglib2" prefix="acme" %> On ${day} of ${month} in the year ${year}
${day} - ${month} - ${year}
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/foo/jsp/tagfile.jsp000066400000000000000000000017671261716203600252600ustar00rootroot00000000000000<%@ taglib prefix="acme" tagdir="/WEB-INF/tags" %>

JSP 2.0 Tag File Example


Panel tag created from JSP fragment file in WEB-INF/tags


First panel.
Second panel.
Second panel.
Second panel.
Second panel.
Third panel.
A panel in a panel. Third panel.
jetty-9.2.14.v20151106/jetty-ant/src/test/resources/webapp-test.xml000066400000000000000000000012411261716203600245100ustar00rootroot00000000000000 jetty-9.2.14.v20151106/jetty-cdi/000077500000000000000000000000001261716203600157325ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/pom.xml000066400000000000000000000030731261716203600172520ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-cdi Jetty :: CDI Configurations http://www.eclipse.org/jetty jar ${project.groupId}.${project.artifactId} org.apache.maven.plugins maven-assembly-plugin package single config org.eclipse.jetty jetty-plus ${project.version} org.eclipse.jetty jetty-deploy ${project.version} jetty-9.2.14.v20151106/jetty-cdi/src/000077500000000000000000000000001261716203600165215ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/000077500000000000000000000000001261716203600174455ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/config/000077500000000000000000000000001261716203600207125ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/config/etc/000077500000000000000000000000001261716203600214655ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/config/etc/jetty-cdi.xml000066400000000000000000000011711261716203600241030ustar00rootroot00000000000000 jetty-9.2.14.v20151106/jetty-cdi/src/main/config/modules/000077500000000000000000000000001261716203600223625ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/config/modules/cdi.mod000066400000000000000000000010641261716203600236230ustar00rootroot00000000000000# # CDI / Weld Jetty module # [depend] deploy annotations plus # JSP (and EL) are requirements for CDI and Weld jsp [files] lib/weld/ http://central.maven.org/maven2/org/jboss/weld/servlet/weld-servlet/2.2.5.Final/weld-servlet-2.2.5.Final.jar|lib/weld/weld-servlet-2.2.5.Final.jar [lib] lib/weld/weld-servlet-2.2.5.Final.jar lib/jetty-cdi-${jetty.version}.jar [xml] etc/jetty-cdi.xml [license] Weld is an open source project hosted on Github and released under the Apache 2.0 license. http://weld.cdi-spec.org/ http://www.apache.org/licenses/LICENSE-2.0.html jetty-9.2.14.v20151106/jetty-cdi/src/main/java/000077500000000000000000000000001261716203600203665ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/java/org/000077500000000000000000000000001261716203600211555ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/java/org/eclipse/000077500000000000000000000000001261716203600226015ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600237405ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/000077500000000000000000000000001261716203600244775ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-cdi/src/main/java/org/eclipse/jetty/cdi/WeldDeploymentBinding.java000066400000000000000000000061271261716203600315770ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.cdi; import javax.naming.Reference; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.plus.jndi.Resource; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.webapp.WebAppContext; /** * Perform some basic weld configuration of WebAppContext */ public class WeldDeploymentBinding implements AppLifeCycle.Binding { public String[] getBindingTargets() { return new String[] { "deploying" }; } public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); if (handler == null) { throw new NullPointerException("No Handler created for App: " + app); } if (handler instanceof WebAppContext) { WebAppContext webapp = (WebAppContext)handler; // Add context specific weld container reference. // See https://issues.jboss.org/browse/WELD-1710 // and https://github.com/weld/core/blob/2.2.5.Final/environments/servlet/core/src/main/java/org/jboss/weld/environment/servlet/WeldServletLifecycle.java#L244-L253 webapp.setInitParameter("org.jboss.weld.environment.container.class", "org.jboss.weld.environment.jetty.JettyContainer"); // Setup Weld BeanManager reference Reference ref = new Reference("javax.enterprise.inject.spi.BeanManager", "org.jboss.weld.resources.ManagerObjectFactory", null); new Resource(webapp,"BeanManager",ref); // webapp cannot change / replace weld classes webapp.addSystemClass("org.jboss.weld."); webapp.addSystemClass("org.jboss.classfilewriter."); webapp.addSystemClass("org.jboss.logging."); webapp.addSystemClass("com.google.common."); // don't hide weld classes from webapps (allow webapp to use ones from system classloader) webapp.addServerClass("-org.jboss.weld."); webapp.addServerClass("-org.jboss.classfilewriter."); webapp.addServerClass("-org.jboss.logging."); webapp.addServerClass("-com.google.common."); } } } jetty-9.2.14.v20151106/jetty-client/000077500000000000000000000000001261716203600164515ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/pom.xml000066400000000000000000000102421261716203600177650ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-client Jetty :: Asynchronous HTTP Client http://www.eclipse.org/jetty ${project.groupId}.client target/test-policy org.apache.maven.plugins maven-assembly-plugin package single config org.apache.felix maven-bundle-plugin true manifest javax.net.*,* org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.client.* org.apache.maven.plugins maven-dependency-plugin unpack generate-test-resources unpack org.eclipse.jetty.toolchain jetty-test-policy ${jetty-test-policy-version} jar true **/*.keystore,**/*.pem ${jetty.test.policy.loc} org.eclipse.jetty jetty-http ${project.version} org.eclipse.jetty jetty-io ${project.version} org.eclipse.jetty jetty-server ${project.version} test org.eclipse.jetty jetty-security ${project.version} test org.eclipse.jetty.toolchain jetty-test-helper test jetty-9.2.14.v20151106/jetty-client/src/000077500000000000000000000000001261716203600172405ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/000077500000000000000000000000001261716203600201645ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/config/000077500000000000000000000000001261716203600214315ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/config/modules/000077500000000000000000000000001261716203600231015ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/config/modules/client.mod000066400000000000000000000001021261716203600250510ustar00rootroot00000000000000# # Client Feature # [lib] lib/jetty-client-${jetty.version}.jar jetty-9.2.14.v20151106/jetty-client/src/main/java/000077500000000000000000000000001261716203600211055ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/000077500000000000000000000000001261716203600216745ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/000077500000000000000000000000001261716203600233205ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600244575ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/000077500000000000000000000000001261716203600257355ustar00rootroot00000000000000AbstractHttpClientTransport.java000066400000000000000000000141721261716203600342050ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.Map; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.SelectChannelEndPoint; import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public abstract class AbstractHttpClientTransport extends ContainerLifeCycle implements HttpClientTransport { protected static final Logger LOG = Log.getLogger(HttpClientTransport.class); private final int selectors; private volatile HttpClient client; private volatile SelectorManager selectorManager; protected AbstractHttpClientTransport(int selectors) { this.selectors = selectors; } protected HttpClient getHttpClient() { return client; } @Override public void setHttpClient(HttpClient client) { this.client = client; } @Override protected void doStart() throws Exception { selectorManager = newSelectorManager(client); selectorManager.setConnectTimeout(client.getConnectTimeout()); addBean(selectorManager); super.doStart(); } @Override protected void doStop() throws Exception { super.doStop(); removeBean(selectorManager); } @Override public void connect(SocketAddress address, Map context) { SocketChannel channel = null; try { channel = SocketChannel.open(); HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY); HttpClient client = destination.getHttpClient(); SocketAddress bindAddress = client.getBindAddress(); if (bindAddress != null) channel.bind(bindAddress); configure(client, channel); context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, destination.getHost()); context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, destination.getPort()); if (client.isConnectBlocking()) { channel.socket().connect(address, (int)client.getConnectTimeout()); channel.configureBlocking(false); selectorManager.accept(channel, context); } else { channel.configureBlocking(false); if (channel.connect(address)) selectorManager.accept(channel, context); else selectorManager.connect(channel, context); } } // Must catch all exceptions, since some like // UnresolvedAddressException are not IOExceptions. catch (Throwable x) { try { if (channel != null) channel.close(); } catch (IOException xx) { LOG.ignore(xx); } finally { connectFailed(context, x); } } } protected void connectFailed(Map context, Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("Could not connect to {}", context.get(HTTP_DESTINATION_CONTEXT_KEY)); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY); promise.failed(x); } protected void configure(HttpClient client, SocketChannel channel) throws IOException { channel.socket().setTcpNoDelay(client.isTCPNoDelay()); } protected SelectorManager newSelectorManager(HttpClient client) { return new ClientSelectorManager(client, selectors); } protected class ClientSelectorManager extends SelectorManager { private final HttpClient client; protected ClientSelectorManager(HttpClient client, int selectors) { super(client.getExecutor(), client.getScheduler(), selectors); this.client = client; } @Override protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey key) { return new SelectChannelEndPoint(channel, selector, key, getScheduler(), client.getIdleTimeout()); } @Override public org.eclipse.jetty.io.Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException { @SuppressWarnings("unchecked") Map context = (Map)attachment; HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY); return destination.getClientConnectionFactory().newConnection(endPoint, context); } @Override protected void connectionFailed(SocketChannel channel, Throwable x, Object attachment) { @SuppressWarnings("unchecked") Map context = (Map)attachment; connectFailed(context, x); } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/AsyncContentProvider.java000066400000000000000000000026741261716203600327340ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.EventListener; import org.eclipse.jetty.client.api.ContentProvider; /** * A {@link ContentProvider} that notifies listeners that content is available. */ public interface AsyncContentProvider extends ContentProvider { /** * @param listener the listener to be notified of content availability */ public void setListener(Listener listener); /** * A listener that is notified of content availability */ public interface Listener extends EventListener { /** * Callback method invoked when content is available */ public void onContent(); } } AuthenticationProtocolHandler.java000066400000000000000000000176371261716203600345360ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public abstract class AuthenticationProtocolHandler implements ProtocolHandler { public static final int DEFAULT_MAX_CONTENT_LENGTH = 4096; public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class); private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE); private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication"; private final HttpClient client; private final int maxContentLength; private final ResponseNotifier notifier; protected AuthenticationProtocolHandler(HttpClient client, int maxContentLength) { this.client = client; this.maxContentLength = maxContentLength; this.notifier = new ResponseNotifier(); } protected HttpClient getHttpClient() { return client; } protected abstract HttpHeader getAuthenticateHeader(); protected abstract HttpHeader getAuthorizationHeader(); protected abstract URI getAuthenticationURI(Request request); @Override public Response.Listener getResponseListener() { // Return new instances every time to keep track of the response content return new AuthenticationListener(); } private class AuthenticationListener extends BufferingResponseListener { private AuthenticationListener() { super(maxContentLength); } @Override public void onComplete(Result result) { HttpRequest request = (HttpRequest)result.getRequest(); ContentResponse response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding()); if (result.isFailed()) { Throwable failure = result.getFailure(); if (LOG.isDebugEnabled()) LOG.debug("Authentication challenge failed {}", failure); forwardFailureComplete(request, result.getRequestFailure(), response, result.getResponseFailure()); return; } HttpConversation conversation = request.getConversation(); if (conversation.getAttribute(AUTHENTICATION_ATTRIBUTE) != null) { // We have already tried to authenticate, but we failed again if (LOG.isDebugEnabled()) LOG.debug("Bad credentials for {}", request); forwardSuccessComplete(request, response); return; } HttpHeader header = getAuthenticateHeader(); List headerInfos = parseAuthenticateHeader(response, header); if (headerInfos.isEmpty()) { if (LOG.isDebugEnabled()) LOG.debug("Authentication challenge without {} header", header); forwardFailureComplete(request, null, response, new HttpResponseException("HTTP protocol violation: Authentication challenge without " + header + " header", response)); return; } Authentication authentication = null; Authentication.HeaderInfo headerInfo = null; URI uri = getAuthenticationURI(request); if (uri != null) { for (Authentication.HeaderInfo element : headerInfos) { authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm()); if (authentication != null) { headerInfo = element; break; } } } if (authentication == null) { if (LOG.isDebugEnabled()) LOG.debug("No authentication available for {}", request); forwardSuccessComplete(request, response); return; } final Authentication.Result authnResult = authentication.authenticate(request, response, headerInfo, conversation); if (LOG.isDebugEnabled()) LOG.debug("Authentication result {}", authnResult); if (authnResult == null) { forwardSuccessComplete(request, response); return; } conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true); Request newRequest = client.copyRequest(request, request.getURI()); authnResult.apply(newRequest); newRequest.onResponseSuccess(new Response.SuccessListener() { @Override public void onSuccess(Response response) { client.getAuthenticationStore().addAuthenticationResult(authnResult); } }).send(null); } private void forwardSuccessComplete(HttpRequest request, Response response) { HttpConversation conversation = request.getConversation(); conversation.updateResponseListeners(null); notifier.forwardSuccessComplete(conversation.getResponseListeners(), request, response); } private void forwardFailureComplete(HttpRequest request, Throwable requestFailure, Response response, Throwable responseFailure) { HttpConversation conversation = request.getConversation(); conversation.updateResponseListeners(null); notifier.forwardFailureComplete(conversation.getResponseListeners(), request, requestFailure, response, responseFailure); } private List parseAuthenticateHeader(Response response, HttpHeader header) { // TODO: these should be ordered by strength List result = new ArrayList<>(); List values = Collections.list(response.getHeaders().getValues(header.asString())); for (String value : values) { Matcher matcher = AUTHENTICATE_PATTERN.matcher(value); if (matcher.matches()) { String type = matcher.group(1); String realm = matcher.group(2); String params = matcher.group(3); Authentication.HeaderInfo headerInfo = new Authentication.HeaderInfo(type, realm, params, getAuthorizationHeader()); result.add(headerInfo); } } return result; } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java000066400000000000000000000263341261716203600315410ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Sweeper; public class ConnectionPool implements Closeable, Dumpable, Sweeper.Sweepable { protected static final Logger LOG = Log.getLogger(ConnectionPool.class); private final AtomicInteger connectionCount = new AtomicInteger(); private final ReentrantLock lock = new ReentrantLock(); private final Destination destination; private final int maxConnections; private final Promise requester; private final BlockingDeque idleConnections; private final BlockingQueue activeConnections; public ConnectionPool(Destination destination, int maxConnections, Promise requester) { this.destination = destination; this.maxConnections = maxConnections; this.requester = requester; this.idleConnections = new LinkedBlockingDeque<>(maxConnections); this.activeConnections = new BlockingArrayQueue<>(maxConnections); } public int getConnectionCount() { return connectionCount.get(); } public BlockingQueue getIdleConnections() { return idleConnections; } public BlockingQueue getActiveConnections() { return activeConnections; } public Connection acquire() { Connection connection = activateIdle(); if (connection == null) connection = tryCreate(); return connection; } private Connection tryCreate() { while (true) { int current = getConnectionCount(); final int next = current + 1; if (next > maxConnections) { if (LOG.isDebugEnabled()) LOG.debug("Max connections {}/{} reached", current, maxConnections); // Try again the idle connections return activateIdle(); } if (connectionCount.compareAndSet(current, next)) { if (LOG.isDebugEnabled()) LOG.debug("Connection {}/{} creation", next, maxConnections); destination.newConnection(new Promise() { @Override public void succeeded(Connection connection) { if (LOG.isDebugEnabled()) LOG.debug("Connection {}/{} creation succeeded {}", next, maxConnections, connection); idleCreated(connection); requester.succeeded(connection); } @Override public void failed(Throwable x) { if (LOG.isDebugEnabled()) LOG.debug("Connection " + next + "/" + maxConnections + " creation failed", x); connectionCount.decrementAndGet(); requester.failed(x); } }); // Try again the idle connections return activateIdle(); } } } protected void idleCreated(Connection connection) { boolean idle; final ReentrantLock lock = this.lock; lock.lock(); try { // Use "cold" new connections as last. idle = idleConnections.offerLast(connection); } finally { lock.unlock(); } idle(connection, idle); } private Connection activateIdle() { boolean acquired; Connection connection; final ReentrantLock lock = this.lock; lock.lock(); try { connection = idleConnections.pollFirst(); if (connection == null) return null; acquired = activeConnections.offer(connection); } finally { lock.unlock(); } if (acquired) { if (LOG.isDebugEnabled()) LOG.debug("Connection active {}", connection); acquired(connection); return connection; } else { if (LOG.isDebugEnabled()) LOG.debug("Connection active overflow {}", connection); connection.close(); return null; } } protected void acquired(Connection connection) { } public boolean release(Connection connection) { boolean idle; final ReentrantLock lock = this.lock; lock.lock(); try { if (!activeConnections.remove(connection)) return false; // Make sure we use "hot" connections first. idle = idleConnections.offerFirst(connection); } finally { lock.unlock(); } released(connection); return idle(connection, idle); } protected boolean idle(Connection connection, boolean idle) { if (idle) { if (LOG.isDebugEnabled()) LOG.debug("Connection idle {}", connection); return true; } else { if (LOG.isDebugEnabled()) LOG.debug("Connection idle overflow {}", connection); connection.close(); return false; } } protected void released(Connection connection) { } public boolean remove(Connection connection) { boolean activeRemoved; boolean idleRemoved; final ReentrantLock lock = this.lock; lock.lock(); try { activeRemoved = activeConnections.remove(connection); idleRemoved = idleConnections.remove(connection); } finally { lock.unlock(); } if (activeRemoved) released(connection); boolean removed = activeRemoved || idleRemoved; if (removed) { int pooled = connectionCount.decrementAndGet(); if (LOG.isDebugEnabled()) LOG.debug("Connection removed {} - pooled: {}", connection, pooled); } return removed; } public boolean isActive(Connection connection) { final ReentrantLock lock = this.lock; lock.lock(); try { return activeConnections.contains(connection); } finally { lock.unlock(); } } public boolean isIdle(Connection connection) { final ReentrantLock lock = this.lock; lock.lock(); try { return idleConnections.contains(connection); } finally { lock.unlock(); } } public boolean isEmpty() { return connectionCount.get() == 0; } public void close() { List idles = new ArrayList<>(); List actives = new ArrayList<>(); final ReentrantLock lock = this.lock; lock.lock(); try { idles.addAll(idleConnections); idleConnections.clear(); actives.addAll(activeConnections); activeConnections.clear(); } finally { lock.unlock(); } connectionCount.set(0); for (Connection connection : idles) connection.close(); // A bit drastic, but we cannot wait for all requests to complete for (Connection connection : actives) connection.close(); } @Override public String dump() { return ContainerLifeCycle.dump(this); } @Override public void dump(Appendable out, String indent) throws IOException { List actives = new ArrayList<>(); List idles = new ArrayList<>(); final ReentrantLock lock = this.lock; lock.lock(); try { actives.addAll(activeConnections); idles.addAll(idleConnections); } finally { lock.unlock(); } ContainerLifeCycle.dumpObject(out, this); ContainerLifeCycle.dump(out, indent, actives, idles); } @Override public boolean sweep() { List toSweep = new ArrayList<>(); final ReentrantLock lock = this.lock; lock.lock(); try { for (Connection connection : getActiveConnections()) { if (connection instanceof Sweeper.Sweepable) toSweep.add(((Sweeper.Sweepable)connection)); } } finally { lock.unlock(); } for (Sweeper.Sweepable candidate : toSweep) { if (candidate.sweep()) { boolean removed = getActiveConnections().remove(candidate); LOG.warn("Connection swept: {}{}{} from active connections{}{}", candidate, System.lineSeparator(), removed ? "Removed" : "Not removed", System.lineSeparator(), dump()); } } return false; } @Override public String toString() { int activeSize; int idleSize; final ReentrantLock lock = this.lock; lock.lock(); try { activeSize = activeConnections.size(); idleSize = idleConnections.size(); } finally { lock.unlock(); } return String.format("%s[c=%d/%d,a=%d,i=%d]", getClass().getSimpleName(), connectionCount.get(), maxConnections, activeSize, idleSize); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/ContentDecoder.java000066400000000000000000000052151261716203600315030ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; /** * {@link ContentDecoder} decodes content bytes of a response. * * @see Factory */ public interface ContentDecoder { /** *

Decodes the bytes in the given {@code buffer} and returns decoded bytes, if any.

* * @param buffer the buffer containing encoded bytes * @return a buffer containing decoded bytes, if any */ public abstract ByteBuffer decode(ByteBuffer buffer); /** * Factory for {@link ContentDecoder}s; subclasses must implement {@link #newContentDecoder()}. *

* {@link Factory} have an {@link #getEncoding() encoding}, which is the string used in * {@code Accept-Encoding} request header and in {@code Content-Encoding} response headers. *

* {@link Factory} instances are configured in {@link HttpClient} via * {@link HttpClient#getContentDecoderFactories()}. */ public static abstract class Factory { private final String encoding; protected Factory(String encoding) { this.encoding = encoding; } /** * @return the type of the decoders created by this factory */ public String getEncoding() { return encoding; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Factory)) return false; Factory that = (Factory)obj; return encoding.equals(that.encoding); } @Override public int hashCode() { return encoding.hashCode(); } /** * Factory method for {@link ContentDecoder}s * * @return a new instance of a {@link ContentDecoder} */ public abstract ContentDecoder newContentDecoder(); } } ContinueProtocolHandler.java000066400000000000000000000117051261716203600333310ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.List; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; public class ContinueProtocolHandler implements ProtocolHandler { private static final String ATTRIBUTE = ContinueProtocolHandler.class.getName() + ".100continue"; private final HttpClient client; private final ResponseNotifier notifier; public ContinueProtocolHandler(HttpClient client) { this.client = client; this.notifier = new ResponseNotifier(); } @Override public boolean accept(Request request, Response response) { boolean expect100 = request.getHeaders().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); HttpConversation conversation = ((HttpRequest)request).getConversation(); boolean handled100 = conversation.getAttribute(ATTRIBUTE) != null; return expect100 && !handled100; } @Override public Response.Listener getResponseListener() { // Return new instances every time to keep track of the response content return new ContinueListener(); } protected class ContinueListener extends BufferingResponseListener { @Override public void onSuccess(Response response) { // Handling of success must be done here and not from onComplete(), // since the onComplete() is not invoked because the request is not completed yet. HttpConversation conversation = ((HttpRequest)response.getRequest()).getConversation(); // Mark the 100 Continue response as handled conversation.setAttribute(ATTRIBUTE, Boolean.TRUE); // Reset the conversation listeners, since we are going to receive another response code conversation.updateResponseListeners(null); HttpExchange exchange = conversation.getExchanges().peekLast(); assert exchange.getResponse() == response; switch (response.getStatus()) { case 100: { // All good, continue exchange.resetResponse(); exchange.proceed(null); break; } default: { // Server either does not support 100 Continue, // or it does and wants to refuse the request content, // or we got some other HTTP status code like a redirect. List listeners = exchange.getResponseListeners(); HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getMediaType(), getEncoding()); notifier.forwardSuccess(listeners, contentResponse); exchange.proceed(new HttpRequestException("Expectation failed", exchange.getRequest())); break; } } } @Override public void onFailure(Response response, Throwable failure) { HttpConversation conversation = ((HttpRequest)response.getRequest()).getConversation(); // Mark the 100 Continue response as handled conversation.setAttribute(ATTRIBUTE, Boolean.TRUE); // Reset the conversation listeners to allow the conversation to be completed conversation.updateResponseListeners(null); HttpExchange exchange = conversation.getExchanges().peekLast(); assert exchange.getResponse() == response; List listeners = exchange.getResponseListeners(); HttpContentResponse contentResponse = new HttpContentResponse(response, getContent(), getMediaType(), getEncoding()); notifier.forwardFailureComplete(listeners, exchange.getRequest(), exchange.getRequestFailure(), contentResponse, failure); } @Override public void onComplete(Result result) { } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/GZIPContentDecoder.java000066400000000000000000000307031261716203600321750ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.zip.DataFormatException; import java.util.zip.Inflater; import java.util.zip.ZipException; import org.eclipse.jetty.util.BufferUtil; /** * {@link ContentDecoder} for the "gzip" encoding. */ public class GZIPContentDecoder implements ContentDecoder { private final Inflater inflater = new Inflater(true); private final byte[] bytes; private byte[] output; private State state; private int size; private int value; private byte flags; public GZIPContentDecoder() { this(2048); } public GZIPContentDecoder(int bufferSize) { this.bytes = new byte[bufferSize]; reset(); } /** * {@inheritDoc} *

If the decoding did not produce any output, for example because it consumed gzip header * or trailer bytes, it returns a buffer with zero capacity.

*

This method never returns null.

*

The given {@code buffer}'s position will be modified to reflect the bytes consumed during * the decoding.

*

The decoding may be finished without consuming the buffer completely if the buffer contains * gzip bytes plus other bytes (either plain or gzipped).

*/ @Override public ByteBuffer decode(ByteBuffer buffer) { try { while (buffer.hasRemaining()) { byte currByte = buffer.get(); switch (state) { case INITIAL: { buffer.position(buffer.position() - 1); state = State.ID; break; } case ID: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 2) { if (value != 0x8B1F) throw new ZipException("Invalid gzip bytes"); state = State.CM; } break; } case CM: { if ((currByte & 0xFF) != 0x08) throw new ZipException("Invalid gzip compression method"); state = State.FLG; break; } case FLG: { flags = currByte; state = State.MTIME; size = 0; value = 0; break; } case MTIME: { // Skip the 4 MTIME bytes ++size; if (size == 4) state = State.XFL; break; } case XFL: { // Skip XFL state = State.OS; break; } case OS: { // Skip OS state = State.FLAGS; break; } case FLAGS: { buffer.position(buffer.position() - 1); if ((flags & 0x04) == 0x04) { state = State.EXTRA_LENGTH; size = 0; value = 0; } else if ((flags & 0x08) == 0x08) state = State.NAME; else if ((flags & 0x10) == 0x10) state = State.COMMENT; else if ((flags & 0x2) == 0x2) { state = State.HCRC; size = 0; value = 0; } else state = State.DATA; break; } case EXTRA_LENGTH: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 2) state = State.EXTRA; break; } case EXTRA: { // Skip EXTRA bytes --value; if (value == 0) { // Clear the EXTRA flag and loop on the flags flags &= ~0x04; state = State.FLAGS; } break; } case NAME: { // Skip NAME bytes if (currByte == 0) { // Clear the NAME flag and loop on the flags flags &= ~0x08; state = State.FLAGS; } break; } case COMMENT: { // Skip COMMENT bytes if (currByte == 0) { // Clear the COMMENT flag and loop on the flags flags &= ~0x10; state = State.FLAGS; } break; } case HCRC: { // Skip HCRC ++size; if (size == 2) { // Clear the HCRC flag and loop on the flags flags &= ~0x02; state = State.FLAGS; } break; } case DATA: { buffer.position(buffer.position() - 1); while (true) { int decoded = inflate(bytes); if (decoded == 0) { if (inflater.needsInput()) { if (buffer.hasRemaining()) { byte[] input = new byte[buffer.remaining()]; buffer.get(input); inflater.setInput(input); } else { if (output != null) { ByteBuffer result = ByteBuffer.wrap(output); output = null; return result; } break; } } else if (inflater.finished()) { int remaining = inflater.getRemaining(); buffer.position(buffer.limit() - remaining); state = State.CRC; size = 0; value = 0; break; } else { throw new ZipException("Invalid inflater state"); } } else { if (output == null) { // Save the inflated bytes and loop to see if we have finished output = Arrays.copyOf(bytes, decoded); } else { // Accumulate inflated bytes and loop to see if we have finished byte[] newOutput = Arrays.copyOf(output, output.length + decoded); System.arraycopy(bytes, 0, newOutput, output.length, decoded); output = newOutput; } } } break; } case CRC: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 4) { // From RFC 1952, compliant decoders need not to verify the CRC state = State.ISIZE; size = 0; value = 0; } break; } case ISIZE: { value += (currByte & 0xFF) << 8 * size; ++size; if (size == 4) { if (value != inflater.getBytesWritten()) throw new ZipException("Invalid input size"); ByteBuffer result = output == null ? BufferUtil.EMPTY_BUFFER : ByteBuffer.wrap(output); reset(); return result; } break; } default: throw new ZipException(); } } return BufferUtil.EMPTY_BUFFER; } catch (ZipException x) { throw new RuntimeException(x); } } private int inflate(byte[] bytes) throws ZipException { try { return inflater.inflate(bytes); } catch (DataFormatException x) { throw new ZipException(x.getMessage()); } } private void reset() { inflater.reset(); Arrays.fill(bytes, (byte)0); output = null; state = State.INITIAL; size = 0; value = 0; flags = 0; } protected boolean isFinished() { return state == State.INITIAL; } /** * Specialized {@link ContentDecoder.Factory} for the "gzip" encoding. */ public static class Factory extends ContentDecoder.Factory { private final int bufferSize; public Factory() { this(2048); } public Factory(int bufferSize) { super("gzip"); this.bufferSize = bufferSize; } @Override public ContentDecoder newContentDecoder() { return new GZIPContentDecoder(bufferSize); } } private enum State { INITIAL, ID, CM, FLG, MTIME, XFL, OS, FLAGS, EXTRA_LENGTH, EXTRA, NAME, COMMENT, HCRC, DATA, CRC, ISIZE } } HttpAuthenticationStore.java000066400000000000000000000053471261716203600333660ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; public class HttpAuthenticationStore implements AuthenticationStore { private final List authentications = new CopyOnWriteArrayList<>(); private final Map results = new ConcurrentHashMap<>(); @Override public void addAuthentication(Authentication authentication) { authentications.add(authentication); } @Override public void removeAuthentication(Authentication authentication) { authentications.remove(authentication); } @Override public void clearAuthentications() { authentications.clear(); } @Override public Authentication findAuthentication(String type, URI uri, String realm) { for (Authentication authentication : authentications) { if (authentication.matches(type, uri, realm)) return authentication; } return null; } @Override public void addAuthenticationResult(Authentication.Result result) { results.put(result.getURI(), result); } @Override public void removeAuthenticationResult(Authentication.Result result) { results.remove(result.getURI()); } @Override public void clearAuthenticationResults() { results.clear(); } @Override public Authentication.Result findAuthenticationResult(URI uri) { // TODO: I should match the longest URI for (Map.Entry entry : results.entrySet()) { if (uri.toString().startsWith(entry.getKey().toString())) return entry.getValue(); } return null; } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpChannel.java000066400000000000000000000103311261716203600310060ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public abstract class HttpChannel { protected static final Logger LOG = Log.getLogger(HttpChannel.class); private final HttpDestination _destination; private HttpExchange _exchange; protected HttpChannel(HttpDestination destination) { this._destination = destination; } public HttpDestination getHttpDestination() { return _destination; } /** *

Associates the given {@code exchange} to this channel in order to be sent over the network.

*

If the association is successful, the exchange can be sent. Otherwise, the channel must be * disposed because whoever terminated the exchange did not do it - it did not have the channel yet.

* * @param exchange the exchange to associate * @return true if the association was successful, false otherwise */ public boolean associate(HttpExchange exchange) { boolean result = false; boolean abort = true; synchronized (this) { if (_exchange == null) { abort = false; result = exchange.associate(this); if (result) _exchange = exchange; } } if (abort) exchange.getRequest().abort(new UnsupportedOperationException("Pipelined requests not supported")); if (LOG.isDebugEnabled()) LOG.debug("{} associated {} to {}", exchange, result, this); return result; } public boolean disassociate(HttpExchange exchange) { boolean result = false; synchronized (this) { HttpExchange existing = _exchange; _exchange = null; if (existing == exchange) { existing.disassociate(this); result = true; } } if (LOG.isDebugEnabled()) LOG.debug("{} disassociated {} from {}", exchange, result, this); return result; } public HttpExchange getHttpExchange() { synchronized (this) { return _exchange; } } protected abstract HttpSender getHttpSender(); protected abstract HttpReceiver getHttpReceiver(); public abstract void send(); public abstract void release(); public void proceed(HttpExchange exchange, Throwable failure) { getHttpSender().proceed(exchange, failure); } public boolean abort(HttpExchange exchange, Throwable requestFailure, Throwable responseFailure) { boolean requestAborted = false; if (requestFailure != null) requestAborted = getHttpSender().abort(exchange, requestFailure); boolean responseAborted = false; if (responseFailure != null) responseAborted = abortResponse(exchange, responseFailure); return requestAborted || responseAborted; } public boolean abortResponse(HttpExchange exchange, Throwable failure) { return getHttpReceiver().abort(exchange, failure); } public void exchangeTerminated(HttpExchange exchange, Result result) { disassociate(exchange); } @Override public String toString() { return String.format("%s@%x(exchange=%s)", getClass().getSimpleName(), hashCode(), getHttpExchange()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java000066400000000000000000001115271261716203600306650ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.CookieManager; import java.net.CookiePolicy; import java.net.CookieStore; import java.net.Socket; import java.net.SocketAddress; import java.net.URI; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.util.FormContentProvider; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Jetty; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.SocketAddressResolver; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler; import org.eclipse.jetty.util.thread.Scheduler; /** *

{@link HttpClient} provides an efficient, asynchronous, non-blocking implementation * to perform HTTP requests to a server through a simple API that offers also blocking semantic.

*

{@link HttpClient} provides easy-to-use methods such as {@link #GET(String)} that allow to perform HTTP * requests in a one-liner, but also gives the ability to fine tune the configuration of requests via * {@link HttpClient#newRequest(URI)}.

*

{@link HttpClient} acts as a central configuration point for network parameters (such as idle timeouts) * and HTTP parameters (such as whether to follow redirects).

*

{@link HttpClient} transparently pools connections to servers, but allows direct control of connections * for cases where this is needed.

*

{@link HttpClient} also acts as a central configuration point for cookies, via {@link #getCookieStore()}.

*

Typical usage:

*
 * HttpClient httpClient = new HttpClient();
 * httpClient.start();
 *
 * // One liner:
 * httpClient.GET("http://localhost:8080/").getStatus();
 *
 * // Building a request with a timeout
 * ContentResponse response = httpClient.newRequest("http://localhost:8080")
 *         .timeout(5, TimeUnit.SECONDS)
 *         .send();
 * int status = response.status();
 *
 * // Asynchronously
 * httpClient.newRequest("http://localhost:8080").send(new Response.CompleteListener()
 * {
 *     @Override
 *     public void onComplete(Result result)
 *     {
 *         ...
 *     }
 * });
 * 
*/ public class HttpClient extends ContainerLifeCycle { private static final Logger LOG = Log.getLogger(HttpClient.class); private final ConcurrentMap destinations = new ConcurrentHashMap<>(); private final List handlers = new ArrayList<>(); private final List requestListeners = new ArrayList<>(); private final AuthenticationStore authenticationStore = new HttpAuthenticationStore(); private final Set decoderFactories = new ContentDecoderFactorySet(); private final ProxyConfiguration proxyConfig = new ProxyConfiguration(); private final HttpClientTransport transport; private final SslContextFactory sslContextFactory; private volatile CookieManager cookieManager; private volatile CookieStore cookieStore; private volatile Executor executor; private volatile ByteBufferPool byteBufferPool; private volatile Scheduler scheduler; private volatile SocketAddressResolver resolver; private volatile HttpField agentField = new HttpField(HttpHeader.USER_AGENT, "Jetty/" + Jetty.VERSION); private volatile boolean followRedirects = true; private volatile int maxConnectionsPerDestination = 64; private volatile int maxRequestsQueuedPerDestination = 1024; private volatile int requestBufferSize = 4096; private volatile int responseBufferSize = 16384; private volatile int maxRedirects = 8; private volatile SocketAddress bindAddress; private volatile long connectTimeout = 15000; private volatile long addressResolutionTimeout = 15000; private volatile long idleTimeout; private volatile boolean tcpNoDelay = true; private volatile boolean dispatchIO = true; private volatile boolean strictEventOrdering = false; private volatile HttpField encodingField; private volatile boolean removeIdleDestinations = false; private volatile boolean connectBlocking = false; /** * Creates a {@link HttpClient} instance that can perform requests to non-TLS destinations only * (that is, requests with the "http" scheme only, and not "https"). * * @see #HttpClient(SslContextFactory) to perform requests to TLS destinations. */ public HttpClient() { this(null); } /** * Creates a {@link HttpClient} instance that can perform requests to non-TLS and TLS destinations * (that is, both requests with the "http" scheme and with the "https" scheme). * * @param sslContextFactory the {@link SslContextFactory} that manages TLS encryption * @see #getSslContextFactory() */ public HttpClient(SslContextFactory sslContextFactory) { this(new HttpClientTransportOverHTTP(), sslContextFactory); } public HttpClient(HttpClientTransport transport, SslContextFactory sslContextFactory) { this.transport = transport; this.sslContextFactory = sslContextFactory; } public HttpClientTransport getTransport() { return transport; } /** * @return the {@link SslContextFactory} that manages TLS encryption * @see #HttpClient(SslContextFactory) */ public SslContextFactory getSslContextFactory() { return sslContextFactory; } @Override protected void doStart() throws Exception { if (sslContextFactory != null) addBean(sslContextFactory); String name = HttpClient.class.getSimpleName() + "@" + hashCode(); if (executor == null) { QueuedThreadPool threadPool = new QueuedThreadPool(); threadPool.setName(name); executor = threadPool; } addBean(executor); if (byteBufferPool == null) byteBufferPool = new MappedByteBufferPool(); addBean(byteBufferPool); if (scheduler == null) scheduler = new ScheduledExecutorScheduler(name + "-scheduler", false); addBean(scheduler); transport.setHttpClient(this); addBean(transport); if (resolver == null) resolver = new SocketAddressResolver.Async(executor, scheduler, getAddressResolutionTimeout()); addBean(resolver); handlers.add(new ContinueProtocolHandler(this)); handlers.add(new RedirectProtocolHandler(this)); handlers.add(new WWWAuthenticationProtocolHandler(this)); handlers.add(new ProxyAuthenticationProtocolHandler(this)); decoderFactories.add(new GZIPContentDecoder.Factory()); cookieManager = newCookieManager(); cookieStore = cookieManager.getCookieStore(); super.doStart(); } private CookieManager newCookieManager() { return new CookieManager(getCookieStore(), CookiePolicy.ACCEPT_ALL); } @Override protected void doStop() throws Exception { cookieStore.removeAll(); decoderFactories.clear(); handlers.clear(); for (HttpDestination destination : destinations.values()) destination.close(); destinations.clear(); requestListeners.clear(); authenticationStore.clearAuthentications(); authenticationStore.clearAuthenticationResults(); super.doStop(); } /** * Returns a non thread-safe list of {@link org.eclipse.jetty.client.api.Request.Listener}s that can be modified before * performing requests. * * @return a list of {@link org.eclipse.jetty.client.api.Request.Listener} that can be used to add and remove listeners */ public List getRequestListeners() { return requestListeners; } /** * @return the cookie store associated with this instance */ public CookieStore getCookieStore() { return cookieStore; } /** * @param cookieStore the cookie store associated with this instance */ public void setCookieStore(CookieStore cookieStore) { this.cookieStore = Objects.requireNonNull(cookieStore); this.cookieManager = newCookieManager(); } /** * Keep this method package-private because its interface is so ugly * that we really don't want to expose it more than strictly needed. * * @return the cookie manager */ CookieManager getCookieManager() { return cookieManager; } /** * @return the authentication store associated with this instance */ public AuthenticationStore getAuthenticationStore() { return authenticationStore; } /** * Returns a non thread-safe set of {@link ContentDecoder.Factory}s that can be modified before * performing requests. * * @return a set of {@link ContentDecoder.Factory} that can be used to add and remove content decoder factories */ public Set getContentDecoderFactories() { return decoderFactories; } /** * Performs a GET request to the specified URI. * * @param uri the URI to GET * @return the {@link ContentResponse} for the request * @see #GET(URI) */ public ContentResponse GET(String uri) throws InterruptedException, ExecutionException, TimeoutException { return GET(URI.create(uri)); } /** * Performs a GET request to the specified URI. * * @param uri the URI to GET * @return the {@link ContentResponse} for the request * @see #newRequest(URI) */ public ContentResponse GET(URI uri) throws InterruptedException, ExecutionException, TimeoutException { return newRequest(uri).send(); } /** * Performs a POST request to the specified URI with the given form parameters. * * @param uri the URI to POST * @param fields the fields composing the form name/value pairs * @return the {@link ContentResponse} for the request */ public ContentResponse FORM(String uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException { return FORM(URI.create(uri), fields); } /** * Performs a POST request to the specified URI with the given form parameters. * * @param uri the URI to POST * @param fields the fields composing the form name/value pairs * @return the {@link ContentResponse} for the request */ public ContentResponse FORM(URI uri, Fields fields) throws InterruptedException, ExecutionException, TimeoutException { return POST(uri).content(new FormContentProvider(fields)).send(); } /** * Creates a POST request to the specified URI. * * @param uri the URI to POST to * @return the POST request * @see #POST(URI) */ public Request POST(String uri) { return POST(URI.create(uri)); } /** * Creates a POST request to the specified URI. * * @param uri the URI to POST to * @return the POST request */ public Request POST(URI uri) { return newRequest(uri).method(HttpMethod.POST); } /** * Creates a new request with the "http" scheme and the specified host and port * * @param host the request host * @param port the request port * @return the request just created */ public Request newRequest(String host, int port) { return newRequest(new Origin("http", host, port).asString()); } /** * Creates a new request with the specified URI. * * @param uri the URI to request * @return the request just created */ public Request newRequest(String uri) { return newRequest(URI.create(uri)); } /** * Creates a new request with the specified URI. * * @param uri the URI to request * @return the request just created */ public Request newRequest(URI uri) { return newHttpRequest(newConversation(), uri); } protected Request copyRequest(HttpRequest oldRequest, URI newURI) { Request newRequest = newHttpRequest(oldRequest.getConversation(), newURI); newRequest.method(oldRequest.getMethod()) .version(oldRequest.getVersion()) .content(oldRequest.getContent()) .idleTimeout(oldRequest.getIdleTimeout(), TimeUnit.MILLISECONDS) .timeout(oldRequest.getTimeout(), TimeUnit.MILLISECONDS) .followRedirects(oldRequest.isFollowRedirects()); for (HttpField field : oldRequest.getHeaders()) { HttpHeader header = field.getHeader(); // We have a new URI, so skip the host header if present. if (HttpHeader.HOST == header) continue; // Remove expectation headers. if (HttpHeader.EXPECT == header) continue; // Remove cookies. if (HttpHeader.COOKIE == header) continue; // Remove authorization headers. if (HttpHeader.AUTHORIZATION == header || HttpHeader.PROXY_AUTHORIZATION == header) continue; String value = field.getValue(); if (!newRequest.getHeaders().contains(header, value)) newRequest.header(field.getName(), value); } return newRequest; } protected HttpRequest newHttpRequest(HttpConversation conversation, URI uri) { return new HttpRequest(this, conversation, uri); } /** * Returns a {@link Destination} for the given scheme, host and port. * Applications may use {@link Destination}s to create {@link Connection}s * that will be outside {@link HttpClient}'s pooling mechanism, to explicitly * control the connection lifecycle (in particular their termination with * {@link Connection#close()}). * * @param scheme the destination scheme * @param host the destination host * @param port the destination port * @return the destination * @see #getDestinations() */ public Destination getDestination(String scheme, String host, int port) { return destinationFor(scheme, host, port); } protected HttpDestination destinationFor(String scheme, String host, int port) { port = normalizePort(scheme, port); Origin origin = new Origin(scheme, host, port); HttpDestination destination = destinations.get(origin); if (destination == null) { destination = transport.newHttpDestination(origin); if (isRunning()) { HttpDestination existing = destinations.putIfAbsent(origin, destination); if (existing != null) { destination = existing; } else { if (LOG.isDebugEnabled()) LOG.debug("Created {}", destination); } if (!isRunning()) destinations.remove(origin); } } return destination; } protected boolean removeDestination(HttpDestination destination) { return destinations.remove(destination.getOrigin()) != null; } /** * @return the list of destinations known to this {@link HttpClient}. */ public List getDestinations() { return new ArrayList(destinations.values()); } protected void send(final HttpRequest request, List listeners) { String scheme = request.getScheme().toLowerCase(Locale.ENGLISH); if (!HttpScheme.HTTP.is(scheme) && !HttpScheme.HTTPS.is(scheme)) throw new IllegalArgumentException("Invalid protocol " + scheme); String host = request.getHost().toLowerCase(Locale.ENGLISH); HttpDestination destination = destinationFor(scheme, host, request.getPort()); destination.send(request, listeners); } protected void newConnection(final HttpDestination destination, final Promise promise) { Origin.Address address = destination.getConnectAddress(); resolver.resolve(address.getHost(), address.getPort(), new Promise() { @Override public void succeeded(SocketAddress socketAddress) { Map context = new HashMap<>(); context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination); context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise); transport.connect(socketAddress, context); } @Override public void failed(Throwable x) { promise.failed(x); } }); } private HttpConversation newConversation() { return new HttpConversation(); } public List getProtocolHandlers() { return handlers; } protected ProtocolHandler findProtocolHandler(Request request, Response response) { // Optimized to avoid allocations of iterator instances List protocolHandlers = getProtocolHandlers(); for (int i = 0; i < protocolHandlers.size(); ++i) { ProtocolHandler handler = protocolHandlers.get(i); if (handler.accept(request, response)) return handler; } return null; } /** * @return the {@link ByteBufferPool} of this {@link HttpClient} */ public ByteBufferPool getByteBufferPool() { return byteBufferPool; } /** * @param byteBufferPool the {@link ByteBufferPool} of this {@link HttpClient} */ public void setByteBufferPool(ByteBufferPool byteBufferPool) { this.byteBufferPool = byteBufferPool; } /** * @return the max time, in milliseconds, a connection can take to connect to destinations */ public long getConnectTimeout() { return connectTimeout; } /** * @param connectTimeout the max time, in milliseconds, a connection can take to connect to destinations * @see java.net.Socket#connect(SocketAddress, int) */ public void setConnectTimeout(long connectTimeout) { this.connectTimeout = connectTimeout; } /** * @return the timeout, in milliseconds, for the default {@link SocketAddressResolver} created at startup * @see #getSocketAddressResolver() */ public long getAddressResolutionTimeout() { return addressResolutionTimeout; } /** *

Sets the socket address resolution timeout used by the default {@link SocketAddressResolver} * created by this {@link HttpClient} at startup.

*

For more fine tuned configuration of socket address resolution, see * {@link #setSocketAddressResolver(SocketAddressResolver)}.

* * @param addressResolutionTimeout the timeout, in milliseconds, for the default {@link SocketAddressResolver} created at startup * @see #setSocketAddressResolver(SocketAddressResolver) */ public void setAddressResolutionTimeout(long addressResolutionTimeout) { this.addressResolutionTimeout = addressResolutionTimeout; } /** * @return the max time, in milliseconds, a connection can be idle (that is, without traffic of bytes in either direction) */ public long getIdleTimeout() { return idleTimeout; } /** * @param idleTimeout the max time, in milliseconds, a connection can be idle (that is, without traffic of bytes in either direction) */ public void setIdleTimeout(long idleTimeout) { this.idleTimeout = idleTimeout; } /** * @return the address to bind socket channels to * @see #setBindAddress(SocketAddress) */ public SocketAddress getBindAddress() { return bindAddress; } /** * @param bindAddress the address to bind socket channels to * @see #getBindAddress() * @see SocketChannel#bind(SocketAddress) */ public void setBindAddress(SocketAddress bindAddress) { this.bindAddress = bindAddress; } /** * @return the "User-Agent" HTTP field of this {@link HttpClient} */ public HttpField getUserAgentField() { return agentField; } /** * @param agent the "User-Agent" HTTP header string of this {@link HttpClient} */ public void setUserAgentField(HttpField agent) { if (agent.getHeader() != HttpHeader.USER_AGENT) throw new IllegalArgumentException(); this.agentField = agent; } /** * @return whether this {@link HttpClient} follows HTTP redirects * @see Request#isFollowRedirects() */ public boolean isFollowRedirects() { return followRedirects; } /** * @param follow whether this {@link HttpClient} follows HTTP redirects * @see #setMaxRedirects(int) */ public void setFollowRedirects(boolean follow) { this.followRedirects = follow; } /** * @return the {@link Executor} of this {@link HttpClient} */ public Executor getExecutor() { return executor; } /** * @param executor the {@link Executor} of this {@link HttpClient} */ public void setExecutor(Executor executor) { this.executor = executor; } /** * @return the {@link Scheduler} of this {@link HttpClient} */ public Scheduler getScheduler() { return scheduler; } /** * @param scheduler the {@link Scheduler} of this {@link HttpClient} */ public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } /** * @return the {@link SocketAddressResolver} of this {@link HttpClient} */ public SocketAddressResolver getSocketAddressResolver() { return resolver; } /** * @param resolver the {@link SocketAddressResolver} of this {@link HttpClient} */ public void setSocketAddressResolver(SocketAddressResolver resolver) { this.resolver = resolver; } /** * @return the max number of connections that this {@link HttpClient} opens to {@link Destination}s */ public int getMaxConnectionsPerDestination() { return maxConnectionsPerDestination; } /** * Sets the max number of connections to open to each destinations. *

* RFC 2616 suggests that 2 connections should be opened per each destination, * but browsers commonly open 6. * If this {@link HttpClient} is used for load testing, it is common to have only one destination * (the server to load test), and it is recommended to set this value to a high value (at least as * much as the threads present in the {@link #getExecutor() executor}). * * @param maxConnectionsPerDestination the max number of connections that this {@link HttpClient} opens to {@link Destination}s */ public void setMaxConnectionsPerDestination(int maxConnectionsPerDestination) { this.maxConnectionsPerDestination = maxConnectionsPerDestination; } /** * @return the max number of requests that may be queued to a {@link Destination}. */ public int getMaxRequestsQueuedPerDestination() { return maxRequestsQueuedPerDestination; } /** * Sets the max number of requests that may be queued to a destination. *

* If this {@link HttpClient} performs a high rate of requests to a destination, * and all the connections managed by that destination are busy with other requests, * then new requests will be queued up in the destination. * This parameter controls how many requests can be queued before starting to reject them. * If this {@link HttpClient} is used for load testing, it is common to have this parameter * set to a high value, although this may impact latency (requests sit in the queue for a long * time before being sent). * * @param maxRequestsQueuedPerDestination the max number of requests that may be queued to a {@link Destination}. */ public void setMaxRequestsQueuedPerDestination(int maxRequestsQueuedPerDestination) { this.maxRequestsQueuedPerDestination = maxRequestsQueuedPerDestination; } /** * @return the size of the buffer used to write requests */ public int getRequestBufferSize() { return requestBufferSize; } /** * @param requestBufferSize the size of the buffer used to write requests */ public void setRequestBufferSize(int requestBufferSize) { this.requestBufferSize = requestBufferSize; } /** * @return the size of the buffer used to read responses */ public int getResponseBufferSize() { return responseBufferSize; } /** * @param responseBufferSize the size of the buffer used to read responses */ public void setResponseBufferSize(int responseBufferSize) { this.responseBufferSize = responseBufferSize; } /** * @return the max number of HTTP redirects that are followed * @see #setMaxRedirects(int) */ public int getMaxRedirects() { return maxRedirects; } /** * @param maxRedirects the max number of HTTP redirects that are followed * @see #setFollowRedirects(boolean) */ public void setMaxRedirects(int maxRedirects) { this.maxRedirects = maxRedirects; } /** * @return whether TCP_NODELAY is enabled */ public boolean isTCPNoDelay() { return tcpNoDelay; } /** * @param tcpNoDelay whether TCP_NODELAY is enabled * @see java.net.Socket#setTcpNoDelay(boolean) */ public void setTCPNoDelay(boolean tcpNoDelay) { this.tcpNoDelay = tcpNoDelay; } /** * @return true to dispatch I/O operations in a different thread, false to execute them in the selector thread * @see #setDispatchIO(boolean) */ public boolean isDispatchIO() { return dispatchIO; } /** * Whether to dispatch I/O operations from the selector thread to a different thread. *

* This implementation never blocks on I/O operation, but invokes application callbacks that may * take time to execute or block on other I/O. * If application callbacks are known to take time or block on I/O, then parameter {@code dispatchIO} * should be set to true. * If application callbacks are known to be quick and never block on I/O, then parameter {@code dispatchIO} * may be set to false. * * @param dispatchIO true to dispatch I/O operations in a different thread, * false to execute them in the selector thread */ public void setDispatchIO(boolean dispatchIO) { this.dispatchIO = dispatchIO; } /** * @return whether request events must be strictly ordered * @see #setStrictEventOrdering(boolean) */ public boolean isStrictEventOrdering() { return strictEventOrdering; } /** * Whether request/response events must be strictly ordered with respect to connection usage. *

* From the point of view of connection usage, the connection can be reused just before the * "complete" event notified to {@link org.eclipse.jetty.client.api.Response.CompleteListener}s * (but after the "success" event). *

* When a request/response exchange is completing, the destination may have another request * queued to be sent to the server. * If the connection for that destination is reused for the second request before the "complete" * event of the first exchange, it may happen that the "begin" event of the second request * happens before the "complete" event of the first exchange. *

* Enforcing strict ordering of events so that a "begin" event of a request can never happen * before the "complete" event of the previous exchange comes with the cost of increased * connection usage. * In case of HTTP redirects and strict event ordering, for example, the redirect request will * be forced to open a new connection because it is typically sent from the complete listener * when the connection cannot yet be reused. * When strict event ordering is not enforced, the redirect request will reuse the already * open connection making the system more efficient. *

* The default value for this property is {@code false}. * * @param strictEventOrdering whether request/response events must be strictly ordered */ public void setStrictEventOrdering(boolean strictEventOrdering) { this.strictEventOrdering = strictEventOrdering; } /** * @return whether destinations that have no connections should be removed * @see #setRemoveIdleDestinations(boolean) */ public boolean isRemoveIdleDestinations() { return removeIdleDestinations; } /** * Whether destinations that have no connections (nor active nor idle) should be removed. *

* Applications typically make request to a limited number of destinations so keeping * destinations around is not a problem for the memory or the GC. * However, for applications that hit millions of different destinations (e.g. a spider * bot) it would be useful to be able to remove the old destinations that won't be visited * anymore and leave space for new destinations. * * @param removeIdleDestinations whether destinations that have no connections should be removed * @see org.eclipse.jetty.client.ConnectionPool */ public void setRemoveIdleDestinations(boolean removeIdleDestinations) { this.removeIdleDestinations = removeIdleDestinations; } /** * @return whether {@code connect()} operations are performed in blocking mode */ public boolean isConnectBlocking() { return connectBlocking; } /** *

Whether {@code connect()} operations are performed in blocking mode.

*

If {@code connect()} are performed in blocking mode, then {@link Socket#connect(SocketAddress, int)} * will be used to connect to servers.

*

Otherwise, {@link SocketChannel#connect(SocketAddress)} will be used in non-blocking mode, * therefore registering for {@link SelectionKey#OP_CONNECT} and finishing the connect operation * when the NIO system emits that event.

* * @param connectBlocking whether {@code connect()} operations are performed in blocking mode */ public void setConnectBlocking(boolean connectBlocking) { this.connectBlocking = connectBlocking; } /** * @return the forward proxy configuration */ public ProxyConfiguration getProxyConfiguration() { return proxyConfig; } protected HttpField getAcceptEncodingField() { return encodingField; } protected String normalizeHost(String host) { if (host != null && host.matches("\\[.*\\]")) return host.substring(1, host.length() - 1); return host; } protected int normalizePort(String scheme, int port) { return port > 0 ? port : HttpScheme.HTTPS.is(scheme) ? 443 : 80; } protected boolean isDefaultPort(String scheme, int port) { return HttpScheme.HTTPS.is(scheme) ? port == 443 : port == 80; } @Override public void dump(Appendable out, String indent) throws IOException { dumpThis(out); dump(out, indent, getBeans(), destinations.values()); } private class ContentDecoderFactorySet implements Set { private final Set set = new HashSet<>(); @Override public boolean add(ContentDecoder.Factory e) { boolean result = set.add(e); invalidate(); return result; } @Override public boolean addAll(Collection c) { boolean result = set.addAll(c); invalidate(); return result; } @Override public boolean remove(Object o) { boolean result = set.remove(o); invalidate(); return result; } @Override public boolean removeAll(Collection c) { boolean result = set.removeAll(c); invalidate(); return result; } @Override public boolean retainAll(Collection c) { boolean result = set.retainAll(c); invalidate(); return result; } @Override public void clear() { set.clear(); invalidate(); } @Override public int size() { return set.size(); } @Override public boolean isEmpty() { return set.isEmpty(); } @Override public boolean contains(Object o) { return set.contains(o); } @Override public boolean containsAll(Collection c) { return set.containsAll(c); } @Override public Iterator iterator() { final Iterator iterator = set.iterator(); return new Iterator() { @Override public boolean hasNext() { return iterator.hasNext(); } @Override public ContentDecoder.Factory next() { return iterator.next(); } @Override public void remove() { iterator.remove(); invalidate(); } }; } @Override public Object[] toArray() { return set.toArray(); } @Override public T[] toArray(T[] a) { return set.toArray(a); } private void invalidate() { if (set.isEmpty()) { encodingField = null; } else { StringBuilder value = new StringBuilder(); for (Iterator iterator = set.iterator(); iterator.hasNext();) { ContentDecoder.Factory decoderFactory = iterator.next(); value.append(decoderFactory.getEncoding()); if (iterator.hasNext()) value.append(","); } encodingField = new HttpField(HttpHeader.ACCEPT_ENCODING, value.toString()); } } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java000066400000000000000000000057021261716203600325770ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.SocketAddress; import java.util.Map; import org.eclipse.jetty.io.ClientConnectionFactory; /** * {@link HttpClientTransport} represents what transport implementations should provide * in order to plug-in a different transport for {@link HttpClient}. *

* While the {@link HttpClient} APIs define the HTTP semantic (request, response, headers, etc.) * how a HTTP exchange is carried over the network depends on implementations of this class. *

* The default implementation uses the HTTP protocol to carry over the network the HTTP exchange, * but the HTTP exchange may also be carried using the SPDY protocol or the FCGI protocol or, in future, * other protocols. */ public interface HttpClientTransport extends ClientConnectionFactory { public static final String HTTP_DESTINATION_CONTEXT_KEY = "http.destination"; public static final String HTTP_CONNECTION_PROMISE_CONTEXT_KEY = "http.connection.promise"; /** * Sets the {@link HttpClient} instance on this transport. *

* This is needed because of a chicken-egg problem: in order to create the {@link HttpClient} * a {@link HttpClientTransport} is needed, that therefore cannot have a reference yet to the * {@link HttpClient}. * * @param client the {@link HttpClient} that uses this transport. */ public void setHttpClient(HttpClient client); /** * Creates a new, transport-specific, {@link HttpDestination} object. *

* {@link HttpDestination} controls the destination-connection cardinality: protocols like * HTTP have 1-N cardinality, while multiplexed protocols like SPDY have a 1-1 cardinality. * * @param origin the destination origin * @return a new, transport-specific, {@link HttpDestination} object */ public HttpDestination newHttpDestination(Origin origin); /** * Establishes a physical connection to the given {@code address}. * * @param address the address to connect to * @param context the context information to establish the connection */ public void connect(SocketAddress address, Map context); } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java000066400000000000000000000137021261716203600315420ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.CookieStore; import java.net.HttpCookie; import java.net.URI; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; public abstract class HttpConnection implements Connection { private static final HttpField CHUNKED_FIELD = new HttpField(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED); private final HttpDestination destination; protected HttpConnection(HttpDestination destination) { this.destination = destination; } public HttpClient getHttpClient() { return destination.getHttpClient(); } public HttpDestination getHttpDestination() { return destination; } @Override public void send(Request request, Response.CompleteListener listener) { ArrayList listeners = new ArrayList<>(2); if (request.getTimeout() > 0) { TimeoutCompleteListener timeoutListener = new TimeoutCompleteListener(request); timeoutListener.schedule(getHttpClient().getScheduler()); listeners.add(timeoutListener); } if (listener != null) listeners.add(listener); HttpExchange exchange = new HttpExchange(getHttpDestination(), (HttpRequest)request, listeners); send(exchange); } protected abstract void send(HttpExchange exchange); protected void normalizeRequest(Request request) { String method = request.getMethod(); HttpVersion version = request.getVersion(); HttpFields headers = request.getHeaders(); ContentProvider content = request.getContent(); ProxyConfiguration.Proxy proxy = destination.getProxy(); // Make sure the path is there String path = request.getPath(); if (path.trim().length() == 0) { path = "/"; request.path(path); } if (proxy != null && !HttpMethod.CONNECT.is(method)) { path = request.getURI().toString(); request.path(path); } // If we are HTTP 1.1, add the Host header if (version.getVersion() > 10) { if (!headers.containsKey(HttpHeader.HOST.asString())) headers.put(getHttpDestination().getHostField()); } // Add content headers if (content != null) { if (content instanceof ContentProvider.Typed) { if (!headers.containsKey(HttpHeader.CONTENT_TYPE.asString())) { String contentType = ((ContentProvider.Typed)content).getContentType(); if (contentType != null) headers.put(HttpHeader.CONTENT_TYPE, contentType); } } long contentLength = content.getLength(); if (contentLength >= 0) { if (!headers.containsKey(HttpHeader.CONTENT_LENGTH.asString())) headers.put(HttpHeader.CONTENT_LENGTH, String.valueOf(contentLength)); } else { if (!headers.containsKey(HttpHeader.TRANSFER_ENCODING.asString())) headers.put(CHUNKED_FIELD); } } // Cookies CookieStore cookieStore = getHttpClient().getCookieStore(); if (cookieStore != null) { URI uri = request.getURI(); StringBuilder cookies = null; if (uri != null) cookies = convertCookies(cookieStore.get(uri), null); cookies = convertCookies(request.getCookies(), cookies); if (cookies != null) request.header(HttpHeader.COOKIE.asString(), cookies.toString()); } // Authorization URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI(); if (authenticationURI != null) { Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI); if (authnResult != null) authnResult.apply(request); } } private StringBuilder convertCookies(List cookies, StringBuilder builder) { for (int i = 0; i < cookies.size(); ++i) { if (builder == null) builder = new StringBuilder(); if (builder.length() > 0) builder.append("; "); HttpCookie cookie = cookies.get(i); builder.append(cookie.getName()).append("=").append(cookie.getValue()); } return builder; } @Override public String toString() { return String.format("%s@%h", getClass().getSimpleName(), this); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContent.java000066400000000000000000000157541261716203600310660ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.Closeable; import java.nio.ByteBuffer; import java.util.Collections; import java.util.Iterator; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * {@link HttpContent} is a stateful, linear representation of the request content provided * by a {@link ContentProvider} that can be traversed one-way to obtain content buffers to * send to a HTTP server. *

* {@link HttpContent} offers the notion of a one-way cursor to traverse the content. * The cursor starts in a virtual "before" position and can be advanced using {@link #advance()} * until it reaches a virtual "after" position where the content is fully consumed. *

 *      +---+  +---+  +---+  +---+  +---+
 *      |   |  |   |  |   |  |   |  |   |
 *      +---+  +---+  +---+  +---+  +---+
 *   ^           ^                    ^    ^
 *   |           | --> advance()      |    |
 *   |           |                  last   |
 *   |           |                         |
 * before        |                        after
 *               |
 *            current
 * 
* At each valid (non-before and non-after) cursor position, {@link HttpContent} provides the following state: *
    *
  • the buffer containing the content to send, via {@link #getByteBuffer()}
  • *
  • a copy of the content buffer that can be used for notifications, via {@link #getContent()}
  • *
  • whether the buffer to write is the last one, via {@link #isLast()}
  • *
* {@link HttpContent} may not have content, if the related {@link ContentProvider} is {@code null}, and this * is reflected by {@link #hasContent()}. *

* {@link HttpContent} may have {@link AsyncContentProvider deferred content}, in which case {@link #advance()} * moves the cursor to a position that provides {@code null} {@link #getByteBuffer() buffer} and * {@link #getContent() content}. When the deferred content is available, a further call to {@link #advance()} * will move the cursor to a position that provides non {@code null} buffer and content. */ public class HttpContent implements Callback, Closeable { private static final Logger LOG = Log.getLogger(HttpContent.class); private static final ByteBuffer AFTER = ByteBuffer.allocate(0); private final ContentProvider provider; private final Iterator iterator; private ByteBuffer buffer; private volatile ByteBuffer content; public HttpContent(ContentProvider provider) { this.provider = provider; this.iterator = provider == null ? Collections.emptyIterator() : provider.iterator(); } /** * @return whether there is any content at all */ public boolean hasContent() { return provider != null; } /** * @return whether the cursor points to the last content */ public boolean isLast() { return !iterator.hasNext(); } /** * @return the {@link ByteBuffer} containing the content at the cursor's position */ public ByteBuffer getByteBuffer() { return buffer; } /** * @return a {@link ByteBuffer#slice()} of {@link #getByteBuffer()} at the cursor's position */ public ByteBuffer getContent() { return content; } /** * Advances the cursor to the next block of content. *

* The next block of content may be valid (which yields a non-null buffer * returned by {@link #getByteBuffer()}), but may also be deferred * (which yields a null buffer returned by {@link #getByteBuffer()}). *

* If the block of content pointed by the new cursor position is valid, this method returns true. * * @return true if there is content at the new cursor's position, false otherwise. */ public boolean advance() { boolean advanced; boolean hasNext; ByteBuffer bytes; if (iterator instanceof Synchronizable) { synchronized (((Synchronizable)iterator).getLock()) { advanced = iterator.hasNext(); bytes = advanced ? iterator.next() : null; hasNext = advanced && iterator.hasNext(); } } else { advanced = iterator.hasNext(); bytes = advanced ? iterator.next() : null; hasNext = advanced && iterator.hasNext(); } if (advanced) { buffer = bytes; content = bytes == null ? null : bytes.slice(); if (LOG.isDebugEnabled()) LOG.debug("Advanced content to {} chunk {}", hasNext ? "next" : "last", bytes); return bytes != null; } else { if (content != AFTER) { content = buffer = AFTER; if (LOG.isDebugEnabled()) LOG.debug("Advanced content past last chunk"); } return false; } } /** * @return whether the cursor has been advanced past the {@link #isLast() last} position. */ public boolean isConsumed() { return content == AFTER; } @Override public void succeeded() { if (isConsumed()) return; if (iterator instanceof Callback) ((Callback)iterator).succeeded(); } @Override public void failed(Throwable x) { if (isConsumed()) return; if (iterator instanceof Callback) ((Callback)iterator).failed(x); } @Override public void close() { try { if (iterator instanceof Closeable) ((Closeable)iterator).close(); } catch (Exception x) { LOG.ignore(x); } } @Override public String toString() { return String.format("%s@%x - has=%b,last=%b,consumed=%b,buffer=%s", getClass().getSimpleName(), hashCode(), hasContent(), isLast(), isConsumed(), BufferUtil.toDetailString(getContent())); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpContentResponse.java000066400000000000000000000066421261716203600326010ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import java.util.List; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; public class HttpContentResponse implements ContentResponse { private final Response response; private final byte[] content; private final String mediaType; private final String encoding; public HttpContentResponse(Response response, byte[] content, String mediaType, String encoding) { this.response = response; this.content = content; this.mediaType = mediaType; this.encoding = encoding; } @Override public Request getRequest() { return response.getRequest(); } @Override public List getListeners(Class listenerClass) { return response.getListeners(listenerClass); } @Override public HttpVersion getVersion() { return response.getVersion(); } @Override public int getStatus() { return response.getStatus(); } @Override public String getReason() { return response.getReason(); } @Override public HttpFields getHeaders() { return response.getHeaders(); } @Override public boolean abort(Throwable cause) { return response.abort(cause); } @Override public String getMediaType() { return mediaType; } @Override public String getEncoding() { return encoding; } @Override public byte[] getContent() { return content; } @Override public String getContentAsString() { String encoding = this.encoding; if (encoding == null) { return new String(getContent(), StandardCharsets.UTF_8); } else { try { return new String(getContent(), encoding); } catch (UnsupportedEncodingException e) { throw new UnsupportedCharsetException(encoding); } } } @Override public String toString() { return String.format("%s[%s %d %s - %d bytes]", HttpContentResponse.class.getSimpleName(), getVersion(), getStatus(), getReason(), getContent().length); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConversation.java000066400000000000000000000137311261716203600321170ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.ArrayList; import java.util.Deque; import java.util.List; import java.util.concurrent.ConcurrentLinkedDeque; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.util.AttributesMap; public class HttpConversation extends AttributesMap { private final Deque exchanges = new ConcurrentLinkedDeque<>(); private volatile List listeners; public Deque getExchanges() { return exchanges; } /** * Returns the list of response listeners that needs to be notified of response events. * This list changes as the conversation proceeds, as follows: *

    *
  1. * request R1 send => conversation.updateResponseListeners(null) *
      *
    • exchanges in conversation: E1
    • *
    • listeners to be notified: E1.listeners
    • *
    *
  2. *
  3. * response R1 arrived, 401 => conversation.updateResponseListeners(AuthenticationProtocolHandler.listener) *
      *
    • exchanges in conversation: E1
    • *
    • listeners to be notified: AuthenticationProtocolHandler.listener
    • *
    *
  4. *
  5. * request R2 send => conversation.updateResponseListeners(null) *
      *
    • exchanges in conversation: E1 + E2
    • *
    • listeners to be notified: E2.listeners + E1.listeners
    • *
    *
  6. *
  7. * response R2 arrived, 302 => conversation.updateResponseListeners(RedirectProtocolHandler.listener) *
      *
    • exchanges in conversation: E1 + E2
    • *
    • listeners to be notified: E2.listeners + RedirectProtocolHandler.listener
    • *
    *
  8. *
  9. * request R3 send => conversation.updateResponseListeners(null) *
      *
    • exchanges in conversation: E1 + E2 + E3
    • *
    • listeners to be notified: E3.listeners + E1.listeners
    • *
    *
  10. *
  11. * response R3 arrived, 200 => conversation.updateResponseListeners(null) *
      *
    • exchanges in conversation: E1 + E2 + E3
    • *
    • listeners to be notified: E3.listeners + E1.listeners
    • *
    *
  12. *
* Basically the override conversation listener replaces the first exchange response listener, * and we also notify the last exchange response listeners (if it's not also the first). * * This scheme allows for protocol handlers to not worry about other protocol handlers, or to worry * too much about notifying the first exchange response listeners, but still allowing a protocol * handler to perform completion activities while another protocol handler performs new ones (as an * example, the {@link AuthenticationProtocolHandler} stores the successful authentication credentials * while the {@link RedirectProtocolHandler} performs a redirect). * * @return the list of response listeners that needs to be notified of response events */ public List getResponseListeners() { return listeners; } /** * Requests to update the response listener, eventually using the given override response listener, * that must be notified instead of the first exchange response listeners. * This works in conjunction with {@link #getResponseListeners()}, returning the appropriate response * listeners that needs to be notified of response events. * * @param overrideListener the override response listener */ public void updateResponseListeners(Response.ResponseListener overrideListener) { // Create a new instance to avoid that iterating over the listeners // will notify a listener that may send a new request and trigger // another call to this method which will build different listeners // which may be iterated over when the iteration continues. List listeners = new ArrayList<>(); HttpExchange firstExchange = exchanges.peekFirst(); HttpExchange lastExchange = exchanges.peekLast(); if (firstExchange == lastExchange) { if (overrideListener != null) listeners.add(overrideListener); else listeners.addAll(firstExchange.getResponseListeners()); } else { // Order is important, we want to notify the last exchange first listeners.addAll(lastExchange.getResponseListeners()); if (overrideListener != null) listeners.add(overrideListener); else listeners.addAll(firstExchange.getResponseListeners()); } this.listeners = listeners; } public boolean abort(Throwable cause) { HttpExchange exchange = exchanges.peekLast(); return exchange != null && exchange.abort(cause); } @Override public String toString() { return String.format("%s[%x]", HttpConversation.class.getSimpleName(), hashCode()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java000066400000000000000000000213241261716203600317230ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.Closeable; import java.io.IOException; import java.nio.channels.AsynchronousCloseException; import java.util.ArrayList; import java.util.List; import java.util.Queue; import java.util.concurrent.RejectedExecutionException; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public abstract class HttpDestination implements Destination, Closeable, Dumpable { protected static final Logger LOG = Log.getLogger(HttpDestination.class); private final HttpClient client; private final Origin origin; private final Queue exchanges; private final RequestNotifier requestNotifier; private final ResponseNotifier responseNotifier; private final ProxyConfiguration.Proxy proxy; private final ClientConnectionFactory connectionFactory; private final HttpField hostField; public HttpDestination(HttpClient client, Origin origin) { this.client = client; this.origin = origin; this.exchanges = newExchangeQueue(client); this.requestNotifier = new RequestNotifier(client); this.responseNotifier = new ResponseNotifier(); ProxyConfiguration proxyConfig = client.getProxyConfiguration(); proxy = proxyConfig.match(origin); ClientConnectionFactory connectionFactory = client.getTransport(); if (proxy != null) { connectionFactory = proxy.newClientConnectionFactory(connectionFactory); } else { if (HttpScheme.HTTPS.is(getScheme())) connectionFactory = newSslClientConnectionFactory(connectionFactory); } this.connectionFactory = connectionFactory; String host = getHost(); if (!client.isDefaultPort(getScheme(), getPort())) host += ":" + getPort(); hostField = new HttpField(HttpHeader.HOST, host); } protected Queue newExchangeQueue(HttpClient client) { return new BlockingArrayQueue<>(client.getMaxRequestsQueuedPerDestination()); } protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory) { return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory); } public HttpClient getHttpClient() { return client; } public Origin getOrigin() { return origin; } public Queue getHttpExchanges() { return exchanges; } public RequestNotifier getRequestNotifier() { return requestNotifier; } public ResponseNotifier getResponseNotifier() { return responseNotifier; } public ProxyConfiguration.Proxy getProxy() { return proxy; } public ClientConnectionFactory getClientConnectionFactory() { return connectionFactory; } @Override public String getScheme() { return origin.getScheme(); } @Override public String getHost() { // InetSocketAddress.getHostString() transforms the host string // in case of IPv6 addresses, so we return the original host string return origin.getAddress().getHost(); } @Override public int getPort() { return origin.getAddress().getPort(); } public Origin.Address getConnectAddress() { return proxy == null ? origin.getAddress() : proxy.getAddress(); } public HttpField getHostField() { return hostField; } protected void send(HttpRequest request, List listeners) { if (!getScheme().equalsIgnoreCase(request.getScheme())) throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this); if (!getHost().equalsIgnoreCase(request.getHost())) throw new IllegalArgumentException("Invalid request host " + request.getHost() + " for destination " + this); int port = request.getPort(); if (port >= 0 && getPort() != port) throw new IllegalArgumentException("Invalid request port " + port + " for destination " + this); HttpExchange exchange = new HttpExchange(this, request, listeners); if (client.isRunning()) { if (enqueue(exchanges, exchange)) { if (!client.isRunning() && exchanges.remove(exchange)) { request.abort(new RejectedExecutionException(client + " is stopping")); } else { if (LOG.isDebugEnabled()) LOG.debug("Queued {} for {}", request, this); requestNotifier.notifyQueued(request); send(); } } else { if (LOG.isDebugEnabled()) LOG.debug("Max queue size {} exceeded by {} for {}", client.getMaxRequestsQueuedPerDestination(), request, this); request.abort(new RejectedExecutionException("Max requests per destination " + client.getMaxRequestsQueuedPerDestination() + " exceeded for " + this)); } } else { request.abort(new RejectedExecutionException(client + " is stopped")); } } protected boolean enqueue(Queue queue, HttpExchange exchange) { return queue.offer(exchange); } protected abstract void send(); public void newConnection(Promise promise) { createConnection(promise); } protected void createConnection(Promise promise) { client.newConnection(this, promise); } public boolean remove(HttpExchange exchange) { return exchanges.remove(exchange); } public void close() { abort(new AsynchronousCloseException()); if (LOG.isDebugEnabled()) LOG.debug("Closed {}", this); } public void release(Connection connection) { } public void close(Connection connection) { } /** * Aborts all the {@link HttpExchange}s queued in this destination. * * @param cause the abort cause */ public void abort(Throwable cause) { // Copy the queue of exchanges and fail only those that are queued at this moment. // The application may queue another request from the failure/complete listener // and we don't want to fail it immediately as if it was queued before the failure. // The call to Request.abort() will remove the exchange from the exchanges queue. for (HttpExchange exchange : new ArrayList<>(exchanges)) exchange.getRequest().abort(cause); } @Override public String dump() { return ContainerLifeCycle.dump(this); } @Override public void dump(Appendable out, String indent) throws IOException { ContainerLifeCycle.dumpObject(out, toString()); } public String asString() { return origin.asString(); } @Override public String toString() { return String.format("%s[%s]%x%s,queue=%d", HttpDestination.class.getSimpleName(), asString(), hashCode(), proxy == null ? "" : "(via " + proxy + ")", exchanges.size()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpExchange.java000066400000000000000000000217471261716203600311750ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.List; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class HttpExchange { private static final Logger LOG = Log.getLogger(HttpExchange.class); private final HttpDestination destination; private final HttpRequest request; private final List listeners; private final HttpResponse response; private State requestState = State.PENDING; private State responseState = State.PENDING; private HttpChannel _channel; private Throwable requestFailure; private Throwable responseFailure; public HttpExchange(HttpDestination destination, HttpRequest request, List listeners) { this.destination = destination; this.request = request; this.listeners = listeners; this.response = new HttpResponse(request, listeners); HttpConversation conversation = request.getConversation(); conversation.getExchanges().offer(this); conversation.updateResponseListeners(null); } public HttpConversation getConversation() { return request.getConversation(); } public HttpRequest getRequest() { return request; } public Throwable getRequestFailure() { synchronized (this) { return requestFailure; } } public List getResponseListeners() { return listeners; } public HttpResponse getResponse() { return response; } public Throwable getResponseFailure() { synchronized (this) { return responseFailure; } } /** *

Associates the given {@code channel} to this exchange.

*

Works in strict collaboration with {@link HttpChannel#associate(HttpExchange)}.

* * @param channel the channel to associate to this exchange * @return true if the channel could be associated, false otherwise */ boolean associate(HttpChannel channel) { boolean result = false; boolean abort = false; synchronized (this) { // Only associate if the exchange state is initial, // as the exchange could be already failed. if (requestState == State.PENDING && responseState == State.PENDING) { abort = _channel != null; if (!abort) { _channel = channel; result = true; } } } if (abort) request.abort(new IllegalStateException(toString())); return result; } void disassociate(HttpChannel channel) { boolean abort = false; synchronized (this) { if (_channel != channel || requestState != State.TERMINATED || responseState != State.TERMINATED) abort = true; _channel = null; } if (abort) request.abort(new IllegalStateException(toString())); } private HttpChannel getHttpChannel() { synchronized (this) { return _channel; } } public boolean requestComplete(Throwable failure) { synchronized (this) { return completeRequest(failure); } } private boolean completeRequest(Throwable failure) { if (requestState == State.PENDING) { requestState = State.COMPLETED; requestFailure = failure; return true; } return false; } public boolean responseComplete(Throwable failure) { synchronized (this) { return completeResponse(failure); } } private boolean completeResponse(Throwable failure) { if (responseState == State.PENDING) { responseState = State.COMPLETED; responseFailure = failure; return true; } return false; } public Result terminateRequest() { Result result = null; synchronized (this) { if (requestState == State.COMPLETED) requestState = State.TERMINATED; if (requestState == State.TERMINATED && responseState == State.TERMINATED) result = new Result(getRequest(), requestFailure, getResponse(), responseFailure); } if (LOG.isDebugEnabled()) LOG.debug("Terminated request for {}, result: {}", this, result); return result; } public Result terminateResponse() { Result result = null; synchronized (this) { if (responseState == State.COMPLETED) responseState = State.TERMINATED; if (requestState == State.TERMINATED && responseState == State.TERMINATED) result = new Result(getRequest(), requestFailure, getResponse(), responseFailure); } if (LOG.isDebugEnabled()) LOG.debug("Terminated response for {}, result: {}", this, result); return result; } public boolean abort(Throwable failure) { // Atomically change the state of this exchange to be completed. // This will avoid that this exchange can be associated to a channel. boolean abortRequest; boolean abortResponse; synchronized (this) { abortRequest = completeRequest(failure); abortResponse = completeResponse(failure); } if (LOG.isDebugEnabled()) LOG.debug("Failed {}: req={}/rsp={} {}", this, abortRequest, abortResponse, failure); if (!abortRequest && !abortResponse) return false; // We failed this exchange, deal with it. // Case #1: exchange was in the destination queue. if (destination.remove(this)) { if (LOG.isDebugEnabled()) LOG.debug("Aborting while queued {}: {}", this, failure); notifyFailureComplete(failure); return true; } HttpChannel channel = getHttpChannel(); if (channel == null) { // Case #2: exchange was not yet associated. // Because this exchange is failed, when associate() is called // it will return false, and the caller will dispose the channel. if (LOG.isDebugEnabled()) LOG.debug("Aborted before association {}: {}", this, failure); notifyFailureComplete(failure); return true; } // Case #3: exchange was already associated. boolean aborted = channel.abort(this, abortRequest ? failure : null, abortResponse ? failure : null); if (LOG.isDebugEnabled()) LOG.debug("Aborted ({}) while active {}: {}", aborted, this, failure); return aborted; } private void notifyFailureComplete(Throwable failure) { destination.getRequestNotifier().notifyFailure(request, failure); List listeners = getConversation().getResponseListeners(); ResponseNotifier responseNotifier = destination.getResponseNotifier(); responseNotifier.notifyFailure(listeners, response, failure); responseNotifier.notifyComplete(listeners, new Result(request, failure, response, failure)); } public void resetResponse() { synchronized (this) { responseState = State.PENDING; responseFailure = null; } } public void proceed(Throwable failure) { HttpChannel channel = getHttpChannel(); if (channel != null) channel.proceed(this, failure); } @Override public String toString() { synchronized (this) { return String.format("%s@%x req=%s/%s@%h res=%s/%s@%h", HttpExchange.class.getSimpleName(), hashCode(), requestState, requestFailure, requestFailure, responseState, responseFailure, responseFailure); } } private enum State { PENDING, COMPLETED, TERMINATED } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java000066400000000000000000000210561261716203600305650ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URI; import java.util.Map; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; public class HttpProxy extends ProxyConfiguration.Proxy { public HttpProxy(String host, int port) { this(new Origin.Address(host, port), false); } public HttpProxy(Origin.Address address, boolean secure) { super(address, secure); } @Override public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory) { return new HttpProxyClientConnectionFactory(connectionFactory); } @Override public URI getURI() { String scheme = isSecure() ? HttpScheme.HTTPS.asString() : HttpScheme.HTTP.asString(); return URI.create(new Origin(scheme, getAddress()).asString()); } public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory { private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class); private final ClientConnectionFactory connectionFactory; public HttpProxyClientConnectionFactory(ClientConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } @Override public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException { @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY); final ProxyPromise proxyPromise = new ProxyPromise(endPoint, promise, context); // Replace the promise with the proxy one context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, proxyPromise); return connectionFactory.newConnection(endPoint, context); } /** * Decides whether to establish a proxy tunnel using HTTP CONNECT. * It is implemented as a promise because it needs to establish the * tunnel after the TCP connection is succeeded, and needs to notify * the nested promise when the tunnel is established (or failed). */ private class ProxyPromise implements Promise { private final EndPoint endPoint; private final Promise promise; private final Map context; private ProxyPromise(EndPoint endPoint, Promise promise, Map context) { this.endPoint = endPoint; this.promise = promise; this.context = context; } @Override public void succeeded(Connection connection) { HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); if (HttpScheme.HTTPS.is(destination.getScheme())) { SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory(); if (sslContextFactory != null) { tunnel(destination, connection); } else { String message = String.format("Cannot perform requests over SSL, no %s in %s", SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName()); promise.failed(new IllegalStateException(message)); } } else { promise.succeeded(connection); } } @Override public void failed(Throwable x) { promise.failed(x); } private void tunnel(HttpDestination destination, final Connection connection) { String target = destination.getOrigin().getAddress().asString(); Origin.Address proxyAddress = destination.getConnectAddress(); HttpClient httpClient = destination.getHttpClient(); Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort()) .scheme(HttpScheme.HTTP.asString()) .method(HttpMethod.CONNECT) .path(target) .header(HttpHeader.HOST, target) .timeout(httpClient.getConnectTimeout(), TimeUnit.MILLISECONDS); connection.send(connect, new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) { tunnelFailed(result.getFailure()); } else { Response response = result.getResponse(); if (response.getStatus() == 200) { tunnelSucceeded(); } else { tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response)); } } } }); } private void tunnelSucceeded() { try { // Replace the promise back with the original context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise); HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); HttpClient client = destination.getHttpClient(); ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory); HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection(); org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context); Helper.replaceConnection(oldConnection, newConnection); // Avoid setting fill interest in the old Connection, // without closing the underlying EndPoint. oldConnection.softClose(); if (LOG.isDebugEnabled()) LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection); } catch (Throwable x) { tunnelFailed(x); } } private void tunnelFailed(Throwable failure) { endPoint.close(); failed(failure); } } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpReceiver.java000066400000000000000000000501321261716203600312050ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.CountingCallback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * {@link HttpReceiver} provides the abstract code to implement the various steps of the receive of HTTP responses. *

* {@link HttpReceiver} maintains a state machine that is updated when the steps of receiving a response are executed. *

* Subclasses must handle the transport-specific details, for example how to read from the raw socket and how to parse * the bytes read from the socket. Then they have to call the methods defined in this class in the following order: *

    *
  1. {@link #responseBegin(HttpExchange)}, when the HTTP response data containing the HTTP status code * is available
  2. *
  3. {@link #responseHeader(HttpExchange, HttpField)}, when a HTTP field is available
  4. *
  5. {@link #responseHeaders(HttpExchange)}, when all HTTP headers are available
  6. *
  7. {@link #responseContent(HttpExchange, ByteBuffer, Callback)}, when HTTP content is available
  8. *
  9. {@link #responseSuccess(HttpExchange)}, when the response is successful
  10. *
* At any time, subclasses may invoke {@link #responseFailure(Throwable)} to indicate that the response has failed * (for example, because of I/O exceptions). * At any time, user threads may abort the response which will cause {@link #responseFailure(Throwable)} to be * invoked. *

* The state machine maintained by this class ensures that the response steps are not executed by an I/O thread * if the response has already been failed. * * @see HttpSender */ public abstract class HttpReceiver { protected static final Logger LOG = Log.getLogger(HttpReceiver.class); private final AtomicReference responseState = new AtomicReference<>(ResponseState.IDLE); private final HttpChannel channel; private ContentDecoder decoder; private Throwable failure; protected HttpReceiver(HttpChannel channel) { this.channel = channel; } protected HttpChannel getHttpChannel() { return channel; } protected HttpExchange getHttpExchange() { return channel.getHttpExchange(); } protected HttpDestination getHttpDestination() { return channel.getHttpDestination(); } /** * Method to be invoked when the response status code is available. *

* Subclasses must have set the response status code on the {@link Response} object of the {@link HttpExchange} * prior invoking this method. *

* This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.BeginListener}s. * * @param exchange the HTTP exchange * @return whether the processing should continue */ protected boolean responseBegin(HttpExchange exchange) { if (!updateResponseState(ResponseState.IDLE, ResponseState.TRANSIENT)) return false; HttpConversation conversation = exchange.getConversation(); HttpResponse response = exchange.getResponse(); // Probe the protocol handlers HttpDestination destination = getHttpDestination(); HttpClient client = destination.getHttpClient(); ProtocolHandler protocolHandler = client.findProtocolHandler(exchange.getRequest(), response); Response.Listener handlerListener = null; if (protocolHandler != null) { handlerListener = protocolHandler.getResponseListener(); if (LOG.isDebugEnabled()) LOG.debug("Found protocol handler {}", protocolHandler); } exchange.getConversation().updateResponseListeners(handlerListener); if (LOG.isDebugEnabled()) LOG.debug("Response begin {}", response); ResponseNotifier notifier = destination.getResponseNotifier(); notifier.notifyBegin(conversation.getResponseListeners(), response); if (updateResponseState(ResponseState.TRANSIENT, ResponseState.BEGIN)) return true; terminateResponse(exchange); return false; } /** * Method to be invoked when a response HTTP header is available. *

* Subclasses must not have added the header to the {@link Response} object of the {@link HttpExchange} * prior invoking this method. *

* This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.HeaderListener}s and storing cookies. * * @param exchange the HTTP exchange * @param field the response HTTP field * @return whether the processing should continue */ protected boolean responseHeader(HttpExchange exchange, HttpField field) { out: while (true) { ResponseState current = responseState.get(); switch (current) { case BEGIN: case HEADER: { if (updateResponseState(current, ResponseState.TRANSIENT)) break out; break; } default: { return false; } } } HttpResponse response = exchange.getResponse(); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); boolean process = notifier.notifyHeader(exchange.getConversation().getResponseListeners(), response, field); if (process) { response.getHeaders().add(field); HttpHeader fieldHeader = field.getHeader(); if (fieldHeader != null) { switch (fieldHeader) { case SET_COOKIE: case SET_COOKIE2: { storeCookie(exchange.getRequest().getURI(), field); break; } default: { break; } } } } if (updateResponseState(ResponseState.TRANSIENT, ResponseState.HEADER)) return true; terminateResponse(exchange); return false; } protected void storeCookie(URI uri, HttpField field) { try { String value = field.getValue(); if (value != null) { Map> header = new HashMap<>(1); header.put(field.getHeader().asString(), Collections.singletonList(value)); getHttpDestination().getHttpClient().getCookieManager().put(uri, header); } } catch (IOException x) { if (LOG.isDebugEnabled()) LOG.debug(x); } } /** * Method to be invoked after all response HTTP headers are available. *

* This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.HeadersListener}s. * * @param exchange the HTTP exchange * @return whether the processing should continue */ protected boolean responseHeaders(HttpExchange exchange) { out: while (true) { ResponseState current = responseState.get(); switch (current) { case BEGIN: case HEADER: { if (updateResponseState(current, ResponseState.TRANSIENT)) break out; break; } default: { return false; } } } HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) LOG.debug("Response headers {}{}{}", response, System.lineSeparator(), response.getHeaders().toString().trim()); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); notifier.notifyHeaders(exchange.getConversation().getResponseListeners(), response); Enumeration contentEncodings = response.getHeaders().getValues(HttpHeader.CONTENT_ENCODING.asString(), ","); if (contentEncodings != null) { for (ContentDecoder.Factory factory : getHttpDestination().getHttpClient().getContentDecoderFactories()) { while (contentEncodings.hasMoreElements()) { if (factory.getEncoding().equalsIgnoreCase(contentEncodings.nextElement())) { this.decoder = factory.newContentDecoder(); break; } } } } if (updateResponseState(ResponseState.TRANSIENT, ResponseState.HEADERS)) return true; terminateResponse(exchange); return false; } /** * Method to be invoked when response HTTP content is available. *

* This method takes case of decoding the content, if necessary, and notifying {@link org.eclipse.jetty.client.api.Response.ContentListener}s. * * @param exchange the HTTP exchange * @param buffer the response HTTP content buffer * @return whether the processing should continue */ protected boolean responseContent(HttpExchange exchange, ByteBuffer buffer, final Callback callback) { out: while (true) { ResponseState current = responseState.get(); switch (current) { case HEADERS: case CONTENT: { if (updateResponseState(current, ResponseState.TRANSIENT)) break out; break; } default: { return false; } } } HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) LOG.debug("Response content {}{}{}", response, System.lineSeparator(), BufferUtil.toDetailString(buffer)); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); List listeners = exchange.getConversation().getResponseListeners(); ContentDecoder decoder = this.decoder; if (decoder == null) { notifier.notifyContent(listeners, response, buffer, callback); } else { List decodeds = new ArrayList<>(2); while (buffer.hasRemaining()) { ByteBuffer decoded = decoder.decode(buffer); if (!decoded.hasRemaining()) continue; decodeds.add(decoded); if (LOG.isDebugEnabled()) LOG.debug("Response content decoded ({}) {}{}{}", decoder, response, System.lineSeparator(), BufferUtil.toDetailString(decoded)); } if (decodeds.isEmpty()) { callback.succeeded(); } else { int size = decodeds.size(); CountingCallback counter = new CountingCallback(callback, size); for (int i = 0; i < size; ++i) notifier.notifyContent(listeners, response, decodeds.get(i), counter); } } if (updateResponseState(ResponseState.TRANSIENT, ResponseState.CONTENT)) return true; terminateResponse(exchange); return false; } /** * Method to be invoked when the response is successful. *

* This method takes case of notifying {@link org.eclipse.jetty.client.api.Response.SuccessListener}s and possibly * {@link org.eclipse.jetty.client.api.Response.CompleteListener}s (if the exchange is completed). * * @param exchange the HTTP exchange * @return whether the response was processed as successful */ protected boolean responseSuccess(HttpExchange exchange) { // Mark atomically the response as completed, with respect // to concurrency between response success and response failure. if (!exchange.responseComplete(null)) return false; responseState.set(ResponseState.IDLE); // Reset to be ready for another response. reset(); HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) LOG.debug("Response success {}", response); List listeners = exchange.getConversation().getResponseListeners(); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); notifier.notifySuccess(listeners, response); // Special case for 100 Continue that cannot // be handled by the ContinueProtocolHandler. if (exchange.getResponse().getStatus() == HttpStatus.CONTINUE_100) return true; // Mark atomically the response as terminated, with // respect to concurrency between request and response. Result result = exchange.terminateResponse(); terminateResponse(exchange, result); return true; } /** * Method to be invoked when the response is failed. *

* This method takes care of notifying {@link org.eclipse.jetty.client.api.Response.FailureListener}s. * * @param failure the response failure * @return whether the response was processed as failed */ protected boolean responseFailure(Throwable failure) { HttpExchange exchange = getHttpExchange(); // In case of a response error, the failure has already been notified // and it is possible that a further attempt to read in the receive // loop throws an exception that reenters here but without exchange; // or, the server could just have timed out the connection. if (exchange == null) return false; // Mark atomically the response as completed, with respect // to concurrency between response success and response failure. if (exchange.responseComplete(failure)) return abort(exchange, failure); return false; } private void terminateResponse(HttpExchange exchange) { Result result = exchange.terminateResponse(); terminateResponse(exchange, result); } private void terminateResponse(HttpExchange exchange, Result result) { HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) LOG.debug("Response complete {}", response); if (result != null) { boolean ordered = getHttpDestination().getHttpClient().isStrictEventOrdering(); if (!ordered) channel.exchangeTerminated(exchange, result); if (LOG.isDebugEnabled()) LOG.debug("Request/Response {}: {}", failure == null ? "succeeded" : "failed", result); List listeners = exchange.getConversation().getResponseListeners(); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); notifier.notifyComplete(listeners, result); if (ordered) channel.exchangeTerminated(exchange, result); } } /** * Resets this {@link HttpReceiver} state. *

* Subclasses should override (but remember to call {@code super}) to reset their own state. *

* Either this method or {@link #dispose()} is called. */ protected void reset() { decoder = null; } /** * Disposes this {@link HttpReceiver} state. *

* Subclasses should override (but remember to call {@code super}) to dispose their own state. *

* Either this method or {@link #reset()} is called. */ protected void dispose() { decoder = null; } public boolean abort(HttpExchange exchange, Throwable failure) { // Update the state to avoid more response processing. boolean terminate; out: while (true) { ResponseState current = responseState.get(); switch (current) { case FAILURE: { return false; } default: { if (updateResponseState(current, ResponseState.FAILURE)) { terminate = current != ResponseState.TRANSIENT; break out; } break; } } } this.failure = failure; dispose(); HttpResponse response = exchange.getResponse(); if (LOG.isDebugEnabled()) LOG.debug("Response failure {} {} on {}: {}", response, exchange, getHttpChannel(), failure); List listeners = exchange.getConversation().getResponseListeners(); ResponseNotifier notifier = getHttpDestination().getResponseNotifier(); notifier.notifyFailure(listeners, response, failure); if (terminate) { // Mark atomically the response as terminated, with // respect to concurrency between request and response. Result result = exchange.terminateResponse(); terminateResponse(exchange, result); } else { if (LOG.isDebugEnabled()) LOG.debug("Concurrent failure: response termination skipped, performed by helpers"); } return true; } private boolean updateResponseState(ResponseState from, ResponseState to) { boolean updated = responseState.compareAndSet(from, to); if (!updated) { if (LOG.isDebugEnabled()) LOG.debug("State update failed: {} -> {}: {}", from, to, responseState.get()); } return updated; } @Override public String toString() { return String.format("%s@%x(rsp=%s,failure=%s)", getClass().getSimpleName(), hashCode(), responseState, failure); } /** * The request states {@link HttpReceiver} goes through when receiving a response. */ private enum ResponseState { /** * One of the response*() methods is being executed. */ TRANSIENT, /** * The response is not yet received, the initial state */ IDLE, /** * The response status code has been received */ BEGIN, /** * The response headers are being received */ HEADER, /** * All the response headers have been received */ HEADERS, /** * The response content is being received */ CONTENT, /** * The response is failed */ FAILURE } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRedirector.java000066400000000000000000000307131261716203600315460ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * Utility class that handles HTTP redirects. *

* Applications can disable redirection via {@link Request#followRedirects(boolean)} * and then rely on this class to perform the redirect in a simpler way, for example: *

 * HttpRedirector redirector = new HttpRedirector(httpClient);
 *
 * Request request = httpClient.newRequest("http://host/path").followRedirects(false);
 * ContentResponse response = request.send();
 * while (redirector.isRedirect(response))
 * {
 *     // Validate the redirect URI
 *     if (!validate(redirector.extractRedirectURI(response)))
 *         break;
 *
 *     Result result = redirector.redirect(request, response);
 *     request = result.getRequest();
 *     response = result.getResponse();
 * }
 * 
*/ public class HttpRedirector { private static final Logger LOG = Log.getLogger(HttpRedirector.class); private static final String SCHEME_REGEXP = "(^https?)"; private static final String AUTHORITY_REGEXP = "([^/\\?#]+)"; // The location may be relative so the scheme://authority part may be missing private static final String DESTINATION_REGEXP = "(" + SCHEME_REGEXP + "://" + AUTHORITY_REGEXP + ")?"; private static final String PATH_REGEXP = "([^\\?#]*)"; private static final String QUERY_REGEXP = "([^#]*)"; private static final String FRAGMENT_REGEXP = "(.*)"; private static final Pattern URI_PATTERN = Pattern.compile(DESTINATION_REGEXP + PATH_REGEXP + QUERY_REGEXP + FRAGMENT_REGEXP); private static final String ATTRIBUTE = HttpRedirector.class.getName() + ".redirects"; private final HttpClient client; private final ResponseNotifier notifier; public HttpRedirector(HttpClient client) { this.client = client; this.notifier = new ResponseNotifier(); } /** * @param response the response to check for redirects * @return whether the response code is a HTTP redirect code */ public boolean isRedirect(Response response) { switch (response.getStatus()) { case 301: case 302: case 303: case 307: case 308: return true; default: return false; } } /** * Redirects the given {@code response}, blocking until the redirect is complete. * * @param request the original request that triggered the redirect * @param response the response to the original request * @return a {@link Result} object containing the request to the redirected location and its response * @throws InterruptedException if the thread is interrupted while waiting for the redirect to complete * @throws ExecutionException if the redirect failed * @see #redirect(Request, Response, Response.CompleteListener) */ public Result redirect(Request request, Response response) throws InterruptedException, ExecutionException { final AtomicReference resultRef = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); Request redirect = redirect(request, response, new BufferingResponseListener() { @Override public void onComplete(Result result) { resultRef.set(new Result(result.getRequest(), result.getRequestFailure(), new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding()), result.getResponseFailure())); latch.countDown(); } }); try { latch.await(); Result result = resultRef.get(); if (result.isFailed()) throw new ExecutionException(result.getFailure()); return result; } catch (InterruptedException x) { // If the application interrupts, we need to abort the redirect redirect.abort(x); throw x; } } /** * Redirects the given {@code response} asynchronously. * * @param request the original request that triggered the redirect * @param response the response to the original request * @param listener the listener that receives response events * @return the request to the redirected location */ public Request redirect(Request request, Response response, Response.CompleteListener listener) { if (isRedirect(response)) { String location = response.getHeaders().get("Location"); URI newURI = extractRedirectURI(response); if (newURI != null) { if (LOG.isDebugEnabled()) LOG.debug("Redirecting to {} (Location: {})", newURI, location); return redirect(request, response, listener, newURI); } else { fail(request, response, new HttpResponseException("Invalid 'Location' header: " + location, response)); return null; } } else { fail(request, response, new HttpResponseException("Cannot redirect: " + response, response)); return null; } } /** * Extracts and sanitizes (by making it absolute and escaping paths and query parameters) * the redirect URI of the given {@code response}. * * @param response the response to extract the redirect URI from * @return the absolute redirect URI, or null if the response does not contain a valid redirect location */ public URI extractRedirectURI(Response response) { String location = response.getHeaders().get("location"); if (location != null) return sanitize(location); return null; } private URI sanitize(String location) { // Redirects should be valid, absolute, URIs, with properly escaped paths and encoded // query parameters. However, shit happens, and here we try our best to recover. try { // Direct hit first: if passes, we're good return new URI(location); } catch (URISyntaxException x) { Matcher matcher = URI_PATTERN.matcher(location); if (matcher.matches()) { String scheme = matcher.group(2); String authority = matcher.group(3); String path = matcher.group(4); String query = matcher.group(5); if (query.length() == 0) query = null; String fragment = matcher.group(6); if (fragment.length() == 0) fragment = null; try { return new URI(scheme, authority, path, query, fragment); } catch (URISyntaxException xx) { // Give up } } return null; } } private Request redirect(Request request, Response response, Response.CompleteListener listener, URI newURI) { if (!newURI.isAbsolute()) newURI = request.getURI().resolve(newURI); int status = response.getStatus(); switch (status) { case 301: { String method = request.getMethod(); if (HttpMethod.GET.is(method) || HttpMethod.HEAD.is(method) || HttpMethod.PUT.is(method)) return redirect(request, response, listener, newURI, method); else if (HttpMethod.POST.is(method)) return redirect(request, response, listener, newURI, HttpMethod.GET.asString()); fail(request, response, new HttpResponseException("HTTP protocol violation: received 301 for non GET/HEAD/POST/PUT request", response)); return null; } case 302: { String method = request.getMethod(); if (HttpMethod.HEAD.is(method) || HttpMethod.PUT.is(method)) return redirect(request, response, listener, newURI, method); else return redirect(request, response, listener, newURI, HttpMethod.GET.asString()); } case 303: { String method = request.getMethod(); if (HttpMethod.HEAD.is(method)) return redirect(request, response, listener, newURI, method); else return redirect(request, response, listener, newURI, HttpMethod.GET.asString()); } case 307: case 308: { // Keep same method return redirect(request, response, listener, newURI, request.getMethod()); } default: { fail(request, response, new HttpResponseException("Unhandled HTTP status code " + status, response)); return null; } } } private Request redirect(Request request, Response response, Response.CompleteListener listener, URI location, String method) { HttpRequest httpRequest = (HttpRequest)request; HttpConversation conversation = httpRequest.getConversation(); Integer redirects = (Integer)conversation.getAttribute(ATTRIBUTE); if (redirects == null) redirects = 0; if (redirects < client.getMaxRedirects()) { ++redirects; conversation.setAttribute(ATTRIBUTE, redirects); return sendRedirect(httpRequest, response, listener, location, method); } else { fail(request, response, new HttpResponseException("Max redirects exceeded " + redirects, response)); return null; } } private Request sendRedirect(final HttpRequest httpRequest, Response response, Response.CompleteListener listener, URI location, String method) { try { Request redirect = client.copyRequest(httpRequest, location); // Use given method redirect.method(method); redirect.onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request redirect) { Throwable cause = httpRequest.getAbortCause(); if (cause != null) redirect.abort(cause); } }); redirect.send(listener); return redirect; } catch (Throwable x) { fail(httpRequest, response, x); return null; } } protected void fail(Request request, Response response, Throwable failure) { HttpConversation conversation = ((HttpRequest)request).getConversation(); conversation.updateResponseListeners(null); List listeners = conversation.getResponseListeners(); notifier.notifyFailure(listeners, response, failure); notifier.notifyComplete(listeners, new Result(request, response, failure)); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java000066400000000000000000000522371261716203600311010ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.HttpCookie; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.charset.UnsupportedCharsetException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.PathContentProvider; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Fields; public class HttpRequest implements Request { private static final URI NULL_URI = URI.create("null:0"); private final HttpFields headers = new HttpFields(); private final Fields params = new Fields(true); private final List responseListeners = new ArrayList<>(); private final AtomicReference aborted = new AtomicReference<>(); private final HttpClient client; private final HttpConversation conversation; private final String host; private final int port; private URI uri; private String scheme; private String path; private String query; private String method = HttpMethod.GET.asString(); private HttpVersion version = HttpVersion.HTTP_1_1; private long idleTimeout; private long timeout; private ContentProvider content; private boolean followRedirects; private List cookies; private Map attributes; private List requestListeners; protected HttpRequest(HttpClient client, HttpConversation conversation, URI uri) { this.client = client; this.conversation = conversation; scheme = uri.getScheme(); host = client.normalizeHost(uri.getHost()); port = client.normalizePort(scheme, uri.getPort()); path = uri.getRawPath(); query = uri.getRawQuery(); extractParams(query); followRedirects(client.isFollowRedirects()); idleTimeout = client.getIdleTimeout(); HttpField acceptEncodingField = client.getAcceptEncodingField(); if (acceptEncodingField != null) headers.put(acceptEncodingField); HttpField userAgentField = client.getUserAgentField(); if (userAgentField != null) headers.put(userAgentField); } protected HttpConversation getConversation() { return conversation; } @Override public String getScheme() { return scheme; } @Override public Request scheme(String scheme) { this.scheme = scheme; this.uri = null; return this; } @Override public String getHost() { return host; } @Override public int getPort() { return port; } @Override public String getMethod() { return method; } @Override public Request method(HttpMethod method) { return method(method.asString()); } @Override public Request method(String method) { this.method = Objects.requireNonNull(method).toUpperCase(Locale.ENGLISH); return this; } @Override public String getPath() { return path; } @Override public Request path(String path) { URI uri = newURI(path); if (uri == null) { this.path = path; this.query = null; } else { String rawPath = uri.getRawPath(); if (uri.isOpaque()) rawPath = path; if (rawPath == null) rawPath = ""; this.path = rawPath; String query = uri.getRawQuery(); if (query != null) { this.query = query; params.clear(); extractParams(query); } if (uri.isAbsolute()) this.path = buildURI(false).toString(); } this.uri = null; return this; } @Override public String getQuery() { return query; } @Override public URI getURI() { if (uri == null) uri = buildURI(true); return uri == NULL_URI ? null : uri; } @Override public HttpVersion getVersion() { return version; } @Override public Request version(HttpVersion version) { this.version = Objects.requireNonNull(version); return this; } @Override public Request param(String name, String value) { return param(name, value, false); } private Request param(String name, String value, boolean fromQuery) { params.add(name, value); if (!fromQuery) { // If we have an existing query string, preserve it and append the new parameter. if (query != null) query += "&" + urlEncode(name) + "=" + urlEncode(value); else query = buildQuery(); uri = null; } return this; } @Override public Fields getParams() { return new Fields(params, true); } @Override public String getAgent() { return headers.get(HttpHeader.USER_AGENT); } @Override public Request agent(String agent) { headers.put(HttpHeader.USER_AGENT, agent); return this; } @Override public Request accept(String... accepts) { StringBuilder result = new StringBuilder(); for (String accept : accepts) { if (result.length() > 0) result.append(", "); result.append(accept); } if (result.length() > 0) headers.put(HttpHeader.ACCEPT, result.toString()); return this; } @Override public Request header(String name, String value) { if (value == null) headers.remove(name); else headers.add(name, value); return this; } @Override public Request header(HttpHeader header, String value) { if (value == null) headers.remove(header); else headers.add(header, value); return this; } @Override public List getCookies() { return cookies != null ? cookies : Collections.emptyList(); } @Override public Request cookie(HttpCookie cookie) { if (cookies == null) cookies = new ArrayList<>(); cookies.add(cookie); return this; } @Override public Request attribute(String name, Object value) { if (attributes == null) attributes = new HashMap<>(4); attributes.put(name, value); return this; } @Override public Map getAttributes() { return attributes != null ? attributes : Collections.emptyMap(); } @Override public HttpFields getHeaders() { return headers; } @Override @SuppressWarnings("unchecked") public List getRequestListeners(Class type) { // This method is invoked often in a request/response conversation, // so we avoid allocation if there is no need to filter. if (type == null || requestListeners == null) return requestListeners != null ? (List)requestListeners : Collections.emptyList(); ArrayList result = new ArrayList<>(); for (RequestListener listener : requestListeners) if (type.isInstance(listener)) result.add((T)listener); return result; } @Override public Request listener(Request.Listener listener) { return requestListener(listener); } @Override public Request onRequestQueued(final QueuedListener listener) { return requestListener(new QueuedListener() { @Override public void onQueued(Request request) { listener.onQueued(request); } }); } @Override public Request onRequestBegin(final BeginListener listener) { return requestListener(new BeginListener() { @Override public void onBegin(Request request) { listener.onBegin(request); } }); } @Override public Request onRequestHeaders(final HeadersListener listener) { return requestListener(new HeadersListener() { @Override public void onHeaders(Request request) { listener.onHeaders(request); } }); } @Override public Request onRequestCommit(final CommitListener listener) { return requestListener(new CommitListener() { @Override public void onCommit(Request request) { listener.onCommit(request); } }); } @Override public Request onRequestContent(final ContentListener listener) { return requestListener(new ContentListener() { @Override public void onContent(Request request, ByteBuffer content) { listener.onContent(request, content); } }); } @Override public Request onRequestSuccess(final SuccessListener listener) { return requestListener(new SuccessListener() { @Override public void onSuccess(Request request) { listener.onSuccess(request); } }); } @Override public Request onRequestFailure(final FailureListener listener) { return requestListener(new FailureListener() { @Override public void onFailure(Request request, Throwable failure) { listener.onFailure(request, failure); } }); } private Request requestListener(RequestListener listener) { if (requestListeners == null) requestListeners = new ArrayList<>(); requestListeners.add(listener); return this; } @Override public Request onResponseBegin(final Response.BeginListener listener) { this.responseListeners.add(new Response.BeginListener() { @Override public void onBegin(Response response) { listener.onBegin(response); } }); return this; } @Override public Request onResponseHeader(final Response.HeaderListener listener) { this.responseListeners.add(new Response.HeaderListener() { @Override public boolean onHeader(Response response, HttpField field) { return listener.onHeader(response, field); } }); return this; } @Override public Request onResponseHeaders(final Response.HeadersListener listener) { this.responseListeners.add(new Response.HeadersListener() { @Override public void onHeaders(Response response) { listener.onHeaders(response); } }); return this; } @Override public Request onResponseContent(final Response.ContentListener listener) { this.responseListeners.add(new Response.AsyncContentListener() { @Override public void onContent(Response response, ByteBuffer content, Callback callback) { try { listener.onContent(response, content); callback.succeeded(); } catch (Exception x) { callback.failed(x); } } }); return this; } @Override public Request onResponseContentAsync(final Response.AsyncContentListener listener) { this.responseListeners.add(new Response.AsyncContentListener() { @Override public void onContent(Response response, ByteBuffer content, Callback callback) { listener.onContent(response, content, callback); } }); return this; } @Override public Request onResponseSuccess(final Response.SuccessListener listener) { this.responseListeners.add(new Response.SuccessListener() { @Override public void onSuccess(Response response) { listener.onSuccess(response); } }); return this; } @Override public Request onResponseFailure(final Response.FailureListener listener) { this.responseListeners.add(new Response.FailureListener() { @Override public void onFailure(Response response, Throwable failure) { listener.onFailure(response, failure); } }); return this; } @Override public Request onComplete(final Response.CompleteListener listener) { this.responseListeners.add(new Response.CompleteListener() { @Override public void onComplete(Result result) { listener.onComplete(result); } }); return this; } @Override public ContentProvider getContent() { return content; } @Override public Request content(ContentProvider content) { return content(content, null); } @Override public Request content(ContentProvider content, String contentType) { if (contentType != null) header(HttpHeader.CONTENT_TYPE, contentType); this.content = content; return this; } @Override public Request file(Path file) throws IOException { return file(file, "application/octet-stream"); } @Override public Request file(Path file, String contentType) throws IOException { return content(new PathContentProvider(contentType, file)); } @Override public boolean isFollowRedirects() { return followRedirects; } @Override public Request followRedirects(boolean follow) { this.followRedirects = follow; return this; } @Override public long getIdleTimeout() { return idleTimeout; } @Override public Request idleTimeout(long timeout, TimeUnit unit) { this.idleTimeout = unit.toMillis(timeout); return this; } @Override public long getTimeout() { return timeout; } @Override public Request timeout(long timeout, TimeUnit unit) { this.timeout = unit.toMillis(timeout); return this; } @Override public ContentResponse send() throws InterruptedException, TimeoutException, ExecutionException { FutureResponseListener listener = new FutureResponseListener(this); send(this, listener); try { long timeout = getTimeout(); if (timeout <= 0) return listener.get(); return listener.get(timeout, TimeUnit.MILLISECONDS); } catch (Throwable x) { // Differently from the Future, the semantic of this method is that if // the send() is interrupted or times out, we abort the request. abort(x); throw x; } } @Override public void send(Response.CompleteListener listener) { TimeoutCompleteListener timeoutListener = null; try { if (getTimeout() > 0) { timeoutListener = new TimeoutCompleteListener(this); timeoutListener.schedule(client.getScheduler()); responseListeners.add(timeoutListener); } send(this, listener); } catch (Throwable x) { // Do not leak the scheduler task if we // can't even start sending the request. if (timeoutListener != null) timeoutListener.cancel(); throw x; } } private void send(HttpRequest request, Response.CompleteListener listener) { if (listener != null) responseListeners.add(listener); client.send(request, responseListeners); } @Override public boolean abort(Throwable cause) { if (aborted.compareAndSet(null, Objects.requireNonNull(cause))) { if (content instanceof Callback) ((Callback)content).failed(cause); return conversation.abort(cause); } return false; } @Override public Throwable getAbortCause() { return aborted.get(); } private String buildQuery() { StringBuilder result = new StringBuilder(); for (Iterator iterator = params.iterator(); iterator.hasNext(); ) { Fields.Field field = iterator.next(); List values = field.getValues(); for (int i = 0; i < values.size(); ++i) { if (i > 0) result.append("&"); result.append(field.getName()).append("="); result.append(urlEncode(values.get(i))); } if (iterator.hasNext()) result.append("&"); } return result.toString(); } private String urlEncode(String value) { if (value == null) return ""; String encoding = "UTF-8"; try { return URLEncoder.encode(value, encoding); } catch (UnsupportedEncodingException e) { throw new UnsupportedCharsetException(encoding); } } private void extractParams(String query) { if (query != null) { for (String nameValue : query.split("&")) { String[] parts = nameValue.split("="); if (parts.length > 0) { String name = urlDecode(parts[0]); if (name.trim().length() == 0) continue; param(name, parts.length < 2 ? "" : urlDecode(parts[1]), true); } } } } private String urlDecode(String value) { String charset = "UTF-8"; try { return URLDecoder.decode(value, charset); } catch (UnsupportedEncodingException x) { throw new UnsupportedCharsetException(charset); } } private URI buildURI(boolean withQuery) { String path = getPath(); String query = getQuery(); if (query != null && withQuery) path += "?" + query; URI result = newURI(path); if (result == null) return NULL_URI; if (!result.isAbsolute() && !result.isOpaque()) result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path); return result; } private URI newURI(String uri) { try { return new URI(uri); } catch (URISyntaxException x) { // The "path" of a HTTP request may not be a URI, // for example for CONNECT 127.0.0.1:8080 or OPTIONS *. return null; } } @Override public String toString() { return String.format("%s[%s %s %s]@%x", HttpRequest.class.getSimpleName(), getMethod(), getPath(), getVersion(), hashCode()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequestException.java000066400000000000000000000022221261716203600327450ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Request; public class HttpRequestException extends Throwable { private final Request request; public HttpRequestException(String message, Request request) { super(message); this.request = request; } public Request getRequest() { return request; } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpResponse.java000066400000000000000000000054301261716203600312400ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; public class HttpResponse implements Response { private final HttpFields headers = new HttpFields(); private final Request request; private final List listeners; private HttpVersion version; private int status; private String reason; public HttpResponse(Request request, List listeners) { this.request = request; this.listeners = listeners; } @Override public Request getRequest() { return request; } public HttpVersion getVersion() { return version; } public HttpResponse version(HttpVersion version) { this.version = version; return this; } @Override public int getStatus() { return status; } public HttpResponse status(int status) { this.status = status; return this; } public String getReason() { return reason; } public HttpResponse reason(String reason) { this.reason = reason; return this; } @Override public HttpFields getHeaders() { return headers; } @Override public List getListeners(Class type) { ArrayList result = new ArrayList<>(); for (ResponseListener listener : listeners) if (type == null || type.isInstance(listener)) result.add((T)listener); return result; } @Override public boolean abort(Throwable cause) { return request.abort(cause); } @Override public String toString() { return String.format("%s[%s %d %s]@%x", HttpResponse.class.getSimpleName(), getVersion(), getStatus(), getReason(), hashCode()); } } HttpResponseException.java000066400000000000000000000022451261716203600330410ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Response; public class HttpResponseException extends RuntimeException { private final Response response; public HttpResponseException(String message, Response response) { super(message); this.response = response; } public Response getResponse() { return response; } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/HttpSender.java000066400000000000000000000762541261716203600306760ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * {@link HttpSender} abstracts the algorithm to send HTTP requests, so that subclasses only implement * the transport-specific code to send requests over the wire, implementing * {@link #sendHeaders(HttpExchange, HttpContent, Callback)} and * {@link #sendContent(HttpExchange, HttpContent, Callback)}. *

* {@link HttpSender} governs two state machines. *

* The request state machine is updated by {@link HttpSender} as the various steps of sending a request * are executed, see {@link RequestState}. * At any point in time, a user thread may abort the request, which may (if the request has not been * completely sent yet) move the request state machine to {@link RequestState#FAILURE}. * The request state machine guarantees that the request steps are executed (by I/O threads) only if * the request has not been failed already. *

* The sender state machine is updated by {@link HttpSender} from three sources: deferred content notifications * (via {@link #onContent()}), 100-continue notifications (via {@link #proceed(HttpExchange, Throwable)}) * and normal request send (via {@link #sendContent(HttpExchange, HttpContent, Callback)}). * This state machine must guarantee that the request sending is never executed concurrently: only one of * those sources may trigger the call to {@link #sendContent(HttpExchange, HttpContent, Callback)}. * * @see HttpReceiver */ public abstract class HttpSender implements AsyncContentProvider.Listener { protected static final Logger LOG = Log.getLogger(HttpSender.class); private final AtomicReference requestState = new AtomicReference<>(RequestState.QUEUED); private final AtomicReference senderState = new AtomicReference<>(SenderState.IDLE); private final Callback commitCallback = new CommitCallback(); private final IteratingCallback contentCallback = new ContentCallback(); private final Callback lastCallback = new LastContentCallback(); private final HttpChannel channel; private HttpContent content; private Throwable failure; protected HttpSender(HttpChannel channel) { this.channel = channel; } protected HttpChannel getHttpChannel() { return channel; } protected HttpExchange getHttpExchange() { return channel.getHttpExchange(); } @Override public void onContent() { HttpExchange exchange = getHttpExchange(); if (exchange == null) return; while (true) { SenderState current = senderState.get(); switch (current) { case IDLE: { SenderState newSenderState = SenderState.SENDING; if (updateSenderState(current, newSenderState)) { if (LOG.isDebugEnabled()) LOG.debug("Deferred content available, {} -> {}", current, newSenderState); contentCallback.iterate(); return; } break; } case SENDING: { SenderState newSenderState = SenderState.SENDING_WITH_CONTENT; if (updateSenderState(current, newSenderState)) { if (LOG.isDebugEnabled()) LOG.debug("Deferred content available, {} -> {}", current, newSenderState); return; } break; } case EXPECTING: { SenderState newSenderState = SenderState.EXPECTING_WITH_CONTENT; if (updateSenderState(current, newSenderState)) { if (LOG.isDebugEnabled()) LOG.debug("Deferred content available, {} -> {}", current, newSenderState); return; } break; } case PROCEEDING: { SenderState newSenderState = SenderState.PROCEEDING_WITH_CONTENT; if (updateSenderState(current, newSenderState)) { if (LOG.isDebugEnabled()) LOG.debug("Deferred content available, {} -> {}", current, newSenderState); return; } break; } case SENDING_WITH_CONTENT: case EXPECTING_WITH_CONTENT: case PROCEEDING_WITH_CONTENT: case WAITING: case COMPLETED: case FAILED: { if (LOG.isDebugEnabled()) LOG.debug("Deferred content available, {}", current); return; } default: { illegalSenderState(current); return; } } } } public void send(HttpExchange exchange) { if (!queuedToBegin(exchange)) return; Request request = exchange.getRequest(); ContentProvider contentProvider = request.getContent(); HttpContent content = this.content = new HttpContent(contentProvider); SenderState newSenderState = SenderState.SENDING; if (expects100Continue(request)) newSenderState = content.hasContent() ? SenderState.EXPECTING_WITH_CONTENT : SenderState.EXPECTING; out: while (true) { SenderState current = senderState.get(); switch (current) { case IDLE: case COMPLETED: { if (updateSenderState(current, newSenderState)) break out; break; } default: { illegalSenderState(current); return; } } } // Setting the listener may trigger calls to onContent() by other // threads so we must set it only after the sender state has been updated if (contentProvider instanceof AsyncContentProvider) ((AsyncContentProvider)contentProvider).setListener(this); if (!beginToHeaders(exchange)) return; sendHeaders(exchange, content, commitCallback); } protected boolean expects100Continue(Request request) { return request.getHeaders().contains(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()); } protected boolean queuedToBegin(HttpExchange exchange) { if (!updateRequestState(RequestState.QUEUED, RequestState.TRANSIENT)) return false; Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request begin {}", request); RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier(); notifier.notifyBegin(request); if (updateRequestState(RequestState.TRANSIENT, RequestState.BEGIN)) return true; terminateRequest(exchange); return false; } protected boolean beginToHeaders(HttpExchange exchange) { if (!updateRequestState(RequestState.BEGIN, RequestState.TRANSIENT)) return false; Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request headers {}{}{}", request, System.lineSeparator(), request.getHeaders().toString().trim()); RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier(); notifier.notifyHeaders(request); if (updateRequestState(RequestState.TRANSIENT, RequestState.HEADERS)) return true; terminateRequest(exchange); return false; } protected boolean headersToCommit(HttpExchange exchange) { if (!updateRequestState(RequestState.HEADERS, RequestState.TRANSIENT)) return false; Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request committed {}", request); RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier(); notifier.notifyCommit(request); if (updateRequestState(RequestState.TRANSIENT, RequestState.COMMIT)) return true; terminateRequest(exchange); return false; } protected boolean someToContent(HttpExchange exchange, ByteBuffer content) { RequestState current = requestState.get(); switch (current) { case COMMIT: case CONTENT: { if (!updateRequestState(current, RequestState.TRANSIENT)) return false; Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request content {}{}{}", request, System.lineSeparator(), BufferUtil.toDetailString(content)); RequestNotifier notifier = getHttpChannel().getHttpDestination().getRequestNotifier(); notifier.notifyContent(request, content); if (updateRequestState(RequestState.TRANSIENT, RequestState.CONTENT)) return true; terminateRequest(exchange); return false; } default: { return false; } } } protected boolean someToSuccess(HttpExchange exchange) { RequestState current = requestState.get(); switch (current) { case COMMIT: case CONTENT: { // Mark atomically the request as completed, with respect // to concurrency between request success and request failure. if (!exchange.requestComplete(null)) return false; requestState.set(RequestState.QUEUED); // Reset to be ready for another request. reset(); Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request success {}", request); HttpDestination destination = getHttpChannel().getHttpDestination(); destination.getRequestNotifier().notifySuccess(exchange.getRequest()); // Mark atomically the request as terminated, with // respect to concurrency between request and response. Result result = exchange.terminateRequest(); terminateRequest(exchange, null, result); return true; } default: { return false; } } } protected boolean anyToFailure(Throwable failure) { HttpExchange exchange = getHttpExchange(); if (exchange == null) return false; // Mark atomically the request as completed, with respect // to concurrency between request success and request failure. if (exchange.requestComplete(failure)) return abort(exchange, failure); return false; } private void terminateRequest(HttpExchange exchange) { // In abort(), the state is updated before the failure is recorded // to avoid to overwrite it, so here we may read a null failure. Throwable failure = this.failure; if (failure == null) failure = new HttpRequestException("Concurrent failure", exchange.getRequest()); Result result = exchange.terminateRequest(); terminateRequest(exchange, failure, result); } private void terminateRequest(HttpExchange exchange, Throwable failure, Result result) { Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Terminating request {}", request); if (result == null) { if (failure != null) { if (exchange.responseComplete(failure)) { if (LOG.isDebugEnabled()) LOG.debug("Response failure from request {} {}", request, exchange); getHttpChannel().abortResponse(exchange, failure); } } } else { HttpDestination destination = getHttpChannel().getHttpDestination(); boolean ordered = destination.getHttpClient().isStrictEventOrdering(); if (!ordered) channel.exchangeTerminated(exchange, result); if (LOG.isDebugEnabled()) LOG.debug("Request/Response {}: {}", failure == null ? "succeeded" : "failed", result); HttpConversation conversation = exchange.getConversation(); destination.getResponseNotifier().notifyComplete(conversation.getResponseListeners(), result); if (ordered) channel.exchangeTerminated(exchange, result); } } /** * Implementations should send the HTTP headers over the wire, possibly with some content, * in a single write, and notify the given {@code callback} of the result of this operation. *

* If there is more content to send, then {@link #sendContent(HttpExchange, HttpContent, Callback)} * will be invoked. * * @param exchange the exchange to send * @param content the content to send * @param callback the callback to notify */ protected abstract void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback); /** * Implementations should send the content at the {@link HttpContent} cursor position over the wire. *

* The {@link HttpContent} cursor is advanced by {@link HttpSender} at the right time, and if more * content needs to be sent, this method is invoked again; subclasses need only to send the content * at the {@link HttpContent} cursor position. *

* This method is invoked one last time when {@link HttpContent#isConsumed()} is true and therefore * there is no actual content to send. * This is done to allow subclasses to write "terminal" bytes (such as the terminal chunk when the * transfer encoding is chunked) if their protocol needs to. * * @param exchange the exchange to send * @param content the content to send * @param callback the callback to notify */ protected abstract void sendContent(HttpExchange exchange, HttpContent content, Callback callback); protected void reset() { HttpContent content = this.content; this.content = null; content.close(); senderState.set(SenderState.COMPLETED); } protected void dispose() { HttpContent content = this.content; this.content = null; if (content != null) content.close(); senderState.set(SenderState.FAILED); } public void proceed(HttpExchange exchange, Throwable failure) { if (!expects100Continue(exchange.getRequest())) return; if (failure != null) { anyToFailure(failure); return; } while (true) { SenderState current = senderState.get(); switch (current) { case EXPECTING: { // We are still sending the headers, but we already got the 100 Continue. if (updateSenderState(current, SenderState.PROCEEDING)) { if (LOG.isDebugEnabled()) LOG.debug("Proceeding while expecting"); return; } break; } case EXPECTING_WITH_CONTENT: { // More deferred content was submitted to onContent(), we already // got the 100 Continue, but we may be still sending the headers // (for example, with SSL we may have sent the encrypted data, // received the 100 Continue but not yet updated the decrypted // WriteFlusher so sending more content now may result in a // WritePendingException). if (updateSenderState(current, SenderState.PROCEEDING_WITH_CONTENT)) { if (LOG.isDebugEnabled()) LOG.debug("Proceeding while scheduled"); return; } break; } case WAITING: { // We received the 100 Continue, now send the content if any. if (updateSenderState(current, SenderState.SENDING)) { if (LOG.isDebugEnabled()) LOG.debug("Proceeding while waiting"); contentCallback.iterate(); return; } break; } case FAILED: { return; } default: { illegalSenderState(current); return; } } } } public boolean abort(HttpExchange exchange, Throwable failure) { // Update the state to avoid more request processing. boolean terminate; out: while (true) { RequestState current = requestState.get(); switch (current) { case FAILURE: { return false; } default: { if (updateRequestState(current, RequestState.FAILURE)) { terminate = current != RequestState.TRANSIENT; break out; } break; } } } this.failure = failure; dispose(); Request request = exchange.getRequest(); if (LOG.isDebugEnabled()) LOG.debug("Request failure {} {} on {}: {}", request, exchange, getHttpChannel(), failure); HttpDestination destination = getHttpChannel().getHttpDestination(); destination.getRequestNotifier().notifyFailure(request, failure); if (terminate) { // Mark atomically the request as terminated, with // respect to concurrency between request and response. Result result = exchange.terminateRequest(); terminateRequest(exchange, failure, result); } else { if (LOG.isDebugEnabled()) LOG.debug("Concurrent failure: request termination skipped, performed by helpers"); } return true; } private boolean updateRequestState(RequestState from, RequestState to) { boolean updated = requestState.compareAndSet(from, to); if (!updated && LOG.isDebugEnabled()) LOG.debug("RequestState update failed: {} -> {}: {}", from, to, requestState.get()); return updated; } private boolean updateSenderState(SenderState from, SenderState to) { boolean updated = senderState.compareAndSet(from, to); if (!updated && LOG.isDebugEnabled()) LOG.debug("SenderState update failed: {} -> {}: {}", from, to, senderState.get()); return updated; } private void illegalSenderState(SenderState current) { anyToFailure(new IllegalStateException("Expected " + current + " found " + senderState.get() + " instead")); } @Override public String toString() { return String.format("%s@%x(req=%s,snd=%s,failure=%s)", getClass().getSimpleName(), hashCode(), requestState, senderState, failure); } /** * The request states {@link HttpSender} goes through when sending a request. */ private enum RequestState { /** * One of the state transition methods is being executed. */ TRANSIENT, /** * The request is queued, the initial state */ QUEUED, /** * The request has been dequeued */ BEGIN, /** * The request headers (and possibly some content) is about to be sent */ HEADERS, /** * The request headers (and possibly some content) have been sent */ COMMIT, /** * The request content is being sent */ CONTENT, /** * The request is failed */ FAILURE } /** * The sender states {@link HttpSender} goes through when sending a request. */ private enum SenderState { /** * {@link HttpSender} is not sending request headers nor request content */ IDLE, /** * {@link HttpSender} is sending the request header or request content */ SENDING, /** * {@link HttpSender} is currently sending the request, and deferred content is available to be sent */ SENDING_WITH_CONTENT, /** * {@link HttpSender} is sending the headers but will wait for 100 Continue before sending the content */ EXPECTING, /** * {@link HttpSender} is currently sending the headers, will wait for 100 Continue, and deferred content is available to be sent */ EXPECTING_WITH_CONTENT, /** * {@link HttpSender} has sent the headers and is waiting for 100 Continue */ WAITING, /** * {@link HttpSender} is sending the headers, while 100 Continue has arrived */ PROCEEDING, /** * {@link HttpSender} is sending the headers, while 100 Continue has arrived, and deferred content is available to be sent */ PROCEEDING_WITH_CONTENT, /** * {@link HttpSender} has finished to send the request */ COMPLETED, /** * {@link HttpSender} has failed to send the request */ FAILED } private class CommitCallback implements Callback { @Override public void succeeded() { try { HttpContent content = HttpSender.this.content; if (content == null) return; content.succeeded(); process(); } catch (Throwable x) { anyToFailure(x); } } @Override public void failed(Throwable failure) { HttpContent content = HttpSender.this.content; if (content == null) return; content.failed(failure); anyToFailure(failure); } private void process() throws Exception { HttpExchange exchange = getHttpExchange(); if (exchange == null) return; if (!headersToCommit(exchange)) return; HttpContent content = HttpSender.this.content; if (content == null) return; if (!content.hasContent()) { // No content to send, we are done. someToSuccess(exchange); } else { // Was any content sent while committing ? ByteBuffer contentBuffer = content.getContent(); if (contentBuffer != null) { if (!someToContent(exchange, contentBuffer)) return; } while (true) { SenderState current = senderState.get(); switch (current) { case SENDING: { contentCallback.iterate(); return; } case SENDING_WITH_CONTENT: { // We have deferred content to send. updateSenderState(current, SenderState.SENDING); break; } case EXPECTING: { // We sent the headers, wait for the 100 Continue response. if (updateSenderState(current, SenderState.WAITING)) return; break; } case EXPECTING_WITH_CONTENT: { // We sent the headers, we have deferred content to send, // wait for the 100 Continue response. if (updateSenderState(current, SenderState.WAITING)) return; break; } case PROCEEDING: { // We sent the headers, we have the 100 Continue response, // we have no content to send. if (updateSenderState(current, SenderState.IDLE)) return; break; } case PROCEEDING_WITH_CONTENT: { // We sent the headers, we have the 100 Continue response, // we have deferred content to send. updateSenderState(current, SenderState.SENDING); break; } case FAILED: { return; } default: { illegalSenderState(current); return; } } } } } } private class ContentCallback extends IteratingCallback { @Override protected Action process() throws Exception { HttpExchange exchange = getHttpExchange(); if (exchange == null) return Action.IDLE; HttpContent content = HttpSender.this.content; if (content == null) return Action.IDLE; while (true) { boolean advanced = content.advance(); boolean consumed = content.isConsumed(); if (LOG.isDebugEnabled()) LOG.debug("Content {} consumed {} for {}", advanced, consumed, exchange.getRequest()); if (advanced) { sendContent(exchange, content, this); return Action.SCHEDULED; } if (consumed) { sendContent(exchange, content, lastCallback); return Action.IDLE; } SenderState current = senderState.get(); switch (current) { case SENDING: { if (updateSenderState(current, SenderState.IDLE)) { if (LOG.isDebugEnabled()) LOG.debug("Content is deferred for {}", exchange.getRequest()); return Action.IDLE; } break; } case SENDING_WITH_CONTENT: { updateSenderState(current, SenderState.SENDING); break; } default: { illegalSenderState(current); return Action.IDLE; } } } } @Override public void succeeded() { HttpExchange exchange = getHttpExchange(); if (exchange == null) return; HttpContent content = HttpSender.this.content; if (content == null) return; content.succeeded(); ByteBuffer buffer = content.getContent(); someToContent(exchange, buffer); super.succeeded(); } @Override public void onCompleteFailure(Throwable failure) { HttpContent content = HttpSender.this.content; if (content == null) return; content.failed(failure); anyToFailure(failure); } @Override protected void onCompleteSuccess() { // Nothing to do, since we always return false from process(). // Termination is obtained via LastContentCallback. } } private class LastContentCallback implements Callback { @Override public void succeeded() { HttpExchange exchange = getHttpExchange(); if (exchange == null) return; HttpContent content = HttpSender.this.content; if (content == null) return; content.succeeded(); someToSuccess(exchange); } @Override public void failed(Throwable failure) { HttpContent content = HttpSender.this.content; if (content == null) return; content.failed(failure); anyToFailure(failure); } } } LeakTrackingConnectionPool.java000066400000000000000000000051451261716203600337370ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.util.LeakDetector; import org.eclipse.jetty.util.Promise; public class LeakTrackingConnectionPool extends ConnectionPool { private final LeakDetector leakDetector = new LeakDetector() { @Override protected void leaked(LeakInfo leakInfo) { LeakTrackingConnectionPool.this.leaked(leakInfo); } }; public LeakTrackingConnectionPool(Destination destination, int maxConnections, Promise connectionPromise) { super(destination, maxConnections, connectionPromise); start(); } private void start() { try { leakDetector.start(); } catch (Exception x) { throw new RuntimeException(x); } } @Override public void close() { stop(); super.close(); } private void stop() { try { leakDetector.stop(); } catch (Exception x) { throw new RuntimeException(x); } } @Override protected void acquired(Connection connection) { if (!leakDetector.acquired(connection)) LOG.info("Connection {}@{} not tracked", connection, leakDetector.id(connection)); } @Override protected void released(Connection connection) { if (!leakDetector.released(connection)) LOG.info("Connection {}@{} released but not acquired", connection, leakDetector.id(connection)); } protected void leaked(LeakDetector.LeakInfo leakInfo) { LOG.info("Connection " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames()); } } MultiplexHttpDestination.java000066400000000000000000000116351261716203600335540ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.util.Promise; public abstract class MultiplexHttpDestination extends HttpDestination implements Promise { private final AtomicReference connect = new AtomicReference<>(ConnectState.DISCONNECTED); private C connection; protected MultiplexHttpDestination(HttpClient client, Origin origin) { super(client, origin); } @Override protected void send() { while (true) { ConnectState current = connect.get(); switch (current) { case DISCONNECTED: { if (!connect.compareAndSet(current, ConnectState.CONNECTING)) break; newConnection(this); return; } case CONNECTING: { // Waiting to connect, just return return; } case CONNECTED: { if (process(connection, false)) break; return; } default: { abort(new IllegalStateException("Invalid connection state " + current)); return; } } } } @Override @SuppressWarnings("unchecked") public void succeeded(Connection result) { C connection = this.connection = (C)result; if (connect.compareAndSet(ConnectState.CONNECTING, ConnectState.CONNECTED)) { process(connection, true); } else { connection.close(); failed(new IllegalStateException()); } } @Override public void failed(Throwable x) { connect.set(ConnectState.DISCONNECTED); } protected boolean process(final C connection, boolean dispatch) { HttpClient client = getHttpClient(); final HttpExchange exchange = getHttpExchanges().poll(); if (LOG.isDebugEnabled()) LOG.debug("Processing {} on {}", exchange, connection); if (exchange == null) return false; final Request request = exchange.getRequest(); Throwable cause = request.getAbortCause(); if (cause != null) { if (LOG.isDebugEnabled()) LOG.debug("Aborted before processing {}: {}", exchange, cause); // It may happen that the request is aborted before the exchange // is created. Aborting the exchange a second time will result in // a no-operation, so we just abort here to cover that edge case. exchange.abort(cause); } else { if (dispatch) { client.getExecutor().execute(new Runnable() { @Override public void run() { send(connection, exchange); } }); } else { send(connection, exchange); } } return true; } @Override public void close() { super.close(); C connection = this.connection; if (connection != null) connection.close(); } @Override public void close(Connection connection) { super.close(connection); while (true) { ConnectState current = connect.get(); if (connect.compareAndSet(current, ConnectState.DISCONNECTED)) { if (getHttpClient().isRemoveIdleDestinations()) getHttpClient().removeDestination(this); break; } } } protected abstract void send(C connection, HttpExchange exchange); private enum ConnectState { DISCONNECTED, CONNECTING, CONNECTED } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java000066400000000000000000000061301261716203600300270ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.Objects; import org.eclipse.jetty.util.URIUtil; public class Origin { private final String scheme; private final Address address; public Origin(String scheme, String host, int port) { this(scheme, new Address(host, port)); } public Origin(String scheme, Address address) { this.scheme = Objects.requireNonNull(scheme); this.address = address; } public String getScheme() { return scheme; } public Address getAddress() { return address; } public String asString() { StringBuilder result = new StringBuilder(); URIUtil.appendSchemeHostPort(result, scheme, address.host, address.port); return result.toString(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Origin that = (Origin)obj; return scheme.equals(that.scheme) && address.equals(that.address); } @Override public int hashCode() { int result = scheme.hashCode(); result = 31 * result + address.hashCode(); return result; } public static class Address { private final String host; private final int port; public Address(String host, int port) { this.host = Objects.requireNonNull(host); this.port = port; } public String getHost() { return host; } public int getPort() { return port; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; Address that = (Address)obj; return host.equals(that.host) && port == that.port; } @Override public int hashCode() { int result = host.hashCode(); result = 31 * result + port; return result; } public String asString() { return String.format("%s:%d", host, port); } @Override public String toString() { return asString(); } } } PoolingHttpDestination.java000066400000000000000000000166551261716203600332070ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.util.Arrays; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.thread.Sweeper; public abstract class PoolingHttpDestination extends HttpDestination implements Promise { private final ConnectionPool connectionPool; public PoolingHttpDestination(HttpClient client, Origin origin) { super(client, origin); this.connectionPool = newConnectionPool(client); Sweeper sweeper = client.getBean(Sweeper.class); if (sweeper != null) sweeper.offer(connectionPool); } protected ConnectionPool newConnectionPool(HttpClient client) { return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this); } public ConnectionPool getConnectionPool() { return connectionPool; } @Override @SuppressWarnings("unchecked") public void succeeded(Connection connection) { send(true); } @Override public void failed(final Throwable x) { getHttpClient().getExecutor().execute(new Runnable() { @Override public void run() { abort(x); } }); } @Override protected void send() { send(false); } private void send(boolean dispatch) { if (getHttpExchanges().isEmpty()) return; C connection = acquire(); if (connection != null) process(connection, dispatch); } @SuppressWarnings("unchecked") public C acquire() { return (C)connectionPool.acquire(); } /** *

Processes a new connection making it idle or active depending on whether requests are waiting to be sent.

*

A new connection is created when a request needs to be executed; it is possible that the request that * triggered the request creation is executed by another connection that was just released, so the new connection * may become idle.

*

If a request is waiting to be executed, it will be dequeued and executed by the new connection.

* * @param connection the new connection * @param dispatch whether to dispatch the processing to another thread */ public void process(final C connection, boolean dispatch) { HttpClient client = getHttpClient(); final HttpExchange exchange = getHttpExchanges().poll(); if (LOG.isDebugEnabled()) LOG.debug("Processing exchange {} on {} of {}", exchange, connection, this); if (exchange == null) { if (!connectionPool.release(connection)) connection.close(); if (!client.isRunning()) { if (LOG.isDebugEnabled()) LOG.debug("{} is stopping", client); connection.close(); } } else { final Request request = exchange.getRequest(); Throwable cause = request.getAbortCause(); if (cause != null) { if (LOG.isDebugEnabled()) LOG.debug("Aborted before processing {}: {}", exchange, cause); // It may happen that the request is aborted before the exchange // is created. Aborting the exchange a second time will result in // a no-operation, so we just abort here to cover that edge case. exchange.abort(cause); } else { if (dispatch) { client.getExecutor().execute(new Runnable() { @Override public void run() { send(connection, exchange); } }); } else { send(connection, exchange); } } } } protected abstract void send(C connection, HttpExchange exchange); @Override public void release(Connection c) { @SuppressWarnings("unchecked") C connection = (C)c; if (LOG.isDebugEnabled()) LOG.debug("{} released", connection); HttpClient client = getHttpClient(); if (client.isRunning()) { if (connectionPool.isActive(connection)) { process(connection, false); } else { if (LOG.isDebugEnabled()) LOG.debug("{} explicit", connection); } } else { if (LOG.isDebugEnabled()) LOG.debug("{} is stopped", client); connection.close(); } } @Override public void close(Connection oldConnection) { super.close(oldConnection); connectionPool.remove(oldConnection); if (getHttpExchanges().isEmpty()) { if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty()) { // There is a race condition between this thread removing the destination // and another thread queueing a request to this same destination. // If this destination is removed, but the request queued, a new connection // will be opened, the exchange will be executed and eventually the connection // will idle timeout and be closed. Meanwhile a new destination will be created // in HttpClient and will be used for other requests. getHttpClient().removeDestination(this); } } else { // We need to execute queued requests even if this connection failed. // We may create a connection that is not needed, but it will eventually // idle timeout, so no worries. C newConnection = acquire(); if (newConnection != null) process(newConnection, false); } } public void close() { super.close(); connectionPool.close(); } @Override public void dump(Appendable out, String indent) throws IOException { super.dump(out, indent); ContainerLifeCycle.dump(out, indent, Arrays.asList(connectionPool)); } @Override public String toString() { return String.format("%s,pool=%s", super.toString(), connectionPool); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/ProtocolHandler.java000066400000000000000000000020631261716203600317000ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; public interface ProtocolHandler { public boolean accept(Request request, Response response); public Response.Listener getResponseListener(); } ProxyAuthenticationProtocolHandler.java000066400000000000000000000041451261716203600355660ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler { public ProxyAuthenticationProtocolHandler(HttpClient client) { this(client, DEFAULT_MAX_CONTENT_LENGTH); } public ProxyAuthenticationProtocolHandler(HttpClient client, int maxContentLength) { super(client, maxContentLength); } @Override public boolean accept(Request request, Response response) { return response.getStatus() == HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407; } @Override protected HttpHeader getAuthenticateHeader() { return HttpHeader.PROXY_AUTHENTICATE; } @Override protected HttpHeader getAuthorizationHeader() { return HttpHeader.PROXY_AUTHORIZATION; } @Override protected URI getAuthenticationURI(Request request) { HttpDestination destination = getHttpClient().destinationFor(request.getScheme(), request.getHost(), request.getPort()); ProxyConfiguration.Proxy proxy = destination.getProxy(); return proxy != null ? proxy.getURI() : request.getURI(); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java000066400000000000000000000120341261716203600324510ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.eclipse.jetty.io.ClientConnectionFactory; /** * The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}. *

* Applications add subclasses of {@link Proxy} to this configuration via: *

 * ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
 * proxyConfig.getProxies().add(new HttpProxy(proxyHost, 8080));
 * 
* * @see HttpClient#getProxyConfiguration() */ public class ProxyConfiguration { private final List proxies = new ArrayList<>(); public List getProxies() { return proxies; } public Proxy match(Origin origin) { for (Proxy proxy : getProxies()) { if (proxy.matches(origin)) return proxy; } return null; } public static abstract class Proxy { private final Set included = new HashSet<>(); private final Set excluded = new HashSet<>(); private final Origin.Address address; private final boolean secure; protected Proxy(Origin.Address address, boolean secure) { this.address = address; this.secure = secure; } /** * @return the address of this proxy */ public Origin.Address getAddress() { return address; } /** * @return whether the connection to the proxy must be secured via TLS */ public boolean isSecure() { return secure; } /** * @return the list of origins that must be proxied * @see #matches(Origin) * @see #getExcludedAddresses() */ public Set getIncludedAddresses() { return included; } /** * @return the list of origins that must not be proxied. * @see #matches(Origin) * @see #getIncludedAddresses() */ public Set getExcludedAddresses() { return excluded; } /** * @return an URI representing this proxy, or null if no URI can represent this proxy */ public URI getURI() { return null; } /** * Matches the given {@code origin} with the included and excluded addresses, * returning true if the given {@code origin} is to be proxied. * * @param origin the origin to test for proxying * @return true if the origin must be proxied, false otherwise */ public boolean matches(Origin origin) { boolean result = included.isEmpty(); Origin.Address address = origin.getAddress(); for (String included : this.included) { if (matches(address, included)) { result = true; break; } } for (String excluded : this.excluded) { if (matches(address, excluded)) { result = false; break; } } return result; } private boolean matches(Origin.Address address, String pattern) { // TODO: add support for CIDR notation like 192.168.0.0/24, see DoSFilter int colon = pattern.indexOf(':'); if (colon < 0) return pattern.equals(address.getHost()); String host = pattern.substring(0, colon); String port = pattern.substring(colon + 1); return host.equals(address.getHost()) && port.equals(String.valueOf(address.getPort())); } /** * @param connectionFactory the nested {@link ClientConnectionFactory} * @return a new {@link ClientConnectionFactory} for this {@link Proxy} */ public abstract ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory); @Override public String toString() { return address.toString(); } } } RedirectProtocolHandler.java000066400000000000000000000034541261716203600333100ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; public class RedirectProtocolHandler extends Response.Listener.Adapter implements ProtocolHandler { private final HttpRedirector redirector; public RedirectProtocolHandler(HttpClient client) { redirector = new HttpRedirector(client); } @Override public boolean accept(Request request, Response response) { return redirector.isRedirect(response) && request.isFollowRedirects(); } @Override public Response.Listener getResponseListener() { return this; } @Override public void onComplete(Result result) { Request request = result.getRequest(); Response response = result.getResponse(); if (result.isSucceeded()) redirector.redirect(request, response, null); else redirector.fail(request, response, result.getFailure()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/RequestNotifier.java000066400000000000000000000220221261716203600317260ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; import java.util.List; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class RequestNotifier { private static final Logger LOG = Log.getLogger(ResponseNotifier.class); private final HttpClient client; public RequestNotifier(HttpClient client) { this.client = client; } public void notifyQueued(Request request) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.QueuedListener) notifyQueued((Request.QueuedListener)listener, request); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifyQueued(listener, request); } } private void notifyQueued(Request.QueuedListener listener, Request request) { try { listener.onQueued(request); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyBegin(Request request) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.BeginListener) notifyBegin((Request.BeginListener)listener, request); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifyBegin(listener, request); } } private void notifyBegin(Request.BeginListener listener, Request request) { try { listener.onBegin(request); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyHeaders(Request request) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.HeadersListener) notifyHeaders((Request.HeadersListener)listener, request); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifyHeaders(listener, request); } } private void notifyHeaders(Request.HeadersListener listener, Request request) { try { listener.onHeaders(request); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyCommit(Request request) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.CommitListener) notifyCommit((Request.CommitListener)listener, request); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifyCommit(listener, request); } } private void notifyCommit(Request.CommitListener listener, Request request) { try { listener.onCommit(request); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyContent(Request request, ByteBuffer content) { // Slice the buffer to avoid that listeners peek into data they should not look at. content = content.slice(); if (!content.hasRemaining()) return; // Optimized to avoid allocations of iterator instances. List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.ContentListener) { // The buffer was sliced, so we always clear it (position=0, limit=capacity) // before passing it to the listener that may consume it. content.clear(); notifyContent((Request.ContentListener)listener, request, content); } } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); // The buffer was sliced, so we always clear it (position=0, limit=capacity) // before passing it to the listener that may consume it. content.clear(); notifyContent(listener, request, content); } } private void notifyContent(Request.ContentListener listener, Request request, ByteBuffer content) { try { listener.onContent(request, content); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifySuccess(Request request) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.SuccessListener) notifySuccess((Request.SuccessListener)listener, request); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifySuccess(listener, request); } } private void notifySuccess(Request.SuccessListener listener, Request request) { try { listener.onSuccess(request); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyFailure(Request request, Throwable failure) { // Optimized to avoid allocations of iterator instances List requestListeners = request.getRequestListeners(null); for (int i = 0; i < requestListeners.size(); ++i) { Request.RequestListener listener = requestListeners.get(i); if (listener instanceof Request.FailureListener) notifyFailure((Request.FailureListener)listener, request, failure); } List listeners = client.getRequestListeners(); for (int i = 0; i < listeners.size(); ++i) { Request.Listener listener = listeners.get(i); notifyFailure(listener, request, failure); } } private void notifyFailure(Request.FailureListener listener, Request request, Throwable failure) { try { listener.onFailure(request, failure); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java000066400000000000000000000245701261716203600321060ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.List; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IteratingNestedCallback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class ResponseNotifier { private static final Logger LOG = Log.getLogger(ResponseNotifier.class); public void notifyBegin(List listeners, Response response) { // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.BeginListener) notifyBegin((Response.BeginListener)listener, response); } } private void notifyBegin(Response.BeginListener listener, Response response) { try { listener.onBegin(response); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public boolean notifyHeader(List listeners, Response response, HttpField field) { boolean result = true; // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.HeaderListener) result &= notifyHeader((Response.HeaderListener)listener, response, field); } return result; } private boolean notifyHeader(Response.HeaderListener listener, Response response, HttpField field) { try { return listener.onHeader(response, field); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); return false; } } public void notifyHeaders(List listeners, Response response) { // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.HeadersListener) notifyHeaders((Response.HeadersListener)listener, response); } } private void notifyHeaders(Response.HeadersListener listener, Response response) { try { listener.onHeaders(response); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyContent(List listeners, Response response, ByteBuffer buffer, Callback callback) { // Here we use an IteratingNestedCallback not to avoid the stack overflow, but to // invoke the listeners one after the other. When all of them have invoked the // callback they got passed, the callback passed to this method is finally invoked. ContentCallback contentCallback = new ContentCallback(listeners, response, buffer, callback); contentCallback.iterate(); } private void notifyContent(Response.AsyncContentListener listener, Response response, ByteBuffer buffer, Callback callback) { try { listener.onContent(response, buffer, callback); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifySuccess(List listeners, Response response) { // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.SuccessListener) notifySuccess((Response.SuccessListener)listener, response); } } private void notifySuccess(Response.SuccessListener listener, Response response) { try { listener.onSuccess(response); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyFailure(List listeners, Response response, Throwable failure) { // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.FailureListener) notifyFailure((Response.FailureListener)listener, response, failure); } } private void notifyFailure(Response.FailureListener listener, Response response, Throwable failure) { try { listener.onFailure(response, failure); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void notifyComplete(List listeners, Result result) { // Optimized to avoid allocations of iterator instances for (int i = 0; i < listeners.size(); ++i) { Response.ResponseListener listener = listeners.get(i); if (listener instanceof Response.CompleteListener) notifyComplete((Response.CompleteListener)listener, result); } } private void notifyComplete(Response.CompleteListener listener, Result result) { try { listener.onComplete(result); } catch (Throwable x) { LOG.info("Exception while notifying listener " + listener, x); } } public void forwardSuccess(List listeners, Response response) { notifyBegin(listeners, response); for (Iterator iterator = response.getHeaders().iterator(); iterator.hasNext();) { HttpField field = iterator.next(); if (!notifyHeader(listeners, response, field)) iterator.remove(); } notifyHeaders(listeners, response); if (response instanceof ContentResponse) // TODO: handle callback notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter()); notifySuccess(listeners, response); } public void forwardSuccessComplete(List listeners, Request request, Response response) { forwardSuccess(listeners, response); notifyComplete(listeners, new Result(request, response)); } public void forwardFailure(List listeners, Response response, Throwable failure) { notifyBegin(listeners, response); for (Iterator iterator = response.getHeaders().iterator(); iterator.hasNext();) { HttpField field = iterator.next(); if (!notifyHeader(listeners, response, field)) iterator.remove(); } notifyHeaders(listeners, response); if (response instanceof ContentResponse) // TODO: handle callback notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), new Callback.Adapter()); notifyFailure(listeners, response, failure); } public void forwardFailureComplete(List listeners, Request request, Throwable requestFailure, Response response, Throwable responseFailure) { forwardFailure(listeners, response, responseFailure); notifyComplete(listeners, new Result(request, requestFailure, response, responseFailure)); } private class ContentCallback extends IteratingNestedCallback { private final List listeners; private final Response response; private final ByteBuffer buffer; private int index; private ContentCallback(List listeners, Response response, ByteBuffer buffer, Callback callback) { super(callback); this.listeners = listeners; this.response = response; // Slice the buffer to avoid that listeners peek into data they should not look at. this.buffer = buffer.slice(); } @Override protected Action process() throws Exception { if (index == listeners.size()) return Action.SUCCEEDED; Response.ResponseListener listener = listeners.get(index); if (listener instanceof Response.AsyncContentListener) { // The buffer was sliced, so we always clear it // (clear => position=0, limit=capacity) before // passing it to the listener that may consume it. buffer.clear(); ResponseNotifier.this.notifyContent((Response.AsyncContentListener)listener, response, buffer, this); return Action.SCHEDULED; } else { succeeded(); return Action.SCHEDULED; } } @Override public void succeeded() { ++index; super.succeeded(); } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java000066400000000000000000000213741261716203600310170ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.concurrent.Executor; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class Socks4Proxy extends ProxyConfiguration.Proxy { public Socks4Proxy(String host, int port) { this(new Origin.Address(host, port), false); } public Socks4Proxy(Origin.Address address, boolean secure) { super(address, secure); } @Override public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory) { return new Socks4ProxyClientConnectionFactory(connectionFactory); } public static class Socks4ProxyClientConnectionFactory implements ClientConnectionFactory { private final ClientConnectionFactory connectionFactory; public Socks4ProxyClientConnectionFactory(ClientConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } @Override public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException { HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); Executor executor = destination.getHttpClient().getExecutor(); return new Socks4ProxyConnection(endPoint, executor, connectionFactory, context); } } private static class Socks4ProxyConnection extends AbstractConnection implements Callback { private static final Pattern IPv4_PATTERN = Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})"); private static final Logger LOG = Log.getLogger(Socks4ProxyConnection.class); private final Socks4Parser parser = new Socks4Parser(); private final ClientConnectionFactory connectionFactory; private final Map context; public Socks4ProxyConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map context) { super(endPoint, executor); this.connectionFactory = connectionFactory; this.context = context; } @Override public void onOpen() { super.onOpen(); writeSocks4Connect(); } /** * Writes the SOCKS "connect" bytes, differentiating between SOCKS 4 and 4A; * the former sends an IPv4 address, the latter the full domain name. */ private void writeSocks4Connect() { HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); String host = destination.getHost(); short port = (short)destination.getPort(); Matcher matcher = IPv4_PATTERN.matcher(host); if (matcher.matches()) { // SOCKS 4 ByteBuffer buffer = ByteBuffer.allocate(9); buffer.put((byte)4).put((byte)1).putShort(port); for (int i = 1; i <= 4; ++i) buffer.put((byte)Integer.parseInt(matcher.group(i))); buffer.put((byte)0); buffer.flip(); getEndPoint().write(this, buffer); } else { // SOCKS 4A byte[] hostBytes = host.getBytes(StandardCharsets.UTF_8); ByteBuffer buffer = ByteBuffer.allocate(9 + hostBytes.length + 1); buffer.put((byte)4).put((byte)1).putShort(port); buffer.put((byte)0).put((byte)0).put((byte)0).put((byte)1).put((byte)0); buffer.put(hostBytes).put((byte)0); buffer.flip(); getEndPoint().write(this, buffer); } } @Override public void succeeded() { if (LOG.isDebugEnabled()) LOG.debug("Written SOCKS4 connect request"); fillInterested(); } @Override public void failed(Throwable x) { close(); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY); promise.failed(x); } @Override public void onFillable() { try { while (true) { // Avoid to read too much from the socket: ask // the parser how much left there is to read. ByteBuffer buffer = BufferUtil.allocate(parser.expected()); int filled = getEndPoint().fill(buffer); if (LOG.isDebugEnabled()) LOG.debug("Read SOCKS4 connect response, {} bytes", filled); if (filled < 0) throw new IOException("SOCKS4 tunnel failed, connection closed"); if (filled == 0) { fillInterested(); return; } if (parser.parse(buffer)) return; } } catch (Throwable x) { failed(x); } } private void onSocks4Response(int responseCode) throws IOException { if (responseCode == 0x5A) tunnel(); else throw new IOException("SOCKS4 tunnel failed with code " + responseCode); } private void tunnel() { try { HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); HttpClient client = destination.getHttpClient(); ClientConnectionFactory connectionFactory = this.connectionFactory; if (HttpScheme.HTTPS.is(destination.getScheme())) connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory); org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(getEndPoint(), context); ClientConnectionFactory.Helper.replaceConnection(this, connection); if (LOG.isDebugEnabled()) LOG.debug("SOCKS4 tunnel established: {} over {}", this, connection); } catch (Throwable x) { failed(x); } } private class Socks4Parser { private static final int EXPECTED_LENGTH = 8; private int cursor; private int response; private boolean parse(ByteBuffer buffer) throws IOException { while (buffer.hasRemaining()) { byte current = buffer.get(); if (cursor == 1) response = current & 0xFF; ++cursor; if (cursor == EXPECTED_LENGTH) { onSocks4Response(response); return true; } } return false; } private int expected() { return EXPECTED_LENGTH - cursor; } } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/Synchronizable.java000066400000000000000000000027201261716203600315730ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; /** *

Implementations of this interface expose a lock object * via {@link #getLock()} so that callers can synchronize * externally on that lock:

*
 * if (iterator instanceof Synchronizable)
 * {
 *     Object element = null;
 *     synchronized (((Synchronizable)iterator).getLock())
 *     {
 *         if (iterator.hasNext())
 *             element = iterator.next();
 *     }
 * }
 * 
*

In the example above, the calls to {@code hasNext()} and * {@code next()} are performed "atomically".

*/ public interface Synchronizable { /** * @return the lock object to synchronize on */ public Object getLock(); } TimeoutCompleteListener.java000066400000000000000000000053641261716203600333560ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Scheduler; public class TimeoutCompleteListener implements Response.CompleteListener, Runnable { private static final Logger LOG = Log.getLogger(TimeoutCompleteListener.class); private final AtomicReference task = new AtomicReference<>(); private final Request request; public TimeoutCompleteListener(Request request) { this.request = request; } @Override public void onComplete(Result result) { cancel(); } public boolean schedule(Scheduler scheduler) { long timeout = request.getTimeout(); Scheduler.Task task = scheduler.schedule(this, timeout, TimeUnit.MILLISECONDS); Scheduler.Task existing = this.task.getAndSet(task); if (existing != null) { existing.cancel(); cancel(); throw new IllegalStateException(); } if (LOG.isDebugEnabled()) LOG.debug("Scheduled timeout task {} in {} ms for {}", task, timeout, request); return true; } @Override public void run() { if (LOG.isDebugEnabled()) LOG.debug("Executing timeout task {} for {}", task, request); request.abort(new TimeoutException("Total timeout elapsed")); } public void cancel() { Scheduler.Task task = this.task.getAndSet(null); if (task != null) { boolean cancelled = task.cancel(); if (LOG.isDebugEnabled()) LOG.debug("Cancelled (successfully: {}) timeout task {}", cancelled, task); } } } WWWAuthenticationProtocolHandler.java000066400000000000000000000035431261716203600351320ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.URI; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler { public WWWAuthenticationProtocolHandler(HttpClient client) { this(client, DEFAULT_MAX_CONTENT_LENGTH); } public WWWAuthenticationProtocolHandler(HttpClient client, int maxContentLength) { super(client, maxContentLength); } @Override public boolean accept(Request request, Response response) { return response.getStatus() == HttpStatus.UNAUTHORIZED_401; } @Override protected HttpHeader getAuthenticateHeader() { return HttpHeader.WWW_AUTHENTICATE; } @Override protected HttpHeader getAuthorizationHeader() { return HttpHeader.AUTHORIZATION; } @Override protected URI getAuthenticationURI(Request request) { return request.getURI(); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/000077500000000000000000000000001261716203600265065ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Authentication.java000066400000000000000000000123051261716203600323310ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.net.URI; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.Attributes; /** * {@link Authentication} represents a mechanism to authenticate requests for protected resources. *

* {@link Authentication}s are added to an {@link AuthenticationStore}, which is then * {@link #matches(String, URI, String) queried} to find the right * {@link Authentication} mechanism to use based on its type, URI and realm, as returned by * {@code WWW-Authenticate} response headers. *

* If an {@link Authentication} mechanism is found, it is then * {@link #authenticate(Request, ContentResponse, HeaderInfo, Attributes) executed} for the given request, * returning an {@link Authentication.Result}, which is then stored in the {@link AuthenticationStore} * so that subsequent requests can be preemptively authenticated. */ public interface Authentication { /** * Matches {@link Authentication}s based on the given parameters * @param type the {@link Authentication} type such as "Basic" or "Digest" * @param uri the request URI * @param realm the authentication realm as provided in the {@code WWW-Authenticate} response header * @return true if this authentication matches, false otherwise */ boolean matches(String type, URI uri, String realm); /** * Executes the authentication mechanism for the given request, returning a {@link Result} that can be * used to actually authenticate the request via {@link Result#apply(Request)}. *

* If a request for {@code "/secure"} returns a {@link Result}, then the result may be used for other * requests such as {@code "/secure/foo"} or {@code "/secure/bar"}, unless those resources are protected * by other realms. * * @param request the request to execute the authentication mechanism for * @param response the 401 response obtained in the previous attempt to request the protected resource * @param headerInfo the {@code WWW-Authenticate} (or {@code Proxy-Authenticate}) header chosen for this * authentication (among the many that the response may contain) * @param context the conversation context in case the authentication needs multiple exchanges * to be completed and information needs to be stored across exchanges * @return the authentication result, or null if the authentication could not be performed */ Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context); /** * Structure holding information about the {@code WWW-Authenticate} (or {@code Proxy-Authenticate}) header. */ public static class HeaderInfo { private final String type; private final String realm; private final String params; private final HttpHeader header; public HeaderInfo(String type, String realm, String params, HttpHeader header) { this.type = type; this.realm = realm; this.params = params; this.header = header; } /** * @return the authentication type (for example "Basic" or "Digest") */ public String getType() { return type; } /** * @return the realm name */ public String getRealm() { return realm; } /** * @return additional authentication parameters */ public String getParameters() { return params; } /** * @return the {@code Authorization} (or {@code Proxy-Authorization}) header */ public HttpHeader getHeader() { return header; } } /** * {@link Result} holds the information needed to authenticate a {@link Request} via {@link #apply(Request)}. */ public static interface Result { /** * @return the URI of the request that has been used to generate this {@link Result} */ URI getURI(); /** * Applies the authentication result to the given request. * Typically, a {@code Authorization} header is added to the request, with the right information to * successfully authenticate at the server. * * @param request the request to authenticate */ void apply(Request request); } } AuthenticationStore.java000066400000000000000000000052301261716203600332660ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.net.URI; /** * A store for {@link Authentication}s and {@link Authentication.Result}s. */ public interface AuthenticationStore { /** * @param authentication the {@link Authentication} to add */ public void addAuthentication(Authentication authentication); /** * @param authentication the {@link Authentication} to remove */ public void removeAuthentication(Authentication authentication); /** * Removes all {@link Authentication}s stored */ public void clearAuthentications(); /** * Returns the authentication that matches the given type (for example, "Basic" or "Digest"), * the given request URI and the given realm. * If no such authentication can be found, returns null. * * @param type the {@link Authentication} type such as "Basic" or "Digest" * @param uri the request URI * @param realm the authentication realm * @return the authentication that matches the given parameters, or null */ public Authentication findAuthentication(String type, URI uri, String realm); /** * @param result the {@link Authentication.Result} to add */ public void addAuthenticationResult(Authentication.Result result); /** * @param result the {@link Authentication.Result} to remove */ public void removeAuthenticationResult(Authentication.Result result); /** * Removes all authentication results stored */ public void clearAuthenticationResults(); /** * Returns an {@link Authentication.Result} that matches the given URI, or null if no * {@link Authentication.Result}s match the given URI. * * @param uri the request URI * @return the {@link Authentication.Result} that matches the given URI, or null */ public Authentication.Result findAuthenticationResult(URI uri); } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Connection.java000066400000000000000000000034611261716203600314540ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.io.Closeable; /** * {@link Connection} represent a connection to a {@link Destination} and allow applications to send * requests via {@link #send(Request, Response.CompleteListener)}. *

* {@link Connection}s are normally pooled by {@link Destination}s, but unpooled {@link Connection}s * may be created by applications that want to do their own connection management via * {@link Destination#newConnection(Promise)} and {@link Connection#close()}. */ public interface Connection extends Closeable { /** * Sends a request with an associated response listener. *

* {@link Request#send(Response.CompleteListener)} will eventually call this method to send the request. * It is exposed to allow applications to send requests via unpooled connections. * * @param request the request to send * @param listener the response listener */ void send(Request request, Response.CompleteListener listener); @Override void close(); } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentProvider.java000066400000000000000000000040331261716203600324760ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.nio.ByteBuffer; /** * {@link ContentProvider} provides a source of request content. *

* Implementations should return an {@link Iterator} over the request content. * If the request content comes from a source that needs to be closed (for * example, an {@link InputStream}), then the iterator implementation class * must implement {@link Closeable} and will be closed when the request is * completed (either successfully or failed). *

* Applications should rely on utility classes such as {@link ByteBufferContentProvider} * or {@link PathContentProvider}. */ public interface ContentProvider extends Iterable { /** * @return the content length, if known, or -1 if the content length is unknown */ long getLength(); /** * An extension of {@link ContentProvider} that provides a content type string * to be used as a {@code Content-Type} HTTP header in requests. */ public interface Typed extends ContentProvider { /** * @return the content type string such as "application/octet-stream" or * "application/json;charset=UTF8", or null if no content type must be set */ public String getContentType(); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/ContentResponse.java000066400000000000000000000027331261716203600325070ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; /** * A specialized {@link Response} that can hold a limited content in memory. */ public interface ContentResponse extends Response { /** * @return the media type of the content, such as "text/html" or "application/octet-stream" */ String getMediaType(); /** * @return the encoding of the content, such as "UTF-8" */ String getEncoding(); /** * @return the response content */ byte[] getContent(); /** * @return the response content as a string, decoding the bytes using the charset * provided by the {@code Content-Type} header, if any, or UTF-8. */ String getContentAsString(); } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java000066400000000000000000000044511261716203600316360ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import org.eclipse.jetty.util.Promise; /** * {@link Destination} represents the triple made of the {@link #getScheme}, the {@link #getHost} * and the {@link #getPort}. *

* {@link Destination} holds a pool of {@link Connection}s, but allows to create unpooled * connections if the application wants full control over connection management via {@link #newConnection(Promise)}. *

* {@link Destination}s may be obtained via {@link HttpClient#getDestination(String, String, int)} */ public interface Destination { /** * @return the scheme of this destination, such as "http" or "https" */ String getScheme(); /** * @return the host of this destination, such as "127.0.0.1" or "google.com" */ String getHost(); /** * @return the port of this destination such as 80 or 443 */ int getPort(); /** * Creates asynchronously a new, unpooled, {@link Connection} that will be returned * at a later time through the given {@link Promise}. *

* Use {@link FuturePromise} to wait for the connection: *

     * Destination destination = ...;
     * FuturePromise<Connection> futureConnection = new FuturePromise<>();
     * destination.newConnection(futureConnection);
     * Connection connection = futureConnection.get(5, TimeUnit.SECONDS);
     * 
* * @param promise the promise of a new, unpooled, {@link Connection} */ void newConnection(Promise promise); } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Request.java000066400000000000000000000426411261716203600310100ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.io.IOException; import java.net.HttpCookie; import java.net.URI; import java.nio.ByteBuffer; import java.nio.file.Path; import java.util.EventListener; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Fields; /** *

{@link Request} represents a HTTP request, and offers a fluent interface to customize * various attributes such as the path, the headers, the content, etc.

*

You can create {@link Request} objects via {@link HttpClient#newRequest(String)} and * you can send them using either {@link #send()} for a blocking semantic, or * {@link #send(Response.CompleteListener)} for an asynchronous semantic.

* * @see Response */ public interface Request { /** * @return the scheme of this request, such as "http" or "https" */ String getScheme(); /** * @param scheme the scheme of this request, such as "http" or "https" * @return this request object */ Request scheme(String scheme); /** * @return the host of this request, such as "127.0.0.1" or "google.com" */ String getHost(); /** * @return the port of this request such as 80 or 443 */ int getPort(); /** * @return the method of this request, such as GET or POST, as a String */ String getMethod(); /** * @param method the method of this request, such as GET or POST * @return this request object */ Request method(HttpMethod method); /** * @param method the method of this request, such as GET or POST * @return this request object */ Request method(String method); /** * @return the path of this request, such as "/" or "/path" - without the query * @see #getQuery() */ String getPath(); /** * Specifies the path - and possibly the query - of this request. * If the query part is specified, parameter values must be properly * {@link URLEncoder#encode(String, String) UTF-8 URL encoded}. * For example, if the value for parameter "currency" is the euro symbol € then the * query string for this parameter must be "currency=%E2%82%AC". * For transparent encoding of parameter values, use {@link #param(String, String)}. * * @param path the path of this request, such as "/" or "/path?param=1" * @return this request object */ Request path(String path); /** * @return the query string of this request such as "param=1" * @see #getPath() * @see #getParams() */ String getQuery(); /** * @return the full URI of this request such as "http://host:port/path?param=1" */ URI getURI(); /** * @return the HTTP version of this request, such as "HTTP/1.1" */ HttpVersion getVersion(); /** * @param version the HTTP version of this request, such as "HTTP/1.1" * @return this request object */ Request version(HttpVersion version); /** * @return the query parameters of this request */ Fields getParams(); /** * Adds a query parameter with the given name and value. * The value is {@link URLEncoder#encode(String, String) UTF-8 URL encoded}. * * @param name the name of the query parameter * @param value the value of the query parameter * @return this request object */ Request param(String name, String value); /** * @return the headers of this request */ HttpFields getHeaders(); /** * @param name the name of the header * @param value the value of the header * @return this request object * @see #header(HttpHeader, String) */ Request header(String name, String value); /** *

Adds the given {@code value} to the specified {@code header}.

*

Multiple calls with the same parameters will add multiple values; * use the value {@code null} to remove the header completely.

* * @param header the header name * @param value the value of the header * @return this request object */ Request header(HttpHeader header, String value); /** * @return the cookies associated with this request */ List getCookies(); /** * @param cookie a cookie for this request * @return this request object */ Request cookie(HttpCookie cookie); /** * @param name the name of the attribute * @param value the value of the attribute * @return this request object */ Request attribute(String name, Object value); /** * @return the attributes of this request */ Map getAttributes(); /** * @return the content provider of this request */ ContentProvider getContent(); /** * @param content the content provider of this request * @return this request object */ Request content(ContentProvider content); /** * @param content the content provider of this request * @return this request object */ Request content(ContentProvider content, String contentType); /** * Shortcut method to specify a file as a content for this request, with the default content type of * "application/octect-stream". * * @param file the file to upload * @return this request object * @throws IOException if the file does not exist or cannot be read */ Request file(Path file) throws IOException; /** * Shortcut method to specify a file as a content for this request, with the given content type. * * @param file the file to upload * @param contentType the content type of the file * @return this request object * @throws IOException if the file does not exist or cannot be read */ Request file(Path file, String contentType) throws IOException; /** * @return the user agent for this request */ String getAgent(); /** * @param agent the user agent for this request (corresponds to the {@code User-Agent} header) * @return this request object */ Request agent(String agent); /** * @param accepts the media types that are acceptable in the response, such as * "text/plain;q=0.5" or "text/html" (corresponds to the {@code Accept} header) * @return this request object */ Request accept(String... accepts); /** * @return the idle timeout for this request, in milliseconds */ long getIdleTimeout(); /** * @param timeout the idle timeout for this request * @param unit the idle timeout unit * @return this request object */ Request idleTimeout(long timeout, TimeUnit unit); /** * @return the total timeout for this request, in milliseconds */ long getTimeout(); /** * @param timeout the total timeout for the request/response conversation * @param unit the timeout unit * @return this request object */ Request timeout(long timeout, TimeUnit unit); /** * @return whether this request follows redirects */ boolean isFollowRedirects(); /** * @param follow whether this request follows redirects * @return this request object */ Request followRedirects(boolean follow); /** * @param listenerClass the class of the listener, or null for all listeners classes * @return the listeners for request events of the given class */ List getRequestListeners(Class listenerClass); /** * @param listener a listener for request events * @return this request object */ Request listener(Listener listener); /** * @param listener a listener for request queued event * @return this request object */ Request onRequestQueued(QueuedListener listener); /** * @param listener a listener for request begin event * @return this request object */ Request onRequestBegin(BeginListener listener); /** * @param listener a listener for request headers event * @return this request object */ Request onRequestHeaders(HeadersListener listener); /** * @param listener a listener for request commit event * @return this request object */ Request onRequestCommit(CommitListener listener); /** * @param listener a listener for request content events * @return this request object */ Request onRequestContent(ContentListener listener); /** * @param listener a listener for request success event * @return this request object */ Request onRequestSuccess(SuccessListener listener); /** * @param listener a listener for request failure event * @return this request object */ Request onRequestFailure(FailureListener listener); /** * @param listener a listener for response begin event * @return this request object */ Request onResponseBegin(Response.BeginListener listener); /** * @param listener a listener for response header event * @return this request object */ Request onResponseHeader(Response.HeaderListener listener); /** * @param listener a listener for response headers event * @return this request object */ Request onResponseHeaders(Response.HeadersListener listener); /** * @param listener a consuming listener for response content events * @return this request object */ Request onResponseContent(Response.ContentListener listener); /** * @param listener an asynchronous listener for response content events * @return this request object */ Request onResponseContentAsync(Response.AsyncContentListener listener); /** * @param listener a listener for response success event * @return this request object */ Request onResponseSuccess(Response.SuccessListener listener); /** * @param listener a listener for response failure event * @return this request object */ Request onResponseFailure(Response.FailureListener listener); /** * @param listener a listener for complete event * @return this request object */ Request onComplete(Response.CompleteListener listener); /** * Sends this request and returns the response. *

* This method should be used when a simple blocking semantic is needed, and when it is known * that the response content can be buffered without exceeding memory constraints. *

* For example, this method is not appropriate to download big files from a server; consider using * {@link #send(Response.CompleteListener)} instead, passing your own {@link Response.Listener} or a utility * listener such as {@link InputStreamResponseListener}. *

* The method returns when the {@link Response.CompleteListener complete event} is fired. * * @return a {@link ContentResponse} for this request * @see Response.CompleteListener#onComplete(Result) */ ContentResponse send() throws InterruptedException, TimeoutException, ExecutionException; /** *

Sends this request and asynchronously notifies the given listener for response events.

*

This method should be used when the application needs to be notified of the various response events * as they happen, or when the application needs to efficiently manage the response content.

*

The listener passed to this method may implement not only {@link Response.CompleteListener} * but also other response listener interfaces, and all the events implemented will be notified. * This allows application code to write a single listener class to handle all relevant events.

* * @param listener the listener that receives response events */ void send(Response.CompleteListener listener); /** * Attempts to abort the send of this request. * * @param cause the abort cause, must not be null * @return whether the abort succeeded */ boolean abort(Throwable cause); /** * @return the abort cause passed to {@link #abort(Throwable)}, * or null if this request has not been aborted */ Throwable getAbortCause(); /** * Common, empty, super-interface for request listeners. */ public interface RequestListener extends EventListener { } /** * Listener for the request queued event. */ public interface QueuedListener extends RequestListener { /** * Callback method invoked when the request is queued, waiting to be sent * * @param request the request being queued */ public void onQueued(Request request); } /** * Listener for the request begin event. */ public interface BeginListener extends RequestListener { /** * Callback method invoked when the request begins being processed in order to be sent. * This is the last opportunity to modify the request. * * @param request the request that begins being processed */ public void onBegin(Request request); } /** * Listener for the request headers event. */ public interface HeadersListener extends RequestListener { /** * Callback method invoked when the request headers (and perhaps small content) are ready to be sent. * The request has been converted into bytes, but not yet sent to the server, and further modifications * to the request may have no effect. * @param request the request that is about to be committed */ public void onHeaders(Request request); } /** * Listener for the request committed event. */ public interface CommitListener extends RequestListener { /** * Callback method invoked when the request headers (and perhaps small content) have been sent. * The request is now committed, and in transit to the server, and further modifications to the * request may have no effect. * @param request the request that has been committed */ public void onCommit(Request request); } /** * Listener for the request content event. */ public interface ContentListener extends RequestListener { /** * Callback method invoked when a chunk of request content has been sent successfully. * Changes to bytes in the given buffer have no effect, as the content has already been sent. * @param request the request that has been committed */ public void onContent(Request request, ByteBuffer content); } /** * Listener for the request succeeded event. */ public interface SuccessListener extends RequestListener { /** * Callback method invoked when the request has been successfully sent. * * @param request the request sent */ public void onSuccess(Request request); } /** * Listener for the request failed event. */ public interface FailureListener extends RequestListener { /** * Callback method invoked when the request has failed to be sent * @param request the request that failed * @param failure the failure */ public void onFailure(Request request, Throwable failure); } /** * Listener for all request events. */ public interface Listener extends QueuedListener, BeginListener, HeadersListener, CommitListener, ContentListener, SuccessListener, FailureListener { /** * An empty implementation of {@link Listener} */ public static class Adapter implements Listener { @Override public void onQueued(Request request) { } @Override public void onBegin(Request request) { } @Override public void onHeaders(Request request) { } @Override public void onCommit(Request request) { } @Override public void onContent(Request request, ByteBuffer content) { } @Override public void onSuccess(Request request) { } @Override public void onFailure(Request request, Throwable failure) { } } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Response.java000066400000000000000000000214571261716203600311600ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.nio.ByteBuffer; import java.util.EventListener; import java.util.List; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.Callback; /** *

{@link Response} represents a HTTP response and offers methods to retrieve status code, HTTP version * and headers.

*

{@link Response} objects are passed as parameters to {@link Response.Listener} callbacks, or as * future result of {@link Request#send()}.

*

{@link Response} objects do not contain getters for the response content, because it may be too large * to fit into memory. * The response content should be retrieved via {@link Response.Listener#onContent(Response, ByteBuffer) content * events}, or via utility classes such as {@link BufferingResponseListener}.

*/ public interface Response { /** * @return the request associated with this response */ Request getRequest(); /** * @return the response listener passed to {@link Request#send(CompleteListener)} */ List getListeners(Class listenerClass); /** * @return the HTTP version of this response, such as "HTTP/1.1" */ HttpVersion getVersion(); /** * @return the HTTP status code of this response, such as 200 or 404 */ int getStatus(); /** * @return the HTTP reason associated to the {@link #getStatus} */ String getReason(); /** * @return the headers of this response */ HttpFields getHeaders(); /** * Attempts to abort the receive of this response. * * @param cause the abort cause, must not be null * @return whether the abort succeeded */ boolean abort(Throwable cause); /** * Common, empty, super-interface for response listeners */ public interface ResponseListener extends EventListener { } /** * Listener for the response begin event. */ public interface BeginListener extends ResponseListener { /** * Callback method invoked when the response line containing HTTP version, * HTTP status code and reason has been received and parsed. *

* This method is the best approximation to detect when the first bytes of the response arrived to the client. * * @param response the response containing the response line data */ public void onBegin(Response response); } /** * Listener for a response header event. */ public interface HeaderListener extends ResponseListener { /** * Callback method invoked when a response header has been received, * returning whether the header should be processed or not. * * @param response the response containing the response line data and the headers so far * @param field the header received * @return true to process the header, false to skip processing of the header */ public boolean onHeader(Response response, HttpField field); } /** * Listener for the response headers event. */ public interface HeadersListener extends ResponseListener { /** * Callback method invoked when the response headers have been received and parsed. * * @param response the response containing the response line data and the headers */ public void onHeaders(Response response); } /** * Listener for the response content events. */ public interface ContentListener extends ResponseListener { /** * Callback method invoked when the response content has been received. * This method may be invoked multiple times, and the {@code content} buffer must be consumed * before returning from this method. * * @param response the response containing the response line data and the headers * @param content the content bytes received */ public void onContent(Response response, ByteBuffer content); } public interface AsyncContentListener extends ResponseListener { /** * Callback method invoked asynchronously when the response content has been received. * * @param response the response containing the response line data and the headers * @param content the content bytes received * @param callback the callback to call when the content is consumed. */ public void onContent(Response response, ByteBuffer content, Callback callback); } /** * Listener for the response succeeded event. */ public interface SuccessListener extends ResponseListener { /** * Callback method invoked when the whole response has been successfully received. * * @param response the response containing the response line data and the headers */ public void onSuccess(Response response); } /** * Listener for the response failure event. */ public interface FailureListener extends ResponseListener { /** * Callback method invoked when the response has failed in the process of being received * * @param response the response containing data up to the point the failure happened * @param failure the failure happened */ public void onFailure(Response response, Throwable failure); } /** * Listener for the request and response completed event. */ public interface CompleteListener extends ResponseListener { /** * Callback method invoked when the request and the response have been processed, * either successfully or not. *

* The {@code result} parameter contains the request, the response, and eventual failures. *

* Requests may complete after response, for example in case of big uploads that are * discarded or read asynchronously by the server. * This method is always invoked after {@link SuccessListener#onSuccess(Response)} or * {@link FailureListener#onFailure(Response, Throwable)}, and only when request indicates that * it is completed. * * @param result the result of the request / response exchange */ public void onComplete(Result result); } /** * Listener for all response events. */ public interface Listener extends BeginListener, HeaderListener, HeadersListener, ContentListener, AsyncContentListener, SuccessListener, FailureListener, CompleteListener { /** * An empty implementation of {@link Listener} */ public static class Adapter implements Listener { @Override public void onBegin(Response response) { } @Override public boolean onHeader(Response response, HttpField field) { return true; } @Override public void onHeaders(Response response) { } @Override public void onContent(Response response, ByteBuffer content) { } @Override public void onContent(Response response, ByteBuffer content, Callback callback) { try { onContent(response, content); callback.succeeded(); } catch (Exception x) { callback.failed(x); } } @Override public void onSuccess(Response response) { } @Override public void onFailure(Response response, Throwable failure) { } @Override public void onComplete(Result result) { } } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/Result.java000066400000000000000000000061301261716203600306270ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; /** * The result of a request / response exchange, containing the {@link Request}, the {@link Response} * and eventual failures of either. */ public class Result { private final Request request; private final Throwable requestFailure; private final Response response; private final Throwable responseFailure; public Result(Request request, Response response) { this(request, null, response, null); } public Result(Request request, Response response, Throwable responseFailure) { this(request, null, response, responseFailure); } public Result(Request request, Throwable requestFailure, Response response) { this(request, requestFailure, response, null); } public Result(Request request, Throwable requestFailure, Response response, Throwable responseFailure) { this.request = request; this.requestFailure = requestFailure; this.response = response; this.responseFailure = responseFailure; } /** * @return the request object */ public Request getRequest() { return request; } /** * @return the request failure, if any */ public Throwable getRequestFailure() { return requestFailure; } /** * @return the response object */ public Response getResponse() { return response; } /** * @return the response failure, if any */ public Throwable getResponseFailure() { return responseFailure; } /** * @return whether both the request and the response succeeded */ public boolean isSucceeded() { return getFailure() == null; } /** * @return whether either the response or the request failed */ public boolean isFailed() { return !isSucceeded(); } /** * @return the response failure, if any, otherwise the request failure, if any */ public Throwable getFailure() { return responseFailure != null ? responseFailure : requestFailure; } @Override public String toString() { return String.format("%s[%s > %s] %s", Result.class.getSimpleName(), request, response, getFailure()); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/api/package-info.java000066400000000000000000000015501261716203600316760ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Client : API Classes */ package org.eclipse.jetty.client.api; jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http/000077500000000000000000000000001261716203600267145ustar00rootroot00000000000000HttpChannelOverHTTP.java000066400000000000000000000072371261716203600332550ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import org.eclipse.jetty.client.HttpChannel; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpReceiver; import org.eclipse.jetty.client.HttpSender; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpVersion; public class HttpChannelOverHTTP extends HttpChannel { private final HttpConnectionOverHTTP connection; private final HttpSenderOverHTTP sender; private final HttpReceiverOverHTTP receiver; public HttpChannelOverHTTP(HttpConnectionOverHTTP connection) { super(connection.getHttpDestination()); this.connection = connection; this.sender = newHttpSender(); this.receiver = newHttpReceiver(); } protected HttpSenderOverHTTP newHttpSender() { return new HttpSenderOverHTTP(this); } protected HttpReceiverOverHTTP newHttpReceiver() { return new HttpReceiverOverHTTP(this); } @Override protected HttpSender getHttpSender() { return sender; } @Override protected HttpReceiver getHttpReceiver() { return receiver; } public HttpConnectionOverHTTP getHttpConnection() { return connection; } @Override public void send() { HttpExchange exchange = getHttpExchange(); if (exchange != null) sender.send(exchange); } @Override public void release() { connection.release(); } public void receive() { receiver.receive(); } @Override public void exchangeTerminated(HttpExchange exchange, Result result) { super.exchangeTerminated(exchange, result); Response response = result.getResponse(); HttpFields responseHeaders = response.getHeaders(); boolean close = result.isFailed() || receiver.isShutdown(); if (!close) { if (response.getVersion().compareTo(HttpVersion.HTTP_1_1) < 0) { // HTTP 1.0 must close the connection unless it has an explicit keep alive. close = !responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()); } else { // HTTP 1.1 or greater closes only if it has an explicit close. close = responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); } } if (close) connection.close(); else release(); } @Override public String toString() { return String.format("%s[send=%s,recv=%s]", super.toString(), sender, receiver); } } HttpClientTransportOverHTTP.java000066400000000000000000000054201261716203600350300ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.io.IOException; import java.util.Map; import org.eclipse.jetty.client.AbstractHttpClientTransport; import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.Promise; public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport { public HttpClientTransportOverHTTP() { this(Math.max(1, Runtime.getRuntime().availableProcessors() / 2)); } public HttpClientTransportOverHTTP(int selectors) { super(selectors); } @Override public HttpDestination newHttpDestination(Origin origin) { return new HttpDestinationOverHTTP(getHttpClient(), origin); } @Override public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException { HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY); HttpConnectionOverHTTP connection = newHttpConnection(endPoint, destination, promise); if (LOG.isDebugEnabled()) LOG.debug("Created {}", connection); return connection; } protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise) { return new HttpConnectionOverHTTP(endPoint, destination, promise); } /** * @deprecated use {@link #newHttpConnection(EndPoint, HttpDestination, Promise)} instead */ @Deprecated protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination) { throw new UnsupportedOperationException("Deprecated, override newHttpConnection(EndPoint, HttpDestination, Promise) instead"); } } HttpConnectionOverHTTP.java000066400000000000000000000154341261716203600340020ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.nio.channels.AsynchronousCloseException; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.client.HttpConnection; import org.eclipse.jetty.client.HttpDestination; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Sweeper; public class HttpConnectionOverHTTP extends AbstractConnection implements Connection, Sweeper.Sweepable { private static final Logger LOG = Log.getLogger(HttpConnectionOverHTTP.class); private final AtomicBoolean closed = new AtomicBoolean(); private final AtomicInteger sweeps = new AtomicInteger(); private final Promise promise; private final Delegate delegate; private final HttpChannelOverHTTP channel; private long idleTimeout; /** * @deprecated use {@link #HttpConnectionOverHTTP(EndPoint, HttpDestination, Promise)} instead */ @Deprecated public HttpConnectionOverHTTP(EndPoint endPoint, HttpDestination destination) { this(endPoint, destination, new Promise.Adapter()); throw new UnsupportedOperationException("Deprecated, use HttpConnectionOverHTTP(EndPoint, HttpDestination, Promise) instead"); } public HttpConnectionOverHTTP(EndPoint endPoint, HttpDestination destination, Promise promise) { super(endPoint, destination.getHttpClient().getExecutor(), destination.getHttpClient().isDispatchIO()); this.promise = promise; this.delegate = new Delegate(destination); this.channel = newHttpChannel(); } protected HttpChannelOverHTTP newHttpChannel() { return new HttpChannelOverHTTP(this); } public HttpChannelOverHTTP getHttpChannel() { return channel; } public HttpDestinationOverHTTP getHttpDestination() { return (HttpDestinationOverHTTP)delegate.getHttpDestination(); } @Override public void send(Request request, Response.CompleteListener listener) { delegate.send(request, listener); } protected void send(HttpExchange exchange) { delegate.send(exchange); } @Override public void onOpen() { super.onOpen(); fillInterested(); promise.succeeded(this); } public boolean isClosed() { return closed.get(); } @Override protected boolean onReadTimeout() { if (LOG.isDebugEnabled()) LOG.debug("{} idle timeout", this); close(new TimeoutException()); return false; } @Override public void onFillable() { HttpExchange exchange = channel.getHttpExchange(); if (exchange != null) { channel.receive(); } else { // If there is no exchange, then could be either a remote close, // or garbage bytes; in both cases we close the connection close(); } } public void release() { // Restore idle timeout getEndPoint().setIdleTimeout(idleTimeout); getHttpDestination().release(this); } @Override public void close() { close(new AsynchronousCloseException()); } protected void close(Throwable failure) { if (softClose()) { // First close then abort, to be sure that the connection cannot be reused // from an onFailure() handler or by blocking code waiting for completion. getHttpDestination().close(this); getEndPoint().shutdownOutput(); if (LOG.isDebugEnabled()) LOG.debug("{} oshut", this); getEndPoint().close(); if (LOG.isDebugEnabled()) LOG.debug("{} closed", this); abort(failure); } } public boolean softClose() { return closed.compareAndSet(false, true); } protected boolean abort(Throwable failure) { HttpExchange exchange = channel.getHttpExchange(); return exchange != null && exchange.getRequest().abort(failure); } @Override public boolean sweep() { if (!closed.get()) return false; if (sweeps.incrementAndGet() < 4) return false; return true; } @Override public String toString() { return String.format("%s@%h(l:%s <-> r:%s,closed=%b)[%s]", getClass().getSimpleName(), this, getEndPoint().getLocalAddress(), getEndPoint().getRemoteAddress(), closed.get(), channel); } private class Delegate extends HttpConnection { private Delegate(HttpDestination destination) { super(destination); } @Override protected void send(HttpExchange exchange) { Request request = exchange.getRequest(); normalizeRequest(request); // Save the old idle timeout to restore it EndPoint endPoint = getEndPoint(); idleTimeout = endPoint.getIdleTimeout(); endPoint.setIdleTimeout(request.getIdleTimeout()); // One channel per connection, just delegate the send if (channel.associate(exchange)) channel.send(); else channel.release(); } @Override public void close() { HttpConnectionOverHTTP.this.close(); } @Override public String toString() { return HttpConnectionOverHTTP.this.toString(); } } } HttpDestinationOverHTTP.java000066400000000000000000000025361261716203600341630ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.PoolingHttpDestination; public class HttpDestinationOverHTTP extends PoolingHttpDestination { public HttpDestinationOverHTTP(HttpClient client, Origin origin) { super(client, origin); } @Override protected void send(HttpConnectionOverHTTP connection, HttpExchange exchange) { connection.send(exchange); } } HttpReceiverOverHTTP.java000066400000000000000000000217531261716203600334500ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.io.EOFException; import java.nio.ByteBuffer; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpReceiver; import org.eclipse.jetty.client.HttpResponse; import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.CompletableCallback; public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.ResponseHandler { private final HttpParser parser = new HttpParser(this); private ByteBuffer buffer; private boolean shutdown; public HttpReceiverOverHTTP(HttpChannelOverHTTP channel) { super(channel); } @Override public HttpChannelOverHTTP getHttpChannel() { return (HttpChannelOverHTTP)super.getHttpChannel(); } private HttpConnectionOverHTTP getHttpConnection() { return getHttpChannel().getHttpConnection(); } protected ByteBuffer getResponseBuffer() { return buffer; } public void receive() { if (buffer == null) acquireBuffer(); process(); } private void acquireBuffer() { HttpClient client = getHttpDestination().getHttpClient(); ByteBufferPool bufferPool = client.getByteBufferPool(); buffer = bufferPool.acquire(client.getResponseBufferSize(), true); } private void releaseBuffer() { if (buffer == null) throw new IllegalStateException(); if (BufferUtil.hasContent(buffer)) throw new IllegalStateException(); HttpClient client = getHttpDestination().getHttpClient(); ByteBufferPool bufferPool = client.getByteBufferPool(); bufferPool.release(buffer); buffer = null; } private void process() { try { HttpConnectionOverHTTP connection = getHttpConnection(); EndPoint endPoint = connection.getEndPoint(); while (true) { // Connection may be closed in a parser callback. if (connection.isClosed()) { if (LOG.isDebugEnabled()) LOG.debug("{} closed", connection); releaseBuffer(); return; } if (parse()) return; int read = endPoint.fill(buffer); if (LOG.isDebugEnabled()) LOG.debug("Read {} bytes {} from {}", read, BufferUtil.toDetailString(buffer), endPoint); if (read > 0) { if (parse()) return; } else if (read == 0) { releaseBuffer(); fillInterested(); return; } else { releaseBuffer(); shutdown(); return; } } } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug(x); BufferUtil.clear(buffer); if (buffer != null) releaseBuffer(); failAndClose(x); } } /** * Parses a HTTP response in the receivers buffer. * * @return true to indicate that parsing should be interrupted (and will be resumed by another thread). */ private boolean parse() { while (true) { // Must parse even if the buffer is fully consumed, to allow the // parser to advance from asynchronous content to response complete. boolean handle = parser.parseNext(buffer); if (LOG.isDebugEnabled()) LOG.debug("Parsed {}, remaining {} {}", handle, buffer.remaining(), parser); if (handle || !buffer.hasRemaining()) return handle; } } protected void fillInterested() { getHttpChannel().getHttpConnection().fillInterested(); } private void shutdown() { // Mark this receiver as shutdown, so that we can // close the connection when the exchange terminates. // We cannot close the connection from here because // the request may still be in process. shutdown = true; // Shutting down the parser may invoke messageComplete() or earlyEOF(). // In case of content delimited by EOF, without a Connection: close // header, the connection will be closed at exchange termination // thanks to the flag we have set above. parser.atEOF(); parser.parseNext(BufferUtil.EMPTY_BUFFER); } protected boolean isShutdown() { return shutdown; } @Override public int getHeaderCacheSize() { // TODO get from configuration return 256; } @Override public boolean startResponse(HttpVersion version, int status, String reason) { HttpExchange exchange = getHttpExchange(); if (exchange == null) return false; String method = exchange.getRequest().getMethod(); parser.setHeadResponse(HttpMethod.HEAD.is(method) || HttpMethod.CONNECT.is(method)); exchange.getResponse().version(version).status(status).reason(reason); responseBegin(exchange); return false; } @Override public boolean parsedHeader(HttpField field) { HttpExchange exchange = getHttpExchange(); if (exchange == null) return false; responseHeader(exchange, field); return false; } @Override public boolean headerComplete() { HttpExchange exchange = getHttpExchange(); if (exchange == null) return false; responseHeaders(exchange); return false; } @Override public boolean content(ByteBuffer buffer) { HttpExchange exchange = getHttpExchange(); if (exchange == null) return false; CompletableCallback callback = new CompletableCallback() { @Override public void resume() { if (LOG.isDebugEnabled()) LOG.debug("Content consumed asynchronously, resuming processing"); process(); } public void abort(Throwable x) { failAndClose(x); } }; responseContent(exchange, buffer, callback); return callback.tryComplete(); } @Override public boolean messageComplete() { HttpExchange exchange = getHttpExchange(); if (exchange != null) responseSuccess(exchange); return false; } @Override public void earlyEOF() { HttpExchange exchange = getHttpExchange(); HttpConnectionOverHTTP connection = getHttpConnection(); if (exchange == null) connection.close(); else failAndClose(new EOFException(String.valueOf(connection))); } @Override public void badMessage(int status, String reason) { HttpExchange exchange = getHttpExchange(); if (exchange != null) { HttpResponse response = exchange.getResponse(); response.status(status).reason(reason); failAndClose(new HttpResponseException("HTTP protocol violation: bad response on " + getHttpConnection(), response)); } } @Override protected void reset() { super.reset(); parser.reset(); } @Override protected void dispose() { super.dispose(); parser.close(); } private void failAndClose(Throwable failure) { if (responseFailure(failure)) getHttpConnection().close(failure); } @Override public String toString() { return String.format("%s[%s]", super.toString(), parser); } } HttpSenderOverHTTP.java000066400000000000000000000210521261716203600331140ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.nio.ByteBuffer; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpContent; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpRequestException; import org.eclipse.jetty.client.HttpSender; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.Callback; public class HttpSenderOverHTTP extends HttpSender { private final HttpGenerator generator = new HttpGenerator(); public HttpSenderOverHTTP(HttpChannelOverHTTP channel) { super(channel); } @Override public HttpChannelOverHTTP getHttpChannel() { return (HttpChannelOverHTTP)super.getHttpChannel(); } @Override protected void sendHeaders(HttpExchange exchange, HttpContent content, Callback callback) { Request request = exchange.getRequest(); ContentProvider requestContent = request.getContent(); long contentLength = requestContent == null ? -1 : requestContent.getLength(); String path = request.getPath(); String query = request.getQuery(); if (query != null) path += "?" + query; HttpGenerator.RequestInfo requestInfo = new HttpGenerator.RequestInfo(request.getVersion(), request.getHeaders(), contentLength, request.getMethod(), path); try { HttpClient client = getHttpChannel().getHttpDestination().getHttpClient(); ByteBufferPool bufferPool = client.getByteBufferPool(); ByteBuffer header = bufferPool.acquire(client.getRequestBufferSize(), false); ByteBuffer chunk = null; ByteBuffer contentBuffer = null; boolean lastContent = false; if (!expects100Continue(request)) { content.advance(); contentBuffer = content.getByteBuffer(); lastContent = content.isLast(); } while (true) { HttpGenerator.Result result = generator.generateRequest(requestInfo, header, chunk, contentBuffer, lastContent); switch (result) { case NEED_CHUNK: { chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false); break; } case FLUSH: { int size = 1; boolean hasChunk = chunk != null; if (hasChunk) ++size; boolean hasContent = contentBuffer != null; if (hasContent) ++size; ByteBuffer[] toWrite = new ByteBuffer[size]; ByteBuffer[] toRecycle = new ByteBuffer[hasChunk ? 2 : 1]; toWrite[0] = header; toRecycle[0] = header; if (hasChunk) { toWrite[1] = chunk; toRecycle[1] = chunk; } if (hasContent) toWrite[toWrite.length - 1] = contentBuffer; EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint(); endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, toRecycle), toWrite); return; } case DONE: { // The headers have already been generated, perhaps by a concurrent abort. callback.failed(new HttpRequestException("Could not generate headers", request)); return; } default: { callback.failed(new IllegalStateException(result.toString())); return; } } } } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug(x); callback.failed(x); } } @Override protected void sendContent(HttpExchange exchange, HttpContent content, Callback callback) { try { HttpClient client = getHttpChannel().getHttpDestination().getHttpClient(); ByteBufferPool bufferPool = client.getByteBufferPool(); ByteBuffer chunk = null; while (true) { ByteBuffer contentBuffer = content.getByteBuffer(); boolean lastContent = content.isLast(); HttpGenerator.Result result = generator.generateRequest(null, null, chunk, contentBuffer, lastContent); switch (result) { case NEED_CHUNK: { chunk = bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false); break; } case FLUSH: { EndPoint endPoint = getHttpChannel().getHttpConnection().getEndPoint(); if (chunk != null) endPoint.write(new ByteBufferRecyclerCallback(callback, bufferPool, chunk), chunk, contentBuffer); else endPoint.write(callback, contentBuffer); return; } case SHUTDOWN_OUT: { shutdownOutput(); break; } case CONTINUE: { break; } case DONE: { assert generator.isEnd(); callback.succeeded(); return; } default: { throw new IllegalStateException(); } } } } catch (Exception x) { if (LOG.isDebugEnabled()) LOG.debug(x); callback.failed(x); } } @Override protected void reset() { generator.reset(); super.reset(); } @Override protected void dispose() { generator.abort(); super.dispose(); shutdownOutput(); } private void shutdownOutput() { getHttpChannel().getHttpConnection().getEndPoint().shutdownOutput(); } @Override public String toString() { return String.format("%s[%s]", super.toString(), generator); } private class ByteBufferRecyclerCallback implements Callback { private final Callback callback; private final ByteBufferPool pool; private final ByteBuffer[] buffers; private ByteBufferRecyclerCallback(Callback callback, ByteBufferPool pool, ByteBuffer... buffers) { this.callback = callback; this.pool = pool; this.buffers = buffers; } @Override public void succeeded() { for (ByteBuffer buffer : buffers) { assert !buffer.hasRemaining(); pool.release(buffer); } callback.succeeded(); } @Override public void failed(Throwable x) { for (ByteBuffer buffer : buffers) pool.release(buffer); callback.failed(x); } } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/package-info.java000066400000000000000000000061241261716203600311270ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Client : Implementation and Core Classes * * This package provides APIs, utility classes and an implementation of an asynchronous HTTP client. *

* The core class is {@link org.eclipse.jetty.client.api.HttpClient}, which acts as a central configuration object (for example * for {@link org.eclipse.jetty.client.api.HttpClient#setIdleTimeout(long) idle timeouts}, {@link org.eclipse.jetty.client.api.HttpClient#setMaxConnectionsPerDestination(int) * max connections per destination}, etc.) and as a factory for {@link Request} objects. *

* The HTTP protocol is based on the request/response paradigm, a unit that in this implementation is called * exchange and is represented by {@link org.eclipse.jetty.client.api.HttpExchange}. * An initial request may trigger a sequence of exchanges with one or more servers, called a conversation * and represented by {@link org.eclipse.jetty.client.api.HttpConversation}. A typical example of a conversation is a redirect, where * upon a request for a resource URI, the server replies with a redirect (for example with the 303 status code) * to another URI. This conversation is made of a first exchange made of the original request and its 303 response, * and of a second exchange made of the request for the new URI and its 200 response. *

* {@link org.eclipse.jetty.client.api.HttpClient} holds a number of {@link org.eclipse.jetty.client.api.HttpDestination destinations}, which in turn hold a number of * pooled {@link org.eclipse.jetty.client.api.HttpConnection connections}. *

* When a request is sent, its exchange is associated to a connection, either taken from an idle queue or created * anew, and when both the request and response are completed, the exchange is disassociated from the connection. * Conversations may span multiple connections on different destinations, and therefore are maintained at the * {@link org.eclipse.jetty.client.api.HttpClient} level. *

* Applications may decide to send the request and wait for the response in a blocking way, using * {@link org.eclipse.jetty.client.api.Request#send()}. * Alternatively, application may ask to be notified of response events asynchronously, using * {@link org.eclipse.jetty.client.api.Request#send(Response.Listener)}. */ package org.eclipse.jetty.client; jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util/000077500000000000000000000000001261716203600267125ustar00rootroot00000000000000AbstractTypedContentProvider.java000066400000000000000000000023051261716203600353150ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import org.eclipse.jetty.client.api.ContentProvider; public abstract class AbstractTypedContentProvider implements ContentProvider.Typed { private final String contentType; protected AbstractTypedContentProvider(String contentType) { this.contentType = contentType; } @Override public String getContentType() { return contentType; } } BasicAuthentication.java000066400000000000000000000065371261716203600334320ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.net.URI; import java.nio.charset.StandardCharsets; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.B64Code; /** * Implementation of the HTTP "Basic" authentication defined in RFC 2617. *

* Applications should create objects of this class and add them to the * {@link AuthenticationStore} retrieved from the {@link HttpClient} * via {@link HttpClient#getAuthenticationStore()}. */ public class BasicAuthentication implements Authentication { private final URI uri; private final String realm; private final String user; private final String password; /** * @param uri the URI to match for the authentication * @param realm the realm to match for the authentication * @param user the user that wants to authenticate * @param password the password of the user */ public BasicAuthentication(URI uri, String realm, String user, String password) { this.uri = uri; this.realm = realm; this.user = user; this.password = password; } @Override public boolean matches(String type, URI uri, String realm) { if (!"basic".equalsIgnoreCase(type)) return false; if (!uri.toString().startsWith(this.uri.toString())) return false; return this.realm.equals(realm); } @Override public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context) { String value = "Basic " + B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); return new BasicResult(headerInfo.getHeader(), uri, value); } private static class BasicResult implements Result { private final HttpHeader header; private final URI uri; private final String value; public BasicResult(HttpHeader header, URI uri, String value) { this.header = header; this.uri = uri; this.value = value; } @Override public URI getURI() { return uri; } @Override public void apply(Request request) { request.header(header, value); } @Override public String toString() { return String.format("Basic authentication result for %s", uri); } } } BufferingResponseListener.java000066400000000000000000000140601261716203600346330ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Locale; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response.Listener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.BufferUtil; /** *

Implementation of {@link Listener} that buffers the content up to a maximum length * specified to the constructors.

*

The content may be retrieved from {@link #onSuccess(Response)} or {@link #onComplete(Result)} * via {@link #getContent()} or {@link #getContentAsString()}.

*/ public abstract class BufferingResponseListener extends Listener.Adapter { private final int maxLength; private volatile ByteBuffer buffer; private volatile String mediaType; private volatile String encoding; /** * Creates an instance with a default maximum length of 2 MiB. */ public BufferingResponseListener() { this(2 * 1024 * 1024); } /** * Creates an instance with the given maximum length * * @param maxLength the maximum length of the content */ public BufferingResponseListener(int maxLength) { this.maxLength = maxLength; } @Override public void onHeaders(Response response) { super.onHeaders(response); HttpFields headers = response.getHeaders(); long length = headers.getLongField(HttpHeader.CONTENT_LENGTH.asString()); if (length > maxLength) { response.abort(new IllegalArgumentException("Buffering capacity exceeded")); return; } buffer = BufferUtil.allocate(length > 0 ? (int)length : 1024); String contentType = headers.get(HttpHeader.CONTENT_TYPE); if (contentType != null) { String media = contentType; String charset = "charset="; int index = contentType.toLowerCase(Locale.ENGLISH).indexOf(charset); if (index > 0) { media = contentType.substring(0, index); String encoding = contentType.substring(index + charset.length()); // Sometimes charsets arrive with an ending semicolon int semicolon = encoding.indexOf(';'); if (semicolon > 0) encoding = encoding.substring(0, semicolon).trim(); this.encoding = encoding; } int semicolon = media.indexOf(';'); if (semicolon > 0) media = media.substring(0, semicolon).trim(); this.mediaType = media; } } @Override public void onContent(Response response, ByteBuffer content) { int length = content.remaining(); if (length > BufferUtil.space(buffer)) { int requiredCapacity = buffer == null ? 0 : buffer.capacity() + length; if (requiredCapacity > maxLength) response.abort(new IllegalArgumentException("Buffering capacity exceeded")); int newCapacity = Math.min(Integer.highestOneBit(requiredCapacity) << 1, maxLength); buffer = BufferUtil.ensureCapacity(buffer, newCapacity); } BufferUtil.append(buffer, content); } @Override public abstract void onComplete(Result result); public String getMediaType() { return mediaType; } public String getEncoding() { return encoding; } /** * @return the content as bytes * @see #getContentAsString() */ public byte[] getContent() { if (buffer == null) return new byte[0]; return BufferUtil.toArray(buffer); } /** * @return the content as a string, using the "Content-Type" header to detect the encoding * or defaulting to UTF-8 if the encoding could not be detected. * @see #getContentAsString(String) */ public String getContentAsString() { String encoding = this.encoding; if (encoding == null) return getContentAsString(StandardCharsets.UTF_8); return getContentAsString(encoding); } /** * @param encoding the encoding of the content bytes * @return the content as a string, with the specified encoding * @see #getContentAsString() */ public String getContentAsString(String encoding) { if (buffer == null) return null; return BufferUtil.toString(buffer, Charset.forName(encoding)); } /** * @param encoding the encoding of the content bytes * @return the content as a string, with the specified encoding * @see #getContentAsString() */ public String getContentAsString(Charset encoding) { if (buffer == null) return null; return BufferUtil.toString(buffer, encoding); } /** * @return Content as InputStream */ public InputStream getContentAsInputStream() { if (buffer == null) return new ByteArrayInputStream(new byte[]{}); return new ByteArrayInputStream(buffer.array(), buffer.arrayOffset(), buffer.remaining()); } } ByteBufferContentProvider.java000066400000000000000000000053461261716203600346110ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; /** * A {@link ContentProvider} for {@link ByteBuffer}s. *

* The position and limit of the {@link ByteBuffer}s passed to the constructor are not modified, * and each invocation of the {@link #iterator()} method returns a {@link ByteBuffer#slice() slice} * of the original {@link ByteBuffer}. */ public class ByteBufferContentProvider extends AbstractTypedContentProvider { private final ByteBuffer[] buffers; private final int length; public ByteBufferContentProvider(ByteBuffer... buffers) { this("application/octet-stream", buffers); } public ByteBufferContentProvider(String contentType, ByteBuffer... buffers) { super(contentType); this.buffers = buffers; int length = 0; for (ByteBuffer buffer : buffers) length += buffer.remaining(); this.length = length; } @Override public long getLength() { return length; } @Override public Iterator iterator() { return new Iterator() { private int index; @Override public boolean hasNext() { return index < buffers.length; } @Override public ByteBuffer next() { try { ByteBuffer buffer = buffers[index]; buffers[index] = buffer.slice(); ++index; return buffer; } catch (ArrayIndexOutOfBoundsException x) { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } BytesContentProvider.java000066400000000000000000000045061261716203600336370ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; /** * A {@link ContentProvider} for byte arrays. */ public class BytesContentProvider extends AbstractTypedContentProvider { private final byte[][] bytes; private final long length; public BytesContentProvider(byte[]... bytes) { this("application/octet-stream", bytes); } public BytesContentProvider(String contentType, byte[]... bytes) { super(contentType); this.bytes = bytes; long length = 0; for (byte[] buffer : bytes) length += buffer.length; this.length = length; } @Override public long getLength() { return length; } @Override public Iterator iterator() { return new Iterator() { private int index; @Override public boolean hasNext() { return index < bytes.length; } @Override public ByteBuffer next() { try { return ByteBuffer.wrap(bytes[index++]); } catch (ArrayIndexOutOfBoundsException x) { throw new NoSuchElementException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } } DeferredContentProvider.java000066400000000000000000000240321261716203600342650ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.Closeable; import java.io.IOException; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.AsyncContentProvider; import org.eclipse.jetty.client.Synchronizable; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.util.ArrayQueue; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; /** * A {@link ContentProvider} that allows to add content after {@link Request#send(Response.CompleteListener)} * has been called, therefore providing the request content at a later time. *

* {@link DeferredContentProvider} can only be used in conjunction with * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()}) * because it provides content asynchronously. *

* The deferred content is provided once and then fully consumed. * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator * because the stream has been consumed on the first invocation. * However, it is possible for subclasses to override {@link #offer(ByteBuffer)} and {@link #close()} to copy * the content to another location (for example a file) and be able to support multiple invocations * of of {@link #iterator()} returning the iterator provided by this * class on the first invocation, and an iterator on the bytes copied to the other location * for subsequent invocations. *

* Typical usage of {@link DeferredContentProvider} is in asynchronous proxies, where HTTP headers arrive * separately from HTTP content chunks. *

* The deferred content must be provided through {@link #offer(ByteBuffer)}, which can be invoked multiple * times, and when all content has been provided it must be signaled with a call to {@link #close()}. *

* Example usage: *

 * HttpClient httpClient = ...;
 *
 * // Use try-with-resources to autoclose DeferredContentProvider
 * try (DeferredContentProvider content = new DeferredContentProvider())
 * {
 *     httpClient.newRequest("localhost", 8080)
 *             .content(content)
 *             .send(new Response.CompleteListener()
 *             {
 *                 @Override
 *                 public void onComplete(Result result)
 *                 {
 *                     // Your logic here
 *                 }
 *             });
 *
 *     // At a later time...
 *     content.offer(ByteBuffer.wrap("some content".getBytes()));
 * }
 * 
*/ public class DeferredContentProvider implements AsyncContentProvider, Callback, Closeable { private static final Chunk CLOSE = new Chunk(BufferUtil.EMPTY_BUFFER, Callback.Adapter.INSTANCE); private final Object lock = this; private final ArrayQueue chunks = new ArrayQueue<>(4, 64, lock); private final AtomicReference listener = new AtomicReference<>(); private final DeferredContentProviderIterator iterator = new DeferredContentProviderIterator(); private final AtomicBoolean closed = new AtomicBoolean(); private long length = -1; private int size; private Throwable failure; /** * Creates a new {@link DeferredContentProvider} with the given initial content * * @param buffers the initial content */ public DeferredContentProvider(ByteBuffer... buffers) { for (ByteBuffer buffer : buffers) offer(buffer); } @Override public void setListener(Listener listener) { if (!this.listener.compareAndSet(null, listener)) throw new IllegalStateException(String.format("The same %s instance cannot be used in multiple requests", AsyncContentProvider.class.getName())); if (isClosed()) { synchronized (lock) { long total = 0; for (Chunk chunk : chunks) total += chunk.buffer.remaining(); length = total; } } } @Override public long getLength() { return length; } /** * Adds the given content buffer to this content provider * and notifies the listener that content is available. * * @param buffer the content to add * @return true if the content was added, false otherwise */ public boolean offer(ByteBuffer buffer) { return offer(buffer, Callback.Adapter.INSTANCE); } public boolean offer(ByteBuffer buffer, Callback callback) { return offer(new Chunk(buffer, callback)); } private boolean offer(Chunk chunk) { Throwable failure; boolean result = false; synchronized (lock) { failure = this.failure; if (failure == null) { result = chunks.offer(chunk); if (result && chunk != CLOSE) ++size; } } if (failure != null) chunk.callback.failed(failure); else if (result) notifyListener(); return result; } private void clear() { synchronized (lock) { chunks.clear(); } } public void flush() throws IOException { synchronized (lock) { try { while (true) { if (failure != null) throw new IOException(failure); if (size == 0) break; lock.wait(); } } catch (InterruptedException x) { throw new InterruptedIOException(); } } } /** * No more content will be added to this content provider * and notifies the listener that no more content is available. */ public void close() { if (closed.compareAndSet(false, true)) offer(CLOSE); } public boolean isClosed() { return closed.get(); } @Override public void succeeded() { } @Override public void failed(Throwable failure) { iterator.failed(failure); } private void notifyListener() { Listener listener = this.listener.get(); if (listener != null) listener.onContent(); } @Override public Iterator iterator() { return iterator; } private class DeferredContentProviderIterator implements Iterator, Callback, Synchronizable { private Chunk current; @Override public boolean hasNext() { synchronized (lock) { return chunks.peek() != CLOSE; } } @Override public ByteBuffer next() { synchronized (lock) { Chunk chunk = current = chunks.poll(); if (chunk == CLOSE) { // Slow path: reinsert the CLOSE chunk // so that hasNext() works correctly. chunks.add(0, CLOSE); throw new NoSuchElementException(); } return chunk == null ? null : chunk.buffer; } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void succeeded() { Chunk chunk; synchronized (lock) { chunk = current; if (chunk != null) { --size; lock.notify(); } } if (chunk != null) chunk.callback.succeeded(); } @Override public void failed(Throwable x) { List chunks = new ArrayList<>(); synchronized (lock) { failure = x; // Transfer all chunks to fail them all. Chunk chunk = current; current = null; if (chunk != null) chunks.add(chunk); chunks.addAll(DeferredContentProvider.this.chunks); clear(); lock.notify(); } for (Chunk chunk : chunks) chunk.callback.failed(x); } @Override public Object getLock() { return lock; } } public static class Chunk { public final ByteBuffer buffer; public final Callback callback; public Chunk(ByteBuffer buffer, Callback callback) { this.buffer = Objects.requireNonNull(buffer); this.callback = Objects.requireNonNull(callback); } @Override public String toString() { return String.format("%s@%x", getClass().getSimpleName(), hashCode()); } } } DigestAuthentication.java000066400000000000000000000233351261716203600336230ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.net.URI; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.TypeUtil; /** * Implementation of the HTTP "Digest" authentication defined in RFC 2617. *

* Applications should create objects of this class and add them to the * {@link AuthenticationStore} retrieved from the {@link HttpClient} * via {@link HttpClient#getAuthenticationStore()}. */ public class DigestAuthentication implements Authentication { private static final Pattern PARAM_PATTERN = Pattern.compile("([^=]+)=(.*)"); private final URI uri; private final String realm; private final String user; private final String password; /** * @param uri the URI to match for the authentication * @param realm the realm to match for the authentication * @param user the user that wants to authenticate * @param password the password of the user */ public DigestAuthentication(URI uri, String realm, String user, String password) { this.uri = uri; this.realm = realm; this.user = user; this.password = password; } @Override public boolean matches(String type, URI uri, String realm) { if (!"digest".equalsIgnoreCase(type)) return false; if (!uri.toString().startsWith(this.uri.toString())) return false; return this.realm.equals(realm); } @Override public Result authenticate(Request request, ContentResponse response, HeaderInfo headerInfo, Attributes context) { Map params = parseParameters(headerInfo.getParameters()); String nonce = params.get("nonce"); if (nonce == null || nonce.length() == 0) return null; String opaque = params.get("opaque"); String algorithm = params.get("algorithm"); if (algorithm == null) algorithm = "MD5"; MessageDigest digester = getMessageDigest(algorithm); if (digester == null) return null; String serverQOP = params.get("qop"); String clientQOP = null; if (serverQOP != null) { List serverQOPValues = Arrays.asList(serverQOP.split(",")); if (serverQOPValues.contains("auth")) clientQOP = "auth"; else if (serverQOPValues.contains("auth-int")) clientQOP = "auth-int"; } return new DigestResult(headerInfo.getHeader(), uri, response.getContent(), realm, user, password, algorithm, nonce, clientQOP, opaque); } private Map parseParameters(String wwwAuthenticate) { Map result = new HashMap<>(); List parts = splitParams(wwwAuthenticate); for (String part : parts) { Matcher matcher = PARAM_PATTERN.matcher(part); if (matcher.matches()) { String name = matcher.group(1).trim().toLowerCase(Locale.ENGLISH); String value = matcher.group(2).trim(); if (value.startsWith("\"") && value.endsWith("\"")) value = value.substring(1, value.length() - 1); result.put(name, value); } } return result; } private List splitParams(String paramString) { List result = new ArrayList<>(); int start = 0; for (int i = 0; i < paramString.length(); ++i) { int quotes = 0; char ch = paramString.charAt(i); switch (ch) { case '\\': ++i; break; case '"': ++quotes; break; case ',': if (quotes % 2 == 0) { String element = paramString.substring(start, i).trim(); if (element.length() > 0) result.add(element); start = i + 1; } break; default: break; } } result.add(paramString.substring(start, paramString.length()).trim()); return result; } private MessageDigest getMessageDigest(String algorithm) { try { return MessageDigest.getInstance(algorithm); } catch (NoSuchAlgorithmException x) { return null; } } private class DigestResult implements Result { private final AtomicInteger nonceCount = new AtomicInteger(); private final HttpHeader header; private final URI uri; private final byte[] content; private final String realm; private final String user; private final String password; private final String algorithm; private final String nonce; private final String qop; private final String opaque; public DigestResult(HttpHeader header, URI uri, byte[] content, String realm, String user, String password, String algorithm, String nonce, String qop, String opaque) { this.header = header; this.uri = uri; this.content = content; this.realm = realm; this.user = user; this.password = password; this.algorithm = algorithm; this.nonce = nonce; this.qop = qop; this.opaque = opaque; } @Override public URI getURI() { return uri; } @Override public void apply(Request request) { MessageDigest digester = getMessageDigest(algorithm); if (digester == null) return; String A1 = user + ":" + realm + ":" + password; String hashA1 = toHexString(digester.digest(A1.getBytes(StandardCharsets.ISO_8859_1))); String A2 = request.getMethod() + ":" + request.getURI(); if ("auth-int".equals(qop)) A2 += ":" + toHexString(digester.digest(content)); String hashA2 = toHexString(digester.digest(A2.getBytes(StandardCharsets.ISO_8859_1))); String nonceCount; String clientNonce; String A3; if (qop != null) { nonceCount = nextNonceCount(); clientNonce = newClientNonce(); A3 = hashA1 + ":" + nonce + ":" + nonceCount + ":" + clientNonce + ":" + qop + ":" + hashA2; } else { nonceCount = null; clientNonce = null; A3 = hashA1 + ":" + nonce + ":" + hashA2; } String hashA3 = toHexString(digester.digest(A3.getBytes(StandardCharsets.ISO_8859_1))); StringBuilder value = new StringBuilder("Digest"); value.append(" username=\"").append(user).append("\""); value.append(", realm=\"").append(realm).append("\""); value.append(", nonce=\"").append(nonce).append("\""); if (opaque != null) value.append(", opaque=\"").append(opaque).append("\""); value.append(", algorithm=\"").append(algorithm).append("\""); value.append(", uri=\"").append(request.getURI()).append("\""); if (qop != null) { value.append(", qop=\"").append(qop).append("\""); value.append(", nc=\"").append(nonceCount).append("\""); value.append(", cnonce=\"").append(clientNonce).append("\""); } value.append(", response=\"").append(hashA3).append("\""); request.header(header, value.toString()); } private String nextNonceCount() { String padding = "00000000"; String next = Integer.toHexString(nonceCount.incrementAndGet()).toLowerCase(Locale.ENGLISH); return padding.substring(0, padding.length() - next.length()) + next; } private String newClientNonce() { Random random = new Random(); byte[] bytes = new byte[8]; random.nextBytes(bytes); return toHexString(bytes); } private String toHexString(byte[] bytes) { return TypeUtil.toHexString(bytes).toLowerCase(Locale.ENGLISH); } } } FormContentProvider.java000066400000000000000000000047261261716203600334600ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.charset.UnsupportedCharsetException; import org.eclipse.jetty.util.Fields; /** * A {@link ContentProvider} for form uploads with the * "application/x-www-form-urlencoded" content type. */ public class FormContentProvider extends StringContentProvider { public FormContentProvider(Fields fields) { this(fields, StandardCharsets.UTF_8); } public FormContentProvider(Fields fields, Charset charset) { super("application/x-www-form-urlencoded", convert(fields, charset), charset); } public static String convert(Fields fields) { return convert(fields, StandardCharsets.UTF_8); } public static String convert(Fields fields, Charset charset) { // Assume 32 chars between name and value. StringBuilder builder = new StringBuilder(fields.getSize() * 32); for (Fields.Field field : fields) { for (String value : field.getValues()) { if (builder.length() > 0) builder.append("&"); builder.append(encode(field.getName(), charset)).append("=").append(encode(value, charset)); } } return builder.toString(); } private static String encode(String value, Charset charset) { try { return URLEncoder.encode(value, charset.name()); } catch (UnsupportedEncodingException x) { throw new UnsupportedCharsetException(charset.name()); } } } FutureResponseListener.java000066400000000000000000000074221261716203600342020ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.client.HttpContentResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Result; /** * A {@link BufferingResponseListener} that is also a {@link Future}, to allow applications * to block (indefinitely or for a timeout) until {@link #onComplete(Result)} is called, * or to {@link #cancel(boolean) abort} the request/response conversation. *

* Typical usage is: *

 * Request request = httpClient.newRequest(...)...;
 * FutureResponseListener listener = new FutureResponseListener(request);
 * request.send(listener); // Asynchronous send
 * ContentResponse response = listener.get(5, TimeUnit.SECONDS); // Timed block
 * 
*/ public class FutureResponseListener extends BufferingResponseListener implements Future { private final CountDownLatch latch = new CountDownLatch(1); private final Request request; private ContentResponse response; private Throwable failure; private volatile boolean cancelled; public FutureResponseListener(Request request) { this(request, 2 * 1024 * 1024); } public FutureResponseListener(Request request, int maxLength) { super(maxLength); this.request = request; } public Request getRequest() { return request; } @Override public void onComplete(Result result) { response = new HttpContentResponse(result.getResponse(), getContent(), getMediaType(), getEncoding()); failure = result.getFailure(); latch.countDown(); } @Override public boolean cancel(boolean mayInterruptIfRunning) { cancelled = true; return request.abort(new CancellationException()); } @Override public boolean isCancelled() { return cancelled; } @Override public boolean isDone() { return latch.getCount() == 0 || isCancelled(); } @Override public ContentResponse get() throws InterruptedException, ExecutionException { latch.await(); return getResult(); } @Override public ContentResponse get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { boolean expired = !latch.await(timeout, unit); if (expired) throw new TimeoutException(); return getResult(); } private ContentResponse getResult() throws ExecutionException { if (isCancelled()) throw (CancellationException)new CancellationException().initCause(failure); if (failure != null) throw new ExecutionException(failure); return response; } } InputStreamContentProvider.java000066400000000000000000000211331261716203600350170ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * A {@link ContentProvider} for an {@link InputStream}. *

* The input stream is read once and therefore fully consumed. * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator * because the stream has been consumed on the first invocation. *

* However, it is possible for subclasses to override {@link #onRead(byte[], int, int)} to copy * the content read from the stream to another location (for example a file), and be able to * support multiple invocations of {@link #iterator()}, returning the iterator provided by this * class on the first invocation, and an iterator on the bytes copied to the other location * for subsequent invocations. *

* It is possible to specify, at the constructor, a buffer size used to read content from the * stream, by default 4096 bytes. *

* The {@link InputStream} passed to the constructor is by default closed when is it fully * consumed (or when an exception is thrown while reading it), unless otherwise specified * to the {@link #InputStreamContentProvider(java.io.InputStream, int, boolean) constructor}. */ public class InputStreamContentProvider implements ContentProvider, Callback, Closeable { private static final Logger LOG = Log.getLogger(InputStreamContentProvider.class); private final InputStreamContentProviderIterator iterator = new InputStreamContentProviderIterator(); private final InputStream stream; private final int bufferSize; private final boolean autoClose; public InputStreamContentProvider(InputStream stream) { this(stream, 4096); } public InputStreamContentProvider(InputStream stream, int bufferSize) { this(stream, bufferSize, true); } public InputStreamContentProvider(InputStream stream, int bufferSize, boolean autoClose) { this.stream = stream; this.bufferSize = bufferSize; this.autoClose = autoClose; } @Override public long getLength() { return -1; } /** * Callback method invoked just after having read from the stream, * but before returning the iteration element (a {@link ByteBuffer} * to the caller. *

* Subclasses may override this method to copy the content read from * the stream to another location (a file, or in memory if the content * is known to fit). * * @param buffer the byte array containing the bytes read * @param offset the offset from where bytes should be read * @param length the length of the bytes read * @return a {@link ByteBuffer} wrapping the byte array */ protected ByteBuffer onRead(byte[] buffer, int offset, int length) { if (length <= 0) return BufferUtil.EMPTY_BUFFER; return ByteBuffer.wrap(buffer, offset, length); } /** * Callback method invoked when an exception is thrown while reading * from the stream. * * @param failure the exception thrown while reading from the stream. */ protected void onReadFailure(Throwable failure) { } @Override public Iterator iterator() { return iterator; } @Override public void close() { if (autoClose) { try { stream.close(); } catch (IOException x) { LOG.ignore(x); } } } @Override public void succeeded() { } @Override public void failed(Throwable failure) { // TODO: forward the failure to the iterator. close(); } /** * Iterating over an {@link InputStream} is tricky, because {@link #hasNext()} must return false * if the stream reads -1. However, we don't know what to return until we read the stream, which * means that stream reading must be performed by {@link #hasNext()}, which introduces a side-effect * on what is supposed to be a simple query method (with respect to the Query Command Separation * Principle). *

* Alternatively, we could return {@code true} from {@link #hasNext()} even if we don't know that * we will read -1, but then when {@link #next()} reads -1 it must return an empty buffer. * However this is problematic, since GETs with no content indication would become GET with chunked * content, and not understood by servers. *

* Therefore we need to make sure that {@link #hasNext()} does not perform any side effect (so that * it can be called multiple times) until {@link #next()} is called. */ private class InputStreamContentProviderIterator implements Iterator, Closeable { private Throwable failure; private ByteBuffer buffer; private Boolean hasNext; @Override public boolean hasNext() { try { if (hasNext != null) return hasNext; byte[] bytes = new byte[bufferSize]; int read = stream.read(bytes); if (LOG.isDebugEnabled()) LOG.debug("Read {} bytes from {}", read, stream); if (read > 0) { hasNext = Boolean.TRUE; buffer = onRead(bytes, 0, read); return true; } else if (read < 0) { hasNext = Boolean.FALSE; buffer = null; close(); return false; } else { hasNext = Boolean.TRUE; buffer = BufferUtil.EMPTY_BUFFER; return true; } } catch (Throwable x) { if (LOG.isDebugEnabled()) LOG.debug(x); if (failure == null) { failure = x; onReadFailure(x); // Signal we have more content to cause a call to // next() which will throw NoSuchElementException. hasNext = Boolean.TRUE; buffer = null; close(); return true; } throw new IllegalStateException(); } } @Override public ByteBuffer next() { if (failure != null) { // Consume the failure so that calls to hasNext() will return false. hasNext = Boolean.FALSE; buffer = null; throw (NoSuchElementException)new NoSuchElementException().initCause(failure); } if (!hasNext()) throw new NoSuchElementException(); ByteBuffer result = buffer; if (result == null) { hasNext = Boolean.FALSE; buffer = null; throw new NoSuchElementException(); } else { hasNext = null; buffer = null; return result; } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { InputStreamContentProvider.this.close(); } } } InputStreamResponseListener.java000066400000000000000000000260461261716203600352060ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousCloseException; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Response.Listener; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * Implementation of {@link Listener} that produces an {@link InputStream} * that allows applications to read the response content. *

* Typical usage is: *

 * InputStreamResponseListener listener = new InputStreamResponseListener();
 * client.newRequest(...).send(listener);
 *
 * // Wait for the response headers to arrive
 * Response response = listener.get(5, TimeUnit.SECONDS);
 * if (response.getStatus() == 200)
 * {
 *     // Obtain the input stream on the response content
 *     try (InputStream input = listener.getInputStream())
 *     {
 *         // Read the response content
 *     }
 * }
 * 
*

* The {@link HttpClient} implementation (the producer) will feed the input stream * asynchronously while the application (the consumer) is reading from it. * Chunks of content are maintained in a queue, and it is possible to specify a * maximum buffer size for the bytes held in the queue, by default 16384 bytes. *

* If the consumer is faster than the producer, then the consumer will block * with the typical {@link InputStream#read()} semantic. * If the consumer is slower than the producer, then the producer will block * until the client consumes. */ public class InputStreamResponseListener extends Listener.Adapter { private static final Logger LOG = Log.getLogger(InputStreamResponseListener.class); private static final byte[] EOF = new byte[0]; private static final byte[] CLOSED = new byte[0]; private static final byte[] FAILURE = new byte[0]; private final BlockingQueue queue = new LinkedBlockingQueue<>(); private final AtomicLong length = new AtomicLong(); private final CountDownLatch responseLatch = new CountDownLatch(1); private final CountDownLatch resultLatch = new CountDownLatch(1); private final AtomicReference stream = new AtomicReference<>(); private final long maxBufferSize; private Response response; private Result result; private volatile Throwable failure; private volatile boolean closed; public InputStreamResponseListener() { this(16 * 1024L); } public InputStreamResponseListener(long maxBufferSize) { this.maxBufferSize = maxBufferSize; } @Override public void onHeaders(Response response) { this.response = response; responseLatch.countDown(); } @Override public void onContent(Response response, ByteBuffer content) { if (!closed) { int remaining = content.remaining(); if (remaining > 0) { byte[] bytes = new byte[remaining]; content.get(bytes); if (LOG.isDebugEnabled()) LOG.debug("Queuing {}/{} bytes", bytes, remaining); queue.offer(bytes); long newLength = length.addAndGet(remaining); while (newLength >= maxBufferSize) { if (LOG.isDebugEnabled()) LOG.debug("Queued bytes limit {}/{} exceeded, waiting", newLength, maxBufferSize); // Block to avoid infinite buffering if (!await()) break; newLength = length.get(); if (LOG.isDebugEnabled()) LOG.debug("Queued bytes limit {}/{} exceeded, woken up", newLength, maxBufferSize); } } else { if (LOG.isDebugEnabled()) LOG.debug("Queuing skipped, empty content {}", content); } } else { LOG.debug("Queuing skipped, stream already closed"); } } @Override public void onSuccess(Response response) { if (LOG.isDebugEnabled()) LOG.debug("Queuing end of content {}{}", EOF, ""); queue.offer(EOF); signal(); } @Override public void onFailure(Response response, Throwable failure) { fail(failure); signal(); } @Override public void onComplete(Result result) { if (result.isFailed() && failure == null) fail(result.getFailure()); this.result = result; resultLatch.countDown(); signal(); } private void fail(Throwable failure) { if (LOG.isDebugEnabled()) LOG.debug("Queuing failure {} {}", FAILURE, failure); queue.offer(FAILURE); this.failure = failure; responseLatch.countDown(); } protected boolean await() { try { synchronized (this) { while (length.get() >= maxBufferSize && failure == null && !closed) wait(); // Re-read the values as they may have changed while waiting. return failure == null && !closed; } } catch (InterruptedException x) { Thread.currentThread().interrupt(); return false; } } protected void signal() { synchronized (this) { notifyAll(); } } /** * Waits for the given timeout for the response to be available, then returns it. *

* The wait ends as soon as all the HTTP headers have been received, without waiting for the content. * To wait for the whole content, see {@link #await(long, TimeUnit)}. * * @param timeout the time to wait * @param unit the timeout unit * @return the response * @throws InterruptedException if the thread is interrupted * @throws TimeoutException if the timeout expires * @throws ExecutionException if a failure happened */ public Response get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException { boolean expired = !responseLatch.await(timeout, unit); if (expired) throw new TimeoutException(); if (failure != null) throw new ExecutionException(failure); return response; } /** * Waits for the given timeout for the whole request/response cycle to be finished, * then returns the corresponding result. *

* * @param timeout the time to wait * @param unit the timeout unit * @return the result * @throws InterruptedException if the thread is interrupted * @throws TimeoutException if the timeout expires * @see #get(long, TimeUnit) */ public Result await(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { boolean expired = !resultLatch.await(timeout, unit); if (expired) throw new TimeoutException(); return result; } /** * Returns an {@link InputStream} providing the response content bytes. *

* The method may be invoked only once; subsequent invocations will return a closed {@link InputStream}. * * @return an input stream providing the response content */ public InputStream getInputStream() { InputStream result = new Input(); if (stream.compareAndSet(null, result)) return result; return IO.getClosedStream(); } private class Input extends InputStream { private byte[] bytes; private int index; @Override public int read() throws IOException { while (true) { if (bytes == EOF) { // Mark the fact that we saw -1, // so that in the close case we don't throw index = -1; return -1; } else if (bytes == FAILURE) { throw failure(); } else if (bytes == CLOSED) { if (index < 0) return -1; throw new AsynchronousCloseException(); } else if (bytes != null) { int result = bytes[index] & 0xFF; if (++index == bytes.length) { length.addAndGet(-index); bytes = null; index = 0; signal(); } return result; } else { bytes = take(); if (LOG.isDebugEnabled()) LOG.debug("Dequeued {}/{} bytes", bytes, bytes.length); } } } private IOException failure() { if (failure instanceof IOException) return (IOException)failure; else return new IOException(failure); } private byte[] take() throws IOException { try { return queue.take(); } catch (InterruptedException x) { throw new InterruptedIOException(); } } @Override public void close() throws IOException { if (!closed) { super.close(); if (LOG.isDebugEnabled()) LOG.debug("Queuing close {}{}", CLOSED, ""); queue.offer(CLOSED); closed = true; signal(); } } } } OutputStreamContentProvider.java000066400000000000000000000111531261716203600352210ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.Iterator; import org.eclipse.jetty.client.AsyncContentProvider; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.util.Callback; /** * A {@link ContentProvider} that provides content asynchronously through an {@link OutputStream} * similar to {@link DeferredContentProvider}. *

* {@link OutputStreamContentProvider} can only be used in conjunction with * {@link Request#send(Response.CompleteListener)} (and not with its blocking counterpart {@link Request#send()}) * because it provides content asynchronously. *

* The deferred content is provided once by writing to the {@link #getOutputStream() output stream} * and then fully consumed. * Invocations to the {@link #iterator()} method after the first will return an "empty" iterator * because the stream has been consumed on the first invocation. * However, it is possible for subclasses to support multiple invocations of {@link #iterator()} * by overriding {@link #write(ByteBuffer)} and {@link #close()}, copying the bytes and making them * available for subsequent invocations. *

* Content must be provided by writing to the {@link #getOutputStream() output stream}, that must be * {@link OutputStream#close() closed} when all content has been provided. *

* Example usage: *

 * HttpClient httpClient = ...;
 *
 * // Use try-with-resources to autoclose the output stream
 * OutputStreamContentProvider content = new OutputStreamContentProvider();
 * try (OutputStream output = content.getOutputStream())
 * {
 *     httpClient.newRequest("localhost", 8080)
 *             .content(content)
 *             .send(new Response.CompleteListener()
 *             {
 *                 @Override
 *                 public void onComplete(Result result)
 *                 {
 *                     // Your logic here
 *                 }
 *             });
 *
 *     // At a later time...
 *     output.write("some content".getBytes());
 * }
 * 
*/ public class OutputStreamContentProvider implements AsyncContentProvider, Callback, Closeable { private final DeferredContentProvider deferred = new DeferredContentProvider(); private final OutputStream output = new DeferredOutputStream(); @Override public long getLength() { return deferred.getLength(); } @Override public Iterator iterator() { return deferred.iterator(); } @Override public void setListener(Listener listener) { deferred.setListener(listener); } public OutputStream getOutputStream() { return output; } protected void write(ByteBuffer buffer) { deferred.offer(buffer); } @Override public void close() { deferred.close(); } @Override public void succeeded() { deferred.succeeded(); } @Override public void failed(Throwable failure) { deferred.failed(failure); } private class DeferredOutputStream extends OutputStream { @Override public void write(int b) throws IOException { write(new byte[]{(byte)b}, 0, 1); } @Override public void write(byte[] b, int off, int len) throws IOException { OutputStreamContentProvider.this.write(ByteBuffer.wrap(b, off, len)); flush(); } @Override public void flush() throws IOException { deferred.flush(); } @Override public void close() throws IOException { OutputStreamContentProvider.this.close(); } } } PathContentProvider.java000066400000000000000000000111671261716203600334460ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SeekableByteChannel; import java.nio.file.AccessDeniedException; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Iterator; import java.util.NoSuchElementException; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * A {@link ContentProvider} for files using JDK 7's {@code java.nio.file} APIs. *

* It is possible to specify, at the constructor, a buffer size used to read content from the * stream, by default 4096 bytes. */ public class PathContentProvider extends AbstractTypedContentProvider { private static final Logger LOG = Log.getLogger(PathContentProvider.class); private final Path filePath; private final long fileSize; private final int bufferSize; public PathContentProvider(Path filePath) throws IOException { this(filePath, 4096); } public PathContentProvider(Path filePath, int bufferSize) throws IOException { this("application/octet-stream", filePath, bufferSize); } public PathContentProvider(String contentType, Path filePath) throws IOException { this(contentType, filePath, 4096); } public PathContentProvider(String contentType, Path filePath, int bufferSize) throws IOException { super(contentType); if (!Files.isRegularFile(filePath)) throw new NoSuchFileException(filePath.toString()); if (!Files.isReadable(filePath)) throw new AccessDeniedException(filePath.toString()); this.filePath = filePath; this.fileSize = Files.size(filePath); this.bufferSize = bufferSize; } @Override public long getLength() { return fileSize; } @Override public Iterator iterator() { return new PathIterator(); } private class PathIterator implements Iterator, Closeable { private final ByteBuffer buffer = ByteBuffer.allocateDirect(bufferSize); private SeekableByteChannel channel; private long position; @Override public boolean hasNext() { return position < getLength(); } @Override public ByteBuffer next() { try { if (channel == null) { channel = Files.newByteChannel(filePath, StandardOpenOption.READ); if (LOG.isDebugEnabled()) LOG.debug("Opened file {}", filePath); } buffer.clear(); int read = channel.read(buffer); if (read < 0) throw new NoSuchElementException(); if (LOG.isDebugEnabled()) LOG.debug("Read {} bytes from {}", read, filePath); position += read; if (!hasNext()) close(); buffer.flip(); return buffer; } catch (NoSuchElementException x) { close(); throw x; } catch (Exception x) { close(); throw (NoSuchElementException)new NoSuchElementException().initCause(x); } } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public void close() { try { if (channel != null) channel.close(); } catch (Exception x) { LOG.ignore(x); } } } } StringContentProvider.java000066400000000000000000000032461261716203600340170ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * A {@link ContentProvider} for strings. *

* It is possible to specify, at the constructor, an encoding used to convert * the string into bytes, by default UTF-8. */ public class StringContentProvider extends BytesContentProvider { public StringContentProvider(String content) { this(content, StandardCharsets.UTF_8); } public StringContentProvider(String content, String encoding) { this(content, Charset.forName(encoding)); } public StringContentProvider(String content, Charset charset) { this("text/plain;charset=" + charset.name(), content, charset); } public StringContentProvider(String contentType, String content, Charset charset) { super(contentType, content.getBytes(charset)); } } jetty-9.2.14.v20151106/jetty-client/src/main/java/org/eclipse/jetty/client/util/package-info.java000066400000000000000000000015551261716203600321070ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Client : Utility Classes */ package org.eclipse.jetty.client.util; jetty-9.2.14.v20151106/jetty-client/src/test/000077500000000000000000000000001261716203600202175ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/000077500000000000000000000000001261716203600211405ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/000077500000000000000000000000001261716203600217275ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/000077500000000000000000000000001261716203600233535ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600245125ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/000077500000000000000000000000001261716203600257705ustar00rootroot00000000000000AbstractHttpClientServerTest.java000066400000000000000000000071351261716203600343530ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.Arrays; import java.util.Collection; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Rule; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public abstract class AbstractHttpClientServerTest { @Parameterized.Parameters public static Collection parameters() { return Arrays.asList(new SslContextFactory[]{null}, new SslContextFactory[]{new SslContextFactory()}); } @Rule public final TestTracker tracker = new TestTracker(); protected SslContextFactory sslContextFactory; protected String scheme; protected Server server; protected HttpClient client; protected NetworkConnector connector; public AbstractHttpClientServerTest(SslContextFactory sslContextFactory) { this.sslContextFactory = sslContextFactory; this.scheme = (sslContextFactory == null ? HttpScheme.HTTP : HttpScheme.HTTPS).asString(); } public void start(Handler handler) throws Exception { startServer(handler); startClient(); } protected void startServer(Handler handler) throws Exception { if (sslContextFactory != null) { sslContextFactory.setEndpointIdentificationAlgorithm(""); sslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setTrustStorePath("src/test/resources/truststore.jks"); sslContextFactory.setTrustStorePassword("storepwd"); } if (server == null) { QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); server = new Server(serverThreads); } connector = new ServerConnector(server, sslContextFactory); server.addConnector(connector); server.setHandler(handler); server.start(); } protected void startClient() throws Exception { QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); client = new HttpClient(sslContextFactory); client.setExecutor(clientThreads); client.start(); } @After public void dispose() throws Exception { if (client != null) client.stop(); if (server != null) server.stop(); server = null; } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ContentResponseTest.java000066400000000000000000000114301261716203600326230ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.util.Random; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class ContentResponseTest extends AbstractHttpClientServerTest { public ContentResponseTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testResponseWithoutContentType() throws Exception { final byte[] content = new byte[1024]; new Random().nextBytes(content); start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(content); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(content, response.getContent()); Assert.assertNull(response.getMediaType()); Assert.assertNull(response.getEncoding()); } @Test public void testResponseWithMediaType() throws Exception { final String content = "The quick brown fox jumped over the lazy dog"; final String mediaType = "text/plain"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader(HttpHeader.CONTENT_TYPE.asString(), mediaType); response.getOutputStream().write(content.getBytes("UTF-8")); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(content, response.getContentAsString()); Assert.assertEquals(mediaType, response.getMediaType()); Assert.assertNull(response.getEncoding()); } @Test public void testResponseWithContentType() throws Exception { final String content = "The quick brown fox jumped over the lazy dog"; final String mediaType = "text/plain"; final String encoding = "UTF-8"; final String contentType = mediaType + "; charset=" + encoding; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader(HttpHeader.CONTENT_TYPE.asString(), contentType); response.getOutputStream().write(content.getBytes("UTF-8")); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(content, response.getContentAsString()); Assert.assertEquals(mediaType, response.getMediaType()); Assert.assertEquals(encoding, response.getEncoding()); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/EmptyServerHandler.java000066400000000000000000000025311261716203600324170ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; public class EmptyServerHandler extends AbstractHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ExternalSiteTest.java000066400000000000000000000133651261716203600321120ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.Socket; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; public class ExternalSiteTest { @Rule public final TestTracker tracker = new TestTracker(); private HttpClient client; @Before public void prepare() throws Exception { client = new HttpClient(new SslContextFactory()); client.start(); } @After public void dispose() throws Exception { client.stop(); } @Test public void testExternalSite() throws Exception { String host = "wikipedia.org"; int port = 80; // Verify that we have connectivity assumeCanConnectTo(host, port); final CountDownLatch latch1 = new CountDownLatch(1); client.newRequest(host, port).send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (!result.isFailed() && result.getResponse().getStatus() == 200) latch1.countDown(); } }); Assert.assertTrue(latch1.await(10, TimeUnit.SECONDS)); // Try again the same URI, but without specifying the port final CountDownLatch latch2 = new CountDownLatch(1); client.newRequest("http://" + host).send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isSucceeded()); Assert.assertEquals(200, result.getResponse().getStatus()); latch2.countDown(); } }); Assert.assertTrue(latch2.await(10, TimeUnit.SECONDS)); } @Test public void testExternalSSLSite() throws Exception { client.stop(); client = new HttpClient(new SslContextFactory()); client.start(); String host = "api-3t.paypal.com"; int port = 443; // Verify that we have connectivity assumeCanConnectTo(host, port); final CountDownLatch latch = new CountDownLatch(1); client.newRequest(host, port).scheme("https").path("/nvp").send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200) latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testExternalSiteWrongProtocol() throws Exception { String host = "github.com"; int port = 22; // SSH port // Verify that we have connectivity assumeCanConnectTo(host, port); for (int i = 0; i < 2; ++i) { final CountDownLatch latch = new CountDownLatch(3); client.newRequest(host, port) .onResponseFailure(new Response.FailureListener() { @Override public void onFailure(Response response, Throwable failure) { latch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onFailure(Response response, Throwable failure) { latch.countDown(); } @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); } } @Test public void testExternalSiteRedirect() throws Exception { String host = "twitter.com"; int port = 443; // Verify that we have connectivity assumeCanConnectTo(host, port); ContentResponse response = client.newRequest(host, port) .scheme(HttpScheme.HTTPS.asString()) .path("/twitter") .send(); Assert.assertEquals(200, response.getStatus()); } protected void assumeCanConnectTo(String host, int port) { try { new Socket(host, port).close(); } catch (Throwable x) { Assume.assumeNoException(x); } } } GZIPContentDecoderTest.java000066400000000000000000000255621261716203600330200ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import org.eclipse.jetty.toolchain.test.TestTracker; import org.junit.Rule; import org.junit.Test; public class GZIPContentDecoderTest { @Rule public final TestTracker tracker = new TestTracker(); @Test public void testStreamNoBlocks() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.close(); byte[] bytes = baos.toByteArray(); GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1); int read = input.read(); assertEquals(-1, read); } @Test public void testStreamBigBlockOneByteAtATime() throws Exception { String data = "0123456789ABCDEF"; for (int i = 0; i < 10; ++i) data += data; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); baos = new ByteArrayOutputStream(); GZIPInputStream input = new GZIPInputStream(new ByteArrayInputStream(bytes), 1); int read; while ((read = input.read()) >= 0) baos.write(read); assertEquals(data, new String(baos.toByteArray(), StandardCharsets.UTF_8)); } @Test public void testNoBlocks() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.close(); byte[] bytes = baos.toByteArray(); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); assertEquals(0, decoded.remaining()); } @Test public void testSmallBlock() throws Exception { String data = "0"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes)); assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); } @Test public void testSmallBlockWithGZIPChunkedAtBegin() throws Exception { String data = "0"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); // The header is 10 bytes, chunk at 11 bytes byte[] bytes1 = new byte[11]; System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); byte[] bytes2 = new byte[bytes.length - bytes1.length]; System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); assertEquals(0, decoded.capacity()); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); } @Test public void testSmallBlockWithGZIPChunkedAtEnd() throws Exception { String data = "0"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); // The trailer is 8 bytes, chunk the last 9 bytes byte[] bytes1 = new byte[bytes.length - 9]; System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); byte[] bytes2 = new byte[bytes.length - bytes1.length]; System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); assertFalse(decoder.isFinished()); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); assertEquals(0, decoded.remaining()); assertTrue(decoder.isFinished()); } @Test public void testSmallBlockWithGZIPTrailerChunked() throws Exception { String data = "0"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); // The trailer is 4+4 bytes, chunk the last 3 bytes byte[] bytes1 = new byte[bytes.length - 3]; System.arraycopy(bytes, 0, bytes1, 0, bytes1.length); byte[] bytes2 = new byte[bytes.length - bytes1.length]; System.arraycopy(bytes, bytes1.length, bytes2, 0, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes1)); assertEquals(0, decoded.capacity()); decoded = decoder.decode(ByteBuffer.wrap(bytes2)); assertEquals(data, StandardCharsets.UTF_8.decode(decoded).toString()); } @Test public void testTwoSmallBlocks() throws Exception { String data1 = "0"; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data1.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes1 = baos.toByteArray(); String data2 = "1"; baos = new ByteArrayOutputStream(); output = new GZIPOutputStream(baos); output.write(data2.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes2 = baos.toByteArray(); byte[] bytes = new byte[bytes1.length + bytes2.length]; System.arraycopy(bytes1, 0, bytes, 0, bytes1.length); System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length); GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer buffer = ByteBuffer.wrap(bytes); ByteBuffer decoded = decoder.decode(buffer); assertEquals(data1, StandardCharsets.UTF_8.decode(decoded).toString()); assertTrue(decoder.isFinished()); assertTrue(buffer.hasRemaining()); decoded = decoder.decode(buffer); assertEquals(data2, StandardCharsets.UTF_8.decode(decoded).toString()); assertTrue(decoder.isFinished()); assertFalse(buffer.hasRemaining()); } @Test public void testBigBlock() throws Exception { String data = "0123456789ABCDEF"; for (int i = 0; i < 10; ++i) data += data; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); String result = ""; GZIPContentDecoder decoder = new GZIPContentDecoder(); ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { ByteBuffer decoded = decoder.decode(buffer); result += StandardCharsets.UTF_8.decode(decoded).toString(); } assertEquals(data, result); } @Test public void testBigBlockOneByteAtATime() throws Exception { String data = "0123456789ABCDEF"; for (int i = 0; i < 10; ++i) data += data; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes = baos.toByteArray(); String result = ""; GZIPContentDecoder decoder = new GZIPContentDecoder(64); ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { ByteBuffer decoded = decoder.decode(ByteBuffer.wrap(new byte[]{buffer.get()})); if (decoded.hasRemaining()) result += StandardCharsets.UTF_8.decode(decoded).toString(); } assertEquals(data, result); assertTrue(decoder.isFinished()); } @Test public void testBigBlockWithExtraBytes() throws Exception { String data1 = "0123456789ABCDEF"; for (int i = 0; i < 10; ++i) data1 += data1; ByteArrayOutputStream baos = new ByteArrayOutputStream(); GZIPOutputStream output = new GZIPOutputStream(baos); output.write(data1.getBytes(StandardCharsets.UTF_8)); output.close(); byte[] bytes1 = baos.toByteArray(); String data2 = "HELLO"; byte[] bytes2 = data2.getBytes(StandardCharsets.UTF_8); byte[] bytes = new byte[bytes1.length + bytes2.length]; System.arraycopy(bytes1, 0, bytes, 0, bytes1.length); System.arraycopy(bytes2, 0, bytes, bytes1.length, bytes2.length); String result = ""; GZIPContentDecoder decoder = new GZIPContentDecoder(64); ByteBuffer buffer = ByteBuffer.wrap(bytes); while (buffer.hasRemaining()) { ByteBuffer decoded = decoder.decode(buffer); if (decoded.hasRemaining()) result += StandardCharsets.UTF_8.decode(decoded).toString(); if (decoder.isFinished()) break; } assertEquals(data1, result); assertTrue(buffer.hasRemaining()); assertEquals(data2, StandardCharsets.UTF_8.decode(buffer).toString()); } } HostnameVerificationTest.java000066400000000000000000000147471261716203600335520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.channels.ClosedChannelException; import java.security.cert.CertificateException; import java.util.concurrent.ExecutionException; import javax.net.ssl.SSLHandshakeException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; /** * This test class runs tests to make sure that hostname verification (http://www.ietf.org/rfc/rfc2818.txt * section 3.1) is configurable in SslContextFactory and works as expected. */ public class HostnameVerificationTest { private SslContextFactory clientSslContextFactory = new SslContextFactory(); private Server server = new Server(); private HttpClient client; private NetworkConnector connector; @Before public void setUp() throws Exception { SslContextFactory serverSslContextFactory = new SslContextFactory(); serverSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); serverSslContextFactory.setKeyStorePassword("storepwd"); connector = new ServerConnector(server, serverSslContextFactory); server.addConnector(connector); server.setHandler(new DefaultHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getWriter().write("foobar"); } }); server.start(); // keystore contains a hostname which doesn't match localhost clientSslContextFactory.setKeyStorePath("src/test/resources/keystore.jks"); clientSslContextFactory.setKeyStorePassword("storepwd"); QueuedThreadPool executor = new QueuedThreadPool(); executor.setName(executor.getName() + "-client"); client = new HttpClient(clientSslContextFactory); client.setExecutor(executor); client.start(); } @After public void tearDown() throws Exception { client.stop(); server.stop(); server.join(); } /** * This test is supposed to verify that hostname verification works as described in: * http://www.ietf.org/rfc/rfc2818.txt section 3.1. It uses a certificate with a common name different to localhost * and sends a request to localhost. This should fail with a SSLHandshakeException. * * @throws Exception */ @Test public void simpleGetWithHostnameVerificationEnabledTest() throws Exception { clientSslContextFactory.setEndpointIdentificationAlgorithm("HTTPS"); String uri = "https://localhost:" + connector.getLocalPort() + "/"; try { client.GET(uri); Assert.fail("sending request to client should have failed with an Exception!"); } catch (ExecutionException x) { // The test may fail in 2 ways, since the CertificateException thrown because of the hostname // verification failure is not rethrown immediately by the JDK SSL implementation, but only // rethrown on the next read or write. // Therefore this test may catch a SSLHandshakeException, or a ClosedChannelException. // If it is the former, we verify that its cause is a CertificateException. // ExecutionException wraps an SSLHandshakeException Throwable cause = x.getCause(); if (cause==null) { x.printStackTrace(); Assert.fail("No cause?"); } if (cause instanceof SSLHandshakeException) Assert.assertThat(cause.getCause().getCause(), Matchers.instanceOf(CertificateException.class)); else Assert.assertThat(cause.getCause(), Matchers.instanceOf(ClosedChannelException.class)); } } /** * This test has hostname verification disabled and connecting, ssl handshake and sending the request should just * work fine. * * @throws Exception */ @Test public void simpleGetWithHostnameVerificationDisabledTest() throws Exception { clientSslContextFactory.setEndpointIdentificationAlgorithm(null); String uri = "https://localhost:" + connector.getLocalPort() + "/"; try { client.GET(uri); } catch (ExecutionException e) { Assert.fail("SSLHandshake should work just fine as hostname verification is disabled! " + e.getMessage()); } } /** * This test has hostname verification disabled by setting trustAll to true and connecting, * ssl handshake and sending the request should just work fine. * * @throws Exception */ @Test public void trustAllDisablesHostnameVerificationTest() throws Exception { clientSslContextFactory.setTrustAll(true); String uri = "https://localhost:" + connector.getLocalPort() + "/"; try { client.GET(uri); } catch (ExecutionException e) { Assert.fail("SSLHandshake should work just fine as hostname verification is disabled! " + e.getMessage()); } } } HttpClientAsyncContentTest.java000066400000000000000000000106761261716203600340350ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientAsyncContentTest extends AbstractHttpClientServerTest { public HttpClientAsyncContentTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testSmallAsyncContent() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ServletOutputStream output = response.getOutputStream(); output.write(65); output.flush(); output.write(66); } }); final AtomicInteger contentCount = new AtomicInteger(); final AtomicReference callbackRef = new AtomicReference<>(); final AtomicReference contentLatch = new AtomicReference<>(new CountDownLatch(1)); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseContentAsync(new Response.AsyncContentListener() { @Override public void onContent(Response response, ByteBuffer content, Callback callback) { contentCount.incrementAndGet(); callbackRef.set(callback); contentLatch.get().countDown(); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { completeLatch.countDown(); } }); Assert.assertTrue(contentLatch.get().await(5, TimeUnit.SECONDS)); Callback callback = callbackRef.get(); // Wait a while to be sure that the parsing does not proceed. TimeUnit.MILLISECONDS.sleep(1000); Assert.assertEquals(1, contentCount.get()); // Succeed the content callback to proceed with parsing. callbackRef.set(null); contentLatch.set(new CountDownLatch(1)); callback.succeeded(); Assert.assertTrue(contentLatch.get().await(5, TimeUnit.SECONDS)); callback = callbackRef.get(); // Wait a while to be sure that the parsing does not proceed. TimeUnit.MILLISECONDS.sleep(1000); Assert.assertEquals(2, contentCount.get()); Assert.assertEquals(1, completeLatch.getCount()); // Succeed the content callback to proceed with parsing. callbackRef.set(null); contentLatch.set(new CountDownLatch(1)); callback.succeeded(); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(2, contentCount.get()); } } HttpClientAuthenticationTest.java000066400000000000000000000326511261716203600344010ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Authentication; import org.eclipse.jetty.client.api.AuthenticationStore; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.BasicAuthentication; import org.eclipse.jetty.client.util.DigestAuthentication; import org.eclipse.jetty.security.Authenticator; import org.eclipse.jetty.security.ConstraintMapping; import org.eclipse.jetty.security.ConstraintSecurityHandler; import org.eclipse.jetty.security.HashLoginService; import org.eclipse.jetty.security.LoginService; import org.eclipse.jetty.security.authentication.BasicAuthenticator; import org.eclipse.jetty.security.authentication.DigestAuthenticator; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.security.Constraint; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientAuthenticationTest extends AbstractHttpClientServerTest { private String realm = "TestRealm"; public HttpClientAuthenticationTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } public void startBasic(Handler handler) throws Exception { start(new BasicAuthenticator(), handler); } public void startDigest(Handler handler) throws Exception { start(new DigestAuthenticator(), handler); } private void start(Authenticator authenticator, Handler handler) throws Exception { server = new Server(); File realmFile = MavenTestingUtils.getTestResourceFile("realm.properties"); LoginService loginService = new HashLoginService(realm, realmFile.getAbsolutePath()); server.addBean(loginService); ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler(); Constraint constraint = new Constraint(); constraint.setAuthenticate(true); constraint.setRoles(new String[]{"**"}); //allow any authenticated user ConstraintMapping mapping = new ConstraintMapping(); mapping.setPathSpec("/secure"); mapping.setConstraint(constraint); securityHandler.addConstraintMapping(mapping); securityHandler.setAuthenticator(authenticator); securityHandler.setLoginService(loginService); securityHandler.setHandler(handler); start(securityHandler); } @Test public void test_BasicAuthentication() throws Exception { startBasic(new EmptyServerHandler()); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); test_Authentication(new BasicAuthentication(uri, realm, "basic", "basic")); } @Test public void test_DigestAuthentication() throws Exception { startDigest(new EmptyServerHandler()); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); test_Authentication(new DigestAuthentication(uri, realm, "digest", "digest")); } private void test_Authentication(Authentication authentication) throws Exception { AuthenticationStore authenticationStore = client.getAuthenticationStore(); final AtomicReference requests = new AtomicReference<>(new CountDownLatch(1)); Request.Listener.Adapter requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.get().countDown(); } }; client.getRequestListeners().add(requestListener); // Request without Authentication causes a 401 Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(401, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); client.getRequestListeners().remove(requestListener); authenticationStore.addAuthentication(authentication); requests.set(new CountDownLatch(2)); requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.get().countDown(); } }; client.getRequestListeners().add(requestListener); // Request with authentication causes a 401 (no previous successful authentication) + 200 request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); client.getRequestListeners().remove(requestListener); requests.set(new CountDownLatch(1)); requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.get().countDown(); } }; client.getRequestListeners().add(requestListener); // Further requests do not trigger 401 because there is a previous successful authentication // Remove existing header to be sure it's added by the implementation request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); client.getRequestListeners().remove(requestListener); } @Test public void test_BasicAuthentication_ThenRedirect() throws Exception { startBasic(new AbstractHandler() { private final AtomicInteger requests = new AtomicInteger(); @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (requests.incrementAndGet() == 1) response.sendRedirect(URIUtil.newURI(scheme,request.getServerName(),request.getServerPort(),request.getRequestURI(),null)); } }); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, "basic", "basic")); final CountDownLatch requests = new CountDownLatch(3); Request.Listener.Adapter requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.countDown(); } }; client.getRequestListeners().add(requestListener); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/secure") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.await(5, TimeUnit.SECONDS)); client.getRequestListeners().remove(requestListener); } @Test public void test_Redirect_ThenBasicAuthentication() throws Exception { startBasic(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (request.getRequestURI().endsWith("/redirect")) response.sendRedirect(URIUtil.newURI(scheme,request.getServerName(),request.getServerPort(),"/secure",null)); } }); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, "basic", "basic")); final CountDownLatch requests = new CountDownLatch(3); Request.Listener.Adapter requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.countDown(); } }; client.getRequestListeners().add(requestListener); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/redirect") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.await(5, TimeUnit.SECONDS)); client.getRequestListeners().remove(requestListener); } @Test public void test_BasicAuthentication_WithAuthenticationRemoved() throws Exception { startBasic(new EmptyServerHandler()); final AtomicReference requests = new AtomicReference<>(new CountDownLatch(2)); Request.Listener.Adapter requestListener = new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.get().countDown(); } }; client.getRequestListeners().add(requestListener); AuthenticationStore authenticationStore = client.getAuthenticationStore(); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); BasicAuthentication authentication = new BasicAuthentication(uri, realm, "basic", "basic"); authenticationStore.addAuthentication(authentication); Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); authenticationStore.removeAuthentication(authentication); requests.set(new CountDownLatch(1)); request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); Authentication.Result result = authenticationStore.findAuthenticationResult(request.getURI()); Assert.assertNotNull(result); authenticationStore.removeAuthenticationResult(result); requests.set(new CountDownLatch(1)); request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(401, response.getStatus()); Assert.assertTrue(requests.get().await(5, TimeUnit.SECONDS)); } @Test public void test_BasicAuthentication_WithWrongPassword() throws Exception { startBasic(new EmptyServerHandler()); AuthenticationStore authenticationStore = client.getAuthenticationStore(); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort()); BasicAuthentication authentication = new BasicAuthentication(uri, realm, "basic", "wrong"); authenticationStore.addAuthentication(authentication); Request request = client.newRequest("localhost", connector.getLocalPort()).scheme(scheme).path("/secure"); ContentResponse response = request.timeout(5, TimeUnit.SECONDS).send(); Assert.assertNotNull(response); Assert.assertEquals(401, response.getStatus()); } } HttpClientChunkedContentTest.java000066400000000000000000000173501261716203600343350ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import static org.junit.Assert.assertTrue; public class HttpClientChunkedContentTest { @Rule public final TestTracker tracker = new TestTracker(); private HttpClient client; private void startClient() throws Exception { QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); client = new HttpClient(); client.setExecutor(clientThreads); client.start(); } @After public void dispose() throws Exception { if (client != null) client.stop(); } @Test public void test_Server_HeadersPauseTerminal_Client_Response() throws Exception { startClient(); try (ServerSocket server = new ServerSocket()) { server.bind(new InetSocketAddress("localhost", 0)); final AtomicReference resultRef = new AtomicReference<>(); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", server.getLocalPort()) .timeout(5, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { resultRef.set(result); completeLatch.countDown(); } }); try (Socket socket = server.accept()) { consumeRequestHeaders(socket); OutputStream output = socket.getOutputStream(); String headers = "" + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n"; output.write(headers.getBytes(StandardCharsets.UTF_8)); output.flush(); Thread.sleep(1000); String terminal = "" + "0\r\n" + "\r\n"; output.write(terminal.getBytes(StandardCharsets.UTF_8)); output.flush(); assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Result result = resultRef.get(); assertTrue(result.isSucceeded()); Response response = result.getResponse(); Assert.assertEquals(200, response.getStatus()); } } } @Test public void test_Server_ContentTerminal_Client_ContentDelay() throws Exception { startClient(); try (ServerSocket server = new ServerSocket()) { server.bind(new InetSocketAddress("localhost", 0)); final AtomicReference callbackRef = new AtomicReference<>(); final CountDownLatch firstContentLatch = new CountDownLatch(1); final AtomicReference resultRef = new AtomicReference<>(); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", server.getLocalPort()) .onResponseContentAsync(new Response.AsyncContentListener() { @Override public void onContent(Response response, ByteBuffer content, Callback callback) { if (callbackRef.compareAndSet(null, callback)) firstContentLatch.countDown(); else callback.succeeded(); } }) .timeout(5, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { resultRef.set(result); completeLatch.countDown(); } }); try (Socket socket = server.accept()) { consumeRequestHeaders(socket); OutputStream output = socket.getOutputStream(); String response = "" + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "8\r\n" + "01234567\r\n" + "0\r\n" + "\r\n"; output.write(response.getBytes(StandardCharsets.UTF_8)); output.flush(); // Simulate a delay in consuming the content. assertTrue(firstContentLatch.await(5, TimeUnit.SECONDS)); Thread.sleep(1000); callbackRef.get().succeeded(); // Wait for the client to read 0 and become idle. Thread.sleep(1000); assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Result result = resultRef.get(); assertTrue(result.isSucceeded()); Assert.assertEquals(200, result.getResponse().getStatus()); // Issue another request to be sure the connection is sane. Request request = client.newRequest("localhost", server.getLocalPort()) .timeout(5, TimeUnit.SECONDS); FutureResponseListener listener = new FutureResponseListener(request); request.send(listener); consumeRequestHeaders(socket); output.write(response.getBytes(StandardCharsets.UTF_8)); output.flush(); Assert.assertEquals(200, listener.get(5, TimeUnit.SECONDS).getStatus()); } } } private void consumeRequestHeaders(Socket socket) throws IOException { InputStream input = socket.getInputStream(); int crlfs = 0; while (true) { int read = input.read(); if (read == '\r' || read == '\n') ++crlfs; else crlfs = 0; if (crlfs == 4) break; } } } HttpClientContinueTest.java000066400000000000000000000671531261716203600332130ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientContinueTest extends AbstractHttpClientServerTest { public HttpClientContinueTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void test_Expect100Continue_WithOneContent_Respond100Continue() throws Exception { test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8)); } @Test public void test_Expect100Continue_WithMultipleContents_Respond100Continue() throws Exception { test_Expect100Continue_Respond100Continue("data1".getBytes(StandardCharsets.UTF_8), "data2".getBytes(StandardCharsets.UTF_8), "data3".getBytes(StandardCharsets.UTF_8)); } private void test_Expect100Continue_Respond100Continue(byte[]... contents) throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and copy the content back IO.copy(request.getInputStream(), response.getOutputStream()); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(contents)) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); int index = 0; byte[] responseContent = response.getContent(); for (byte[] content : contents) { for (byte b : content) { Assert.assertEquals(b, responseContent[index++]); } } } @Test public void test_Expect100Continue_WithChunkedContent_Respond100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and copy the content back ServletInputStream input = request.getInputStream(); // Make sure we chunk the response too response.flushBuffer(); IO.copy(input, response.getOutputStream()); } }); byte[] content1 = new byte[10240]; byte[] content2 = new byte[16384]; ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content1, content2) { @Override public long getLength() { return -1; } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); int index = 0; byte[] responseContent = response.getContent(); for (byte b : content1) Assert.assertEquals(b, responseContent[index++]); for (byte b : content2) Assert.assertEquals(b, responseContent[index++]); } @Test public void test_Expect100Continue_WithContent_Respond417ExpectationFailed() throws Exception { test_Expect100Continue_WithContent_RespondError(417); } @Test public void test_Expect100Continue_WithContent_Respond413RequestEntityTooLarge() throws Exception { test_Expect100Continue_WithContent_RespondError(413); } private void test_Expect100Continue_WithContent_RespondError(final int error) throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.sendError(error); } }); byte[] content1 = new byte[10240]; byte[] content2 = new byte[16384]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content1, content2)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertNotNull(result.getRequestFailure()); Assert.assertNull(result.getResponseFailure()); byte[] content = getContent(); Assert.assertNotNull(content); Assert.assertTrue(content.length > 0); Assert.assertEquals(error, result.getResponse().getStatus()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_Expect100Continue_WithContent_WithRedirect() throws Exception { final String data = "success"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (request.getRequestURI().endsWith("/done")) { response.getOutputStream().print(data); } else { // Send 100-Continue and consume the content IO.copy(request.getInputStream(), new ByteArrayOutputStream()); // Send a redirect response.sendRedirect("/done"); } } }); byte[] content = new byte[10240]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.POST) .path("/continue") .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); Assert.assertEquals(200, result.getResponse().getStatus()); Assert.assertEquals(data, getContentAsString()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_Redirect_WithExpect100Continue_WithContent() throws Exception { // A request with Expect: 100-Continue cannot receive non-final responses like 3xx final String data = "success"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (request.getRequestURI().endsWith("/done")) { // Send 100-Continue and consume the content IO.copy(request.getInputStream(), new ByteArrayOutputStream()); response.getOutputStream().print(data); } else { // Send a redirect response.sendRedirect("/done"); } } }); byte[] content = new byte[10240]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.POST) .path("/redirect") .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertNotNull(result.getRequestFailure()); Assert.assertNull(result.getResponseFailure()); Assert.assertEquals(302, result.getResponse().getStatus()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Slow @Test public void test_Expect100Continue_WithContent_WithResponseFailure_Before100Continue() throws Exception { final long idleTimeout = 1000; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); try { TimeUnit.MILLISECONDS.sleep(2 * idleTimeout); } catch (InterruptedException x) { throw new ServletException(x); } } }); client.setIdleTimeout(idleTimeout); byte[] content = new byte[1024]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertNotNull(result.getRequestFailure()); Assert.assertNotNull(result.getResponseFailure()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS)); } @Slow @Test public void test_Expect100Continue_WithContent_WithResponseFailure_After100Continue() throws Exception { final long idleTimeout = 1000; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and consume the content IO.copy(request.getInputStream(), new ByteArrayOutputStream()); try { TimeUnit.MILLISECONDS.sleep(2 * idleTimeout); } catch (InterruptedException x) { throw new ServletException(x); } } }); client.setIdleTimeout(idleTimeout); byte[] content = new byte[1024]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertNull(result.getRequestFailure()); Assert.assertNotNull(result.getResponseFailure()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * idleTimeout, TimeUnit.MILLISECONDS)); } @Test public void test_Expect100Continue_WithContent_WithResponseFailure_During100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and consume the content IO.copy(request.getInputStream(), new ByteArrayOutputStream()); } }); client.getProtocolHandlers().clear(); client.getProtocolHandlers().add(new ContinueProtocolHandler(client) { @Override public Response.Listener getResponseListener() { final Response.Listener listener = super.getResponseListener(); return new Response.Listener.Adapter() { @Override public void onBegin(Response response) { response.abort(new Exception()); } @Override public void onFailure(Response response, Throwable failure) { listener.onFailure(response, failure); } }; } }); byte[] content = new byte[1024]; final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(content)) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertNotNull(result.getRequestFailure()); Assert.assertNotNull(result.getResponseFailure()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Slow @Test public void test_Expect100Continue_WithDeferredContent_Respond100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and echo the content IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] chunk1 = new byte[]{0, 1, 2, 3}; final byte[] chunk2 = new byte[]{4, 5, 6, 7}; final byte[] data = new byte[chunk1.length + chunk2.length]; System.arraycopy(chunk1, 0, data, 0, chunk1.length); System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length); final CountDownLatch latch = new CountDownLatch(1); DeferredContentProvider content = new DeferredContentProvider(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertArrayEquals(data, getContent()); latch.countDown(); } }); Thread.sleep(1000); content.offer(ByteBuffer.wrap(chunk1)); Thread.sleep(1000); content.offer(ByteBuffer.wrap(chunk2)); content.close(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Slow @Test public void test_Expect100Continue_WithInitialAndDeferredContent_Respond100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and echo the content IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] chunk1 = new byte[]{0, 1, 2, 3}; final byte[] chunk2 = new byte[]{4, 5, 6, 7}; final byte[] data = new byte[chunk1.length + chunk2.length]; System.arraycopy(chunk1, 0, data, 0, chunk1.length); System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length); final CountDownLatch latch = new CountDownLatch(1); DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1)); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertArrayEquals(data, getContent()); latch.countDown(); } }); Thread.sleep(1000); content.offer(ByteBuffer.wrap(chunk2)); content.close(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_Expect100Continue_WithConcurrentDeferredContent_Respond100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and echo the content IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7}; final DeferredContentProvider content = new DeferredContentProvider(); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .onRequestHeaders(new org.eclipse.jetty.client.api.Request.HeadersListener() { @Override public void onHeaders(org.eclipse.jetty.client.api.Request request) { content.offer(ByteBuffer.wrap(data)); content.close(); } }) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertArrayEquals(data, getContent()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_Expect100Continue_WithInitialAndConcurrentDeferredContent_Respond100Continue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send 100-Continue and echo the content IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] chunk1 = new byte[]{0, 1, 2, 3}; final byte[] chunk2 = new byte[]{4, 5, 6}; final byte[] data = new byte[chunk1.length + chunk2.length]; System.arraycopy(chunk1, 0, data, 0, chunk1.length); System.arraycopy(chunk2, 0, data, chunk1.length, chunk2.length); final DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(chunk1)); List protocolHandlers = client.getProtocolHandlers(); for (Iterator iterator = protocolHandlers.iterator(); iterator.hasNext();) { ProtocolHandler protocolHandler = iterator.next(); if (protocolHandler instanceof ContinueProtocolHandler) iterator.remove(); } protocolHandlers.add(new ContinueProtocolHandler(client) { @Override public Response.Listener getResponseListener() { return new ContinueListener() { @Override public void onHeaders(Response response) { super.onHeaders(response); content.offer(ByteBuffer.wrap(chunk2)); content.close(); } }; } }); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertArrayEquals(data, getContent()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_Expect100Continue_WithTwoResponsesInOneRead() throws Exception { // There is a chance that the server replies with the 100 Continue response // and immediately after with the "normal" response, say a 200 OK. // These may be read by the client in a single read, and must be handled correctly. startClient(); try (ServerSocket server = new ServerSocket()) { server.bind(new InetSocketAddress("localhost", 0)); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", server.getLocalPort()) .header(HttpHeader.EXPECT, HttpHeaderValue.CONTINUE.asString()) .content(new BytesContentProvider(new byte[]{0})) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.toString(), result.isSucceeded()); Assert.assertEquals(200, result.getResponse().getStatus()); latch.countDown(); } }); try (Socket socket = server.accept()) { // Read the request headers. InputStream input = socket.getInputStream(); int crlfs = 0; while (true) { int read = input.read(); if (read == '\r' || read == '\n') ++crlfs; else crlfs = 0; if (crlfs == 4) break; } OutputStream output = socket.getOutputStream(); String responses = "" + "HTTP/1.1 100 Continue\r\n" + "\r\n" + "HTTP/1.1 200 OK\r\n" + "Transfer-Encoding: chunked\r\n" + "\r\n" + "10\r\n" + "0123456789ABCDEF\r\n"; output.write(responses.getBytes(StandardCharsets.UTF_8)); output.flush(); Thread.sleep(1000); String content = "" + "10\r\n" + "0123456789ABCDEF\r\n" + "0\r\n" + "\r\n"; output.write(content.getBytes(StandardCharsets.UTF_8)); output.flush(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } } } } HttpClientCustomProxyTest.java000066400000000000000000000214741261716203600337370ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URI; import java.nio.ByteBuffer; import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Assert; import org.junit.Test; public class HttpClientCustomProxyTest { public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE}; private Server server; private ServerConnector connector; private HttpClient client; public void prepare(Handler handler) throws Exception { server = new Server(); connector = new ServerConnector(server, new CAFEBABEServerConnectionFactory(new HttpConnectionFactory())); server.addConnector(connector); server.setHandler(handler); server.start(); QueuedThreadPool executor = new QueuedThreadPool(); executor.setName(executor.getName() + "-client"); client = new HttpClient(); client.setExecutor(executor); client.start(); } @After public void dispose() throws Exception { if (client != null) client.stop(); if (server != null) server.stop(); } @Test public void testCustomProxy() throws Exception { final String serverHost = "server"; final int status = HttpStatus.NO_CONTENT_204; prepare(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (!URI.create(baseRequest.getUri().toString()).isAbsolute()) response.setStatus(HttpServletResponse.SC_USE_PROXY); else if (serverHost.equals(request.getServerName())) response.setStatus(status); else response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); } }); // Setup the custom proxy int proxyPort = connector.getLocalPort(); int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy client.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false)); ContentResponse response = client.newRequest(serverHost, serverPort) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(status, response.getStatus()); } private class CAFEBABEProxy extends ProxyConfiguration.Proxy { private CAFEBABEProxy(Origin.Address address, boolean secure) { super(address, secure); } @Override public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory) { return new CAFEBABEClientConnectionFactory(connectionFactory); } } private class CAFEBABEClientConnectionFactory implements ClientConnectionFactory { private final ClientConnectionFactory connectionFactory; private CAFEBABEClientConnectionFactory(ClientConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } @Override public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException { HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); Executor executor = destination.getHttpClient().getExecutor(); return new CAFEBABEConnection(endPoint, executor, connectionFactory, context); } } private class CAFEBABEConnection extends AbstractConnection { private final ClientConnectionFactory connectionFactory; private final Map context; public CAFEBABEConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map context) { super(endPoint, executor); this.connectionFactory = connectionFactory; this.context = context; } @Override public void onOpen() { super.onOpen(); fillInterested(); getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE)); } @Override public void onFillable() { try { ByteBuffer buffer = BufferUtil.allocate(4); int filled = getEndPoint().fill(buffer); Assert.assertEquals(4, filled); Assert.assertArrayEquals(CAFE_BABE, buffer.array()); // We are good, upgrade the connection ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context)); } catch (Throwable x) { close(); @SuppressWarnings("unchecked") Promise promise = (Promise)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY); promise.failed(x); } } } private class CAFEBABEServerConnectionFactory extends AbstractConnectionFactory { private final org.eclipse.jetty.server.ConnectionFactory connectionFactory; private CAFEBABEServerConnectionFactory(org.eclipse.jetty.server.ConnectionFactory connectionFactory) { super("cafebabe"); this.connectionFactory = connectionFactory; } @Override public org.eclipse.jetty.io.Connection newConnection(Connector connector, EndPoint endPoint) { return new CAFEBABEServerConnection(connector, endPoint, connectionFactory); } } private class CAFEBABEServerConnection extends AbstractConnection { private final org.eclipse.jetty.server.ConnectionFactory connectionFactory; public CAFEBABEServerConnection(Connector connector, EndPoint endPoint, org.eclipse.jetty.server.ConnectionFactory connectionFactory) { super(endPoint, connector.getExecutor()); this.connectionFactory = connectionFactory; } @Override public void onOpen() { super.onOpen(); fillInterested(); } @Override public void onFillable() { try { ByteBuffer buffer = BufferUtil.allocate(4); int filled = getEndPoint().fill(buffer); Assert.assertEquals(4, filled); Assert.assertArrayEquals(CAFE_BABE, buffer.array()); getEndPoint().write(new Callback.Adapter(), buffer); // We are good, upgrade the connection ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint())); } catch (Throwable x) { close(); } } } } HttpClientExplicitConnectionTest.java000066400000000000000000000104501261716203600352140ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientExplicitConnectionTest extends AbstractHttpClientServerTest { public HttpClientExplicitConnectionTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testExplicitConnection() throws Exception { start(new EmptyServerHandler()); Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); FuturePromise futureConnection = new FuturePromise<>(); destination.newConnection(futureConnection); try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS)) { Request request = client.newRequest(destination.getHost(), destination.getPort()).scheme(scheme); FutureResponseListener listener = new FutureResponseListener(request); connection.send(request, listener); ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination; ConnectionPool connectionPool = httpDestination.getConnectionPool(); Assert.assertTrue(connectionPool.getActiveConnections().isEmpty()); Assert.assertTrue(connectionPool.getIdleConnections().isEmpty()); } } @Slow @Test public void testExplicitConnectionIsClosedOnRemoteClose() throws Exception { start(new EmptyServerHandler()); Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); FuturePromise futureConnection = new FuturePromise<>(); destination.newConnection(futureConnection); Connection connection = futureConnection.get(5, TimeUnit.SECONDS); Request request = client.newRequest(destination.getHost(), destination.getPort()).scheme(scheme); FutureResponseListener listener = new FutureResponseListener(request); connection.send(request, listener); ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); // Wait some time to have the client is an idle state. TimeUnit.SECONDS.sleep(1); connector.stop(); // Give the connection some time to process the remote close. TimeUnit.SECONDS.sleep(1); HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection; Assert.assertFalse(httpConnection.getEndPoint().isOpen()); HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination; ConnectionPool connectionPool = httpDestination.getConnectionPool(); Assert.assertTrue(connectionPool.getActiveConnections().isEmpty()); Assert.assertTrue(connectionPool.getIdleConnections().isEmpty()); } } HttpClientFailureTest.java000066400000000000000000000237521261716203600330130ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.After; import org.junit.Assert; import org.junit.Test; public class HttpClientFailureTest { private Server server; private ServerConnector connector; private HttpClient client; private void startServer(Handler handler) throws Exception { QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); server = new Server(serverThreads); connector = new ServerConnector(server); server.addConnector(connector); server.setHandler(handler); server.start(); } @After public void dispose() throws Exception { if (server != null) server.stop(); if (client != null) client.stop(); } @Test public void testFailureBeforeRequestCommit() throws Exception { startServer(new EmptyServerHandler()); final AtomicReference connectionRef = new AtomicReference<>(); client = new HttpClient(new HttpClientTransportOverHTTP() { @Override protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise) { HttpConnectionOverHTTP connection = super.newHttpConnection(endPoint, destination, promise); connectionRef.set(connection); return connection; } }, null); client.start(); try { client.newRequest("localhost", connector.getLocalPort()) .onRequestHeaders(new Request.HeadersListener() { @Override public void onHeaders(Request request) { connectionRef.get().getEndPoint().close(); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { // Expected. } ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testFailureAfterRequestCommit() throws Exception { startServer(new EmptyServerHandler()); final AtomicReference connectionRef = new AtomicReference<>(); client = new HttpClient(new HttpClientTransportOverHTTP() { @Override protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise) { HttpConnectionOverHTTP connection = super.newHttpConnection(endPoint, destination, promise); connectionRef.set(connection); return connection; } }, null); client.start(); final CountDownLatch commitLatch = new CountDownLatch(1); final CountDownLatch completeLatch = new CountDownLatch(1); DeferredContentProvider content = new DeferredContentProvider(); client.newRequest("localhost", connector.getLocalPort()) .onRequestCommit(new Request.CommitListener() { @Override public void onCommit(Request request) { connectionRef.get().getEndPoint().close(); commitLatch.countDown(); } }) .content(content) .idleTimeout(2, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) completeLatch.countDown(); } }); Assert.assertTrue(commitLatch.await(5, TimeUnit.SECONDS)); final CountDownLatch contentLatch = new CountDownLatch(1); content.offer(ByteBuffer.allocate(1024), new Callback.Adapter() { @Override public void failed(Throwable x) { contentLatch.countDown(); } }); Assert.assertTrue(commitLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); ConnectionPool connectionPool = connectionRef.get().getHttpDestination().getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } /* @Test public void test_ExchangeIsComplete_WhenRequestFailsMidway_WithResponse() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Echo back IO.copy(request.getInputStream(), response.getOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) // The second ByteBuffer set to null will throw an exception .content(new ContentProvider() { @Override public long getLength() { return -1; } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return true; } @Override public ByteBuffer next() { throw new NoSuchElementException("explicitly_thrown_by_test"); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); final String host = "localhost"; final int port = connector.getLocalPort(); client.newRequest(host, port) .scheme(scheme) .onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request request) { HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); destination.getConnectionPool().getActiveConnections().peek().close(); } }) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } */ } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientGZIPTest.java000066400000000000000000000171051261716203600322470ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InterruptedIOException; import java.util.Arrays; import java.util.concurrent.TimeUnit; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientGZIPTest extends AbstractHttpClientServerTest { public HttpClientGZIPTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testGZIPContentEncoding() throws Exception { final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader("Content-Encoding", "gzip"); GZIPOutputStream gzipOutput = new GZIPOutputStream(response.getOutputStream()); gzipOutput.write(data); gzipOutput.finish(); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(data, response.getContent()); } @Test public void testGZIPContentOneByteAtATime() throws Exception { final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader("Content-Encoding", "gzip"); ByteArrayOutputStream gzipData = new ByteArrayOutputStream(); GZIPOutputStream gzipOutput = new GZIPOutputStream(gzipData); gzipOutput.write(data); gzipOutput.finish(); ServletOutputStream output = response.getOutputStream(); byte[] gzipBytes = gzipData.toByteArray(); for (byte gzipByte : gzipBytes) { output.write(gzipByte); output.flush(); sleep(100); } } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(data, response.getContent()); } @Test public void testGZIPContentSentTwiceInOneWrite() throws Exception { final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader("Content-Encoding", "gzip"); ByteArrayOutputStream gzipData = new ByteArrayOutputStream(); GZIPOutputStream gzipOutput = new GZIPOutputStream(gzipData); gzipOutput.write(data); gzipOutput.finish(); byte[] gzipBytes = gzipData.toByteArray(); byte[] content = Arrays.copyOf(gzipBytes, 2 * gzipBytes.length); System.arraycopy(gzipBytes, 0, content, gzipBytes.length, gzipBytes.length); ServletOutputStream output = response.getOutputStream(); output.write(content); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.assertEquals(200, response.getStatus()); byte[] expected = Arrays.copyOf(data, 2 * data.length); System.arraycopy(data, 0, expected, data.length, data.length); Assert.assertArrayEquals(expected, response.getContent()); } @Test public void testGZIPContentFragmentedBeforeTrailer() throws Exception { // There are 8 trailer bytes to gzip encoding. testGZIPContentFragmented(9); } @Test public void testGZIPContentFragmentedAtTrailer() throws Exception { // There are 8 trailer bytes to gzip encoding. testGZIPContentFragmented(1); } private void testGZIPContentFragmented(final int fragment) throws Exception { final byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader("Content-Encoding", "gzip"); ByteArrayOutputStream gzipData = new ByteArrayOutputStream(); GZIPOutputStream gzipOutput = new GZIPOutputStream(gzipData); gzipOutput.write(data); gzipOutput.finish(); byte[] gzipBytes = gzipData.toByteArray(); byte[] chunk1 = Arrays.copyOfRange(gzipBytes, 0, gzipBytes.length - fragment); byte[] chunk2 = Arrays.copyOfRange(gzipBytes, gzipBytes.length - fragment, gzipBytes.length); ServletOutputStream output = response.getOutputStream(); output.write(chunk1); output.flush(); sleep(500); output.write(chunk2); output.flush(); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(data, response.getContent()); } private static void sleep(long ms) throws IOException { try { TimeUnit.MILLISECONDS.sleep(ms); } catch (InterruptedException x) { throw new InterruptedIOException(); } } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java000066400000000000000000000313601261716203600323540ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.io.LeakTrackingByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.AbstractConnectionFactory; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.LeakDetector; import org.eclipse.jetty.util.SocketAddressResolver; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.Assert; import org.junit.Test; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; public class HttpClientLoadTest extends AbstractHttpClientServerTest { private final Logger logger = Log.getLogger(HttpClientLoadTest.class); public HttpClientLoadTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testIterative() throws Exception { int cores = Runtime.getRuntime().availableProcessors(); final AtomicLong connectionLeaks = new AtomicLong(); start(new LoadHandler()); server.stop(); server.removeConnector(connector); LeakTrackingByteBufferPool serverBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); connector = new ServerConnector(server, connector.getExecutor(), connector.getScheduler(), serverBufferPool , 1, Math.min(1, cores / 2), AbstractConnectionFactory.getFactories(sslContextFactory, new HttpConnectionFactory())); server.addConnector(connector); server.start(); client.stop(); HttpClient newClient = new HttpClient(new HttpClientTransportOverHTTP() { @Override public HttpDestination newHttpDestination(Origin origin) { return new HttpDestinationOverHTTP(getHttpClient(), origin) { @Override protected ConnectionPool newConnectionPool(HttpClient client) { return new LeakTrackingConnectionPool(this, client.getMaxConnectionsPerDestination(), this) { @Override protected void leaked(LeakDetector.LeakInfo resource) { connectionLeaks.incrementAndGet(); } }; } }; } }, sslContextFactory); newClient.setExecutor(client.getExecutor()); newClient.setSocketAddressResolver(new SocketAddressResolver.Sync()); client = newClient; LeakTrackingByteBufferPool clientBufferPool = new LeakTrackingByteBufferPool(new MappedByteBufferPool.Tagged()); client.setByteBufferPool(clientBufferPool); client.setMaxConnectionsPerDestination(32768); client.setMaxRequestsQueuedPerDestination(1024 * 1024); client.setDispatchIO(false); client.setStrictEventOrdering(false); client.start(); Random random = new Random(); // At least 25k requests to warmup properly (use -XX:+PrintCompilation to verify JIT activity) int runs = 1; int iterations = 500; for (int i = 0; i < runs; ++i) { run(random, iterations); } // Re-run after warmup iterations = 5_000; for (int i = 0; i < runs; ++i) { run(random, iterations); } System.gc(); assertThat("Server BufferPool - leaked acquires", serverBufferPool.getLeakedAcquires(), is(0L)); assertThat("Server BufferPool - leaked releases", serverBufferPool.getLeakedReleases(), is(0L)); assertThat("Server BufferPool - unreleased", serverBufferPool.getLeakedResources(), is(0L)); assertThat("Client BufferPool - leaked acquires", clientBufferPool.getLeakedAcquires(), is(0L)); assertThat("Client BufferPool - leaked releases", clientBufferPool.getLeakedReleases(), is(0L)); assertThat("Client BufferPool - unreleased", clientBufferPool.getLeakedResources(), is(0L)); assertThat("Connection Leaks", connectionLeaks.get(), is(0L)); } private void run(Random random, int iterations) throws InterruptedException { CountDownLatch latch = new CountDownLatch(iterations); List failures = new ArrayList<>(); int factor = logger.isDebugEnabled() ? 25 : 1; factor *= "http".equalsIgnoreCase(scheme) ? 10 : 1000; // Dumps the state of the client if the test takes too long final Thread testThread = Thread.currentThread(); Scheduler.Task task = client.getScheduler().schedule(new Runnable() { @Override public void run() { logger.warn("Interrupting test, it is taking too long"); for (String host : Arrays.asList("localhost", "127.0.0.1")) { HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); for (Connection connection : new ArrayList<>(connectionPool.getActiveConnections())) { HttpConnectionOverHTTP active = (HttpConnectionOverHTTP)connection; logger.warn(active.getEndPoint() + " exchange " + active.getHttpChannel().getHttpExchange()); } } testThread.interrupt(); } }, iterations * factor, TimeUnit.MILLISECONDS); long begin = System.nanoTime(); for (int i = 0; i < iterations; ++i) { test(random, latch, failures); // test("http", "localhost", "GET", false, false, 64 * 1024, false, latch, failures); } Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS)); long end = System.nanoTime(); task.cancel(); long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin); logger.info("{} requests in {} ms, {} req/s", iterations, elapsed, elapsed > 0 ? iterations * 1000 / elapsed : -1); for (String failure : failures) System.err.println("FAILED: "+failure); Assert.assertTrue(failures.toString(), failures.isEmpty()); } private void test(Random random, final CountDownLatch latch, final List failures) throws InterruptedException { // Choose a random destination String host = random.nextBoolean() ? "localhost" : "127.0.0.1"; // Choose a random method HttpMethod method = random.nextBoolean() ? HttpMethod.GET : HttpMethod.POST; boolean ssl = HttpScheme.HTTPS.is(scheme); // Choose randomly whether to close the connection on the client or on the server boolean clientClose = false; if (!ssl && random.nextBoolean()) clientClose = true; boolean serverClose = false; if (!ssl && random.nextBoolean()) serverClose = true; int maxContentLength = 64 * 1024; int contentLength = random.nextInt(maxContentLength) + 1; test(scheme, host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures); } private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List failures) throws InterruptedException { Request request = client.newRequest(host, connector.getLocalPort()) .scheme(scheme) .method(method); if (clientClose) request.header(HttpHeader.CONNECTION, "close"); else if (serverClose) request.header("X-Close", "true"); switch (method) { case "GET": request.header("X-Download", String.valueOf(contentLength)); break; case "POST": request.header("X-Upload", String.valueOf(contentLength)); request.content(new BytesContentProvider(new byte[contentLength])); break; } final CountDownLatch requestLatch = new CountDownLatch(1); request.send(new Response.Listener.Adapter() { private final AtomicInteger contentLength = new AtomicInteger(); @Override public void onHeaders(Response response) { if (checkContentLength) { String content = response.getHeaders().get("X-Content"); if (content != null) contentLength.set(Integer.parseInt(content)); } } @Override public void onContent(Response response, ByteBuffer content) { if (checkContentLength) contentLength.addAndGet(-content.remaining()); } @Override public void onComplete(Result result) { if (result.isFailed()) { result.getFailure().printStackTrace(); failures.add("Result failed " + result); } if (checkContentLength && contentLength.get() != 0) failures.add("Content length mismatch " + contentLength); requestLatch.countDown(); latch.countDown(); } }); requestLatch.await(5, TimeUnit.SECONDS); } private class LoadHandler extends AbstractHandler { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { String method = request.getMethod().toUpperCase(Locale.ENGLISH); switch (method) { case "GET": int contentLength = request.getIntHeader("X-Download"); if (contentLength > 0) { response.setHeader("X-Content", String.valueOf(contentLength)); response.getOutputStream().write(new byte[contentLength]); } break; case "POST": response.setHeader("X-Content", request.getHeader("X-Upload")); IO.copy(request.getInputStream(), response.getOutputStream()); break; } if (Boolean.parseBoolean(request.getHeader("X-Close"))) response.setHeader("Connection", "close"); baseRequest.setHandled(true); } } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java000066400000000000000000000147321261716203600326220ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.BasicAuthentication; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.B64Code; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientProxyTest extends AbstractHttpClientServerTest { public HttpClientProxyTest(SslContextFactory sslContextFactory) { // Avoid TLS otherwise CONNECT requests are sent instead of proxied requests super(null); } @Test public void testProxiedRequest() throws Exception { final String serverHost = "server"; final int status = HttpStatus.NO_CONTENT_204; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (!URI.create(baseRequest.getUri().toString()).isAbsolute()) response.setStatus(HttpServletResponse.SC_USE_PROXY); else if (serverHost.equals(request.getServerName())) response.setStatus(status); else response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE); } }); int proxyPort = connector.getLocalPort(); int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort)); ContentResponse response = client.newRequest(serverHost, serverPort) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(status, response.getStatus()); } @Test public void testAuthenticatedProxiedRequest() throws Exception { final String user = "foo"; final String password = "bar"; final String credentials = B64Code.encode(user + ":" + password, StandardCharsets.ISO_8859_1); final String serverHost = "server"; final String realm = "test_realm"; final int status = HttpStatus.NO_CONTENT_204; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString()); if (authorization == null) { response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407); response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + realm + "\""); } else { String prefix = "Basic "; if (authorization.startsWith(prefix)) { String attempt = authorization.substring(prefix.length()); if (credentials.equals(attempt)) response.setStatus(status); } } } }); String proxyHost = "localhost"; int proxyPort = connector.getLocalPort(); int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort)); ContentResponse response1 = client.newRequest(serverHost, serverPort) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); // No Authentication available => 407 Assert.assertEquals(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407, response1.getStatus()); // Add authentication... URI uri = URI.create(scheme + "://" + proxyHost + ":" + proxyPort); client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, realm, user, password)); final AtomicInteger requests = new AtomicInteger(); client.getRequestListeners().add(new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { requests.incrementAndGet(); } }); // ...and perform the request again => 407 + 204 ContentResponse response2 = client.newRequest(serverHost, serverPort) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(status, response2.getStatus()); Assert.assertEquals(2, requests.get()); // Now the authentication result is cached => 204 requests.set(0); ContentResponse response3 = client.newRequest(serverHost, serverPort) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(status, response3.getStatus()); Assert.assertEquals(1, requests.get()); } } HttpClientRedirectTest.java000066400000000000000000000421531261716203600331610ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.channels.UnresolvedAddressException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; public class HttpClientRedirectTest extends AbstractHttpClientServerTest { public HttpClientRedirectTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Before public void prepare() throws Exception { start(new RedirectHandler()); } @Test public void test_303() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void test_303_302() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/302/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void test_303_302_OnDifferentDestinations() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/127.0.0.1/302/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void test_301() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.HEAD) .path("/301/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void test_301_WithWrongMethod() throws Exception { try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.DELETE) .path("/301/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { HttpResponseException xx = (HttpResponseException)x.getCause(); Response response = xx.getResponse(); Assert.assertNotNull(response); Assert.assertEquals(301, response.getStatus()); Assert.assertTrue(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } } @Test public void test_307_WithRequestContent() throws Exception { byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7}; ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.POST) .path("/307/localhost/done") .content(new ByteBufferContentProvider(ByteBuffer.wrap(data))) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); Assert.assertArrayEquals(data, response.getContent()); } @Test public void testMaxRedirections() throws Exception { client.setMaxRedirects(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/302/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { HttpResponseException xx = (HttpResponseException)x.getCause(); Response response = xx.getResponse(); Assert.assertNotNull(response); Assert.assertEquals(302, response.getStatus()); Assert.assertTrue(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } } @Test public void test_303_WithConnectionClose_WithBigRequest() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/done?close=true") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void testDontFollowRedirects() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .followRedirects(false) .path("/303/localhost/done?close=true") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(303, response.getStatus()); Assert.assertTrue(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void testRelativeLocation() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/done?relative=true") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void testAbsoluteURIPathWithSpaces() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/a+space?decode=true") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void testRelativeURIPathWithSpaces() throws Exception { Response response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/a+space?relative=true&decode=true") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(HttpHeader.LOCATION.asString())); } @Test public void testRedirectWithWrongScheme() throws Exception { dispose(); start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setStatus(303); response.setHeader("Location", "ssh://localhost/path"); } }); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/path") .timeout(5, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test @Ignore public void testRedirectFailed() throws Exception { // TODO this test is failing with timout after an ISP upgrade?? DNS dependent? try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/doesNotExist/done") .timeout(5, TimeUnit.SECONDS) .send(); } catch (ExecutionException x) { Assert.assertThat(x.getCause(), Matchers.instanceOf(UnresolvedAddressException.class)); } } @Test public void test_HEAD_301() throws Exception { testSameMethodRedirect(HttpMethod.HEAD, HttpStatus.MOVED_PERMANENTLY_301); } @Test public void test_POST_301() throws Exception { testGETRedirect(HttpMethod.POST, HttpStatus.MOVED_PERMANENTLY_301); } @Test public void test_PUT_301() throws Exception { testSameMethodRedirect(HttpMethod.PUT, HttpStatus.MOVED_PERMANENTLY_301); } @Test public void test_HEAD_302() throws Exception { testSameMethodRedirect(HttpMethod.HEAD, HttpStatus.FOUND_302); } @Test public void test_POST_302() throws Exception { testGETRedirect(HttpMethod.POST, HttpStatus.FOUND_302); } @Test public void test_PUT_302() throws Exception { testSameMethodRedirect(HttpMethod.PUT, HttpStatus.FOUND_302); } @Test public void test_HEAD_303() throws Exception { testSameMethodRedirect(HttpMethod.HEAD, HttpStatus.SEE_OTHER_303); } @Test public void test_POST_303() throws Exception { testGETRedirect(HttpMethod.POST, HttpStatus.SEE_OTHER_303); } @Test public void test_PUT_303() throws Exception { testGETRedirect(HttpMethod.PUT, HttpStatus.SEE_OTHER_303); } @Test public void test_HEAD_307() throws Exception { testSameMethodRedirect(HttpMethod.HEAD, HttpStatus.TEMPORARY_REDIRECT_307); } @Test public void test_POST_307() throws Exception { testSameMethodRedirect(HttpMethod.POST, HttpStatus.TEMPORARY_REDIRECT_307); } @Test public void test_PUT_307() throws Exception { testSameMethodRedirect(HttpMethod.PUT, HttpStatus.TEMPORARY_REDIRECT_307); } @Test public void testHttpRedirector() throws Exception { final HttpRedirector redirector = new HttpRedirector(client); org.eclipse.jetty.client.api.Request request1 = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/303/localhost/302/localhost/done") .timeout(5, TimeUnit.SECONDS) .followRedirects(false); ContentResponse response1 = request1.send(); Assert.assertEquals(303, response1.getStatus()); Assert.assertTrue(redirector.isRedirect(response1)); Result result = redirector.redirect(request1, response1); org.eclipse.jetty.client.api.Request request2 = result.getRequest(); Response response2 = result.getResponse(); Assert.assertEquals(302, response2.getStatus()); Assert.assertTrue(redirector.isRedirect(response2)); final CountDownLatch latch = new CountDownLatch(1); redirector.redirect(request2, response2, new Response.CompleteListener() { @Override public void onComplete(Result result) { Response response3 = result.getResponse(); Assert.assertEquals(200, response3.getStatus()); Assert.assertFalse(redirector.isRedirect(response3)); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } private void testSameMethodRedirect(final HttpMethod method, int redirectCode) throws Exception { testMethodRedirect(method, method, redirectCode); } private void testGETRedirect(final HttpMethod method, int redirectCode) throws Exception { testMethodRedirect(method, HttpMethod.GET, redirectCode); } private void testMethodRedirect(final HttpMethod requestMethod, final HttpMethod redirectMethod, int redirectCode) throws Exception { final AtomicInteger passes = new AtomicInteger(); client.getRequestListeners().add(new org.eclipse.jetty.client.api.Request.Listener.Adapter() { @Override public void onBegin(org.eclipse.jetty.client.api.Request request) { int pass = passes.incrementAndGet(); if (pass == 1) { if (!requestMethod.is(request.getMethod())) request.abort(new Exception()); } else if (pass == 2) { if (!redirectMethod.is(request.getMethod())) request.abort(new Exception()); } else { request.abort(new Exception()); } } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(requestMethod) .path("/" + redirectCode + "/localhost/done") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); } private class RedirectHandler extends AbstractHandler { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { String[] paths = target.split("/", 4); int status = Integer.parseInt(paths[1]); response.setStatus(status); String host = paths[2]; String path = paths[3]; boolean relative = Boolean.parseBoolean(request.getParameter("relative")); String location = relative ? "" : request.getScheme() + "://" + host + ":" + request.getServerPort(); location += "/" + path; if (Boolean.parseBoolean(request.getParameter("decode"))) location = URLDecoder.decode(location, "UTF-8"); response.setHeader("Location", location); if (Boolean.parseBoolean(request.getParameter("close"))) response.setHeader("Connection", "close"); } catch (NumberFormatException x) { response.setStatus(200); // Echo content back IO.copy(request.getInputStream(), response.getOutputStream()); } finally { baseRequest.setHandled(true); } } } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientStreamTest.java000066400000000000000000001205751261716203600327370ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousCloseException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Iterator; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.InputStreamContentProvider; import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.client.util.OutputStreamContentProvider; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; import static java.nio.file.StandardOpenOption.CREATE; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; public class HttpClientStreamTest extends AbstractHttpClientServerTest { public HttpClientStreamTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testFileUpload() throws Exception { // Prepare a big file to upload Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath(); Files.createDirectories(targetTestsDir); Path upload = Paths.get(targetTestsDir.toString(), "http_client_upload.big"); try (OutputStream output = Files.newOutputStream(upload, CREATE)) { byte[] kb = new byte[1024]; for (int i = 0; i < 10 * 1024; ++i) output.write(kb); } start(new EmptyServerHandler()); final AtomicLong requestTime = new AtomicLong(); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .file(upload) .onRequestSuccess(new Request.SuccessListener() { @Override public void onSuccess(Request request) { requestTime.set(System.nanoTime()); } }) .timeout(10, TimeUnit.SECONDS) .send(); long responseTime = System.nanoTime(); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(requestTime.get() <= responseTime); // Give some time to the server to consume the request content // This is just to avoid exception traces in the test output Thread.sleep(1000); } @Test public void testDownload() throws Exception { final byte[] data = new byte[128 * 1024]; byte value = 1; Arrays.fill(data, value); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(data); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); int length = 0; while (input.read() == value) { if (length % 100 == 0) Thread.sleep(1); ++length; } Assert.assertEquals(data.length, length); Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertNotNull(result); Assert.assertFalse(result.isFailed()); Assert.assertSame(response, result.getResponse()); } @Test public void testDownloadOfUTF8Content() throws Exception { final byte[] data = new byte[]{(byte)0xC3, (byte)0xA8}; // UTF-8 representation of è start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(data); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); for (byte b : data) { int read = input.read(); assertTrue(read >= 0); Assert.assertEquals(b & 0xFF, read); } Assert.assertEquals(-1, input.read()); Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertNotNull(result); Assert.assertFalse(result.isFailed()); Assert.assertSame(response, result.getResponse()); } @Test public void testDownloadWithFailure() throws Exception { final byte[] data = new byte[64 * 1024]; byte value = 1; Arrays.fill(data, value); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Say we want to send this much... response.setContentLength(2 * data.length); // ...but write only half... response.getOutputStream().write(data); // ...then shutdown output baseRequest.getHttpChannel().getEndPoint().shutdownOutput(); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); int length = 0; try { length = 0; while (input.read() == value) { if (length % 100 == 0) Thread.sleep(1); ++length; } fail(); } catch (IOException expected) { } Assert.assertEquals(data.length, length); Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertNotNull(result); Assert.assertTrue(result.isFailed()); } @Test(expected = AsynchronousCloseException.class) public void testInputStreamResponseListenerClosedBeforeReading() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); InputStream stream = listener.getInputStream(); // Close the stream immediately stream.close(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(new BytesContentProvider(new byte[]{0, 1, 2, 3})) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); stream.read(); // Throws } @Test public void testInputStreamResponseListenerClosedWhileWaiting() throws Exception { final byte[] chunk1 = new byte[]{0, 1}; final byte[] chunk2 = new byte[]{2, 3}; final CountDownLatch closeLatch = new CountDownLatch(1); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setContentLength(chunk1.length + chunk2.length); ServletOutputStream output = response.getOutputStream(); output.write(chunk1); output.flush(); try { closeLatch.await(5, TimeUnit.SECONDS); output.write(chunk2); output.flush(); } catch (InterruptedException x) { throw new InterruptedIOException(); } } }); final CountDownLatch waitLatch = new CountDownLatch(1); final CountDownLatch waitedLatch = new CountDownLatch(1); InputStreamResponseListener listener = new InputStreamResponseListener(1) { @Override protected boolean await() { waitLatch.countDown(); boolean result = super.await(); waitedLatch.countDown(); return result; } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); InputStream stream = listener.getInputStream(); // Wait until we block Assert.assertTrue(waitLatch.await(5, TimeUnit.SECONDS)); // Close the stream stream.close(); closeLatch.countDown(); // Be sure we're not stuck waiting Assert.assertTrue(waitedLatch.await(5, TimeUnit.SECONDS)); } @Test public void testInputStreamResponseListenerFailedWhileWaiting() throws Exception { final byte[] chunk1 = new byte[]{0, 1}; final byte[] chunk2 = new byte[]{2, 3}; final CountDownLatch closeLatch = new CountDownLatch(1); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setContentLength(chunk1.length + chunk2.length); ServletOutputStream output = response.getOutputStream(); output.write(chunk1); output.flush(); try { closeLatch.await(5, TimeUnit.SECONDS); output.write(chunk2); output.flush(); } catch (InterruptedException x) { throw new InterruptedIOException(); } } }); final CountDownLatch waitLatch = new CountDownLatch(1); final CountDownLatch waitedLatch = new CountDownLatch(1); InputStreamResponseListener listener = new InputStreamResponseListener(1) { @Override protected boolean await() { waitLatch.countDown(); boolean result = super.await(); waitedLatch.countDown(); return result; } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); // Wait until we block Assert.assertTrue(waitLatch.await(5, TimeUnit.SECONDS)); // Fail the response response.abort(new Exception()); closeLatch.countDown(); // Be sure we're not stuck waiting Assert.assertTrue(waitedLatch.await(5, TimeUnit.SECONDS)); } @Test public void testInputStreamResponseListenerConsumingBeforeWaiting() throws Exception { final byte[] data = new byte[]{0, 1}; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setContentLength(data.length); ServletOutputStream output = response.getOutputStream(); output.write(data); output.flush(); } }); final AtomicReference failure = new AtomicReference<>(); InputStreamResponseListener listener = new InputStreamResponseListener(1) { @Override protected boolean await() { // Consume everything just before waiting InputStream stream = getInputStream(); consume(stream, data); return super.await(); } private void consume(InputStream stream, byte[] data) { try { for (byte datum : data) Assert.assertEquals(datum, stream.read()); } catch (IOException x) { failure.compareAndSet(null, x); } } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertEquals(200, result.getResponse().getStatus()); Assert.assertNull(failure.get()); } @Test public void testInputStreamResponseListenerFailedBeforeResponse() throws Exception { start(new EmptyServerHandler()); int port = connector.getLocalPort(); server.stop(); InputStreamResponseListener listener = new InputStreamResponseListener(); // Connect to the wrong port client.newRequest("localhost", port) .scheme(scheme) .send(listener); Result result = listener.await(5, TimeUnit.SECONDS); Assert.assertNotNull(result); } @Test(expected = ExecutionException.class) public void testInputStreamContentProviderThrowingWhileReading() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] data = new byte[]{0, 1, 2, 3}; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(new InputStreamContentProvider(new InputStream() { private int index = 0; @Override public int read() throws IOException { // Will eventually throw ArrayIndexOutOfBounds return data[index++]; } }, data.length / 2)) .timeout(5, TimeUnit.SECONDS) .send(); } @Test(expected = AsynchronousCloseException.class) public void testDownloadWithCloseBeforeContent() throws Exception { final byte[] data = new byte[128 * 1024]; byte value = 3; Arrays.fill(data, value); final CountDownLatch latch = new CountDownLatch(1); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.flushBuffer(); try { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } catch (InterruptedException e) { throw new InterruptedIOException(); } response.getOutputStream().write(data); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); input.close(); latch.countDown(); input.read(); // Throws } @Test(expected = AsynchronousCloseException.class) public void testDownloadWithCloseMiddleOfContent() throws Exception { final byte[] data1 = new byte[1024]; final byte[] data2 = new byte[1024]; final CountDownLatch latch = new CountDownLatch(1); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(data1); response.flushBuffer(); try { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } catch (InterruptedException e) { throw new InterruptedIOException(); } response.getOutputStream().write(data2); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); for (byte datum1 : data1) Assert.assertEquals(datum1, input.read()); input.close(); latch.countDown(); input.read(); // Throws } @Test public void testDownloadWithCloseEndOfContent() throws Exception { final byte[] data = new byte[1024]; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(data); response.flushBuffer(); } }); InputStreamResponseListener listener = new InputStreamResponseListener(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); InputStream input = listener.getInputStream(); Assert.assertNotNull(input); for (byte datum : data) Assert.assertEquals(datum, input.read()); // Read EOF Assert.assertEquals(-1, input.read()); input.close(); // Must not throw Assert.assertEquals(-1, input.read()); } @Slow @Test public void testUploadWithDeferredContentProviderFromInputStream() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), new ByteArrayOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); try (DeferredContentProvider content = new DeferredContentProvider()) { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200) latch.countDown(); } }); // Make sure we provide the content *after* the request has been "sent". Thread.sleep(1000); try (ByteArrayInputStream input = new ByteArrayInputStream(new byte[1024])) { byte[] buffer = new byte[200]; int read; while ((read = input.read(buffer)) >= 0) content.offer(ByteBuffer.wrap(buffer, 0, read)); } } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithDeferredContentAvailableCallbacksNotifiedOnce() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), new ByteArrayOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); final AtomicInteger succeeds = new AtomicInteger(); try (DeferredContentProvider content = new DeferredContentProvider()) { // Make the content immediately available. content.offer(ByteBuffer.allocate(1024), new Callback.Adapter() { @Override public void succeeded() { succeeds.incrementAndGet(); } }); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200) latch.countDown(); } }); } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(1, succeeds.get()); } @Test public void testUploadWithDeferredContentProviderRacingWithSend() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); final byte[] data = new byte[512]; final DeferredContentProvider content = new DeferredContentProvider() { @Override public void setListener(Listener listener) { super.setListener(listener); // Simulate a concurrent call offer(ByteBuffer.wrap(data)); close(); } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200 && Arrays.equals(data, getContent())) latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithDeferredContentProviderRacingWithIterator() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); final byte[] data = new byte[512]; final AtomicReference contentRef = new AtomicReference<>(); final DeferredContentProvider content = new DeferredContentProvider() { @Override public Iterator iterator() { return new Iterator() { // Data for the deferred content iterator: // [0] => deferred // [1] => deferred // [2] => data private final byte[][] iteratorData = new byte[3][]; private final AtomicInteger index = new AtomicInteger(); { iteratorData[0] = null; iteratorData[1] = null; iteratorData[2] = data; } @Override public boolean hasNext() { return index.get() < iteratorData.length; } @Override public ByteBuffer next() { byte[] chunk = iteratorData[index.getAndIncrement()]; ByteBuffer result = chunk == null ? null : ByteBuffer.wrap(chunk); if (index.get() == 2) { contentRef.get().offer(result == null ? BufferUtil.EMPTY_BUFFER : result); contentRef.get().close(); } return result; } @Override public void remove() { } }; } }; contentRef.set(content); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200 && Arrays.equals(data, getContent())) latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithOutputStream() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] data = new byte[512]; final CountDownLatch latch = new CountDownLatch(1); OutputStreamContentProvider content = new OutputStreamContentProvider(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new BufferingResponseListener() { @Override public void onComplete(Result result) { if (result.isSucceeded() && result.getResponse().getStatus() == 200 && Arrays.equals(data, getContent())) latch.countDown(); } }); // Make sure we provide the content *after* the request has been "sent". Thread.sleep(1000); try (OutputStream output = content.getOutputStream()) { output.write(data); } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testBigUploadWithOutputStreamFromInputStream() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final byte[] data = new byte[16 * 1024 * 1024]; new Random().nextBytes(data); final CountDownLatch latch = new CountDownLatch(1); OutputStreamContentProvider content = new OutputStreamContentProvider(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new BufferingResponseListener(data.length) { @Override public void onComplete(Result result) { Assert.assertTrue(result.isSucceeded()); Assert.assertEquals(200, result.getResponse().getStatus()); Assert.assertArrayEquals(data, getContent()); latch.countDown(); } }); // Make sure we provide the content *after* the request has been "sent". Thread.sleep(1000); try (InputStream input = new ByteArrayInputStream(data); OutputStream output = content.getOutputStream()) { byte[] buffer = new byte[1024]; while (true) { int read = input.read(buffer); if (read < 0) break; output.write(buffer, 0, read); } } Assert.assertTrue(latch.await(30, TimeUnit.SECONDS)); } @Test public void testUploadWithOutputStreamFailureToConnect() throws Exception { start(new EmptyServerHandler()); final byte[] data = new byte[512]; final CountDownLatch latch = new CountDownLatch(1); OutputStreamContentProvider content = new OutputStreamContentProvider(); client.newRequest("0.0.0.1", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) latch.countDown(); } }); try (OutputStream output = content.getOutputStream()) { output.write(data); Assert.fail(); } catch (IOException x) { // Expected } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithDeferredContentProviderFailsMultipleOffers() throws Exception { start(new EmptyServerHandler()); final CountDownLatch failLatch = new CountDownLatch(2); final Callback.Adapter callback = new Callback.Adapter() { @Override public void failed(Throwable x) { failLatch.countDown(); } }; final CountDownLatch completeLatch = new CountDownLatch(1); final DeferredContentProvider content = new DeferredContentProvider(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(content) .onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request request) { content.offer(ByteBuffer.wrap(new byte[256]), callback); content.offer(ByteBuffer.wrap(new byte[256]), callback); request.abort(new Exception("explicitly_thrown_by_test")); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) completeLatch.countDown(); } }); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(failLatch.await(5, TimeUnit.SECONDS)); // Make sure that adding more content results in the callback to be failed. final CountDownLatch latch = new CountDownLatch(1); content.offer(ByteBuffer.wrap(new byte[128]), new Callback.Adapter() { @Override public void failed(Throwable x) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithConnectFailureClosesStream() throws Exception { start(new EmptyServerHandler()); final CountDownLatch closeLatch = new CountDownLatch(1); InputStream stream = new ByteArrayInputStream("test".getBytes(StandardCharsets.UTF_8)) { @Override public void close() throws IOException { super.close(); closeLatch.countDown(); } }; InputStreamContentProvider content = new InputStreamContentProvider(stream); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("0.0.0.1", connector.getLocalPort()) .scheme(scheme) .content(content) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); completeLatch.countDown(); } }); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS)); } @Test public void testUploadWithWriteFailureClosesStream() throws Exception { start(new EmptyServerHandler()); final AtomicInteger bytes = new AtomicInteger(); final CountDownLatch closeLatch = new CountDownLatch(1); InputStream stream = new InputStream() { @Override public int read() throws IOException { int result = bytes.incrementAndGet(); switch (result) { case 1: { break; } case 2: { try { connector.stop(); } catch (Exception x) { throw new IOException(x); } break; } default: { result = -1; break; } } return result; } @Override public void close() throws IOException { super.close(); closeLatch.countDown(); } }; InputStreamContentProvider provider = new InputStreamContentProvider(stream, 1); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(provider) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); completeLatch.countDown(); } }); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(closeLatch.await(5, TimeUnit.SECONDS)); } } HttpClientSynchronizationTest.java000066400000000000000000000070431261716203600346200ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.ConnectException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; /** * Verifies that synchronization performed from outside HttpClient does not cause deadlocks */ public class HttpClientSynchronizationTest extends AbstractHttpClientServerTest { public HttpClientSynchronizationTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testSynchronizationOnException() throws Exception { start(new EmptyServerHandler()); int port = connector.getLocalPort(); server.stop(); int count = 10; final CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; ++i) { Request request = client.newRequest("localhost", port) .scheme(scheme); synchronized (this) { request.send(new Response.Listener.Adapter() { @Override public void onFailure(Response response, Throwable failure) { synchronized (HttpClientSynchronizationTest.this) { Assert.assertThat(failure, Matchers.instanceOf(ConnectException.class)); latch.countDown(); } } }); } } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testSynchronizationOnComplete() throws Exception { start(new EmptyServerHandler()); int count = 10; final CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; ++i) { Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme); synchronized (this) { request.send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { synchronized (HttpClientSynchronizationTest.this) { Assert.assertFalse(result.isFailed()); latch.countDown(); } } }); } } Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java000066400000000000000000001617151261716203600315640ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpCookie; import java.net.URI; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.channels.UnresolvedAddressException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.StringContentProvider; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import static java.nio.file.StandardOpenOption.CREATE; public class HttpClientTest extends AbstractHttpClientServerTest { @Rule public TestingDir testdir = new TestingDir(); public HttpClientTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testStoppingClosesConnections() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); String path = "/"; Response response = client.GET(scheme + "://" + host + ":" + port + path); Assert.assertEquals(200, response.getStatus()); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); long start = System.nanoTime(); HttpConnectionOverHTTP connection = null; while (connection == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5) { connection = (HttpConnectionOverHTTP)connectionPool.getIdleConnections().peek(); TimeUnit.MILLISECONDS.sleep(10); } Assert.assertNotNull(connection); String uri = destination.getScheme() + "://" + destination.getHost() + ":" + destination.getPort(); client.getCookieStore().add(URI.create(uri), new HttpCookie("foo", "bar")); client.stop(); Assert.assertEquals(0, client.getDestinations().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertFalse(connection.getEndPoint().isOpen()); } @Test public void test_DestinationCount() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); client.GET(scheme + "://" + host + ":" + port); List destinations = client.getDestinations(); Assert.assertNotNull(destinations); Assert.assertEquals(1, destinations.size()); Destination destination = destinations.get(0); Assert.assertNotNull(destination); Assert.assertEquals(scheme, destination.getScheme()); Assert.assertEquals(host, destination.getHost()); Assert.assertEquals(port, destination.getPort()); } @Test public void test_GET_ResponseWithoutContent() throws Exception { start(new EmptyServerHandler()); Response response = client.GET(scheme + "://localhost:" + connector.getLocalPort()); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } @Test public void test_GET_ResponseWithContent() throws Exception { final byte[] data = new byte[]{0, 1, 2, 3, 4, 5, 6, 7}; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getOutputStream().write(data); baseRequest.setHandled(true); } }); client.setConnectBlocking(true); ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort()); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); byte[] content = response.getContent(); Assert.assertArrayEquals(data, content); } @Test public void test_GET_WithParameters_ResponseWithContent() throws Exception { final String paramName1 = "a"; final String paramName2 = "b"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setCharacterEncoding("UTF-8"); ServletOutputStream output = response.getOutputStream(); String paramValue1 = request.getParameter(paramName1); output.write(paramValue1.getBytes(StandardCharsets.UTF_8)); String paramValue2 = request.getParameter(paramName2); Assert.assertEquals("", paramValue2); output.write("empty".getBytes(StandardCharsets.UTF_8)); baseRequest.setHandled(true); } }); String value1 = "\u20AC"; String paramValue1 = URLEncoder.encode(value1, "UTF-8"); String query = paramName1 + "=" + paramValue1 + "&" + paramName2; ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); String content = new String(response.getContent(), StandardCharsets.UTF_8); Assert.assertEquals(value1 + "empty", content); } @Test public void test_GET_WithParametersMultiValued_ResponseWithContent() throws Exception { final String paramName1 = "a"; final String paramName2 = "b"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setCharacterEncoding("UTF-8"); ServletOutputStream output = response.getOutputStream(); String[] paramValues1 = request.getParameterValues(paramName1); for (String paramValue : paramValues1) output.write(paramValue.getBytes(StandardCharsets.UTF_8)); String paramValue2 = request.getParameter(paramName2); output.write(paramValue2.getBytes(StandardCharsets.UTF_8)); baseRequest.setHandled(true); } }); String value11 = "\u20AC"; String value12 = "\u20AA"; String value2 = "&"; String paramValue11 = URLEncoder.encode(value11, "UTF-8"); String paramValue12 = URLEncoder.encode(value12, "UTF-8"); String paramValue2 = URLEncoder.encode(value2, "UTF-8"); String query = paramName1 + "=" + paramValue11 + "&" + paramName1 + "=" + paramValue12 + "&" + paramName2 + "=" + paramValue2; ContentResponse response = client.GET(scheme + "://localhost:" + connector.getLocalPort() + "/?" + query); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); String content = new String(response.getContent(), StandardCharsets.UTF_8); Assert.assertEquals(value11 + value12 + value2, content); } @Test public void test_POST_WithParameters() throws Exception { final String paramName = "a"; final String paramValue = "\u20AC"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); String value = request.getParameter(paramName); if (paramValue.equals(value)) { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); response.getOutputStream().print(value); } } }); ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort()) .param(paramName, paramValue) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(paramValue, new String(response.getContent(), StandardCharsets.UTF_8)); } @Test public void test_PUT_WithParameters() throws Exception { final String paramName = "a"; final String paramValue = "\u20AC"; final String encodedParamValue = URLEncoder.encode(paramValue, "UTF-8"); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); String value = request.getParameter(paramName); if (paramValue.equals(value)) { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); response.getOutputStream().print(value); } } }); URI uri = URI.create(scheme + "://localhost:" + connector.getLocalPort() + "/path?" + paramName + "=" + encodedParamValue); ContentResponse response = client.newRequest(uri) .method(HttpMethod.PUT) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(paramValue, new String(response.getContent(), StandardCharsets.UTF_8)); } @Test public void test_POST_WithParameters_WithContent() throws Exception { final byte[] content = {0, 1, 2, 3}; final String paramName = "a"; final String paramValue = "\u20AC"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); consume(request.getInputStream()); String value = request.getParameter(paramName); if (paramValue.equals(value)) { response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); response.getOutputStream().write(content); } } }); ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort() + "/?b=1") .param(paramName, paramValue) .content(new BytesContentProvider(content)) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(content, response.getContent()); } @Test public void test_POST_WithContent_NotifiesRequestContentListener() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); consume(request.getInputStream()); } }); final byte[] content = {0, 1, 2, 3}; ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort()) .onRequestContent(new Request.ContentListener() { @Override public void onContent(Request request, ByteBuffer buffer) { byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); if (!Arrays.equals(content, bytes)) request.abort(new Exception()); } }) .content(new BytesContentProvider(content)) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } @Test public void test_POST_WithContent_TracksProgress() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); consume(request.getInputStream()); } }); final AtomicInteger progress = new AtomicInteger(); ContentResponse response = client.POST(scheme + "://localhost:" + connector.getLocalPort()) .onRequestContent(new Request.ContentListener() { @Override public void onContent(Request request, ByteBuffer buffer) { byte[] bytes = new byte[buffer.remaining()]; Assert.assertEquals(1, bytes.length); buffer.get(bytes); Assert.assertEquals(bytes[0], progress.getAndIncrement()); } }) .content(new BytesContentProvider(new byte[]{0}, new byte[]{1}, new byte[]{2}, new byte[]{3}, new byte[]{4})) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(5, progress.get()); } @Test public void test_QueuedRequest_IsSent_WhenPreviousRequestSucceeded() throws Exception { start(new EmptyServerHandler()); client.setMaxConnectionsPerDestination(1); final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(2); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request request) { try { latch.await(); } catch (InterruptedException x) { x.printStackTrace(); } } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { Assert.assertEquals(200, response.getStatus()); successLatch.countDown(); } }); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestQueued(new Request.QueuedListener() { @Override public void onQueued(Request request) { latch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { Assert.assertEquals(200, response.getStatus()); successLatch.countDown(); } }); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } @Test public void test_QueuedRequest_IsSent_WhenPreviousRequestClosedConnection() throws Exception { start(new EmptyServerHandler()); client.setMaxConnectionsPerDestination(1); final long idleTimeout = 1000; client.setIdleTimeout(idleTimeout); final CountDownLatch latch = new CountDownLatch(3); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/one") .listener(new Request.Listener.Adapter() { @Override public void onBegin(Request request) { try { TimeUnit.MILLISECONDS.sleep(2 * idleTimeout); } catch (InterruptedException x) { x.printStackTrace(); } } @Override public void onFailure(Request request, Throwable failure) { latch.countDown(); } }) .onResponseFailure(new Response.FailureListener() { @Override public void onFailure(Response response, Throwable failure) { latch.countDown(); } }) .send(null); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/two") .onResponseSuccess(new Response.SuccessListener() { @Override public void onSuccess(Response response) { Assert.assertEquals(200, response.getStatus()); latch.countDown(); } }) .send(null); Assert.assertTrue(latch.await(5 * idleTimeout, TimeUnit.MILLISECONDS)); } @Test public void test_ExchangeIsComplete_OnlyWhenBothRequestAndResponseAreComplete() throws Exception { start(new EmptyServerHandler()); // Prepare a big file to upload Path targetTestsDir = testdir.getEmptyDir().toPath(); Files.createDirectories(targetTestsDir); Path file = Paths.get(targetTestsDir.toString(), "http_client_conversation.big"); try (OutputStream output = Files.newOutputStream(file, CREATE)) { byte[] kb = new byte[1024]; for (int i = 0; i < 10 * 1024; ++i) output.write(kb); } final CountDownLatch latch = new CountDownLatch(3); final AtomicLong exchangeTime = new AtomicLong(); final AtomicLong requestTime = new AtomicLong(); final AtomicLong responseTime = new AtomicLong(); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .file(file) .onRequestSuccess(new Request.SuccessListener() { @Override public void onSuccess(Request request) { requestTime.set(System.nanoTime()); latch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { responseTime.set(System.nanoTime()); latch.countDown(); } @Override public void onComplete(Result result) { exchangeTime.set(System.nanoTime()); latch.countDown(); } }); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); Assert.assertTrue(requestTime.get() <= exchangeTime.get()); Assert.assertTrue(responseTime.get() <= exchangeTime.get()); // Give some time to the server to consume the request content // This is just to avoid exception traces in the test output Thread.sleep(1000); Files.delete(file); } @Test public void test_ExchangeIsComplete_WhenRequestFailsMidway_WithResponse() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Echo back IO.copy(request.getInputStream(), response.getOutputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) // The second ByteBuffer set to null will throw an exception .content(new ContentProvider() { @Override public long getLength() { return -1; } @Override public Iterator iterator() { return new Iterator() { @Override public boolean hasNext() { return true; } @Override public ByteBuffer next() { throw new NoSuchElementException("explicitly_thrown_by_test"); } @Override public void remove() { throw new UnsupportedOperationException(); } }; } }) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void test_ExchangeIsComplete_WhenRequestFails_WithNoResponse() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); final String host = "localhost"; final int port = connector.getLocalPort(); client.newRequest(host, port) .scheme(scheme) .onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request request) { HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); destination.getConnectionPool().getActiveConnections().peek().close(); } }) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Slow @Test public void test_Request_IdleTimeout() throws Exception { final long idleTimeout = 1000; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); TimeUnit.MILLISECONDS.sleep(2 * idleTimeout); } catch (InterruptedException x) { throw new ServletException(x); } } }); final String host = "localhost"; final int port = connector.getLocalPort(); try { client.newRequest(host, port) .scheme(scheme) .idleTimeout(idleTimeout, TimeUnit.MILLISECONDS) .timeout(3 * idleTimeout, TimeUnit.MILLISECONDS) .send(); Assert.fail(); } catch (ExecutionException expected) { Assert.assertTrue(expected.getCause() instanceof TimeoutException); } // Make another request without specifying the idle timeout, should not fail ContentResponse response = client.newRequest(host, port) .scheme(scheme) .timeout(3 * idleTimeout, TimeUnit.MILLISECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } @Test public void testSendToIPv6Address() throws Exception { start(new EmptyServerHandler()); ContentResponse response = client.newRequest("[::1]", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } @Test public void testHeaderProcessing() throws Exception { final String headerName = "X-Header-Test"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.setHeader(headerName, "X"); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseHeader(new Response.HeaderListener() { @Override public boolean onHeader(Response response, HttpField field) { return !field.getName().equals(headerName); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(response.getHeaders().containsKey(headerName)); } @Test public void testAllHeadersDiscarded() throws Exception { start(new EmptyServerHandler()); int count = 10; final CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; ++i) { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(new Response.Listener.Adapter() { @Override public boolean onHeader(Response response, HttpField field) { return false; } @Override public void onComplete(Result result) { if (result.isSucceeded()) latch.countDown(); } }); } Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); } @Test public void test_HEAD_With_ResponseContentLength() throws Exception { final int length = 1024; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(new byte[length]); } }); // HEAD requests receive a Content-Length header, but do not // receive the content so they must handle this case properly ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.HEAD) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(0, response.getContent().length); // Perform a normal GET request to be sure the content is now read response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(length, response.getContent().length); } @Test public void testConnectThrowsUnresolvedAddressException() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("idontexist", 80) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertTrue(result.getFailure() instanceof UnresolvedAddressException); latch.countDown(); } }); Assert.assertTrue(latch.await(10, TimeUnit.SECONDS)); } @Test public void testCustomUserAgent() throws Exception { final String userAgent = "Test/1.0"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); ArrayList userAgents = Collections.list(request.getHeaders("User-Agent")); Assert.assertEquals(1, userAgents.size()); Assert.assertEquals(userAgent, userAgents.get(0)); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .agent(userAgent) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.USER_AGENT, null) .header(HttpHeader.USER_AGENT, userAgent) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testUserAgentCanBeRemoved() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); ArrayList userAgents = Collections.list(request.getHeaders("User-Agent")); if ("/ua".equals(target)) Assert.assertEquals(1, userAgents.size()); else Assert.assertEquals(0, userAgents.size()); } }); // User agent not specified, use default. ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/ua") .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); // User agent explicitly removed. response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .agent(null) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); // User agent explicitly removed. response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .header(HttpHeader.USER_AGENT, null) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testRequestListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception { start(new EmptyServerHandler()); final AtomicInteger counter = new AtomicInteger(); Request.Listener listener = new Request.Listener() { @Override public void onQueued(Request request) { counter.incrementAndGet(); } @Override public void onBegin(Request request) { counter.incrementAndGet(); } @Override public void onHeaders(Request request) { counter.incrementAndGet(); } @Override public void onCommit(Request request) { counter.incrementAndGet(); } @Override public void onContent(Request request, ByteBuffer content) { // Should not be invoked counter.incrementAndGet(); } @Override public void onFailure(Request request, Throwable failure) { // Should not be invoked counter.incrementAndGet(); } @Override public void onSuccess(Request request) { counter.incrementAndGet(); } }; ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestQueued(listener) .onRequestBegin(listener) .onRequestHeaders(listener) .onRequestCommit(listener) .onRequestContent(listener) .onRequestSuccess(listener) .onRequestFailure(listener) .listener(listener) .send(); Assert.assertEquals(200, response.getStatus()); int expectedEventsTriggeredByOnRequestXXXListeners = 5; int expectedEventsTriggeredByListener = 5; int expected = expectedEventsTriggeredByOnRequestXXXListeners + expectedEventsTriggeredByListener; Assert.assertEquals(expected, counter.get()); } @Test public void testResponseListenerForMultipleEventsIsInvokedOncePerEvent() throws Exception { start(new EmptyServerHandler()); final AtomicInteger counter = new AtomicInteger(); final CountDownLatch latch = new CountDownLatch(1); Response.Listener listener = new Response.Listener() { @Override public void onBegin(Response response) { counter.incrementAndGet(); } @Override public boolean onHeader(Response response, HttpField field) { // Number of header may vary, so don't count return true; } @Override public void onHeaders(Response response) { counter.incrementAndGet(); } @Override public void onContent(Response response, ByteBuffer content) { // Should not be invoked counter.incrementAndGet(); } @Override public void onContent(Response response, ByteBuffer content, Callback callback) { // Should not be invoked counter.incrementAndGet(); } @Override public void onSuccess(Response response) { counter.incrementAndGet(); } @Override public void onFailure(Response response, Throwable failure) { // Should not be invoked counter.incrementAndGet(); } @Override public void onComplete(Result result) { Assert.assertEquals(200, result.getResponse().getStatus()); counter.incrementAndGet(); latch.countDown(); } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseBegin(listener) .onResponseHeader(listener) .onResponseHeaders(listener) .onResponseContent(listener) .onResponseContentAsync(listener) .onResponseSuccess(listener) .onResponseFailure(listener) .send(listener); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); int expectedEventsTriggeredByOnResponseXXXListeners = 3; int expectedEventsTriggeredByCompletionListener = 4; int expected = expectedEventsTriggeredByOnResponseXXXListeners + expectedEventsTriggeredByCompletionListener; Assert.assertEquals(expected, counter.get()); } @Test public void setOnCompleteCallbackWithBlockingSend() throws Exception { final byte[] content = new byte[512]; new Random().nextBytes(content); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.getOutputStream().write(content); } }); final Exchanger ex = new Exchanger(); BufferingResponseListener listener = new BufferingResponseListener() { @Override public void onComplete(Result result) { try { ex.exchange(result.getResponse()); } catch (InterruptedException e) { e.printStackTrace(); } } }; client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(listener); Response response = ex.exchange(null); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(content, listener.getContent()); } @Test public void testCustomHostHeader() throws Exception { final String host = "localhost"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(host, request.getServerName()); } }); ContentResponse response = client.newRequest("http://127.0.0.1:" + connector.getLocalPort() + "/path") .scheme(scheme) .header(HttpHeader.HOST, host) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testHTTP10WithKeepAliveAndContentLength() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Send the headers at this point, then write the content byte[] content = "TEST".getBytes("UTF-8"); response.setContentLength(content.length); response.flushBuffer(); response.getOutputStream().write(content); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .version(HttpVersion.HTTP_1_0) .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())); } @Test public void testHTTP10WithKeepAliveAndNoContentLength() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Send the headers at this point, then write the content response.flushBuffer(); response.getOutputStream().print("TEST"); } }); FuturePromise promise = new FuturePromise<>(); Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); destination.newConnection(promise); try (Connection connection = promise.get(5, TimeUnit.SECONDS)) { long timeout = 5000; Request request = client.newRequest(destination.getHost(), destination.getPort()) .scheme(destination.getScheme()) .version(HttpVersion.HTTP_1_0) .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) .timeout(timeout, TimeUnit.MILLISECONDS); FutureResponseListener listener = new FutureResponseListener(request); connection.send(request, listener); ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS); Assert.assertEquals(200, response.getStatus()); // The parser notifies end-of-content and therefore the CompleteListener // before closing the connection, so we need to wait before checking // that the connection is closed to avoid races. Thread.sleep(1000); Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed()); } } @Test public void testHTTP10WithKeepAliveAndNoContent() throws Exception { start(new EmptyServerHandler()); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .version(HttpVersion.HTTP_1_0) .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())); } @Test public void testLongPollIsAbortedWhenClientIsStopped() throws Exception { final CountDownLatch latch = new CountDownLatch(1); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); request.startAsync(); latch.countDown(); } }); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) completeLatch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); // Stop the client, the complete listener must be invoked. client.stop(); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); } @Test public void testSmallContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception { testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 1024); } @Test public void testBigContentDelimitedByEOFWithSlowRequestHTTP10() throws Exception { testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_0, 128 * 1024); } @Test public void testSmallContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception { testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 1024); } @Test public void testBigContentDelimitedByEOFWithSlowRequestHTTP11() throws Exception { testContentDelimitedByEOFWithSlowRequest(HttpVersion.HTTP_1_1, 128 * 1024); } private void testContentDelimitedByEOFWithSlowRequest(final HttpVersion version, int length) throws Exception { // This test is crafted in a way that the response completes before the request is fully written. // With SSL, the response coming down will close the SSLEngine so it would not be possible to // write the last chunk of the request content, and the request will be failed, failing also the // test, which is not what we want. // This is a limit of Java's SSL implementation that does not allow half closes. Assume.assumeTrue(sslContextFactory == null); final byte[] data = new byte[length]; new Random().nextBytes(data); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); // Send Connection: close to avoid that the server chunks the content with HTTP 1.1. if (version.compareTo(HttpVersion.HTTP_1_0) > 0) response.setHeader("Connection", "close"); response.getOutputStream().write(data); } }); DeferredContentProvider content = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0})); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .version(version) .content(content); FutureResponseListener listener = new FutureResponseListener(request); request.send(listener); // Wait some time to simulate a slow request. Thread.sleep(1000); content.close(); ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(200, response.getStatus()); Assert.assertArrayEquals(data, response.getContent()); } @Test public void testRequestRetries() throws Exception { final int maxRetries = 3; final AtomicInteger requests = new AtomicInteger(); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { int count = requests.incrementAndGet(); if (count == maxRetries) baseRequest.setHandled(true); consume(request.getInputStream()); } }); final CountDownLatch latch = new CountDownLatch(1); new RetryListener(client, scheme, "localhost", connector.getLocalPort(), maxRetries) { @Override protected void completed(Result result) { latch.countDown(); } }.perform(); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testCompleteNotInvokedUntilContentConsumed() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); ServletOutputStream output = response.getOutputStream(); output.write(new byte[1024]); } }); final AtomicReference callbackRef = new AtomicReference<>(); final CountDownLatch contentLatch = new CountDownLatch(1); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(new Response.Listener.Adapter() { @Override public void onContent(Response response, ByteBuffer content, Callback callback) { // Do not notify the callback yet. callbackRef.set(callback); contentLatch.countDown(); } @Override public void onComplete(Result result) { if (result.isSucceeded()) completeLatch.countDown(); } }); Assert.assertTrue(contentLatch.await(5, TimeUnit.SECONDS)); // Make sure the complete event is not emitted. Assert.assertFalse(completeLatch.await(1, TimeUnit.SECONDS)); // Consume the content. callbackRef.get().succeeded(); // Now the complete event is emitted. Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); } @Test public void testRequestSentOnlyAfterConnectionOpen() throws Exception { startServer(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); } }); final AtomicBoolean open = new AtomicBoolean(); client = new HttpClient(new HttpClientTransportOverHTTP() { @Override protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise) { return new HttpConnectionOverHTTP(endPoint, destination, promise) { @Override public void onOpen() { open.set(true); super.onOpen(); } }; } }, sslContextFactory); client.start(); final CountDownLatch latch = new CountDownLatch(2); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestBegin(new Request.BeginListener() { @Override public void onBegin(Request request) { Assert.assertTrue(open.get()); latch.countDown(); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded()) latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } private void consume(InputStream input) throws IOException { while (true) { if (input.read() < 0) break; } } public static abstract class RetryListener implements Response.CompleteListener { private final HttpClient client; private final String scheme; private final String host; private final int port; private final int maxRetries; private int retries; public RetryListener(HttpClient client, String scheme, String host, int port, int maxRetries) { this.client = client; this.scheme = scheme; this.host = host; this.port = port; this.maxRetries = maxRetries; } protected abstract void completed(Result result); @Override public void onComplete(Result result) { if (retries > maxRetries || result.isSucceeded() && result.getResponse().getStatus() == 200) completed(result); else retry(); } private void retry() { ++retries; perform(); } public void perform() { client.newRequest(host, port) .scheme(scheme) .method("POST") .param("attempt", String.valueOf(retries)) .content(new StringContentProvider("0123456789ABCDEF")) .send(this); } } } HttpClientTimeoutTest.java000066400000000000000000000446401261716203600330510ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.net.ssl.SSLEngine; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.InputStreamContentProvider; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; public class HttpClientTimeoutTest extends AbstractHttpClientServerTest { public HttpClientTimeoutTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Slow @Test(expected = TimeoutException.class) public void testTimeoutOnFuture() throws Exception { long timeout = 1000; start(new TimeoutHandler(2 * timeout)); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(timeout, TimeUnit.MILLISECONDS) .send(); } @Slow @Test public void testTimeoutOnListener() throws Exception { long timeout = 1000; start(new TimeoutHandler(2 * timeout)); final CountDownLatch latch = new CountDownLatch(1); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(timeout, TimeUnit.MILLISECONDS); request.send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * timeout, TimeUnit.MILLISECONDS)); } @Slow @Test public void testTimeoutOnQueuedRequest() throws Exception { long timeout = 1000; start(new TimeoutHandler(3 * timeout)); // Only one connection so requests get queued client.setMaxConnectionsPerDestination(1); // The first request has a long timeout final CountDownLatch firstLatch = new CountDownLatch(1); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(4 * timeout, TimeUnit.MILLISECONDS); request.send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); firstLatch.countDown(); } }); // Second request has a short timeout and should fail in the queue final CountDownLatch secondLatch = new CountDownLatch(1); request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(timeout, TimeUnit.MILLISECONDS); request.send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); secondLatch.countDown(); } }); Assert.assertTrue(secondLatch.await(2 * timeout, TimeUnit.MILLISECONDS)); // The second request must fail before the first request has completed Assert.assertTrue(firstLatch.getCount() > 0); Assert.assertTrue(firstLatch.await(5 * timeout, TimeUnit.MILLISECONDS)); } @Slow @Test public void testTimeoutIsCancelledOnSuccess() throws Exception { long timeout = 1000; start(new TimeoutHandler(timeout)); final CountDownLatch latch = new CountDownLatch(1); final byte[] content = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(new InputStreamContentProvider(new ByteArrayInputStream(content))) .timeout(2 * timeout, TimeUnit.MILLISECONDS); request.send(new BufferingResponseListener() { @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); Assert.assertArrayEquals(content, getContent()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * timeout, TimeUnit.MILLISECONDS)); TimeUnit.MILLISECONDS.sleep(2 * timeout); Assert.assertNull(request.getAbortCause()); } @Slow @Test public void testTimeoutOnListenerWithExplicitConnection() throws Exception { long timeout = 1000; start(new TimeoutHandler(2 * timeout)); final CountDownLatch latch = new CountDownLatch(1); Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); FuturePromise futureConnection = new FuturePromise<>(); destination.newConnection(futureConnection); try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS)) { Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(timeout, TimeUnit.MILLISECONDS); connection.send(request, new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * timeout, TimeUnit.MILLISECONDS)); } } @Slow @Test public void testTimeoutIsCancelledOnSuccessWithExplicitConnection() throws Exception { long timeout = 1000; start(new TimeoutHandler(timeout)); final CountDownLatch latch = new CountDownLatch(1); Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); FuturePromise futureConnection = new FuturePromise<>(); destination.newConnection(futureConnection); try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS)) { Request request = client.newRequest(destination.getHost(), destination.getPort()) .scheme(scheme) .timeout(2 * timeout, TimeUnit.MILLISECONDS); connection.send(request, new Response.CompleteListener() { @Override public void onComplete(Result result) { Response response = result.getResponse(); Assert.assertEquals(200, response.getStatus()); Assert.assertFalse(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(3 * timeout, TimeUnit.MILLISECONDS)); TimeUnit.MILLISECONDS.sleep(2 * timeout); Assert.assertNull(request.getAbortCause()); } } @Test public void testIdleTimeout() throws Throwable { long timeout = 1000; start(new TimeoutHandler(2 * timeout)); client.stop(); final AtomicBoolean sslIdle = new AtomicBoolean(); client = new HttpClient(new HttpClientTransportOverHTTP() { @Override public HttpDestination newHttpDestination(Origin origin) { return new HttpDestinationOverHTTP(getHttpClient(), origin) { @Override protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory) { HttpClient client = getHttpClient(); return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory) { @Override protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine) { return new SslConnection(byteBufferPool, executor, endPoint, engine) { @Override protected boolean onReadTimeout() { sslIdle.set(true); return super.onReadTimeout(); } }; } }; } }; } }, sslContextFactory); client.setIdleTimeout(timeout); client.start(); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.fail(); } catch (Exception x) { Assert.assertFalse(sslIdle.get()); Assert.assertThat(x.getCause(), Matchers.instanceOf(TimeoutException.class)); } } @Slow @Test public void testBlockingConnectTimeoutFailsRequest() throws Exception { testConnectTimeoutFailsRequest(true); } @Slow @Test public void testNonBlockingConnectTimeoutFailsRequest() throws Exception { testConnectTimeoutFailsRequest(false); } private void testConnectTimeoutFailsRequest(boolean blocking) throws Exception { String host = "10.255.255.1"; int port = 80; int connectTimeout = 1000; assumeConnectTimeout(host, port, connectTimeout); start(new EmptyServerHandler()); client.stop(); client.setConnectTimeout(connectTimeout); client.setConnectBlocking(blocking); client.start(); final CountDownLatch latch = new CountDownLatch(1); Request request = client.newRequest(host, port); request.scheme(scheme) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) latch.countDown(); } }); Assert.assertTrue(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS)); Assert.assertNotNull(request.getAbortCause()); } @Slow @Test public void testConnectTimeoutIsCancelledByShorterRequestTimeout() throws Exception { String host = "10.255.255.1"; int port = 80; int connectTimeout = 2000; assumeConnectTimeout(host, port, connectTimeout); start(new EmptyServerHandler()); client.stop(); client.setConnectTimeout(connectTimeout); client.start(); final AtomicInteger completes = new AtomicInteger(); final CountDownLatch latch = new CountDownLatch(2); Request request = client.newRequest(host, port); request.scheme(scheme) .timeout(connectTimeout / 2, TimeUnit.MILLISECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { completes.incrementAndGet(); latch.countDown(); } }); Assert.assertFalse(latch.await(2 * connectTimeout, TimeUnit.MILLISECONDS)); Assert.assertEquals(1, completes.get()); Assert.assertNotNull(request.getAbortCause()); } @Test public void retryAfterConnectTimeout() throws Exception { final String host = "10.255.255.1"; final int port = 80; int connectTimeout = 1000; assumeConnectTimeout(host, port, connectTimeout); start(new EmptyServerHandler()); client.stop(); client.setConnectTimeout(connectTimeout); client.start(); final CountDownLatch latch = new CountDownLatch(1); Request request = client.newRequest(host, port); request.scheme(scheme) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) { // Retry client.newRequest(host, port) .scheme(scheme) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isFailed()) latch.countDown(); } }); } } }); Assert.assertTrue(latch.await(333 * connectTimeout, TimeUnit.MILLISECONDS)); Assert.assertNotNull(request.getAbortCause()); } @Test public void testVeryShortTimeout() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(1, TimeUnit.MILLISECONDS) // Very short timeout .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testTimeoutCancelledWhenSendingThrowsException() throws Exception { start(new EmptyServerHandler()); long timeout = 1000; Request request = client.newRequest("badscheme://localhost:" + connector.getLocalPort()); try { request.timeout(timeout, TimeUnit.MILLISECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { } }); Assert.fail(); } catch (Exception expected) { } Thread.sleep(2 * timeout); // If the task was not cancelled, it aborted the request. Assert.assertNull(request.getAbortCause()); } private void assumeConnectTimeout(String host, int port, int connectTimeout) throws IOException { try (Socket socket = new Socket()) { // Try to connect to a private address in the 10.x.y.z range. // These addresses are usually not routed, so an attempt to // connect to them will hang the connection attempt, which is // what we want to simulate in this test. socket.connect(new InetSocketAddress(host, port), connectTimeout); // Abort the test if we can connect. Assume.assumeTrue(false); } catch (SocketTimeoutException x) { // Expected timeout during connect, continue the test. Assume.assumeTrue(true); } catch (Throwable x) { // Abort if any other exception happens. Assume.assumeTrue(false); } } private class TimeoutHandler extends AbstractHandler { private final long timeout; public TimeoutHandler(long timeout) { this.timeout = timeout; } @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); try { TimeUnit.MILLISECONDS.sleep(timeout); IO.copy(request.getInputStream(), response.getOutputStream()); } catch (InterruptedException x) { throw new ServletException(x); } } } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientURITest.java000066400000000000000000000442571261716203600321450ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.URLEncoder; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpClientURITest extends AbstractHttpClientServerTest { public HttpClientURITest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testIPv6Host() throws Exception { start(new EmptyServerHandler()); String host = "::1"; Request request = client.newRequest(host, connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS); Assert.assertEquals(host, request.getHost()); StringBuilder uri = new StringBuilder(); URIUtil.appendSchemeHostPort(uri, scheme, host, connector.getLocalPort()); Assert.assertEquals(uri.toString(), request.getURI().toString()); Assert.assertEquals(HttpStatus.OK_200, request.send().getStatus()); } @Test public void testPath() throws Exception { final String path = "/path"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(path); Assert.assertEquals(path, request.getPath()); Assert.assertNull(request.getQuery()); Fields params = request.getParams(); Assert.assertEquals(0, params.getSize()); Assert.assertTrue(request.getURI().toString().endsWith(path)); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testPathWithQuery() throws Exception { String name = "a"; String value = "1"; final String query = name + "=" + value; final String path = "/path"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); } }); String pathQuery = path + "?" + query; Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(pathQuery); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(1, params.getSize()); Assert.assertEquals(value, params.get(name).getValue()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testPathWithParam() throws Exception { String name = "a"; String value = "1"; final String query = name + "=" + value; final String path = "/path"; String pathQuery = path + "?" + query; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(path) .param(name, value); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(1, params.getSize()); Assert.assertEquals(value, params.get(name).getValue()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testPathWithQueryAndParam() throws Exception { String name1 = "a"; String value1 = "1"; String name2 = "b"; String value2 = "2"; final String query = name1 + "=" + value1 + "&" + name2 + "=" + value2; final String path = "/path"; String pathQuery = path + "?" + query; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(path + "?" + name1 + "=" + value1) .param(name2, value2); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(2, params.getSize()); Assert.assertEquals(value1, params.get(name1).getValue()); Assert.assertEquals(value2, params.get(name2).getValue()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testPathWithQueryAndParamValueEncoded() throws Exception { final String name1 = "a"; final String value1 = "\u20AC"; final String encodedValue1 = URLEncoder.encode(value1, "UTF-8"); final String name2 = "b"; final String value2 = "\u00A5"; String encodedValue2 = URLEncoder.encode(value2, "UTF-8"); final String query = name1 + "=" + encodedValue1 + "&" + name2 + "=" + encodedValue2; final String path = "/path"; String pathQuery = path + "?" + query; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); Assert.assertEquals(value1, request.getParameter(name1)); Assert.assertEquals(value2, request.getParameter(name2)); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(path + "?" + name1 + "=" + encodedValue1) .param(name2, value2); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(2, params.getSize()); Assert.assertEquals(value1, params.get(name1).getValue()); Assert.assertEquals(value2, params.get(name2).getValue()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testNoParameterNameNoParameterValue() throws Exception { final String path = "/path"; final String query = "="; // Bogus query String pathQuery = path + "?" + query; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(pathQuery); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(0, params.getSize()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testNoParameterNameWithParameterValue() throws Exception { final String path = "/path"; final String query = "=1"; // Bogus query String pathQuery = path + "?" + query; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(path, request.getRequestURI()); Assert.assertEquals(query, request.getQueryString()); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .path(pathQuery); Assert.assertEquals(path, request.getPath()); Assert.assertEquals(query, request.getQuery()); Assert.assertTrue(request.getURI().toString().endsWith(pathQuery)); Fields params = request.getParams(); Assert.assertEquals(0, params.getSize()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testCaseSensitiveParameterName() throws Exception { final String name1 = "a"; final String name2 = "A"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(name1, request.getParameter(name1)); Assert.assertEquals(name2, request.getParameter(name2)); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/path?" + name1 + "=" + name1) .param(name2, name2) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testRawQueryIsPreservedInURI() throws Exception { final String name = "a"; final String rawValue = "Hello%20World"; final String rawQuery = name + "=" + rawValue; final String value = "Hello World"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(rawQuery, request.getQueryString()); Assert.assertEquals(value, request.getParameter(name)); } }); String uri = scheme + "://localhost:" + connector.getLocalPort() + "/path?" + rawQuery; Request request = client.newRequest(uri) .timeout(5, TimeUnit.SECONDS); Assert.assertEquals(rawQuery, request.getQuery()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testRawQueryIsPreservedInPath() throws Exception { final String name = "a"; final String rawValue = "Hello%20World"; final String rawQuery = name + "=" + rawValue; final String value = "Hello World"; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(rawQuery, request.getQueryString()); Assert.assertEquals(value, request.getParameter(name)); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/path?" + rawQuery) .timeout(5, TimeUnit.SECONDS); Assert.assertEquals(rawQuery, request.getQuery()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testRawQueryIsPreservedWithParam() throws Exception { final String name1 = "a"; final String name2 = "b"; final String rawValue1 = "Hello%20World"; final String rawQuery1 = name1 + "=" + rawValue1; final String value1 = "Hello World"; final String value2 = "alfa omega"; final String encodedQuery2 = name2 + "=" + URLEncoder.encode(value2, "UTF-8"); final String query = rawQuery1 + "&" + encodedQuery2; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals(query, request.getQueryString()); Assert.assertEquals(value1, request.getParameter(name1)); Assert.assertEquals(value2, request.getParameter(name2)); } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/path?" + rawQuery1) .param(name2, value2) .timeout(5, TimeUnit.SECONDS); Assert.assertEquals(query, request.getQuery()); ContentResponse response = request.send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testSchemeIsCaseInsensitive() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme.toUpperCase()) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } @Test public void testHostIsCaseInsensitive() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); } }); ContentResponse response = client.newRequest("LOCALHOST", connector.getLocalPort()) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); } } HttpClientUploadDuringServerShutdown.java000066400000000000000000000257471261716203600361120ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.InputStream; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpChannelOverHTTP; import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP; import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BytesContentProvider; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.Promise; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.Assert; import org.junit.Test; public class HttpClientUploadDuringServerShutdown { /** * A server used in conjunction with {@link ClientSide}. */ public static class ServerSide { public static void main(String[] args) throws Exception { QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); Server server = new Server(serverThreads); ServerConnector connector = new ServerConnector(server); connector.setPort(8888); server.addConnector(connector); server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); byte[] buffer = new byte[1024]; InputStream input = request.getInputStream(); while (true) { int read = input.read(buffer); if (read < 0) break; long now = System.nanoTime(); long sleep = TimeUnit.MICROSECONDS.toNanos(1); while (System.nanoTime() < now + sleep) { // Wait. } } } }); server.start(); } } /** * An infinite loop of a client uploading content to the server. * The server may be killed while this client is running, and the * behavior should be that this client continues running, failing * exchanges while the server is down, but succeeding them when * the server is up and running. * * @see ServerSide */ public static class ClientSide { public static void main(String[] args) throws Exception { QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(2), null); client.setMaxConnectionsPerDestination(2); client.setIdleTimeout(10000); client.setExecutor(clientThreads); client.start(); Random random = new Random(); while (true) { int count = 1; final CountDownLatch latch = new CountDownLatch(count); for (int i = 0; i < count; ++i) { int length = 16 * 1024 * 1024 + random.nextInt(16 * 1024 * 1024); client.newRequest("localhost", 8888) .content(new BytesContentProvider(new byte[length])) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { latch.countDown(); } }); long sleep = 1 + random.nextInt(10); TimeUnit.MILLISECONDS.sleep(sleep); } latch.await(); } } } @Test public void testUploadDuringServerShutdown() throws Exception { final AtomicReference endPointRef = new AtomicReference<>(); final CountDownLatch serverLatch = new CountDownLatch(1); QueuedThreadPool serverThreads = new QueuedThreadPool(); serverThreads.setName("server"); Server server = new Server(serverThreads); ServerConnector connector = new ServerConnector(server); server.addConnector(connector); server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); endPointRef.set(baseRequest.getHttpChannel().getEndPoint()); serverLatch.countDown(); } }); server.start(); final AtomicBoolean afterSetup = new AtomicBoolean(); final CountDownLatch sendLatch = new CountDownLatch(1); final CountDownLatch beginLatch = new CountDownLatch(1); final CountDownLatch associateLatch = new CountDownLatch(1); QueuedThreadPool clientThreads = new QueuedThreadPool(); clientThreads.setName("client"); HttpClient client = new HttpClient(new HttpClientTransportOverHTTP(1) { @Override protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise) { return new HttpConnectionOverHTTP(endPoint, destination, promise) { @Override protected HttpChannelOverHTTP newHttpChannel() { return new HttpChannelOverHTTP(this) { @Override public void send() { if (afterSetup.get()) { associateLatch.countDown(); } super.send(); } }; } @Override protected void close(Throwable failure) { try { sendLatch.countDown(); beginLatch.await(5, TimeUnit.SECONDS); super.close(failure); } catch (InterruptedException x) { x.printStackTrace(); } } @Override protected boolean abort(Throwable failure) { try { associateLatch.await(5, TimeUnit.SECONDS); return super.abort(failure); } catch (InterruptedException x) { x.printStackTrace(); return false; } } }; } }, null); client.setIdleTimeout(10000); client.setExecutor(clientThreads); client.start(); // Create one connection. client.newRequest("localhost", connector.getLocalPort()).send(); Assert.assertTrue(serverLatch.await(5, TimeUnit.SECONDS)); afterSetup.set(true); Thread.sleep(1000); // Close the connection, so that the receiver is woken // up and will call HttpConnectionOverHTTP.close(). EndPoint endPoint = endPointRef.get(); endPoint.close(); // Wait for close() so that the connection that // is being closed is used to send the request. Assert.assertTrue(sendLatch.await(5, TimeUnit.SECONDS)); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .timeout(10, TimeUnit.SECONDS) .onRequestBegin(new org.eclipse.jetty.client.api.Request.BeginListener() { @Override public void onBegin(org.eclipse.jetty.client.api.Request request) { try { beginLatch.countDown(); completeLatch.await(5, TimeUnit.SECONDS); } catch (InterruptedException x) { x.printStackTrace(); } } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { completeLatch.countDown(); } }); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination("http", "localhost", connector.getLocalPort()); ConnectionPool pool = destination.getConnectionPool(); Assert.assertEquals(0, pool.getConnectionCount()); Assert.assertEquals(0, pool.getIdleConnections().size()); Assert.assertEquals(0, pool.getActiveConnections().size()); } } HttpConnectionLifecycleTest.java000066400000000000000000000505001261716203600341730ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.StdErrLog; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpConnectionLifecycleTest extends AbstractHttpClientServerTest { public HttpConnectionLifecycleTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Override public void start(Handler handler) throws Exception { super.start(handler); client.setStrictEventOrdering(false); } @Test public void test_SuccessfulRequest_ReturnsConnection() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(3); client.newRequest(host, port) .scheme(scheme) .onRequestSuccess(new Request.SuccessListener() { @Override public void onSuccess(Request request) { successLatch.countDown(); } }) .onResponseHeaders(new Response.HeadersListener() { @Override public void onHeaders(Response response) { Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(1, activeConnections.size()); headersLatch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { successLatch.countDown(); } @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); successLatch.countDown(); } }); Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(1, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void test_FailedRequest_RemovesConnection() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); final CountDownLatch beginLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(2); client.newRequest(host, port).scheme(scheme).listener(new Request.Listener.Adapter() { @Override public void onBegin(Request request) { activeConnections.peek().close(); beginLatch.countDown(); } @Override public void onFailure(Request request, Throwable failure) { failureLatch.countDown(); } }).send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); failureLatch.countDown(); } }); Assert.assertTrue(beginLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void test_BadRequest_RemovesConnection() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); final CountDownLatch successLatch = new CountDownLatch(3); client.newRequest(host, port) .scheme(scheme) .listener(new Request.Listener.Adapter() { @Override public void onBegin(Request request) { // Remove the host header, this will make the request invalid request.header(HttpHeader.HOST, null); } @Override public void onSuccess(Request request) { successLatch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { Assert.assertEquals(400, response.getStatus()); // 400 response also come with a Connection: close, // so the connection is closed and removed successLatch.countDown(); } @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); successLatch.countDown(); } }); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Slow @Test public void test_BadRequest_WithSlowRequest_RemovesConnection() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); final long delay = 1000; final CountDownLatch successLatch = new CountDownLatch(3); client.newRequest(host, port) .scheme(scheme) .listener(new Request.Listener.Adapter() { @Override public void onBegin(Request request) { // Remove the host header, this will make the request invalid request.header(HttpHeader.HOST, null); } @Override public void onHeaders(Request request) { try { TimeUnit.MILLISECONDS.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void onSuccess(Request request) { successLatch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onSuccess(Response response) { Assert.assertEquals(400, response.getStatus()); // 400 response also come with a Connection: close, // so the connection is closed and removed successLatch.countDown(); } @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); successLatch.countDown(); } }); Assert.assertTrue(successLatch.await(delay * 5, TimeUnit.MILLISECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void test_ConnectionFailure_RemovesConnection() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); server.stop(); final CountDownLatch failureLatch = new CountDownLatch(2); client.newRequest(host, port) .scheme(scheme) .onRequestFailure(new Request.FailureListener() { @Override public void onFailure(Request request, Throwable failure) { failureLatch.countDown(); } }) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); failureLatch.countDown(); } }); Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void test_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setHeader("Connection", "close"); baseRequest.setHandled(true); } }); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest(host, port) .scheme(scheme) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertFalse(result.isFailed()); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void test_BigRequestContent_ResponseWithConnectionCloseHeader_RemovesConnection() throws Exception { StdErrLog logger = StdErrLog.getLogger(org.eclipse.jetty.server.HttpConnection.class); logger.setHideStacks(true); try { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setHeader("Connection", "close"); baseRequest.setHandled(true); // Don't read request content; this causes the server parser to be closed } }); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); Log.getLogger(HttpConnection.class).info("Expecting java.lang.IllegalStateException: HttpParser{s=CLOSED,..."); final CountDownLatch latch = new CountDownLatch(1); ByteBuffer buffer = ByteBuffer.allocate(16 * 1024 * 1024); Arrays.fill(buffer.array(),(byte)'x'); client.newRequest(host, port) .scheme(scheme) .content(new ByteBufferContentProvider(buffer)) .send(new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertEquals(1, latch.getCount()); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); server.stop(); } finally { logger.setHideStacks(false); } } @Slow @Test public void test_IdleConnection_IsClosed_OnRemoteClose() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); ContentResponse response = client.newRequest(host, port) .scheme(scheme) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); connector.stop(); // Give the connection some time to process the remote close TimeUnit.SECONDS.sleep(1); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } @Test public void testConnectionForHTTP10ResponseIsRemoved() throws Exception { start(new EmptyServerHandler()); String host = "localhost"; int port = connector.getLocalPort(); HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port); ConnectionPool connectionPool = destination.getConnectionPool(); final BlockingQueue idleConnections = connectionPool.getIdleConnections(); Assert.assertEquals(0, idleConnections.size()); final BlockingQueue activeConnections = connectionPool.getActiveConnections(); Assert.assertEquals(0, activeConnections.size()); client.setStrictEventOrdering(false); ContentResponse response = client.newRequest(host, port) .scheme(scheme) .onResponseBegin(new Response.BeginListener() { @Override public void onBegin(Response response) { // Simulate a HTTP 1.0 response has been received. ((HttpResponse)response).version(HttpVersion.HTTP_1_0); } }) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(0, idleConnections.size()); Assert.assertEquals(0, activeConnections.size()); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpCookieTest.java000066400000000000000000000132421261716203600315460ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.net.HttpCookie; import java.net.URI; import java.util.List; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpCookieTest extends AbstractHttpClientServerTest { public HttpCookieTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void test_CookieIsStored() throws Exception { final String name = "foo"; final String value = "bar"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.addCookie(new Cookie(name, value)); baseRequest.setHandled(true); } }); String host = "localhost"; int port = connector.getLocalPort(); String path = "/path"; String uri = scheme + "://" + host + ":" + port + path; Response response = client.GET(uri); Assert.assertEquals(200, response.getStatus()); List cookies = client.getCookieStore().get(URI.create(uri)); Assert.assertNotNull(cookies); Assert.assertEquals(1, cookies.size()); HttpCookie cookie = cookies.get(0); Assert.assertEquals(name, cookie.getName()); Assert.assertEquals(value, cookie.getValue()); } @Test public void test_CookieIsSent() throws Exception { final String name = "foo"; final String value = "bar"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Cookie[] cookies = request.getCookies(); Assert.assertNotNull(cookies); Assert.assertEquals(1, cookies.length); Cookie cookie = cookies[0]; Assert.assertEquals(name, cookie.getName()); Assert.assertEquals(value, cookie.getValue()); } }); String host = "localhost"; int port = connector.getLocalPort(); String path = "/path"; String uri = scheme + "://" + host + ":" + port; HttpCookie cookie = new HttpCookie(name, value); client.getCookieStore().add(URI.create(uri), cookie); Response response = client.GET(scheme + "://" + host + ":" + port + path); Assert.assertEquals(200, response.getStatus()); } @Test public void test_CookieWithoutValue() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); response.addHeader("Set-Cookie", ""); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.assertEquals(200, response.getStatus()); Assert.assertTrue(client.getCookieStore().getCookies().isEmpty()); } @Test public void test_PerRequestCookieIsSent() throws Exception { final String name = "foo"; final String value = "bar"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Cookie[] cookies = request.getCookies(); Assert.assertNotNull(cookies); Assert.assertEquals(1, cookies.length); Cookie cookie = cookies[0]; Assert.assertEquals(name, cookie.getName()); Assert.assertEquals(value, cookie.getValue()); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .cookie(new HttpCookie(name, value)) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/HttpRequestAbortTest.java000066400000000000000000000514071261716203600327620ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpRequestAbortTest extends AbstractHttpClientServerTest { public HttpRequestAbortTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testAbortOnQueued() throws Exception { start(new EmptyServerHandler()); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); final AtomicBoolean begin = new AtomicBoolean(); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .listener(new Request.Listener.Adapter() { @Override public void onQueued(Request request) { aborted.set(request.abort(cause)); latch.countDown(); } @Override public void onBegin(Request request) { begin.set(true); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); Assert.assertFalse(begin.get()); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortOnBegin() throws Exception { start(new EmptyServerHandler()); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch committed = new CountDownLatch(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .listener(new Request.Listener.Adapter() { @Override public void onBegin(Request request) { aborted.set(request.abort(cause)); latch.countDown(); } @Override public void onCommit(Request request) { committed.countDown(); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); Assert.assertFalse(committed.await(1, TimeUnit.SECONDS)); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortOnHeaders() throws Exception { start(new EmptyServerHandler()); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch committed = new CountDownLatch(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .listener(new Request.Listener.Adapter() { @Override public void onHeaders(Request request) { aborted.set(request.abort(cause)); latch.countDown(); } @Override public void onCommit(Request request) { committed.countDown(); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); Assert.assertFalse(committed.await(1, TimeUnit.SECONDS)); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortOnCommit() throws Exception { start(new EmptyServerHandler()); // Test can behave in 2 ways: // A) the request is failed before the response arrived // B) the request is failed after the response arrived final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestCommit(new Request.CommitListener() { @Override public void onCommit(Request request) { aborted.set(request.abort(cause)); latch.countDown(); } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortOnCommitWithContent() throws Exception { final AtomicReference failure = new AtomicReference<>(); start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); IO.copy(request.getInputStream(), response.getOutputStream()); } catch (IOException x) { failure.set(x); throw x; } } }); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestCommit(new Request.CommitListener() { @Override public void onCommit(Request request) { aborted.set(request.abort(cause)); latch.countDown(); } }) .content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1})) { @Override public long getLength() { return -1; } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortOnContent() throws Exception { start(new EmptyServerHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { super.handle(target, baseRequest, request, response); IO.copy(request.getInputStream(), response.getOutputStream()); } }); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestContent(new Request.ContentListener() { @Override public void onContent(Request request, ByteBuffer content) { aborted.set(request.abort(cause)); latch.countDown(); } }) .content(new ByteBufferContentProvider(ByteBuffer.wrap(new byte[]{0}), ByteBuffer.wrap(new byte[]{1})) { @Override public long getLength() { return -1; } }) .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test(expected = InterruptedException.class) public void testInterrupt() throws Exception { final long delay = 1000; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); TimeUnit.MILLISECONDS.sleep(2 * delay); } catch (InterruptedException x) { throw new ServletException(x); } } }); Request request = client.newRequest("localhost", connector.getLocalPort()) .timeout(3 * delay, TimeUnit.MILLISECONDS) .scheme(scheme); final Thread thread = Thread.currentThread(); new Thread() { @Override public void run() { try { TimeUnit.MILLISECONDS.sleep(delay); thread.interrupt(); } catch (InterruptedException x) { throw new RuntimeException(x); } } }.start(); request.send(); } @Test public void testAbortLongPoll() throws Exception { final long delay = 1000; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); TimeUnit.MILLISECONDS.sleep(2 * delay); } catch (InterruptedException x) { throw new ServletException(x); } } }); final Request request = client.newRequest("localhost", connector.getLocalPort()) .timeout(3 * delay, TimeUnit.MILLISECONDS) .scheme(scheme); final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); new Thread() { @Override public void run() { try { TimeUnit.MILLISECONDS.sleep(delay); aborted.set(request.abort(cause)); latch.countDown(); } catch (InterruptedException x) { throw new RuntimeException(x); } } }.start(); try { request.send(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); } HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, "localhost", connector.getLocalPort()); ConnectionPool connectionPool = destination.getConnectionPool(); Assert.assertEquals(0, connectionPool.getConnectionCount()); Assert.assertEquals(0, connectionPool.getActiveConnections().size()); Assert.assertEquals(0, connectionPool.getIdleConnections().size()); } @Test public void testAbortLongPollAsync() throws Exception { final long delay = 1000; start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); TimeUnit.MILLISECONDS.sleep(2 * delay); } catch (InterruptedException x) { throw new ServletException(x); } } }); final Throwable cause = new Exception(); final CountDownLatch latch = new CountDownLatch(1); Request request = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .timeout(3 * delay, TimeUnit.MILLISECONDS); request.send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertSame(cause, result.getFailure()); latch.countDown(); } }); TimeUnit.MILLISECONDS.sleep(delay); request.abort(cause); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testAbortConversation() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); if (!"/done".equals(request.getRequestURI())) response.sendRedirect("/done"); } }); // The test may fail to abort the request in this way: // T1 aborts the request, which aborts the sender, which shuts down the output; // server reads -1 and closes; T2 reads -1 and the receiver fails the response with an EOFException; // T1 tries to abort the receiver, but it's already failed. final Throwable cause = new Exception(); final AtomicBoolean aborted = new AtomicBoolean(); final CountDownLatch latch = new CountDownLatch(1); client.getProtocolHandlers().clear(); client.getProtocolHandlers().add(new RedirectProtocolHandler(client) { @Override public void onComplete(Result result) { // Abort the request after the 3xx response but before issuing the next request if (!result.isFailed()) { aborted.set(result.getRequest().abort(cause)); latch.countDown(); } super.onComplete(result); } }); try { client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/redirect") .timeout(5, TimeUnit.SECONDS) .send(); Assert.fail(); } catch (ExecutionException x) { Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); if (aborted.get()) Assert.assertSame(cause, x.getCause()); } } } HttpResponseAbortTest.java000066400000000000000000000224061261716203600330460ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpResponseAbortTest extends AbstractHttpClientServerTest { public HttpResponseAbortTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testAbortOnBegin() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseBegin(new Response.BeginListener() { @Override public void onBegin(Response response) { response.abort(new Exception()); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testAbortOnHeader() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseHeader(new Response.HeaderListener() { @Override public boolean onHeader(Response response, HttpField field) { response.abort(new Exception()); return true; } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testAbortOnHeaders() throws Exception { start(new EmptyServerHandler()); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseHeaders(new Response.HeadersListener() { @Override public void onHeaders(Response response) { response.abort(new Exception()); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testAbortOnContent() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); OutputStream output = response.getOutputStream(); output.write(1); output.flush(); output.write(2); output.flush(); } catch (IOException ignored) { // The client may have already closed, and we'll get an exception here, but it's expected } } }); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseContent(new Response.ContentListener() { @Override public void onContent(Response response, ByteBuffer content) { response.abort(new Exception()); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); latch.countDown(); } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); } @Test public void testAbortOnContentBeforeRequestTermination() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { try { baseRequest.setHandled(true); OutputStream output = response.getOutputStream(); output.write(1); output.flush(); output.write(2); output.flush(); } catch (IOException ignored) { // The client may have already closed, and we'll get an exception here, but it's expected } } }); final CountDownLatch abortLatch = new CountDownLatch(1); final AtomicInteger completes = new AtomicInteger(); final CountDownLatch completeLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onRequestSuccess(new org.eclipse.jetty.client.api.Request.SuccessListener() { @Override public void onSuccess(org.eclipse.jetty.client.api.Request request) { try { abortLatch.await(5, TimeUnit.SECONDS); } catch (InterruptedException x) { x.printStackTrace(); } } }) .onResponseContent(new Response.ContentListener() { @Override public void onContent(Response response, ByteBuffer content) { try { response.abort(new Exception()); abortLatch.countDown(); // Delay to let the request side to finish its processing. Thread.sleep(1000); } catch (InterruptedException x) { x.printStackTrace(); } } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { completes.incrementAndGet(); Assert.assertTrue(result.isFailed()); completeLatch.countDown(); } }); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); // Wait to be sure that the complete event is only notified once. Thread.sleep(1000); Assert.assertEquals(1, completes.get()); } } HttpResponseConcurrentAbortTest.java000066400000000000000000000146721261716203600351170ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; import org.junit.Test; public class HttpResponseConcurrentAbortTest extends AbstractHttpClientServerTest { private final CountDownLatch callbackLatch = new CountDownLatch(1); private final CountDownLatch failureLatch = new CountDownLatch(1); private final CountDownLatch completeLatch = new CountDownLatch(1); private final AtomicBoolean success = new AtomicBoolean(); public HttpResponseConcurrentAbortTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testAbortOnBegin() throws Exception { start(new EmptyServerHandler()); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseBegin(new Response.BeginListener() { @Override public void onBegin(Response response) { abort(response); } }) .send(new TestResponseListener()); Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(completeLatch.await(6, TimeUnit.SECONDS)); Assert.assertTrue(success.get()); } @Test public void testAbortOnHeader() throws Exception { start(new EmptyServerHandler()); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseHeader(new Response.HeaderListener() { @Override public boolean onHeader(Response response, HttpField field) { abort(response); return true; } }) .send(new TestResponseListener()); Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(success.get()); } @Test public void testAbortOnHeaders() throws Exception { start(new EmptyServerHandler()); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseHeaders(new Response.HeadersListener() { @Override public void onHeaders(Response response) { abort(response); } }) .send(new TestResponseListener()); Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(success.get()); } @Test public void testAbortOnContent() throws Exception { start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); OutputStream output = response.getOutputStream(); output.write(1); output.flush(); } }); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .onResponseContent(new Response.ContentListener() { @Override public void onContent(Response response, ByteBuffer content) { abort(response); } }) .send(new TestResponseListener()); Assert.assertTrue(callbackLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(completeLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(success.get()); } private void abort(final Response response) { new Thread("abort") { @Override public void run() { response.abort(new Exception()); } }.start(); try { // The failure callback must be executed asynchronously. boolean latched = failureLatch.await(4, TimeUnit.SECONDS); success.set(latched); // The complete callback must not be executed // until we return from this callback. latched = completeLatch.await(1, TimeUnit.SECONDS); success.set(!latched); callbackLatch.countDown(); } catch (InterruptedException x) { throw new RuntimeException(x); } } private class TestResponseListener extends Response.Listener.Adapter { @Override public void onFailure(Response response, Throwable failure) { failureLatch.countDown(); } @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); completeLatch.countDown(); } } } ProxyConfigurationTest.java000066400000000000000000000046611261716203600332740ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import org.junit.Assert; import org.junit.Test; public class ProxyConfigurationTest { @Test public void testProxyMatchesWithoutIncludesWithoutExcludes() throws Exception { HttpProxy proxy = new HttpProxy("host", 0); Assert.assertTrue(proxy.matches(new Origin("http", "any", 0))); } @Test public void testProxyMatchesWithOnlyExcludes() throws Exception { HttpProxy proxy = new HttpProxy("host", 0); proxy.getExcludedAddresses().add("1.2.3.4:5"); Assert.assertTrue(proxy.matches(new Origin("http", "any", 0))); Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 0))); Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 5))); } @Test public void testProxyMatchesWithOnlyIncludes() throws Exception { HttpProxy proxy = new HttpProxy("host", 0); proxy.getIncludedAddresses().add("1.2.3.4:5"); Assert.assertFalse(proxy.matches(new Origin("http", "any", 0))); Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 0))); Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 5))); } @Test public void testProxyMatchesWithIncludesAndExcludes() throws Exception { HttpProxy proxy = new HttpProxy("host", 0); proxy.getIncludedAddresses().add("1.2.3.4"); proxy.getExcludedAddresses().add("1.2.3.4:5"); Assert.assertFalse(proxy.matches(new Origin("http", "any", 0))); Assert.assertTrue(proxy.matches(new Origin("http", "1.2.3.4", 0))); Assert.assertFalse(proxy.matches(new Origin("http", "1.2.3.4", 5))); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/Socks4ProxyTest.java000066400000000000000000000141441261716203600317070ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class Socks4ProxyTest { private ServerSocketChannel server; private HttpClient client; @Before public void prepare() throws Exception { server = ServerSocketChannel.open(); server.bind(new InetSocketAddress("localhost", 0)); client = new HttpClient(); client.start(); } @After public void dispose() throws Exception { client.stop(); server.close(); } @Test public void testSocks4Proxy() throws Exception { int proxyPort = server.socket().getLocalPort(); client.getProxyConfiguration().getProxies().add(new Socks4Proxy("localhost", proxyPort)); final CountDownLatch latch = new CountDownLatch(1); byte ip1 = 127; byte ip2 = 0; byte ip3 = 0; byte ip4 = 13; String serverHost = ip1 + "." + ip2 + "." + ip3 + "." + ip4; int serverPort = proxyPort + 1; // Any port will do client.newRequest(serverHost, serverPort) .path("/path") .timeout(5, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded()) latch.countDown(); } }); SocketChannel channel = server.accept(); int socks4MessageLength = 9; ByteBuffer buffer = ByteBuffer.allocate(socks4MessageLength); int read = channel.read(buffer); Assert.assertEquals(socks4MessageLength, read); Assert.assertEquals(4, buffer.get(0) & 0xFF); Assert.assertEquals(1, buffer.get(1) & 0xFF); Assert.assertEquals(serverPort, buffer.getShort(2) & 0xFFFF); Assert.assertEquals(ip1, buffer.get(4) & 0xFF); Assert.assertEquals(ip2, buffer.get(5) & 0xFF); Assert.assertEquals(ip3, buffer.get(6) & 0xFF); Assert.assertEquals(ip4, buffer.get(7) & 0xFF); Assert.assertEquals(0, buffer.get(8) & 0xFF); // Socks4 response. channel.write(ByteBuffer.wrap(new byte[]{0, 0x5A, 0, 0, 0, 0, 0, 0})); buffer = ByteBuffer.allocate(3); read = channel.read(buffer); Assert.assertEquals(3, read); buffer.flip(); Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString()); // Response String response = "" + "HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n"; channel.write(ByteBuffer.wrap(response.getBytes("UTF-8"))); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); channel.close(); } @Test public void testSocks4ProxyWithSplitResponse() throws Exception { int proxyPort = server.socket().getLocalPort(); client.getProxyConfiguration().getProxies().add(new Socks4Proxy("localhost", proxyPort)); final CountDownLatch latch = new CountDownLatch(1); String serverHost = "127.0.0.13"; // Test expects an IP address. int serverPort = proxyPort + 1; // Any port will do client.newRequest(serverHost, serverPort) .path("/path") .timeout(5, TimeUnit.SECONDS) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded()) latch.countDown(); else result.getFailure().printStackTrace(); } }); SocketChannel channel = server.accept(); int socks4MessageLength = 9; ByteBuffer buffer = ByteBuffer.allocate(socks4MessageLength); int read = channel.read(buffer); Assert.assertEquals(socks4MessageLength, read); // Socks4 response, with split bytes. byte[] chunk1 = new byte[]{0, 0x5A, 0}; byte[] chunk2 = new byte[]{0, 0, 0, 0, 0}; channel.write(ByteBuffer.wrap(chunk1)); // Wait before sending the second chunk. Thread.sleep(1000); channel.write(ByteBuffer.wrap(chunk2)); buffer = ByteBuffer.allocate(3); read = channel.read(buffer); Assert.assertEquals(3, read); buffer.flip(); Assert.assertEquals("GET", StandardCharsets.UTF_8.decode(buffer).toString()); // Response String response = "" + "HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n" + "Connection: close\r\n" + "\r\n"; channel.write(ByteBuffer.wrap(response.getBytes("UTF-8"))); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); channel.close(); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/api/000077500000000000000000000000001261716203600265415ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/api/Usage.java000066400000000000000000000265171261716203600304630ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.api; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpCookie; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.util.BasicAuthentication; import org.eclipse.jetty.client.util.DeferredContentProvider; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.client.util.InputStreamContentProvider; import org.eclipse.jetty.client.util.InputStreamResponseListener; import org.eclipse.jetty.client.util.OutputStreamContentProvider; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.util.FuturePromise; import org.junit.Assert; import org.junit.Ignore; import org.junit.Test; @Ignore public class Usage { @Test public void testGETBlocking_ShortAPI() throws Exception { HttpClient client = new HttpClient(); client.start(); // Block to get the response ContentResponse response = client.GET("http://localhost:8080/foo"); // Verify response status code Assert.assertEquals(200, response.getStatus()); // Access headers response.getHeaders().get("Content-Length"); } @Test public void testGETBlocking() throws Exception { HttpClient client = new HttpClient(); client.start(); // Address must be provided, it's the only thing non defaultable Request request = client.newRequest("localhost", 8080) .scheme("https") .method(HttpMethod.GET) .path("/uri") .version(HttpVersion.HTTP_1_1) .param("a", "b") .header("X-Header", "Y-value") .agent("Jetty HTTP Client") .idleTimeout(5000, TimeUnit.MILLISECONDS) .timeout(20, TimeUnit.SECONDS); ContentResponse response = request.send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testGETAsync() throws Exception { HttpClient client = new HttpClient(); client.start(); final AtomicReference responseRef = new AtomicReference<>(); final CountDownLatch latch = new CountDownLatch(1); client.newRequest("localhost", 8080) // Send asynchronously .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded()) { responseRef.set(result.getResponse()); latch.countDown(); } } }); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Response response = responseRef.get(); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } @Test public void testPOSTWithParams_ShortAPI() throws Exception { HttpClient client = new HttpClient(); client.start(); // One liner to POST client.POST("http://localhost:8080").param("a", "\u20AC").send(); } @Test public void testRequestListener() throws Exception { HttpClient client = new HttpClient(); client.start(); Response response = client.newRequest("localhost", 8080) // Add a request listener .listener(new Request.Listener.Adapter() { @Override public void onSuccess(Request request) { } }).send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testRequestWithExplicitConnectionControl() throws Exception { HttpClient client = new HttpClient(); client.start(); // Create an explicit connection, and use try-with-resources to manage it FuturePromise futureConnection = new FuturePromise<>(); client.getDestination("http", "localhost", 8080).newConnection(futureConnection); try (Connection connection = futureConnection.get(5, TimeUnit.SECONDS)) { Request request = client.newRequest("localhost", 8080); // Asynchronous send but using FutureResponseListener FutureResponseListener listener = new FutureResponseListener(request); connection.send(request, listener); // Wait for the response on the listener Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } } @Test public void testFileUpload() throws Exception { HttpClient client = new HttpClient(); client.start(); // One liner to upload files Response response = client.newRequest("localhost", 8080).file(Paths.get("file_to_upload.txt")).send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testCookie() throws Exception { HttpClient client = new HttpClient(); client.start(); // Set a cookie to be sent in requests that match the cookie's domain client.getCookieStore().add(URI.create("http://host:8080/path"), new HttpCookie("name", "value")); // Send a request for the cookie's domain Response response = client.newRequest("host", 8080).send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testBasicAuthentication() throws Exception { HttpClient client = new HttpClient(); client.start(); URI uri = URI.create("http://localhost:8080/secure"); // Setup Basic authentication credentials for TestRealm client.getAuthenticationStore().addAuthentication(new BasicAuthentication(uri, "TestRealm", "username", "password")); // One liner to send the request ContentResponse response = client.newRequest(uri).timeout(5, TimeUnit.SECONDS).send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testFollowRedirects() throws Exception { HttpClient client = new HttpClient(); client.start(); // Do not follow redirects by default client.setFollowRedirects(false); ContentResponse response = client.newRequest("localhost", 8080) // Follow redirects for this request only .followRedirects(true) .timeout(5, TimeUnit.SECONDS) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testResponseInputStream() throws Exception { HttpClient client = new HttpClient(); client.start(); InputStreamResponseListener listener = new InputStreamResponseListener(); // Send asynchronously with the InputStreamResponseListener client.newRequest("localhost", 8080).send(listener); // Call to the listener's get() blocks until the headers arrived Response response = listener.get(5, TimeUnit.SECONDS); // Now check the response information that arrived to decide whether to read the content if (response.getStatus() == 200) { byte[] buffer = new byte[256]; try (InputStream input = listener.getInputStream()) { while (true) { int read = input.read(buffer); if (read < 0) break; // Do something with the bytes just read } } } else { response.abort(new Exception()); } } @Test public void testRequestInputStream() throws Exception { HttpClient client = new HttpClient(); client.start(); InputStream input = new ByteArrayInputStream("content".getBytes(StandardCharsets.UTF_8)); ContentResponse response = client.newRequest("localhost", 8080) // Provide the content as InputStream .content(new InputStreamContentProvider(input)) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testRequestOutputStream() throws Exception { HttpClient client = new HttpClient(); client.start(); OutputStreamContentProvider content = new OutputStreamContentProvider(); try (OutputStream output = content.getOutputStream()) { client.newRequest("localhost", 8080) .content(content) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertEquals(200, result.getResponse().getStatus()); } }); output.write(new byte[1024]); output.write(new byte[512]); output.write(new byte[256]); output.write(new byte[128]); } } @Test public void testProxyUsage() throws Exception { // In proxies, we receive the headers but not the content, so we must be able to send the request with // a lazy content provider that does not block request.send(...) HttpClient client = new HttpClient(); client.start(); final AtomicBoolean sendContent = new AtomicBoolean(true); DeferredContentProvider async = new DeferredContentProvider(ByteBuffer.wrap(new byte[]{0, 1, 2})); client.newRequest("localhost", 8080) .content(async) .send(new Response.Listener.Adapter() { @Override public void onBegin(Response response) { if (response.getStatus() == 404) sendContent.set(false); } }); Thread.sleep(100); if (sendContent.get()) async.offer(ByteBuffer.wrap(new byte[]{0})); Thread.sleep(100); if (sendContent.get()) async.offer(ByteBuffer.wrap(new byte[]{0})); Thread.sleep(100); async.close(); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/http/000077500000000000000000000000001261716203600267475ustar00rootroot00000000000000HttpDestinationOverHTTPTest.java000066400000000000000000000261021261716203600350510ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.util.concurrent.CountDownLatch; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.AbstractHttpClientServerTest; import org.eclipse.jetty.client.ConnectionPool; import org.eclipse.jetty.client.EmptyServerHandler; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest { public HttpDestinationOverHTTPTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Before public void init() throws Exception { start(new EmptyServerHandler()); } @Test public void test_FirstAcquire_WithEmptyQueue() throws Exception { HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())); Connection connection = destination.acquire(); if (connection == null) { // There are no queued requests, so the newly created connection will be idle connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); } Assert.assertNotNull(connection); } @Test public void test_SecondAcquire_AfterFirstAcquire_WithEmptyQueue_ReturnsSameConnection() throws Exception { HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())); Connection connection1 = destination.acquire(); if (connection1 == null) { // There are no queued requests, so the newly created connection will be idle long start = System.nanoTime(); while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5) { TimeUnit.MILLISECONDS.sleep(50); connection1 = destination.getConnectionPool().getIdleConnections().peek(); } Assert.assertNotNull(connection1); Connection connection2 = destination.acquire(); Assert.assertSame(connection1, connection2); } } @Test public void test_SecondAcquire_ConcurrentWithFirstAcquire_WithEmptyQueue_CreatesTwoConnections() throws Exception { final CountDownLatch idleLatch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())) { @Override protected ConnectionPool newConnectionPool(HttpClient client) { return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this) { @Override protected void idleCreated(Connection connection) { try { idleLatch.countDown(); latch.await(5, TimeUnit.SECONDS); super.idleCreated(connection); } catch (InterruptedException x) { x.printStackTrace(); } } }; } }; Connection connection1 = destination.acquire(); // Make sure we entered idleCreated(). Assert.assertTrue(idleLatch.await(5, TimeUnit.SECONDS)); // There are no available existing connections, so acquire() // returns null because we delayed idleCreated() above Assert.assertNull(connection1); // Second attempt also returns null because we delayed idleCreated() above. Connection connection2 = destination.acquire(); Assert.assertNull(connection2); latch.countDown(); // There must be 2 idle connections Connection connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); Assert.assertNotNull(connection); connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS); Assert.assertNotNull(connection); } @Test public void test_Acquire_Process_Release_Acquire_ReturnsSameConnection() throws Exception { HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())); HttpConnectionOverHTTP connection1 = destination.acquire(); long start = System.nanoTime(); while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5) { TimeUnit.MILLISECONDS.sleep(50); connection1 = (HttpConnectionOverHTTP)destination.getConnectionPool().getIdleConnections().peek(); } Assert.assertNotNull(connection1); // Acquire the connection to make it active Assert.assertSame(connection1, destination.acquire()); destination.process(connection1, false); destination.release(connection1); Connection connection2 = destination.acquire(); Assert.assertSame(connection1, connection2); } @Test public void test_IdleConnection_IdleTimeout() throws Exception { long idleTimeout = 1000; client.setIdleTimeout(idleTimeout); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())); Connection connection1 = destination.acquire(); if (connection1 == null) { // There are no queued requests, so the newly created connection will be idle long start = System.nanoTime(); while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5) { TimeUnit.MILLISECONDS.sleep(50); connection1 = destination.getConnectionPool().getIdleConnections().peek(); } Assert.assertNotNull(connection1); TimeUnit.MILLISECONDS.sleep(2 * idleTimeout); connection1 = destination.getConnectionPool().getIdleConnections().poll(); Assert.assertNull(connection1); } } @Test public void test_Request_Failed_If_MaxRequestsQueuedPerDestination_Exceeded() throws Exception { int maxQueued = 1; client.setMaxRequestsQueuedPerDestination(maxQueued); client.setMaxConnectionsPerDestination(1); // Make one request to open the connection and be sure everything is setup properly ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .send(); Assert.assertEquals(200, response.getStatus()); // Send another request that is sent immediately final CountDownLatch successLatch = new CountDownLatch(1); final CountDownLatch failureLatch = new CountDownLatch(1); client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/one") .onRequestQueued(new Request.QueuedListener() { @Override public void onQueued(Request request) { // This request exceeds the maximum queued, should fail client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .path("/two") .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); Assert.assertThat(result.getRequestFailure(), Matchers.instanceOf(RejectedExecutionException.class)); failureLatch.countDown(); } }); } }) .send(new Response.CompleteListener() { @Override public void onComplete(Result result) { if (result.isSucceeded()) successLatch.countDown(); } }); Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } @Test public void testDestinationIsRemoved() throws Exception { String host = "localhost"; int port = connector.getLocalPort(); Destination destinationBefore = client.getDestination(scheme, host, port); ContentResponse response = client.newRequest(host, port) .scheme(scheme) .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()) .send(); Assert.assertEquals(200, response.getStatus()); Destination destinationAfter = client.getDestination(scheme, host, port); Assert.assertSame(destinationBefore, destinationAfter); client.setRemoveIdleDestinations(true); response = client.newRequest(host, port) .scheme(scheme) .header(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()) .send(); Assert.assertEquals(200, response.getStatus()); destinationAfter = client.getDestination(scheme, host, port); Assert.assertNotSame(destinationBefore, destinationAfter); } } HttpReceiverOverHTTPTest.java000066400000000000000000000222531261716203600343370ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.io.EOFException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.io.ByteArrayEndPoint; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.Promise; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; public class HttpReceiverOverHTTPTest { @Rule public final TestTracker tracker = new TestTracker(); private HttpClient client; private HttpDestinationOverHTTP destination; private ByteArrayEndPoint endPoint; private HttpConnectionOverHTTP connection; @Before public void init() throws Exception { client = new HttpClient(); client.start(); destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); endPoint = new ByteArrayEndPoint(); connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); } @After public void destroy() throws Exception { client.stop(); } protected HttpExchange newExchange() { HttpRequest request = (HttpRequest)client.newRequest("http://localhost"); FutureResponseListener listener = new FutureResponseListener(request); HttpExchange exchange = new HttpExchange(destination, request, Collections.singletonList(listener)); boolean associated = connection.getHttpChannel().associate(exchange); Assert.assertTrue(associated); exchange.requestComplete(null); exchange.terminateRequest(); return exchange; } @Test public void test_Receive_NoResponseContent() throws Exception { endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-length: 0\r\n" + "\r\n"); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals("OK", response.getReason()); Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); HttpFields headers = response.getHeaders(); Assert.assertNotNull(headers); Assert.assertEquals(1, headers.size()); Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH)); } @Test public void test_Receive_ResponseContent() throws Exception { String content = "0123456789ABCDEF"; endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-length: " + content.length() + "\r\n" + "\r\n" + content); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals("OK", response.getReason()); Assert.assertSame(HttpVersion.HTTP_1_1, response.getVersion()); HttpFields headers = response.getHeaders(); Assert.assertNotNull(headers); Assert.assertEquals(1, headers.size()); Assert.assertEquals(String.valueOf(content.length()), headers.get(HttpHeader.CONTENT_LENGTH)); String received = listener.getContentAsString(StandardCharsets.UTF_8); Assert.assertEquals(content, received); } @Test public void test_Receive_ResponseContent_EarlyEOF() throws Exception { String content1 = "0123456789"; String content2 = "ABCDEF"; endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); endPoint.setInputEOF(); connection.getHttpChannel().receive(); try { listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) { Assert.assertTrue(e.getCause() instanceof EOFException); } } @Test public void test_Receive_ResponseContent_IdleTimeout() throws Exception { endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-length: 1\r\n" + "\r\n"); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); // Simulate an idle timeout connection.onReadTimeout(); try { listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) { Assert.assertTrue(e.getCause() instanceof TimeoutException); } } @Test public void test_Receive_BadResponse() throws Exception { endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-length: A\r\n" + "\r\n"); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); try { listener.get(5, TimeUnit.SECONDS); Assert.fail(); } catch (ExecutionException e) { Assert.assertTrue(e.getCause() instanceof HttpResponseException); } } @Test public void test_FillInterested_RacingWith_BufferRelease() throws Exception { connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()) { @Override protected HttpChannelOverHTTP newHttpChannel() { return new HttpChannelOverHTTP(this) { @Override protected HttpReceiverOverHTTP newHttpReceiver() { return new HttpReceiverOverHTTP(this) { @Override protected void fillInterested() { // Verify that the buffer has been released // before fillInterested() is called. Assert.assertNull(getResponseBuffer()); // Fill the endpoint so receive is called again. endPoint.setInput("X"); super.fillInterested(); } }; } }; } }; // Partial response to trigger the call to fillInterested(). endPoint.setInput("" + "HTTP/1.1 200 OK\r\n" + "Content-Length: 1\r\n" + "\r\n"); HttpExchange exchange = newExchange(); FutureResponseListener listener = (FutureResponseListener)exchange.getResponseListeners().get(0); connection.getHttpChannel().receive(); Response response = listener.get(5, TimeUnit.SECONDS); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); } } HttpSenderOverHTTPTest.java000066400000000000000000000301401261716203600340050ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/http// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.http; import java.net.URI; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Locale; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.Origin; import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; import org.eclipse.jetty.client.util.ByteBufferContentProvider; import org.eclipse.jetty.io.ByteArrayEndPoint; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.toolchain.test.annotation.Slow; import org.eclipse.jetty.util.Promise; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; public class HttpSenderOverHTTPTest { @Rule public final TestTracker tracker = new TestTracker(); private HttpClient client; @Before public void init() throws Exception { client = new HttpClient(); client.start(); } @After public void destroy() throws Exception { client.stop(); } @Test public void test_Send_NoRequestContent() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); request.listener(new Request.Listener.Adapter() { @Override public void onHeaders(Request request) { headersLatch.countDown(); } @Override public void onSuccess(Request request) { successLatch.countDown(); } }); connection.send(request, null); String requestString = endPoint.takeOutputString(); Assert.assertTrue(requestString.startsWith("GET ")); Assert.assertTrue(requestString.endsWith("\r\n\r\n")); Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } @Slow @Test public void test_Send_NoRequestContent_IncompleteFlush() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); connection.send(request, null); // This take will free space in the buffer and allow for the write to complete StringBuilder builder = new StringBuilder(endPoint.takeOutputString()); // Wait for the write to complete TimeUnit.SECONDS.sleep(1); String chunk = endPoint.takeOutputString(); while (chunk.length() > 0) { builder.append(chunk); chunk = endPoint.takeOutputString(); } String requestString = builder.toString(); Assert.assertTrue(requestString.startsWith("GET ")); Assert.assertTrue(requestString.endsWith("\r\n\r\n")); } @Test public void test_Send_NoRequestContent_Exception() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); // Shutdown output to trigger the exception on write endPoint.shutdownOutput(); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch failureLatch = new CountDownLatch(2); request.listener(new Request.Listener.Adapter() { @Override public void onFailure(Request request, Throwable x) { failureLatch.countDown(); } }); connection.send(request, new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); failureLatch.countDown(); } }); Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); } @Test public void test_Send_NoRequestContent_IncompleteFlush_Exception() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint("", 16); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); final CountDownLatch failureLatch = new CountDownLatch(2); request.listener(new Request.Listener.Adapter() { @Override public void onFailure(Request request, Throwable x) { failureLatch.countDown(); } }); connection.send(request, new Response.Listener.Adapter() { @Override public void onComplete(Result result) { Assert.assertTrue(result.isFailed()); failureLatch.countDown(); } }); // Shutdown output to trigger the exception on write endPoint.shutdownOutput(); // This take will free space in the buffer and allow for the write to complete // although it will fail because we shut down the output endPoint.takeOutputString(); Assert.assertTrue(failureLatch.await(5, TimeUnit.SECONDS)); } @Test public void test_Send_SmallRequestContent_InOneBuffer() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); String content = "abcdef"; request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content.getBytes(StandardCharsets.UTF_8)))); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); request.listener(new Request.Listener.Adapter() { @Override public void onHeaders(Request request) { headersLatch.countDown(); } @Override public void onSuccess(Request request) { successLatch.countDown(); } }); connection.send(request, null); String requestString = endPoint.takeOutputString(); Assert.assertTrue(requestString.startsWith("GET ")); Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } @Test public void test_Send_SmallRequestContent_InTwoBuffers() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); String content1 = "0123456789"; String content2 = "abcdef"; request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8)))); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); request.listener(new Request.Listener.Adapter() { @Override public void onHeaders(Request request) { headersLatch.countDown(); } @Override public void onSuccess(Request request) { successLatch.countDown(); } }); connection.send(request, null); String requestString = endPoint.takeOutputString(); Assert.assertTrue(requestString.startsWith("GET ")); Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content1 + content2)); Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } @Test public void test_Send_SmallRequestContent_Chunked_InTwoChunks() throws Exception { ByteArrayEndPoint endPoint = new ByteArrayEndPoint(); HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", 8080)); HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination, new Promise.Adapter()); Request request = client.newRequest(URI.create("http://localhost/")); String content1 = "0123456789"; String content2 = "ABCDEF"; request.content(new ByteBufferContentProvider(ByteBuffer.wrap(content1.getBytes(StandardCharsets.UTF_8)), ByteBuffer.wrap(content2.getBytes(StandardCharsets.UTF_8))) { @Override public long getLength() { return -1; } }); final CountDownLatch headersLatch = new CountDownLatch(1); final CountDownLatch successLatch = new CountDownLatch(1); request.listener(new Request.Listener.Adapter() { @Override public void onHeaders(Request request) { headersLatch.countDown(); } @Override public void onSuccess(Request request) { successLatch.countDown(); } }); connection.send(request, null); String requestString = endPoint.takeOutputString(); Assert.assertTrue(requestString.startsWith("GET ")); String content = Integer.toHexString(content1.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content1 + "\r\n"; content += Integer.toHexString(content2.length()).toUpperCase(Locale.ENGLISH) + "\r\n" + content2 + "\r\n"; content += "0\r\n\r\n"; Assert.assertTrue(requestString.endsWith("\r\n\r\n" + content)); Assert.assertTrue(headersLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(successLatch.await(5, TimeUnit.SECONDS)); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/000077500000000000000000000000001261716203600265715ustar00rootroot00000000000000SslBytesClientTest.java000066400000000000000000000321601261716203600331260ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ssl// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.ssl; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.SocketTimeoutException; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLSocket; import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class SslBytesClientTest extends SslBytesTest { private ExecutorService threadPool; private HttpClient client; private SslContextFactory sslContextFactory; private SSLServerSocket acceptor; private SimpleProxy proxy; @Before public void init() throws Exception { threadPool = Executors.newCachedThreadPool(); client = new HttpClient(new SslContextFactory(true)); client.setMaxConnectionsPerDestination(1); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); sslContextFactory = client.getSslContextFactory(); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePassword("storepwd"); client.start(); SSLContext sslContext = sslContextFactory.getSslContext(); acceptor = (SSLServerSocket)sslContext.getServerSocketFactory().createServerSocket(0); int serverPort = acceptor.getLocalPort(); proxy = new SimpleProxy(threadPool, "localhost", serverPort); proxy.start(); logger.info(":{} <==> :{}", proxy.getPort(), serverPort); } @After public void destroy() throws Exception { if (acceptor != null) acceptor.close(); if (proxy != null) proxy.stop(); if (client != null) client.stop(); if (threadPool != null) threadPool.shutdownNow(); } @Test public void testHandshake() throws Exception { Request request = client.newRequest("localhost", proxy.getPort()); FutureResponseListener listener = new FutureResponseListener(request); request.scheme(HttpScheme.HTTPS.asString()).send(listener); Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); final SSLSocket server = (SSLSocket)acceptor.accept(); server.setUseClientMode(false); Future handshake = threadPool.submit(new Callable() { public Object call() throws Exception { server.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Server Hello + Certificate + Server Done record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Client Key Exchange record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Change Cipher Spec record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToServer(record); // Client Done record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Change Cipher Spec record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToClient(record); // Server Done record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); // Read request BufferedReader reader = new BufferedReader(new InputStreamReader(server.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertTrue(line.startsWith("GET")); while (line.length() > 0) line = reader.readLine(); // Write response OutputStream output = server.getOutputStream(); output.write(("HTTP/1.1 200 OK\r\n" + "Content-Length: 0\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); output.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); server.close(); } @Test public void testServerRenegotiation() throws Exception { Request request = client.newRequest("localhost", proxy.getPort()); FutureResponseListener listener = new FutureResponseListener(request); request.scheme(HttpScheme.HTTPS.asString()).send(listener); Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); final SSLSocket server = (SSLSocket)acceptor.accept(); server.setUseClientMode(false); Future handshake = threadPool.submit(new Callable() { public Object call() throws Exception { server.startHandshake(); return null; } }); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); // Read request InputStream serverInput = server.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(serverInput, StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertTrue(line.startsWith("GET")); while (line.length() > 0) line = reader.readLine(); OutputStream serverOutput = server.getOutputStream(); byte[] data1 = new byte[1024]; Arrays.fill(data1, (byte)'X'); String content1 = new String(data1, StandardCharsets.UTF_8); byte[] data2 = new byte[1024]; Arrays.fill(data2, (byte)'Y'); final String content2 = new String(data2, StandardCharsets.UTF_8); // Write first part of the response serverOutput.write(("HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1).getBytes(StandardCharsets.UTF_8)); serverOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Renegotiate Future renegotiation = threadPool.submit(new Callable() { public Object call() throws Exception { server.startHandshake(); return null; } }); // Renegotiation Handshake TLSRecord record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Renegotiation Handshake record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Trigger a read to have the server write the final renegotiation steps server.setSoTimeout(100); try { serverInput.read(); Assert.fail(); } catch (SocketTimeoutException x) { // Expected } // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Renegotiation Change Cipher record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToClient(record); // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Renegotiation Change Cipher record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToServer(record); // Renegotiation Handshake record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS)); // Complete the response automaticProxyFlow = proxy.startAutomaticFlow(); serverOutput.write(data2); serverOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); ContentResponse response = listener.get(5, TimeUnit.SECONDS); Assert.assertEquals(HttpStatus.OK_200, response.getStatus()); Assert.assertEquals(data1.length + data2.length, response.getContent().length); server.close(); } @Test public void testServerRenegotiationWhenRenegotiationIsForbidden() throws Exception { sslContextFactory.setRenegotiationAllowed(false); Request request = client.newRequest("localhost", proxy.getPort()); FutureResponseListener listener = new FutureResponseListener(request); request.scheme(HttpScheme.HTTPS.asString()).send(listener); Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); final SSLSocket server = (SSLSocket)acceptor.accept(); server.setUseClientMode(false); Future handshake = threadPool.submit(new Callable() { public Object call() throws Exception { server.startHandshake(); return null; } }); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); // Read request InputStream serverInput = server.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(serverInput, StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertTrue(line.startsWith("GET")); while (line.length() > 0) line = reader.readLine(); OutputStream serverOutput = server.getOutputStream(); byte[] data1 = new byte[1024]; Arrays.fill(data1, (byte)'X'); String content1 = new String(data1, StandardCharsets.UTF_8); byte[] data2 = new byte[1024]; Arrays.fill(data2, (byte)'Y'); final String content2 = new String(data2, StandardCharsets.UTF_8); // Write first part of the response serverOutput.write(("HTTP/1.1 200 OK\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1).getBytes(StandardCharsets.UTF_8)); serverOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Renegotiate threadPool.submit(new Callable() { public Object call() throws Exception { server.startHandshake(); return null; } }); // Renegotiation Handshake TLSRecord record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); record = proxy.readFromClient(); Assert.assertNull(record); server.close(); } } SslBytesServerTest.java000066400000000000000000002174051261716203600331650ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ssl// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.ssl; import static org.hamcrest.Matchers.nullValue; import java.io.BufferedReader; import java.io.EOFException; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSocket; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.ssl.SslBytesTest.TLSRecord.Type; import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.SelectChannelEndPoint; import org.eclipse.jetty.io.SelectorManager.ManagedSelector; import org.eclipse.jetty.io.ssl.SslConnection; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.OS; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; public class SslBytesServerTest extends SslBytesTest { private final AtomicInteger sslFills = new AtomicInteger(); private final AtomicInteger sslFlushes = new AtomicInteger(); private final AtomicInteger httpParses = new AtomicInteger(); private final AtomicReference serverEndPoint = new AtomicReference<>(); private final int idleTimeout = 2000; private ExecutorService threadPool; private Server server; private SslContextFactory sslContextFactory; private int serverPort; private SSLContext sslContext; private SimpleProxy proxy; private Runnable idleHook; @Before public void init() throws Exception { threadPool = Executors.newCachedThreadPool(); server = new Server(); File keyStore = MavenTestingUtils.getTestResourceFile("keystore.jks"); sslContextFactory = new SslContextFactory(); sslContextFactory.setKeyStorePath(keyStore.getAbsolutePath()); sslContextFactory.setKeyStorePassword("storepwd"); HttpConnectionFactory httpFactory = new HttpConnectionFactory() { @Override public Connection newConnection(Connector connector, EndPoint endPoint) { return configure(new HttpConnection(getHttpConfiguration(), connector, endPoint) { @Override protected HttpParser newHttpParser() { return new HttpParser(newRequestHandler(), getHttpConfiguration().getRequestHeaderSize()) { @Override public boolean parseNext(ByteBuffer buffer) { httpParses.incrementAndGet(); return super.parseNext(buffer); } }; } @Override protected boolean onReadTimeout() { final Runnable idleHook = SslBytesServerTest.this.idleHook; if (idleHook != null) idleHook.run(); return super.onReadTimeout(); } }, connector, endPoint); } }; httpFactory.getHttpConfiguration().addCustomizer(new SecureRequestCustomizer()); SslConnectionFactory sslFactory = new SslConnectionFactory(sslContextFactory, httpFactory.getProtocol()) { @Override protected SslConnection newSslConnection(Connector connector, EndPoint endPoint, SSLEngine engine) { return new SslConnection(connector.getByteBufferPool(), connector.getExecutor(), endPoint, engine) { @Override protected DecryptedEndPoint newDecryptedEndPoint() { return new DecryptedEndPoint() { @Override public int fill(ByteBuffer buffer) throws IOException { sslFills.incrementAndGet(); return super.fill(buffer); } @Override public boolean flush(ByteBuffer... appOuts) throws IOException { sslFlushes.incrementAndGet(); return super.flush(appOuts); } }; } }; } }; ServerConnector connector = new ServerConnector(server, null,null,null,1,1,sslFactory, httpFactory) { @Override protected SelectChannelEndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException { SelectChannelEndPoint endp = super.newEndPoint(channel,selectSet,key); serverEndPoint.set(endp); return endp; } }; connector.setIdleTimeout(idleTimeout); connector.setPort(0); server.addConnector(connector); server.setHandler(new AbstractHandler() { @Override public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { try { request.setHandled(true); String contentLength = request.getHeader("Content-Length"); if (contentLength != null) { int length = Integer.parseInt(contentLength); ServletInputStream input = httpRequest.getInputStream(); ServletOutputStream output = httpResponse.getOutputStream(); byte[] buffer = new byte[32 * 1024]; while (length > 0) { int read = input.read(buffer); if (read < 0) throw new EOFException(); length -= read; if (target.startsWith("/echo")) output.write(buffer, 0, read); } } } catch (IOException x) { if (!(target.endsWith("suppress_exception"))) throw x; } } }); server.start(); serverPort = connector.getLocalPort(); sslContext = sslContextFactory.getSslContext(); proxy = new SimpleProxy(threadPool, "localhost", serverPort); proxy.start(); logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort); } @After public void destroy() throws Exception { if (proxy != null) proxy.stop(); if (server != null) server.stop(); if (threadPool != null) threadPool.shutdownNow(); } @Test(timeout=10000) public void testHandshake() throws Exception { final SSLSocket client = newClient(); Future handshake = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); // Server Hello + Certificate + Server Done record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Client Key Exchange record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); // Change Cipher Spec record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); // Client Done record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); // Change Cipher Spec record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Server Done record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); closeClient(client); } @Test public void testHandshakeWithResumedSessionThenClose() throws Exception { // First socket will establish the SSL session SSLSocket client1 = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client1.startHandshake(); client1.close(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); int proxyPort = proxy.getPort(); proxy.stop(); proxy = new SimpleProxy(threadPool, proxyPort, "localhost", serverPort); proxy.start(); logger.info("proxy:{} <==> server:{}", proxy.getPort(), serverPort); final SSLSocket client2 = newClient(proxy); Future handshake = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client2.startHandshake(); return null; } }); // Client Hello with SessionID TLSRecord record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); // Server Hello record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Change Cipher Spec record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Server Done record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Client Key Exchange record = proxy.readFromClient(); Assert.assertNotNull(record); // Client Done TLSRecord doneRecord = proxy.readFromClient(); Assert.assertNotNull(doneRecord); // Close client2.close(); TLSRecord closeRecord = proxy.readFromClient(); Assert.assertNotNull(closeRecord); Assert.assertEquals(TLSRecord.Type.ALERT, closeRecord.getType()); // Flush to server Client Key Exchange + Client Done + Close in one chunk byte[] recordBytes = record.getBytes(); byte[] doneBytes = doneRecord.getBytes(); byte[] closeRecordBytes = closeRecord.getBytes(); byte[] chunk = new byte[recordBytes.length + doneBytes.length + closeRecordBytes.length]; System.arraycopy(recordBytes, 0, chunk, 0, recordBytes.length); System.arraycopy(doneBytes, 0, chunk, recordBytes.length, doneBytes.length); System.arraycopy(closeRecordBytes, 0, chunk, recordBytes.length + doneBytes.length, closeRecordBytes.length); proxy.flushToServer(0, chunk); // Close the raw socket proxy.flushToServer(null); // Expect the server to send a FIN as well record = proxy.readFromServer(); Assert.assertNull(record); // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); } @Test public void testHandshakeWithSplitBoundary() throws Exception { final SSLSocket client = newClient(); Future handshake = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); byte[] bytes = record.getBytes(); byte[] chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); byte[] chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Server Hello + Certificate + Server Done record = proxy.readFromServer(); proxy.flushToClient(record); // Client Key Exchange record = proxy.readFromClient(); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Change Cipher Spec record = proxy.readFromClient(); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Client Done record = proxy.readFromClient(); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Change Cipher Spec record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); // Server Done record = proxy.readFromServer(); Assert.assertNotNull(record); proxy.flushToClient(record); Assert.assertNull(handshake.get(5, TimeUnit.SECONDS)); // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(40)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); // Close Alert record = proxy.readFromClient(); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Socket close record = proxy.readFromClient(); Assert.assertNull(String.valueOf(record), record); proxy.flushToServer(record); // Socket close record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } } @Test public void testClientHelloIncompleteThenReset() throws Exception { final SSLSocket client = newClient(); threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); byte[] bytes = record.getBytes(); byte[] chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); proxy.flushToServer(100, chunk1); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testClientHelloThenReset() throws Exception { final SSLSocket client = newClient(); threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testHandshakeThenReset() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testRequestIncompleteThenReset() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); byte[] bytes = record.getBytes(); byte[] chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); proxy.flushToServer(100, chunk1); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testRequestResponse() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); proxy.flushToServer(record); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // Application data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); closeClient(client); } @Test public void testHandshakeAndRequestOneByteAtATime() throws Exception { final SSLSocket client = newClient(); Future handshake = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Client Hello TLSRecord record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5, b); // Server Hello + Certificate + Server Done record = proxy.readFromServer(); proxy.flushToClient(record); // Client Key Exchange record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5,b); // Change Cipher Spec record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5, b); // Client Done record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5, b); // Change Cipher Spec record = proxy.readFromServer(); proxy.flushToClient(record); // Server Done record = proxy.readFromServer(); proxy.flushToClient(record); Assert.assertNull(handshake.get(1, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5, b); Assert.assertNull(request.get(1, TimeUnit.SECONDS)); // Application data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(1000); Assert.assertThat(sslFills.get(), Matchers.lessThan(1000)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); // An average of 958 httpParses is seen in standard Oracle JDK's // An average of 1183 httpParses is seen in OpenJDK JVMs. Assert.assertThat(httpParses.get(), Matchers.lessThan(500)); client.close(); // Close Alert record = proxy.readFromClient(); for (byte b : record.getBytes()) proxy.flushToServer(5, b); // Socket close record = proxy.readFromClient(); Assert.assertNull(String.valueOf(record), record); proxy.flushToServer(record); // Socket close record = proxy.readFromServer(); // Raw close or alert if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } } @Test public void testRequestWithCloseAlertAndShutdown() throws Exception { // See next test on why we only run in Linux Assume.assumeTrue(OS.IS_LINUX); final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); proxy.flushToServer(record); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); client.close(); // Close Alert record = proxy.readFromClient(); proxy.flushToServer(record); // Socket close record = proxy.readFromClient(); Assert.assertNull(String.valueOf(record), record); proxy.flushToServer(record); // Expect response from server // SSLSocket is limited and we cannot read the response, but we make sure // it is application data and not a close alert record = proxy.readFromServer(); Assert.assertNotNull(record); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); // Socket close record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); } @Test public void testRequestWithCloseAlert() throws Exception { // Currently we are ignoring this test on anything other then linux // http://tools.ietf.org/html/rfc2246#section-7.2.1 // TODO (react to this portion which seems to allow win/mac behavior) // It is required that the other party respond with a close_notify alert of its own // and close down the connection immediately, discarding any pending writes. It is not // required for the initiator of the close to wait for the responding // close_notify alert before closing the read side of the connection. Assume.assumeTrue(OS.IS_LINUX); final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); client.close(); // Close Alert record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); proxy.flushToServer(record); // Do not close the raw socket yet // Expect response from server // SSLSocket is limited and we cannot read the response, but we make sure // it is application data and not a close alert record = proxy.readFromServer(); Assert.assertNotNull(record); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); // Socket close record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); // Socket close record = proxy.readFromClient(); Assert.assertNull(String.valueOf(record), record); proxy.flushToServer(record); } @Test public void testRequestWithRawClose() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // Application data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); // Close the raw socket, this generates a truncation attack proxy.flushToServer(null); // Expect raw close from server OR ALERT record = proxy.readFromServer(); // TODO check that this is OK? if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testRequestWithImmediateRawClose() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record, 0); // Close the raw socket, this generates a truncation attack proxy.flushToServer(null); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // Application data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); // Expect raw close from server record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testRequestWithBigContentWriteBlockedThenReset() throws Exception { // Don't run on Windows (buggy JVM) Assume.assumeTrue(!OS.IS_WINDOWS); final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); byte[] data = new byte[128 * 1024]; Arrays.fill(data, (byte)'X'); final String content = new String(data, StandardCharsets.UTF_8); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET /echo HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: " + content.length() + "\r\n" + "\r\n" + content).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Nine TLSRecords will be generated for the request for (int i = 0; i < 9; ++i) { // Application data TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record, 0); } Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // We asked the server to echo back the data we sent // but we do not read it, thus causing a write interest // on the server. // However, we then simulate that the client resets the // connection, and this will cause an exception in the // server that is trying to write the data TimeUnit.MILLISECONDS.sleep(500); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(40)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(40)); Assert.assertThat(httpParses.get(), Matchers.lessThan(50)); client.close(); } @Test public void testRequestWithBigContentReadBlockedThenReset() throws Exception { // Don't run on Windows (buggy JVM) Assume.assumeTrue(!OS.IS_WINDOWS); final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); byte[] data = new byte[128 * 1024]; Arrays.fill(data, (byte)'X'); final String content = new String(data, StandardCharsets.UTF_8); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET /echo_suppress_exception HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Length: " + content.length() + "\r\n" + "\r\n" + content).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Nine TLSRecords will be generated for the request, // but we write only 5 of them, so the server goes in read blocked state for (int i = 0; i < 5; ++i) { // Application data TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record, 0); } Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // The server should be read blocked, and we send a RST TimeUnit.MILLISECONDS.sleep(500); proxy.sendRSTToServer(); // Wait a while to detect spinning TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(40)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(40)); Assert.assertThat(httpParses.get(), Matchers.lessThan(50)); client.close(); } @Test public void testRequestWithCloseAlertWithSplitBoundary() throws Exception { if (!OS.IS_LINUX) { // currently we are ignoring this test on anything other then linux //http://tools.ietf.org/html/rfc2246#section-7.2.1 // TODO (react to this portion which seems to allow win/mac behavior) //It is required that the other party respond with a close_notify alert of its own //and close down the connection immediately, discarding any pending writes. It is not //required for the initiator of the close to wait for the responding //close_notify alert before closing the read side of the connection. return; } final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord dataRecord = proxy.readFromClient(); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); client.close(); // Close Alert TLSRecord closeRecord = proxy.readFromClient(); // Send request and half of the close alert bytes byte[] dataBytes = dataRecord.getBytes(); byte[] closeBytes = closeRecord.getBytes(); byte[] bytes = new byte[dataBytes.length + closeBytes.length / 2]; System.arraycopy(dataBytes, 0, bytes, 0, dataBytes.length); System.arraycopy(closeBytes, 0, bytes, dataBytes.length, closeBytes.length / 2); proxy.flushToServer(100, bytes); // Send the other half of the close alert bytes bytes = new byte[closeBytes.length - closeBytes.length / 2]; System.arraycopy(closeBytes, closeBytes.length / 2, bytes, 0, bytes.length); proxy.flushToServer(100, bytes); // Do not close the raw socket yet // Expect response from server // SSLSocket is limited and we cannot read the response, but we make sure // it is application data and not a close alert TLSRecord record = proxy.readFromServer(); Assert.assertNotNull(record); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); // Socket close record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); // Now should be a raw close record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); } @Test public void testRequestWithContentWithSplitBoundary() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); final String content = "0123456789ABCDEF"; Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + content.length() + "\r\n" + "\r\n" + content).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Application data TLSRecord record = proxy.readFromClient(); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); byte[] chunk1 = new byte[2 * record.getBytes().length / 3]; System.arraycopy(record.getBytes(), 0, chunk1, 0, chunk1.length); proxy.flushToServer(100, chunk1); byte[] chunk2 = new byte[record.getBytes().length - chunk1.length]; System.arraycopy(record.getBytes(), chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk2); record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); closeClient(client); } @Test public void testRequestWithBigContentWithSplitBoundary() throws Exception { final SSLSocket client = newClient(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Use a content that is larger than the TLS record which is 2^14 (around 16k) byte[] data = new byte[128 * 1024]; Arrays.fill(data, (byte)'X'); final String content = new String(data, StandardCharsets.UTF_8); Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { OutputStream clientOutput = client.getOutputStream(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + content.length() + "\r\n" + "\r\n" + content).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Nine TLSRecords will be generated for the request for (int i = 0; i < 9; ++i) { // Application data TLSRecord record = proxy.readFromClient(); byte[] bytes = record.getBytes(); byte[] chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); byte[] chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(100)); Assert.assertNull(request.get(5, TimeUnit.SECONDS)); TLSRecord record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(100)); closeClient(client); } // TODO work out why this test frequently fails @Ignore @Test(timeout=10000) public void testRequestWithContentWithRenegotiationInMiddleOfContentWhenRenegotiationIsForbidden() throws Exception { assumeJavaVersionSupportsTLSRenegotiations(); sslContextFactory.setRenegotiationAllowed(false); final SSLSocket client = newClient(); final OutputStream clientOutput = client.getOutputStream(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); byte[] data1 = new byte[1024]; Arrays.fill(data1, (byte)'X'); String content1 = new String(data1, StandardCharsets.UTF_8); byte[] data2 = new byte[1024]; Arrays.fill(data2, (byte)'Y'); final String content2 = new String(data2, StandardCharsets.UTF_8); // Write only part of the body automaticProxyFlow = proxy.startAutomaticFlow(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Renegotiate threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Renegotiation Handshake TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Renegotiation now allowed, server has closed record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); proxy.flushToClient(record); record = proxy.readFromServer(); Assert.assertNull(record); // Write the rest of the request threadPool.submit(new Callable() { @Override public Object call() throws Exception { clientOutput.write(content2.getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Trying to write more application data results in an exception since the server closed record = proxy.readFromClient(); proxy.flushToServer(record); try { record = proxy.readFromClient(); Assert.assertNotNull(record); proxy.flushToServer(record); Assert.fail(); } catch (IOException expected) { } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(50)); client.close(); } @Test public void testRequestWithBigContentWithRenegotiationInMiddleOfContent() throws Exception { assumeJavaVersionSupportsTLSRenegotiations(); final SSLSocket client = newClient(); final OutputStream clientOutput = client.getOutputStream(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Use a content that is larger than the TLS record which is 2^14 (around 16k) byte[] data1 = new byte[80 * 1024]; Arrays.fill(data1, (byte)'X'); String content1 = new String(data1, StandardCharsets.UTF_8); byte[] data2 = new byte[48 * 1024]; Arrays.fill(data2, (byte)'Y'); final String content2 = new String(data2, StandardCharsets.UTF_8); // Write only part of the body automaticProxyFlow = proxy.startAutomaticFlow(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Renegotiate Future renegotiation = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Renegotiation Handshake TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Renegotiation Change Cipher record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToClient(record); // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Trigger a read to have the client write the final renegotiation steps client.setSoTimeout(100); try { client.getInputStream().read(); Assert.fail(); } catch (SocketTimeoutException x) { // Expected } // Renegotiation Change Cipher record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToServer(record); // Renegotiation Handshake record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToServer(record); Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS)); // Write the rest of the request Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { clientOutput.write(content2.getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Three TLSRecords will be generated for the remainder of the content for (int i = 0; i < 3; ++i) { // Application data record = proxy.readFromClient(); proxy.flushToServer(record); } Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // Read response // Application Data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(50)); closeClient(client); } @Test(timeout=10000) public void testRequestWithBigContentWithRenegotiationInMiddleOfContentWithSplitBoundary() throws Exception { assumeJavaVersionSupportsTLSRenegotiations(); final SSLSocket client = newClient(); final OutputStream clientOutput = client.getOutputStream(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Use a content that is larger than the TLS record which is 2^14 (around 16k) byte[] data1 = new byte[80 * 1024]; Arrays.fill(data1, (byte)'X'); String content1 = new String(data1, StandardCharsets.UTF_8); byte[] data2 = new byte[48 * 1024]; Arrays.fill(data2, (byte)'Y'); final String content2 = new String(data2, StandardCharsets.UTF_8); // Write only part of the body automaticProxyFlow = proxy.startAutomaticFlow(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + (content1.length() + content2.length()) + "\r\n" + "\r\n" + content1).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Renegotiate Future renegotiation = threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Renegotiation Handshake TLSRecord record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); byte[] bytes = record.getBytes(); byte[] chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); byte[] chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Renegotiation Change Cipher record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); proxy.flushToClient(record); // Renegotiation Handshake record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); proxy.flushToClient(record); // Trigger a read to have the client write the final renegotiation steps client.setSoTimeout(100); try { client.getInputStream().read(); Assert.fail(); } catch (SocketTimeoutException x) { // Expected } // Renegotiation Change Cipher record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.CHANGE_CIPHER_SPEC, record.getType()); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); proxy.flushToServer(100, chunk2); // Renegotiation Handshake record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.HANDSHAKE, record.getType()); bytes = record.getBytes(); chunk1 = new byte[2 * bytes.length / 3]; System.arraycopy(bytes, 0, chunk1, 0, chunk1.length); chunk2 = new byte[bytes.length - chunk1.length]; System.arraycopy(bytes, chunk1.length, chunk2, 0, chunk2.length); proxy.flushToServer(100, chunk1); // Do not write the second chunk now, but merge it with content, see below Assert.assertNull(renegotiation.get(5, TimeUnit.SECONDS)); // Write the rest of the request Future request = threadPool.submit(new Callable() { @Override public Object call() throws Exception { clientOutput.write(content2.getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); return null; } }); // Three TLSRecords will be generated for the remainder of the content // Merge the last chunk of the renegotiation with the first data record record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); byte[] dataBytes = record.getBytes(); byte[] mergedBytes = new byte[chunk2.length + dataBytes.length]; System.arraycopy(chunk2, 0, mergedBytes, 0, chunk2.length); System.arraycopy(dataBytes, 0, mergedBytes, chunk2.length, dataBytes.length); proxy.flushToServer(100, mergedBytes); // Write the remaining 2 TLS records for (int i = 0; i < 2; ++i) { // Application data record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record); } Assert.assertNull(request.get(5, TimeUnit.SECONDS)); // Read response // Application Data record = proxy.readFromServer(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToClient(record); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(50)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(100)); closeClient(client); } @Test public void testServerShutdownOutputClientDoesNotCloseServerCloses() throws Exception { final SSLSocket client = newClient(); final OutputStream clientOutput = client.getOutputStream(); SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); byte[] data = new byte[3 * 1024]; Arrays.fill(data, (byte)'Y'); String content = new String(data, StandardCharsets.UTF_8); automaticProxyFlow = proxy.startAutomaticFlow(); clientOutput.write(("" + "POST / HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + content.length() + "\r\n" + "Connection: close\r\n" + "\r\n" + content).getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader(client.getInputStream(), StandardCharsets.UTF_8)); String line = reader.readLine(); Assert.assertNotNull(line); Assert.assertTrue(line.startsWith("HTTP/1.1 200 ")); while ((line = reader.readLine()) != null) { if (line.trim().length() == 0) break; } Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); // Check client is at EOF Assert.assertEquals(-1, client.getInputStream().read()); // Client should close the socket, but let's hold it open. // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); // The server has shutdown the output since the client sent a Connection: close // but the client does not close, so the server must idle timeout the endPoint. TimeUnit.MILLISECONDS.sleep(idleTimeout + idleTimeout / 2); Assert.assertFalse(serverEndPoint.get().isOpen()); } @Test public void testPlainText() throws Exception { final SSLSocket client = newClient(); threadPool.submit(new Callable() { @Override public Object call() throws Exception { client.startHandshake(); return null; } }); // Instead of passing the Client Hello, we simulate plain text was passed in proxy.flushToServer(0, "GET / HTTP/1.1\r\n".getBytes(StandardCharsets.UTF_8)); // We expect that the server closes the connection immediately TLSRecord record = proxy.readFromServer(); Assert.assertNull(String.valueOf(record), record); // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(20)); client.close(); } @Test public void testRequestConcurrentWithIdleExpiration() throws Exception { final SSLSocket client = newClient(); final OutputStream clientOutput = client.getOutputStream(); final CountDownLatch latch = new CountDownLatch(1); idleHook = new Runnable() { public void run() { if (latch.getCount()==0) return; try { // Send request clientOutput.write(("" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + "\r\n").getBytes(StandardCharsets.UTF_8)); clientOutput.flush(); latch.countDown(); } catch (Exception x) { // Latch won't trigger and test will fail x.printStackTrace(); } } }; SimpleProxy.AutomaticFlow automaticProxyFlow = proxy.startAutomaticFlow(); client.startHandshake(); Assert.assertTrue(automaticProxyFlow.stop(5, TimeUnit.SECONDS)); Assert.assertTrue(latch.await(idleTimeout * 2, TimeUnit.MILLISECONDS)); // Be sure that the server sent a SSL close alert TLSRecord record = proxy.readFromServer(); Assert.assertNotNull(record); Assert.assertEquals(TLSRecord.Type.ALERT, record.getType()); // Write the request to the server, to simulate a request // concurrent with the SSL close alert record = proxy.readFromClient(); Assert.assertEquals(TLSRecord.Type.APPLICATION, record.getType()); proxy.flushToServer(record, 0); // Check that we did not spin TimeUnit.MILLISECONDS.sleep(500); Assert.assertThat(sslFills.get(), Matchers.lessThan(20)); Assert.assertThat(sslFlushes.get(), Matchers.lessThan(20)); Assert.assertThat(httpParses.get(), Matchers.lessThan(50)); record = proxy.readFromServer(); Assert.assertNull(record); TimeUnit.MILLISECONDS.sleep(200); Assert.assertThat(((Dumpable)server.getConnectors()[0]).dump(), Matchers.not(Matchers.containsString("SCEP@"))); } private void assumeJavaVersionSupportsTLSRenegotiations() { // Due to a security bug, TLS renegotiations were disabled in JDK 1.6.0_19-21 // so we check the java version in order to avoid to fail the test. String javaVersion = System.getProperty("java.version"); Pattern regexp = Pattern.compile("1\\.6\\.0_(\\d{2})"); Matcher matcher = regexp.matcher(javaVersion); if (matcher.matches()) { String nano = matcher.group(1); Assume.assumeThat(Integer.parseInt(nano), Matchers.greaterThan(21)); } } private SSLSocket newClient() throws IOException, InterruptedException { return newClient(proxy); } private SSLSocket newClient(SimpleProxy proxy) throws IOException, InterruptedException { SSLSocket client = (SSLSocket)sslContext.getSocketFactory().createSocket("localhost", proxy.getPort()); client.setUseClientMode(true); Assert.assertTrue(proxy.awaitClient(5, TimeUnit.SECONDS)); return client; } private void closeClient(SSLSocket client) throws Exception { client.close(); // Close Alert TLSRecord record = proxy.readFromClient(); proxy.flushToServer(record); // Socket close record = proxy.readFromClient(); Assert.assertNull(String.valueOf(record), record); proxy.flushToServer(record); // Socket close record = proxy.readFromServer(); if (record!=null) { Assert.assertEquals(record.getType(),Type.ALERT); record = proxy.readFromServer(); } Assert.assertThat(record,nullValue()); } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/ssl/SslBytesTest.java000066400000000000000000000305021261716203600320440ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.ssl; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.junit.Assert; import org.junit.Rule; public abstract class SslBytesTest { @Rule public TestTracker tracker = new TestTracker(); protected final Logger logger = Log.getLogger(getClass()); public static class TLSRecord { private final SslBytesServerTest.TLSRecord.Type type; private final byte[] bytes; public TLSRecord(SslBytesServerTest.TLSRecord.Type type, byte[] bytes) { this.type = type; this.bytes = bytes; } public SslBytesServerTest.TLSRecord.Type getType() { return type; } public byte[] getBytes() { return bytes; } @Override public String toString() { return "TLSRecord [" + type + "] " + bytes.length + " bytes"; } public enum Type { CHANGE_CIPHER_SPEC(20), ALERT(21), HANDSHAKE(22), APPLICATION(23); private int code; private Type(int code) { this.code = code; SslBytesServerTest.TLSRecord.Type.Mapper.codes.put(this.code, this); } public static SslBytesServerTest.TLSRecord.Type from(int code) { SslBytesServerTest.TLSRecord.Type result = SslBytesServerTest.TLSRecord.Type.Mapper.codes.get(code); if (result == null) throw new IllegalArgumentException("Invalid TLSRecord.Type " + code); return result; } private static class Mapper { private static final Map codes = new HashMap<>(); } } } public class SimpleProxy implements Runnable { private final CountDownLatch latch = new CountDownLatch(1); private final ExecutorService threadPool; private final int proxyPort; private final String serverHost; private final int serverPort; private volatile ServerSocket serverSocket; private volatile Socket server; private volatile Socket client; public SimpleProxy(ExecutorService threadPool, String serverHost, int serverPort) { this(threadPool, 0, serverHost, serverPort); } public SimpleProxy(ExecutorService threadPool, int proxyPort, String serverHost, int serverPort) { this.threadPool = threadPool; this.proxyPort = proxyPort; this.serverHost = serverHost; this.serverPort = serverPort; } public void start() throws Exception { serverSocket = new ServerSocket(proxyPort); Thread acceptor = new Thread(this); acceptor.start(); server = new Socket(serverHost, serverPort); } public void stop() throws Exception { server.close(); if (client != null) // some tests only run on linux, those won't create a client on other OS client.close(); serverSocket.close(); } public void run() { try { client = serverSocket.accept(); latch.countDown(); } catch (IOException x) { x.printStackTrace(); } } public int getPort() { return serverSocket.getLocalPort(); } public TLSRecord readFromClient() throws IOException { TLSRecord record = read(client); logger.debug("C --> P {}", record); return record; } private TLSRecord read(Socket socket) throws IOException { InputStream input = socket.getInputStream(); int first = -2; while (true) { try { socket.setSoTimeout(500); first = input.read(); break; } catch (SocketTimeoutException x) { if (Thread.currentThread().isInterrupted()) break; } } if (first == -2) throw new InterruptedIOException(); else if (first == -1) return null; if (first >= 0x80) { // SSLv2 Record int hiLength = first & 0x3F; int loLength = input.read(); int length = (hiLength << 8) + loLength; byte[] bytes = new byte[2 + length]; bytes[0] = (byte)first; bytes[1] = (byte)loLength; return read(TLSRecord.Type.HANDSHAKE, input, bytes, 2, length); } else { // TLS Record int major = input.read(); int minor = input.read(); int hiLength = input.read(); int loLength = input.read(); int length = (hiLength << 8) + loLength; byte[] bytes = new byte[1 + 2 + 2 + length]; bytes[0] = (byte)first; bytes[1] = (byte)major; bytes[2] = (byte)minor; bytes[3] = (byte)hiLength; bytes[4] = (byte)loLength; return read(TLSRecord.Type.from(first), input, bytes, 5, length); } } private TLSRecord read(SslBytesServerTest.TLSRecord.Type type, InputStream input, byte[] bytes, int offset, int length) throws IOException { while (length > 0) { int read = input.read(bytes, offset, length); if (read < 0) throw new EOFException(); offset += read; length -= read; } return new TLSRecord(type, bytes); } public void flushToServer(TLSRecord record) throws Exception { flushToServer(record, 100); } public void flushToServer(TLSRecord record, long sleep) throws Exception { if (record == null) { server.shutdownOutput(); if (client.isOutputShutdown()) { client.close(); server.close(); } } else { flush(sleep, server, record.getBytes()); } } public void flushToServer(long sleep, byte... bytes) throws Exception { flush(sleep, server, bytes); } private void flush(long sleep, Socket socket, byte... bytes) throws Exception { OutputStream output = socket.getOutputStream(); output.write(bytes); output.flush(); if (sleep > 0) TimeUnit.MILLISECONDS.sleep(sleep); } public TLSRecord readFromServer() throws IOException { TLSRecord record = read(server); logger.debug("P <-- S {}", record); return record; } public void flushToClient(TLSRecord record) throws Exception { if (record == null) { client.shutdownOutput(); if (server.isOutputShutdown()) { server.close(); client.close(); } } else { flush(0, client, record.getBytes()); } } public SslBytesServerTest.SimpleProxy.AutomaticFlow startAutomaticFlow() throws InterruptedException { final CountDownLatch startLatch = new CountDownLatch(2); final CountDownLatch stopLatch = new CountDownLatch(2); Future clientToServer = threadPool.submit(new Callable() { public Object call() throws Exception { startLatch.countDown(); logger.debug("Automatic flow C --> S started"); try { while (true) { flushToServer(readFromClient(), 0); } } catch (InterruptedIOException x) { return null; } finally { stopLatch.countDown(); logger.debug("Automatic flow C --> S finished"); } } }); Future serverToClient = threadPool.submit(new Callable() { public Object call() throws Exception { startLatch.countDown(); logger.debug("Automatic flow C <-- S started"); try { while (true) { flushToClient(readFromServer()); } } catch (InterruptedIOException x) { return null; } finally { stopLatch.countDown(); logger.debug("Automatic flow C <-- S finished"); } } }); Assert.assertTrue(startLatch.await(5, TimeUnit.SECONDS)); return new SslBytesServerTest.SimpleProxy.AutomaticFlow(stopLatch, clientToServer, serverToClient); } public boolean awaitClient(int time, TimeUnit unit) throws InterruptedException { return latch.await(time, unit); } public void sendRSTToServer() throws IOException { // Calling setSoLinger(true, 0) causes close() // to send a RST instead of a FIN, causing an // exception to be thrown on the other end server.setSoLinger(true, 0); server.close(); } public class AutomaticFlow { private final CountDownLatch stopLatch; private final Future clientToServer; private final Future serverToClient; public AutomaticFlow(CountDownLatch stopLatch, Future clientToServer, Future serverToClient) { this.stopLatch = stopLatch; this.clientToServer = clientToServer; this.serverToClient = serverToClient; } public boolean stop(long time, TimeUnit unit) throws InterruptedException { clientToServer.cancel(true); serverToClient.cancel(true); return stopLatch.await(time, unit); } } } } jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/util/000077500000000000000000000000001261716203600267455ustar00rootroot00000000000000DeferredContentProviderTest.java000066400000000000000000000102751261716203600351640ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.jetty.util.Callback; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; public class DeferredContentProviderTest { private ExecutorService executor; @Before public void prepare() throws Exception { executor = Executors.newCachedThreadPool(); } @After public void dispose() throws Exception { executor.shutdownNow(); } @Test public void testWhenEmptyFlushDoesNotBlock() throws Exception { final DeferredContentProvider provider = new DeferredContentProvider(); Future task = executor.submit(new Callable() { @Override public Object call() throws Exception { provider.flush(); return null; } }); Assert.assertTrue(await(task, 5, TimeUnit.SECONDS)); } @Test public void testOfferFlushBlocksUntilSucceeded() throws Exception { final DeferredContentProvider provider = new DeferredContentProvider(); Iterator iterator = provider.iterator(); provider.offer(ByteBuffer.allocate(0)); Future task = executor.submit(new Callable() { @Override public Object call() throws Exception { provider.flush(); return null; } }); // Wait until flush() blocks. Assert.assertFalse(await(task, 1, TimeUnit.SECONDS)); // Consume the content and succeed the callback. iterator.next(); ((Callback)iterator).succeeded(); // Flush should return. Assert.assertTrue(await(task, 5, TimeUnit.SECONDS)); } @Test public void testCloseFlushDoesNotBlock() throws Exception { final DeferredContentProvider provider = new DeferredContentProvider(); provider.close(); Future task = executor.submit(new Callable() { @Override public Object call() throws Exception { provider.flush(); return null; } }); // Wait until flush() blocks. Assert.assertTrue(await(task, 5, TimeUnit.SECONDS)); } @Test public void testCloseNextHasNextReturnsFalse() throws Exception { DeferredContentProvider provider = new DeferredContentProvider(); Iterator iterator = provider.iterator(); provider.close(); Assert.assertFalse(iterator.hasNext()); try { iterator.next(); Assert.fail(); } catch (NoSuchElementException x) { // Expected } Assert.assertFalse(iterator.hasNext()); } private boolean await(Future task, long time, TimeUnit unit) throws Exception { try { task.get(time, unit); return true; } catch (TimeoutException x) { return false; } } } InputStreamContentProviderTest.java000066400000000000000000000117071261716203600357200ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Assert; import org.junit.Test; public class InputStreamContentProviderTest { @Test public void testHasNextFalseThenNext() { final AtomicBoolean closed = new AtomicBoolean(); InputStream stream = new InputStream() { @Override public int read() throws IOException { return -1; } @Override public void close() throws IOException { super.close(); closed.compareAndSet(false, true); } }; InputStreamContentProvider provider = new InputStreamContentProvider(stream); Iterator iterator = provider.iterator(); Assert.assertNotNull(iterator); Assert.assertFalse(iterator.hasNext()); try { iterator.next(); Assert.fail(); } catch (NoSuchElementException expected) { } Assert.assertFalse(iterator.hasNext()); Assert.assertTrue(closed.get()); } @Test public void testStreamWithContentThenNextThenNext() { final AtomicBoolean closed = new AtomicBoolean(); ByteArrayInputStream stream = new ByteArrayInputStream(new byte[]{1}) { @Override public void close() throws IOException { super.close(); closed.compareAndSet(false, true); } }; InputStreamContentProvider provider = new InputStreamContentProvider(stream); Iterator iterator = provider.iterator(); Assert.assertNotNull(iterator); ByteBuffer buffer = iterator.next(); Assert.assertNotNull(buffer); try { iterator.next(); Assert.fail(); } catch (NoSuchElementException expected) { } Assert.assertFalse(iterator.hasNext()); Assert.assertTrue(closed.get()); } @Test public void testStreamWithExceptionThenNext() { final AtomicBoolean closed = new AtomicBoolean(); InputStream stream = new InputStream() { @Override public int read() throws IOException { throw new IOException(); } @Override public void close() throws IOException { super.close(); closed.compareAndSet(false, true); } }; InputStreamContentProvider provider = new InputStreamContentProvider(stream); Iterator iterator = provider.iterator(); Assert.assertNotNull(iterator); try { iterator.next(); Assert.fail(); } catch (NoSuchElementException expected) { } Assert.assertFalse(iterator.hasNext()); Assert.assertTrue(closed.get()); } @Test public void testHasNextWithExceptionThenNext() { final AtomicBoolean closed = new AtomicBoolean(); InputStream stream = new InputStream() { @Override public int read() throws IOException { throw new IOException(); } @Override public void close() throws IOException { super.close(); closed.compareAndSet(false, true); } }; InputStreamContentProvider provider = new InputStreamContentProvider(stream); Iterator iterator = provider.iterator(); Assert.assertNotNull(iterator); Assert.assertTrue(iterator.hasNext()); try { iterator.next(); Assert.fail(); } catch (NoSuchElementException expected) { } Assert.assertFalse(iterator.hasNext()); Assert.assertTrue(closed.get()); } } TypedContentProviderTest.java000066400000000000000000000126351261716203600345330ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/java/org/eclipse/jetty/client/util// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.client.util; import java.io.IOException; import java.nio.charset.StandardCharsets; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.client.AbstractHttpClientServerTest; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; public class TypedContentProviderTest extends AbstractHttpClientServerTest { public TypedContentProviderTest(SslContextFactory sslContextFactory) { super(sslContextFactory); } @Test public void testFormContentProvider() throws Exception { final String name1 = "a"; final String value1 = "1"; final String name2 = "b"; final String value2 = "2"; final String value3 = "\u20AC"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals("POST", request.getMethod()); Assert.assertEquals(MimeTypes.Type.FORM_ENCODED.asString(), request.getContentType()); Assert.assertEquals(value1, request.getParameter(name1)); String[] values = request.getParameterValues(name2); Assert.assertNotNull(values); Assert.assertEquals(2, values.length); Assert.assertThat(values, Matchers.arrayContainingInAnyOrder(value2, value3)); } }); Fields fields = new Fields(); fields.put(name1, value1); fields.add(name2, value2); fields.add(name2, value3); ContentResponse response = client.FORM(scheme + "://localhost:" + connector.getLocalPort(), fields); Assert.assertEquals(200, response.getStatus()); } @Test public void testFormContentProviderWithDifferentContentType() throws Exception { final String name1 = "a"; final String value1 = "1"; final String name2 = "b"; final String value2 = "2"; Fields fields = new Fields(); fields.put(name1, value1); fields.add(name2, value2); final String content = FormContentProvider.convert(fields); final String contentType = "text/plain;charset=UTF-8"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals("POST", request.getMethod()); Assert.assertEquals(contentType, request.getContentType()); Assert.assertEquals(content, IO.toString(request.getInputStream())); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .method(HttpMethod.POST) .content(new FormContentProvider(fields)) .header(HttpHeader.CONTENT_TYPE, contentType) .send(); Assert.assertEquals(200, response.getStatus()); } @Test public void testTypedContentProviderWithNoContentType() throws Exception { final String content = "data"; start(new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { baseRequest.setHandled(true); Assert.assertEquals("GET", request.getMethod()); Assert.assertNull(request.getContentType()); Assert.assertEquals(content, IO.toString(request.getInputStream())); } }); ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) .scheme(scheme) .content(new StringContentProvider(null, content, StandardCharsets.UTF_8)) .send(); Assert.assertEquals(200, response.getStatus()); } } jetty-9.2.14.v20151106/jetty-client/src/test/resources/000077500000000000000000000000001261716203600222315ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-client/src/test/resources/jetty-logging.properties000066400000000000000000000002131261716203600271260ustar00rootroot00000000000000org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.client.LEVEL=DEBUG jetty-9.2.14.v20151106/jetty-client/src/test/resources/keystore.jks000066400000000000000000000042361261716203600246140ustar00rootroot00000000000000mykey5+00 +**!^Ktxd{)8; mPRFUq }KC'9`h m b-̚94nCbݲ?R_-ښ0Rqgx4nĨzN{sU,a}G)lvXZI$%5w@1O V)xcD9)MT-b<㻻gxJըo.9*)(lS6Wj0{7ZPgll9O4#e N6{eLJV4c<ҳ^j9u[9RC` ;@>|\H ;UZ,7t#a,P xK+`Zn|hgX%*caCLFxEl2@o'Nb> юc!ջJXvA0t +zcc0hIPqֶ9NVzJ\eD ~њ$ {Ν֏65jUV5A*B0C5}&XDž܌{iHl=$uMF!JdmD6H5\"u>p$"%Z{sn=NODb'p qu,[g$OʃQT)E3~mm#Z{kzHzO\/_r7*X4wS8>8R[u"_Mq7s+H%KX.509V0R0:O"0  *H 0j1 0 UUS1 0 UCA10U Palo Alto10U Intalio10U Webtide10U Jetty Project0  120127162501Z21120103162501Z0j1 0 UUS1 0 UCA10U Palo Alto10U Intalio10U Webtide10U Jetty Project0"0  *H 0 OY qh9U]}/:-\XG0<$×w9h[>W&_Xhr-httxߺzW5G)>(Jtm+ HHE#i=pʫOP Q:~ZVK?1"7YqܥOnn+)7J;Xwҁ6E3PYR F\-Dϼ=׺X[%Q2xsd"}ۊJ>8qvU$ ه@IFr.Qr Q90  *H %=5Դ]o}[~h#nDݩ`Si]lKu}z|TROC<ʖ o[|(҇Qx+,`ݡ΋*jPu Ԉ-bnIJ2xCYK.NQuq=yoB Y*[1fZ|+.\,TX4.>aPh !lP{d^nh+-Xn(r9]p: }l3z(边:ilzjetty-9.2.14.v20151106/jetty-client/src/test/resources/realm.properties000066400000000000000000000001001261716203600254360ustar00rootroot00000000000000# Format is :, basic:basic digest:digest jetty-9.2.14.v20151106/jetty-client/src/test/resources/truststore.jks000066400000000000000000000016241261716203600252030ustar00rootroot00000000000000mykey5fX.509V0R0:O"0  *H 0j1 0 UUS1 0 UCA10U Palo Alto10U Intalio10U Webtide10U Jetty Project0  120127162501Z21120103162501Z0j1 0 UUS1 0 UCA10U Palo Alto10U Intalio10U Webtide10U Jetty Project0"0  *H 0 OY qh9U]}/:-\XG0<$×w9h[>W&_Xhr-httxߺzW5G)>(Jtm+ HHE#i=pʫOP Q:~ZVK?1"7YqܥOnn+)7J;Xwҁ6E3PYR F\-Dϼ=׺X[%Q2xsd"}ۊJ>8qvU$ ه@IFr.Qr Q90  *H %=5Դ]o}[~h#nDݩ`Si]lKu}z|TROC<ʖ o[|(҇Qx+,`ݡ΋*jPu Ԉ-bnIJ2xCYK.NQuq=yoB Y*[1fZ|+.\,TX4.>aPh !lP{d^nh+-Xn(r9]p: }l3zm iܖ)aO7jetty-9.2.14.v20151106/jetty-continuation/000077500000000000000000000000001261716203600177055ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/pom.xml000066400000000000000000000037361261716203600212330ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-continuation Jetty :: Continuation Asynchronous API http://www.eclipse.org/jetty ${project.groupId}.continuation org.apache.felix maven-bundle-plugin true manifest org.apache.maven.plugins maven-jar-plugin artifact-jar jar ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.continuation.* javax.servlet javax.servlet-api provided jetty-9.2.14.v20151106/jetty-continuation/src/000077500000000000000000000000001261716203600204745ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/000077500000000000000000000000001261716203600214205ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/000077500000000000000000000000001261716203600223415ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/000077500000000000000000000000001261716203600231305ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/000077500000000000000000000000001261716203600245545ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600257135ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation/000077500000000000000000000000001261716203600304255ustar00rootroot00000000000000Continuation.java000066400000000000000000000412771261716203600336760ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import javax.servlet.ServletResponse; /* ------------------------------------------------------------ */ /** * Continuation. * * A continuation is a mechanism by which a HTTP Request can be suspended and * restarted after a timeout or an asynchronous event has occurred. *

* The continuation mechanism is a portable mechanism that will work * asynchronously without additional configuration of all jetty-7, * jetty-8 and Servlet 3.0 containers. With the addition of * the {@link ContinuationFilter}, the mechanism will also work * asynchronously on jetty-6 and non-asynchronously on any * servlet 2.5 container. *

* The Continuation API is a simplification of the richer async API * provided by the servlet-3.0 and an enhancement of the continuation * API that was introduced with jetty-6. *

*

Continuation Usage

*

* A continuation object is obtained for a request by calling the * factory method {@link ContinuationSupport#getContinuation(ServletRequest)}. * The continuation type returned will depend on the servlet container * being used. *

*

* There are two distinct style of operation of the continuation API. *

*

Suspend/Resume Usage

*

The suspend/resume style is used when a servlet and/or * filter is used to generate the response after a asynchronous wait that is * terminated by an asynchronous handler. *

*
 * Filter/Servlet:
 *   // if we need to get asynchronous results
 *   Object results = request.getAttribute("results);
 *   if (results==null)
 *   {
 *     Continuation continuation = ContinuationSupport.getContinuation(request);
 *     continuation.suspend();
 *     myAsyncHandler.register(continuation);
 *     return; // or continuation.undispatch();
 *   }
 * 
 * async wait ...
 * 
 * Async Handler:
 *   // when the waited for event happens
 *   continuation.setAttribute("results",event);
 *   continuation.resume();
 *   
 * Filter/Servlet:
 *   // when the request is redispatched 
 *   if (results==null)
 *   {
 *     ... // see above
 *   }
 *   else
 *   {
 *     response.getOutputStream().write(process(results));
 *   }
 * 
*

Suspend/Complete Usage

*

* The suspend/complete style is used when an asynchronous handler is used to * generate the response: *

*
 * Filter/Servlet:
 *   // when we want to enter asynchronous mode
 *   Continuation continuation = ContinuationSupport.getContinuation(request);
 *   continuation.suspend(response); // response may be wrapped
 *   myAsyncHandler.register(continuation);
 *   return; // or continuation.undispatch();
 *
 * Wrapping Filter:
 *   // any filter that had wrapped the response should be implemented like:
 *   try
 *   {
 *     chain.doFilter(request,wrappedResponse);
 *   }
 *   finally
 *   {
 *     if (!continuation.isResponseWrapped())
 *       wrappedResponse.finish()
 *     else
 *       continuation.addContinuationListener(myCompleteListener)
 *   }
 *
 * async wait ...
 *
 * Async Handler:
 *   // when the async event happens
 *   continuation.getServletResponse().getOutputStream().write(process(event));
 *   continuation.complete()
 * 
* *

Continuation Timeout

*

* If a continuation is suspended, but neither {@link #complete()} or {@link #resume()} is * called during the period set by {@link #setTimeout(long)}, then the continuation will * expire and {@link #isExpired()} will return true. *

*

* When a continuation expires, the {@link ContinuationListener#onTimeout(Continuation)} * method is called on any {@link ContinuationListener} that has been registered via the * {@link #addContinuationListener(ContinuationListener)} method. The onTimeout handlers * may write a response and call {@link #complete()}. If {@link #complete()} is not called, * then the container will redispatch the request as if {@link #resume()} had been called, * except that {@link #isExpired()} will be true and {@link #isResumed()} will be false. *

* * @see ContinuationSupport * @see ContinuationListener * */ public interface Continuation { public final static String ATTRIBUTE = "org.eclipse.jetty.continuation"; /* ------------------------------------------------------------ */ /** * Set the continuation timeout. * * @param timeoutMs * The time in milliseconds to wait before expiring this * continuation after a call to {@link #suspend()} or {@link #suspend(ServletResponse)}. * A timeout of <=0 means the continuation will never expire. */ void setTimeout(long timeoutMs); /* ------------------------------------------------------------ */ /** * Suspend the processing of the request and associated * {@link ServletResponse}. * *

* After this method has been called, the lifecycle of the request will be * extended beyond the return to the container from the * {@link Servlet#service(ServletRequest, ServletResponse)} method and * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} * calls. When a suspended request is returned to the container after * a dispatch, then the container will not commit the associated response * (unless an exception other than {@link ContinuationThrowable} is thrown). *

* *

* When the thread calling the filter chain and/or servlet has returned to * the container with a suspended request, the thread is freed for other * tasks and the request is held until either: *

    *
  • a call to {@link #resume()}.
  • *
  • a call to {@link #complete()}.
  • *
  • the timeout expires.
  • *
*

* Typically suspend with no arguments is uses when a call to {@link #resume()} * is expected. If a call to {@link #complete()} is expected, then the * {@link #suspend(ServletResponse)} method should be used instead of this method. *

* * @exception IllegalStateException * If the request cannot be suspended */ void suspend(); /* ------------------------------------------------------------ */ /** * Suspend the processing of the request and associated * {@link ServletResponse}. * *

* After this method has been called, the lifecycle of the request will be * extended beyond the return to the container from the * {@link Servlet#service(ServletRequest, ServletResponse)} method and * {@link Filter#doFilter(ServletRequest, ServletResponse, FilterChain)} * calls. When a suspended request is returned to the container after * a dispatch, then the container will not commit the associated response * (unless an exception other than {@link ContinuationThrowable} is thrown). *

*

* When the thread calling the filter chain and/or servlet has returned to * the container with a suspended request, the thread is freed for other * tasks and the request is held until either: *

    *
  • a call to {@link #resume()}.
  • *
  • a call to {@link #complete()}.
  • *
  • the timeout expires.
  • *
*

* Typically suspend with a response argument is uses when a call to {@link #complete()} * is expected. If a call to {@link #resume()} is expected, then the * {@link #suspend()} method should be used instead of this method. *

*

* Filters that may wrap the response object should check {@link #isResponseWrapped()} * to decide if they should destroy/finish the wrapper. If {@link #isResponseWrapped()} * returns true, then the wrapped request has been passed to the asynchronous * handler and the wrapper should not be destroyed/finished until after a call to * {@link #complete()} (potentially using a {@link ContinuationListener#onComplete(Continuation)} * listener). * * @param response The response to return via a call to {@link #getServletResponse()} * @exception IllegalStateException * If the request cannot be suspended */ void suspend(ServletResponse response); /* ------------------------------------------------------------ */ /** * Resume a suspended request. * *

* This method can be called by any thread that has been passed a reference * to a continuation. When called the request is redispatched to the * normal filter chain and servlet processing with {@link #isInitial()} false. *

*

* If resume is called before a suspended request is returned to the * container (ie the thread that called {@link #suspend()} is still * within the filter chain and/or servlet service method), then the resume * does not take effect until the call to the filter chain and/or servlet * returns to the container. In this case both {@link #isSuspended()} and * {@link #isResumed()} return true. Multiple calls to resume are ignored. *

*

* Typically resume() is used after a call to {@link #suspend()} with * no arguments. The dispatch after a resume call will use the original * request and response objects, even if {@link #suspend(ServletResponse)} * had been passed a wrapped response. *

* * @see #suspend() * @exception IllegalStateException if the request is not suspended. * */ void resume(); /* ------------------------------------------------------------ */ /** * Complete a suspended request. * *

* This method can be called by any thread that has been passed a reference * to a suspended request. When a request is completed, the associated * response object committed and flushed. The request is not redispatched. *

* *

* If complete is called before a suspended request is returned to the * container (ie the thread that called {@link #suspend()} is still * within the filter chain and/or servlet service method), then the complete * does not take effect until the call to the filter chain and/or servlet * returns to the container. In this case both {@link #isSuspended()} and * {@link #isResumed()} return true. *

* *

* Typically resume() is used after a call to {@link #suspend(ServletResponse)} with * a possibly wrapped response. The async handler should use the response * provided by {@link #getServletResponse()} to write the response before * calling {@link #complete()}. If the request was suspended with a * call to {@link #suspend()} then no response object will be available via * {@link #getServletResponse()}. *

* *

* Once complete has been called and any thread calling the filter chain * and/or servlet chain has returned to the container, the request lifecycle * is complete. The container is able to recycle request objects, so it is * not valid hold a request or continuation reference after the end of the * life cycle. * * @see #suspend() * @exception IllegalStateException * if the request is not suspended. * */ void complete(); /* ------------------------------------------------------------ */ /** * @return true after {@link #suspend()} has been called and before the * request has been redispatched due to being resumed, completed or * timed out. */ boolean isSuspended(); /* ------------------------------------------------------------ */ /** * @return true if the request has been redispatched by a call to * {@link #resume()}. Returns false after any subsequent call to * suspend */ boolean isResumed(); /* ------------------------------------------------------------ */ /** * @return true after a request has been redispatched as the result of a * timeout. Returns false after any subsequent call to suspend. */ boolean isExpired(); /* ------------------------------------------------------------ */ /** * @return true while the request is within the initial dispatch to the * filter chain and/or servlet. Will return false once the calling * thread has returned to the container after suspend has been * called and during any subsequent redispatch. */ boolean isInitial(); /* ------------------------------------------------------------ */ /** * Is the suspended response wrapped. *

* Filters that wrap the response object should check this method to * determine if they should destroy/finish the wrapped response. If * the request was suspended with a call to {@link #suspend(ServletResponse)} * that passed the wrapped response, then the filter should register * a {@link ContinuationListener} to destroy/finish the wrapped response * during a call to {@link ContinuationListener#onComplete(Continuation)}. * @return True if {@link #suspend(ServletResponse)} has been passed a * {@link ServletResponseWrapper} instance. */ boolean isResponseWrapped(); /* ------------------------------------------------------------ */ /** * Get the suspended response. * @return the {@link ServletResponse} passed to {@link #suspend(ServletResponse)}. */ ServletResponse getServletResponse(); /* ------------------------------------------------------------ */ /** * Add a ContinuationListener. * * @param listener */ void addContinuationListener(ContinuationListener listener); /* ------------------------------------------------------------ */ /** Set a request attribute. * This method is a convenience method to call the {@link ServletRequest#setAttribute(String, Object)} * method on the associated request object. * This is a thread safe call and may be called by any thread. * @param name the attribute name * @param attribute the attribute value */ public void setAttribute(String name, Object attribute); /* ------------------------------------------------------------ */ /** Get a request attribute. * This method is a convenience method to call the {@link ServletRequest#getAttribute(String)} * method on the associated request object. * This is a thread safe call and may be called by any thread. * @param name the attribute name * @return the attribute value */ public Object getAttribute(String name); /* ------------------------------------------------------------ */ /** Remove a request attribute. * This method is a convenience method to call the {@link ServletRequest#removeAttribute(String)} * method on the associated request object. * This is a thread safe call and may be called by any thread. * @param name the attribute name */ public void removeAttribute(String name); /* ------------------------------------------------------------ */ /** * Undispatch the request. *

* This method can be called on a suspended continuation in order * to exit the dispatch to the filter/servlet by throwing a {@link ContinuationThrowable} * which is caught either by the container or the {@link ContinuationFilter}. * This is an alternative to simply returning from the dispatch in the case * where filters in the filter chain may not be prepared to handle a suspended * request. *

* This method should only be used as a last resort and a normal return is a prefereable * solution if filters can be updated to handle that case. * * @throws ContinuationThrowable thrown if the request is suspended. The instance of the * exception may be reused on subsequent calls, so the stack frame may not be accurate. */ public void undispatch() throws ContinuationThrowable; } ContinuationFilter.java000066400000000000000000000125631261716203600350400ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /* ------------------------------------------------------------ */ /** *

ContinuationFilter must be applied to servlet paths that make use of * the asynchronous features provided by {@link Continuation} APIs, but that * are deployed in servlet containers that are a * compliant Servlet 3.0 container.

*

The following init parameters may be used to configure the filter (these are mostly for testing):

*
*
debug
Boolean controlling debug output
*
faux
Boolean to force use of faux continuations
*
*

If the servlet container is not Jetty 7+ nor a Servlet 3 * container, then "faux" continuations will be used.

*

Faux continuations will just put the thread that called {@link Continuation#suspend()} * in wait, and will notify that thread when {@link Continuation#resume()} or * {@link Continuation#complete()} is called.

*

Faux continuations are not threadless continuations (they are "faux" - fake - for this reason) * and as such they will scale less than proper continuations.

*/ public class ContinuationFilter implements Filter { static boolean _initialized; static boolean __debug; // shared debug status private boolean _faux; private boolean _filtered; ServletContext _context; private boolean _debug; public void init(FilterConfig filterConfig) throws ServletException { boolean jetty_7_or_greater="org.eclipse.jetty.servlet".equals(filterConfig.getClass().getPackage().getName()); _context = filterConfig.getServletContext(); String param=filterConfig.getInitParameter("debug"); _debug=param!=null&&Boolean.parseBoolean(param); if (_debug) __debug=true; param=filterConfig.getInitParameter("partial"); param=filterConfig.getInitParameter("faux"); if (param!=null) _faux=Boolean.parseBoolean(param); else _faux=!(jetty_7_or_greater || _context.getMajorVersion()>=3); _filtered=_faux; if (_debug) _context.log("ContinuationFilter "+ " jetty="+jetty_7_or_greater+ " faux="+_faux+ " filtered="+_filtered+ " servlet3="+ContinuationSupport.__servlet3); _initialized=true; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (_filtered) { Continuation c = (Continuation) request.getAttribute(Continuation.ATTRIBUTE); FilteredContinuation fc; if (_faux && (c==null || !(c instanceof FauxContinuation))) { fc = new FauxContinuation(request); request.setAttribute(Continuation.ATTRIBUTE,fc); } else fc=(FilteredContinuation)c; boolean complete=false; while (!complete) { try { if (fc==null || (fc).enter(response)) chain.doFilter(request,response); } catch (ContinuationThrowable e) { debug("faux",e); } finally { if (fc==null) fc = (FilteredContinuation) request.getAttribute(Continuation.ATTRIBUTE); complete=fc==null || (fc).exit(); } } } else { try { chain.doFilter(request,response); } catch (ContinuationThrowable e) { debug("caught",e); } } } private void debug(String string) { if (_debug) { _context.log(string); } } private void debug(String string, Throwable th) { if (_debug) { if (th instanceof ContinuationThrowable) _context.log(string+":"+th); else _context.log(string,th); } } public void destroy() { } public interface FilteredContinuation extends Continuation { boolean enter(ServletResponse response); boolean exit(); } } ContinuationListener.java000066400000000000000000000037031261716203600353740ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import java.util.EventListener; /* ------------------------------------------------------------ */ /** A Continuation Listener *

* A ContinuationListener may be registered with a call to * {@link Continuation#addContinuationListener(ContinuationListener)}. * */ public interface ContinuationListener extends EventListener { /* ------------------------------------------------------------ */ /** * Called when a continuation life cycle is complete and after * any calls to {@link ServletRequestListener#requestDestroyed(javax.servlet.ServletRequestEvent)} * The response may still be written to during the call. * * @param continuation */ public void onComplete(Continuation continuation); /* ------------------------------------------------------------ */ /** * Called when a suspended continuation has timed out. * The response may be written to and the methods * {@link Continuation#resume()} or {@link Continuation#complete()} * may be called by a onTimeout implementation, * @param continuation */ public void onTimeout(Continuation continuation); } ContinuationSupport.java000066400000000000000000000103321261716203600352570ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import java.lang.reflect.Constructor; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestWrapper; import javax.servlet.ServletResponse; /** * ContinuationSupport. * * Factory class for accessing Continuation instances, which with either be * a servlet 3.0 or a faux continuation. */ public class ContinuationSupport { static final boolean __servlet3; static final Class __waitingContinuation; static final Constructor __newServlet3Continuation; static { boolean servlet3Support=false; Constructors3cc=null; try { boolean servlet3=ServletRequest.class.getMethod("startAsync")!=null; if (servlet3) { Class s3c = ContinuationSupport.class.getClassLoader().loadClass("org.eclipse.jetty.continuation.Servlet3Continuation").asSubclass(Continuation.class); s3cc=s3c.getConstructor(ServletRequest.class); servlet3Support=true; } } catch (Exception e) {} finally { __servlet3=servlet3Support; __newServlet3Continuation=s3cc; } Class waiting=null; try { waiting=ContinuationSupport.class.getClassLoader().loadClass("org.mortbay.util.ajax.WaitingContinuation"); } catch (Exception e) { } finally { __waitingContinuation=waiting; } } /* ------------------------------------------------------------ */ /** * Get a Continuation. The type of the Continuation returned may * vary depending on the container in which the application is * deployed. It may be an implementation native to the container (eg * org.eclipse.jetty.server.AsyncContinuation) or one of the utility * implementations provided such as an internal FauxContinuation * or a real implementation like {@link org.eclipse.jetty.continuation.Servlet3Continuation}. * @param request The request * @return a Continuation instance */ public static Continuation getContinuation(ServletRequest request) { Continuation continuation = (Continuation) request.getAttribute(Continuation.ATTRIBUTE); if (continuation!=null) return continuation; while (request instanceof ServletRequestWrapper) request=((ServletRequestWrapper)request).getRequest(); if (__servlet3 ) { try { continuation=__newServlet3Continuation.newInstance(request); request.setAttribute(Continuation.ATTRIBUTE,continuation); return continuation; } catch(Exception e) { throw new RuntimeException(e); } } throw new IllegalStateException("!(Jetty || Servlet 3.0 || ContinuationFilter)"); } /* ------------------------------------------------------------ */ /** * @param request the servlet request * @param response the servlet response * @deprecated use {@link #getContinuation(ServletRequest)} * @return the continuation */ @Deprecated public static Continuation getContinuation(final ServletRequest request, final ServletResponse response) { return getContinuation(request); } } ContinuationThrowable.java000066400000000000000000000034321261716203600355350ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; /* ------------------------------------------------------------ */ /** ContinuationThrowable *

* A ContinuationThrowable is throw by {@link Continuation#undispatch()} * in order to exit the dispatch to a Filter or Servlet. Use of * ContinuationThrowable is discouraged and it is preferable to * allow return to be used. ContinuationThrowables should only be * used when there is a Filter/Servlet which cannot be modified * to avoid committing a response when {@link Continuation#isSuspended()} * is true. *

*

* ContinuationThrowable instances are often reused so that the * stack trace may be entirely unrelated to the calling stack. * A real stack trace may be obtained by enabling debug. *

*

* ContinuationThrowable extends Error as this is more likely * to be uncaught (or rethrown) by a Filter/Servlet. A ContinuationThrowable * does not represent and error condition. *

*/ public class ContinuationThrowable extends Error {} FauxContinuation.java000066400000000000000000000342051261716203600345130ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import java.util.ArrayList; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.ServletResponseWrapper; import org.eclipse.jetty.continuation.ContinuationFilter.FilteredContinuation; /* ------------------------------------------------------------ */ /** * A blocking implementation of Continuation. * This implementation of Continuation is used by the {@link ContinuationFilter} * when there are is no native or asynchronous continuation type available. */ class FauxContinuation implements FilteredContinuation { // common exception used for all continuations. // Turn on debug in ContinuationFilter to see real stack trace. private final static ContinuationThrowable __exception = new ContinuationThrowable(); private static final int __HANDLING=1; // Request dispatched to filter/servlet private static final int __SUSPENDING=2; // Suspend called, but not yet returned to container private static final int __RESUMING=3; // resumed while suspending private static final int __COMPLETING=4; // resumed while suspending or suspended private static final int __SUSPENDED=5; // Suspended and parked private static final int __UNSUSPENDING=6; private static final int __COMPLETE=7; private final ServletRequest _request; private ServletResponse _response; private int _state=__HANDLING; private boolean _initial=true; private boolean _resumed=false; private boolean _timeout=false; private boolean _responseWrapped=false; private long _timeoutMs=30000; private ArrayList _listeners; FauxContinuation(final ServletRequest request) { _request=request; } /* ------------------------------------------------------------ */ public void onComplete() { if (_listeners!=null) for (ContinuationListener l:_listeners) l.onComplete(this); } /* ------------------------------------------------------------ */ public void onTimeout() { if (_listeners!=null) for (ContinuationListener l:_listeners) l.onTimeout(this); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#isResponseWrapped() */ @Override public boolean isResponseWrapped() { return _responseWrapped; } /* ------------------------------------------------------------ */ @Override public boolean isInitial() { synchronized(this) { return _initial; } } /* ------------------------------------------------------------ */ @Override public boolean isResumed() { synchronized(this) { return _resumed; } } /* ------------------------------------------------------------ */ @Override public boolean isSuspended() { synchronized(this) { switch(_state) { case __HANDLING: return false; case __SUSPENDING: case __RESUMING: case __COMPLETING: case __SUSPENDED: return true; case __UNSUSPENDING: default: return false; } } } /* ------------------------------------------------------------ */ @Override public boolean isExpired() { synchronized(this) { return _timeout; } } /* ------------------------------------------------------------ */ @Override public void setTimeout(long timeoutMs) { _timeoutMs = timeoutMs; } /* ------------------------------------------------------------ */ @Override public void suspend(ServletResponse response) { _response=response; _responseWrapped=response instanceof ServletResponseWrapper; suspend(); } /* ------------------------------------------------------------ */ @Override public void suspend() { synchronized (this) { switch(_state) { case __HANDLING: _timeout=false; _resumed=false; _state=__SUSPENDING; return; case __SUSPENDING: case __RESUMING: return; case __COMPLETING: case __SUSPENDED: case __UNSUSPENDING: throw new IllegalStateException(this.getStatusString()); default: throw new IllegalStateException(""+_state); } } } /* ------------------------------------------------------------ */ /* (non-Javadoc) * @see org.mortbay.jetty.Suspendor#resume() */ @Override public void resume() { synchronized (this) { switch(_state) { case __HANDLING: _resumed=true; return; case __SUSPENDING: _resumed=true; _state=__RESUMING; return; case __RESUMING: case __COMPLETING: return; case __SUSPENDED: fauxResume(); _resumed=true; _state=__UNSUSPENDING; break; case __UNSUSPENDING: _resumed=true; return; default: throw new IllegalStateException(this.getStatusString()); } } } /* ------------------------------------------------------------ */ @Override public void complete() { // just like resume, except don't set _resumed=true; synchronized (this) { switch(_state) { case __HANDLING: throw new IllegalStateException(this.getStatusString()); case __SUSPENDING: _state=__COMPLETING; break; case __RESUMING: break; case __COMPLETING: return; case __SUSPENDED: _state=__COMPLETING; fauxResume(); break; case __UNSUSPENDING: return; default: throw new IllegalStateException(this.getStatusString()); } } } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#getServletResponse() */ @Override public boolean enter(ServletResponse response) { _response=response; return true; } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#getServletResponse() */ @Override public ServletResponse getServletResponse() { return _response; } /* ------------------------------------------------------------ */ void handling() { synchronized (this) { _responseWrapped=false; switch(_state) { case __HANDLING: throw new IllegalStateException(this.getStatusString()); case __SUSPENDING: case __RESUMING: throw new IllegalStateException(this.getStatusString()); case __COMPLETING: return; case __SUSPENDED: fauxResume(); _state=__HANDLING; return; case __UNSUSPENDING: _state=__HANDLING; return; default: throw new IllegalStateException(""+_state); } } } /* ------------------------------------------------------------ */ /** * @return true if handling is complete */ @Override public boolean exit() { synchronized (this) { switch(_state) { case __HANDLING: _state=__COMPLETE; onComplete(); return true; case __SUSPENDING: _initial=false; _state=__SUSPENDED; fauxSuspend(); // could block and change state. if (_state==__SUSPENDED || _state==__COMPLETING) { onComplete(); return true; } _initial=false; _state=__HANDLING; return false; case __RESUMING: _initial=false; _state=__HANDLING; return false; case __COMPLETING: _initial=false; _state=__COMPLETE; onComplete(); return true; case __SUSPENDED: case __UNSUSPENDING: default: throw new IllegalStateException(this.getStatusString()); } } } /* ------------------------------------------------------------ */ protected void expire() { // just like resume, except don't set _resumed=true; synchronized (this) { _timeout=true; } onTimeout(); synchronized (this) { switch(_state) { case __HANDLING: return; case __SUSPENDING: _timeout=true; _state=__RESUMING; fauxResume(); return; case __RESUMING: return; case __COMPLETING: return; case __SUSPENDED: _timeout=true; _state=__UNSUSPENDING; break; case __UNSUSPENDING: _timeout=true; return; default: throw new IllegalStateException(this.getStatusString()); } } } private void fauxSuspend() { long expire_at = System.currentTimeMillis()+_timeoutMs; long wait=_timeoutMs; while (_timeoutMs>0 && wait>0) { try { this.wait(wait); } catch (InterruptedException e) { break; } wait=expire_at-System.currentTimeMillis(); } if (_timeoutMs>0 && wait<=0) expire(); } private void fauxResume() { _timeoutMs=0; this.notifyAll(); } @Override public String toString() { return getStatusString(); } String getStatusString() { synchronized (this) { return ((_state==__HANDLING)?"HANDLING": (_state==__SUSPENDING)?"SUSPENDING": (_state==__SUSPENDED)?"SUSPENDED": (_state==__RESUMING)?"RESUMING": (_state==__UNSUSPENDING)?"UNSUSPENDING": (_state==__COMPLETING)?"COMPLETING": ("???"+_state))+ (_initial?",initial":"")+ (_resumed?",resumed":"")+ (_timeout?",timeout":""); } } @Override public void addContinuationListener(ContinuationListener listener) { if (_listeners==null) _listeners=new ArrayList(); _listeners.add(listener); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String) */ @Override public Object getAttribute(String name) { return _request.getAttribute(name); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String) */ @Override public void removeAttribute(String name) { _request.removeAttribute(name); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object) */ @Override public void setAttribute(String name, Object attribute) { _request.setAttribute(name,attribute); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#undispatch() */ @Override public void undispatch() { if (isSuspended()) { if (ContinuationFilter.__debug) throw new ContinuationThrowable(); throw __exception; } throw new IllegalStateException("!suspended"); } } Servlet3Continuation.java000066400000000000000000000171021261716203600353140ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.continuation; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; import javax.servlet.DispatcherType; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.ServletResponseWrapper; /* ------------------------------------------------------------ */ /** * This implementation of Continuation is used by {@link ContinuationSupport} * when it detects that the application has been deployed in a Servlet 3 * server. */ public class Servlet3Continuation implements Continuation, AsyncListener { // Exception reused for all continuations // Turn on debug in ContinuationFilter to see real stack trace. private final static ContinuationThrowable __exception = new ContinuationThrowable(); private final ServletRequest _request; private ServletResponse _response; private AsyncContext _context; private final List _listeners=new ArrayList(); private volatile boolean _initial=true; private volatile boolean _resumed=false; private volatile boolean _expired=false; private volatile boolean _responseWrapped=false; private long _timeoutMs=-1; /* ------------------------------------------------------------ */ public Servlet3Continuation(ServletRequest request) { _request=request; } /* ------------------------------------------------------------ */ @Override public void addContinuationListener(final ContinuationListener listener) { _listeners.add(listener); } /* ------------------------------------------------------------ */ @Override public void complete() { AsyncContext context=_context; if (context==null) throw new IllegalStateException(); _context.complete(); } /* ------------------------------------------------------------ */ @Override public ServletResponse getServletResponse() { return _response; } /* ------------------------------------------------------------ */ @Override public boolean isExpired() { return _expired; } /* ------------------------------------------------------------ */ @Override public boolean isInitial() { return _initial&&_request.getDispatcherType()!=DispatcherType.ASYNC; } /* ------------------------------------------------------------ */ @Override public boolean isResumed() { return _resumed; } /* ------------------------------------------------------------ */ @Override public boolean isSuspended() { if (_request.isAsyncStarted()) return true; try { return _request.getAsyncContext()!=null; } catch(IllegalStateException e) { // ignored } return false; } /* ------------------------------------------------------------ */ public void keepWrappers() { _responseWrapped=true; } /* ------------------------------------------------------------ */ @Override public void resume() { AsyncContext context=_context; if (context==null) throw new IllegalStateException(); _resumed=true; _context.dispatch(); } /* ------------------------------------------------------------ */ @Override public void setTimeout(long timeoutMs) { _timeoutMs=timeoutMs; if (_context!=null) _context.setTimeout(timeoutMs); } /* ------------------------------------------------------------ */ @Override public void suspend(ServletResponse response) { _response=response; _responseWrapped=response instanceof ServletResponseWrapper; _resumed=false; _expired=false; _context=_request.startAsync(); _context.setTimeout(_timeoutMs); _context.addListener(this); } /* ------------------------------------------------------------ */ @Override public void suspend() { _resumed=false; _expired=false; _context=_request.startAsync(); _context.setTimeout(_timeoutMs); _context.addListener(this); } /* ------------------------------------------------------------ */ @Override public boolean isResponseWrapped() { return _responseWrapped; } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#getAttribute(java.lang.String) */ @Override public Object getAttribute(String name) { return _request.getAttribute(name); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#removeAttribute(java.lang.String) */ @Override public void removeAttribute(String name) { _request.removeAttribute(name); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#setAttribute(java.lang.String, java.lang.Object) */ @Override public void setAttribute(String name, Object attribute) { _request.setAttribute(name,attribute); } /* ------------------------------------------------------------ */ /** * @see org.eclipse.jetty.continuation.Continuation#undispatch() */ @Override public void undispatch() { if (isSuspended()) { if (ContinuationFilter.__debug) throw new ContinuationThrowable(); throw __exception; } throw new IllegalStateException("!suspended"); } /* ------------------------------------------------------------ */ @Override public void onComplete(AsyncEvent event) throws IOException { for (ContinuationListener listener:_listeners) listener.onComplete(this); } /* ------------------------------------------------------------ */ @Override public void onError(AsyncEvent event) throws IOException { } /* ------------------------------------------------------------ */ @Override public void onStartAsync(AsyncEvent event) throws IOException { _initial=false; } /* ------------------------------------------------------------ */ @Override public void onTimeout(AsyncEvent event) throws IOException { _initial=false; _expired=true; for (ContinuationListener listener:_listeners) listener.onTimeout(this); if (event.getSuppliedRequest().isAsyncStarted()) event.getAsyncContext().dispatch(); } } package-info.java000066400000000000000000000016011261716203600335330ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-continuation/src/main/java/org/eclipse/jetty/continuation// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Continuation : Generic Async Servlet Method */ package org.eclipse.jetty.continuation; jetty-9.2.14.v20151106/jetty-deploy/000077500000000000000000000000001261716203600164675ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/pom.xml000066400000000000000000000061251261716203600200100ustar00rootroot00000000000000 org.eclipse.jetty jetty-project 9.2.14.v20151106 4.0.0 jetty-deploy Jetty :: Deployers Jetty deployers http://www.eclipse.org/jetty ${project.groupId}.deploy org.apache.felix maven-bundle-plugin true manifest org.eclipse.jetty.jmx.*;version="9.1";resolution:=optional,* <_nouses>true org.apache.maven.plugins maven-assembly-plugin package single config org.apache.maven.plugins maven-jar-plugin ${project.build.outputDirectory}/META-INF/MANIFEST.MF org.codehaus.mojo findbugs-maven-plugin org.eclipse.jetty.deploy.* org.eclipse.jetty.toolchain jetty-test-helper test org.eclipse.jetty jetty-webapp ${project.version} org.eclipse.jetty jetty-xml ${project.version} org.eclipse.jetty jetty-jmx ${project.version} true jetty-9.2.14.v20151106/jetty-deploy/src/000077500000000000000000000000001261716203600172565ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/000077500000000000000000000000001261716203600202025ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/config/000077500000000000000000000000001261716203600214475ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/config/etc/000077500000000000000000000000001261716203600222225ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/config/etc/jetty-deploy.xml000066400000000000000000000056141261716203600254030ustar00rootroot00000000000000 org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern .*/[^/]*servlet-api-[^/]*\.jar$|.*/javax.servlet.jsp.jstl-.*\.jar$|.*/org.apache.taglibs.taglibs-standard-impl-.*\.jar$ / /etc/webdefault.xml jetty-9.2.14.v20151106/jetty-deploy/src/main/config/modules/000077500000000000000000000000001261716203600231175ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/config/modules/deploy.mod000066400000000000000000000004151261716203600251140ustar00rootroot00000000000000# # Deploy Feature # [depend] webapp [lib] lib/jetty-deploy-${jetty.version}.jar [files] webapps/ [xml] etc/jetty-deploy.xml [ini-template] ## DeployManager configuration # Monitored Directory name (relative to jetty.base) # jetty.deploy.monitoredDirName=webapps jetty-9.2.14.v20151106/jetty-deploy/src/main/java/000077500000000000000000000000001261716203600211235ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/000077500000000000000000000000001261716203600217125ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/000077500000000000000000000000001261716203600233365ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/000077500000000000000000000000001261716203600244755ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/000077500000000000000000000000001261716203600257715ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/App.java000066400000000000000000000115141261716203600273560ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.AttributesMap; /** * The information about an App that is managed by the {@link DeploymentManager} */ public class App { private final DeploymentManager _manager; private final AppProvider _provider; private final String _originId; private ContextHandler _context; /** * Create an App with specified Origin ID and archivePath * * @param originId * the origin ID (The ID that the {@link AppProvider} knows * about) * @see App#getOriginId() * @see App#getContextPath() */ public App(DeploymentManager manager, AppProvider provider, String originId) { _manager = manager; _provider = provider; _originId = originId; } /** * Create an App with specified Origin ID and archivePath * * @param originId * the origin ID (The ID that the {@link AppProvider} knows * about) * @see App#getOriginId() * @see App#getContextPath() * @param context * Some implementations of AppProvider might have to use an * already created ContextHandler. */ public App(DeploymentManager manager, AppProvider provider, String originId, ContextHandler context) { this(manager,provider,originId); _context = context; } /* ------------------------------------------------------------ */ /** * @return The deployment manager */ public DeploymentManager getDeploymentManager() { return _manager; } /* ------------------------------------------------------------ */ /** * @return The AppProvider */ public AppProvider getAppProvider() { return _provider; } /* ------------------------------------------------------------ */ /** * Get ContextHandler for the App. * * Create it if needed. * * @return the {@link ContextHandler} to use for the App when fully started. * (Portions of which might be ignored when App is not yet * {@link AppLifeCycle#DEPLOYED} or {@link AppLifeCycle#STARTED}) * @throws Exception */ public ContextHandler getContextHandler() throws Exception { if (_context == null) { _context = getAppProvider().createContextHandler(this); AttributesMap attributes = _manager.getContextAttributes(); if (attributes!=null && attributes.size()>0) { // Merge the manager attributes under the existing attributes attributes = new AttributesMap(attributes); attributes.addAll(_context.getAttributes()); _context.setAttributes(attributes); } } return _context; } /** * The context path {@link App} relating to how it is installed on the * jetty server side. * * NOTE that although the method name indicates that this is a unique * identifier, it is not, as many contexts may have the same contextPath, * yet different virtual hosts. * * @deprecated Use getContextPath instead. * @return the context path for the App */ public String getContextId() { return getContextPath(); } /** * The context path {@link App} relating to how it is installed on the * jetty server side. * * @return the contextPath for the App */ public String getContextPath() { if (this._context == null) { return null; } return this._context.getContextPath(); } /** * The origin of this {@link App} as specified by the {@link AppProvider} * * @return String representing the origin of this app. */ public String getOriginId() { return this._originId; } @Override public String toString() { return "App[" + _context + "," + _originId + "]"; } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppLifeCycle.java000066400000000000000000000132561261716203600311430ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.jetty.deploy.graph.Graph; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * The lifecycle of an App in the {@link DeploymentManager}. * * Setups a the default {@link Graph}, and manages the bindings to the life cycle via the {@link AppLifeCycle.Binding} * annotation. *

* */ public class AppLifeCycle extends Graph { private static final Logger LOG = Log.getLogger(AppLifeCycle.class); private static final String ALL_NODES = "*"; public static interface Binding { /** * Get a list of targets that this implementation should bind to. * * @return the array of String node names to bind to. (use "*" to bind to all known node names) */ String[] getBindingTargets(); /** * Event called to process a {@link AppLifeCycle} binding. * * @param node * the node being processed * @param app * the app being processed * @throws Exception * if any problem severe enough to halt the AppLifeCycle processing */ void processBinding(Node node, App app) throws Exception; } // Well known existing lifecycle Nodes public static final String UNDEPLOYED = "undeployed"; public static final String DEPLOYING = "deploying"; public static final String DEPLOYED = "deployed"; public static final String STARTING = "starting"; public static final String STARTED = "started"; public static final String STOPPING = "stopping"; public static final String UNDEPLOYING = "undeploying"; private Map> lifecyclebindings = new HashMap>(); public AppLifeCycle() { // Define Default Graph // undeployed -> deployed addEdge(UNDEPLOYED,DEPLOYING); addEdge(DEPLOYING,DEPLOYED); // deployed -> started addEdge(DEPLOYED,STARTING); addEdge(STARTING,STARTED); // started -> deployed addEdge(STARTED,STOPPING); addEdge(STOPPING,DEPLOYED); // deployed -> undeployed addEdge(DEPLOYED,UNDEPLOYING); addEdge(UNDEPLOYING,UNDEPLOYED); } public void addBinding(AppLifeCycle.Binding binding) { for (String nodeName : binding.getBindingTargets()) { List bindings = lifecyclebindings.get(nodeName); if (bindings == null) { bindings = new ArrayList(); } bindings.add(binding); lifecyclebindings.put(nodeName,bindings); } } public void removeBinding(AppLifeCycle.Binding binding) { for (String nodeName : binding.getBindingTargets()) { List bindings = lifecyclebindings.get(nodeName); if (bindings != null) bindings.remove(binding); } } /** * Get all {@link Node} bound objects. * * @return Set of Object(s) for all lifecycle bindings. never null. */ public Set getBindings() { Set boundset = new HashSet(); for (List bindings : lifecyclebindings.values()) { boundset.addAll(bindings); } return boundset; } /** * Get all objects bound to a specific {@link Node} * * @return Set of Object(s) for specific lifecycle bindings. never null. */ public Set getBindings(Node node) { return getBindings(node.getName()); } /** * Get all objects bound to a specific {@link Node} * * @return Set of Object(s) for specific lifecycle bindings. never null. */ public Set getBindings(String nodeName) { Set boundset = new HashSet(); // Specific node binding List bindings = lifecyclebindings.get(nodeName); if (bindings != null) { boundset.addAll(bindings); } // Special 'all nodes' binding bindings = lifecyclebindings.get(ALL_NODES); if (bindings != null) { boundset.addAll(bindings); } return boundset; } public void runBindings(Node node, App app, DeploymentManager deploymentManager) throws Throwable { for (Binding binding : getBindings(node)) { if (LOG.isDebugEnabled()) LOG.debug("Calling " + binding.getClass().getName()+" for "+app); binding.processBinding(node,app); } } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/AppProvider.java000066400000000000000000000031441261716203600310710ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.component.LifeCycle; /** * Object responsible for providing {@link App}s to the {@link DeploymentManager} */ public interface AppProvider extends LifeCycle { /** * Set the Deployment Manager * * @param deploymentManager * @throws IllegalStateException * if the provider {@link #isRunning()}. */ void setDeploymentManager(DeploymentManager deploymentManager); /* ------------------------------------------------------------ */ /** Create a ContextHandler for an App * @param app The App * @return A ContextHandler * @throws IOException * @throws Exception */ ContextHandler createContextHandler(App app) throws Exception; } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/ConfigurationManager.java000066400000000000000000000020701261716203600327350ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.util.Map; /** * ConfigurationManager * * Type for allow injection of property values for replacement in jetty xml files during deployment. */ public interface ConfigurationManager { public Map getProperties(); } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/DeploymentManager.java000066400000000000000000000416321261716203600322550ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import org.eclipse.jetty.deploy.bindings.StandardDeployer; import org.eclipse.jetty.deploy.bindings.StandardStarter; import org.eclipse.jetty.deploy.bindings.StandardStopper; import org.eclipse.jetty.deploy.bindings.StandardUndeployer; import org.eclipse.jetty.deploy.graph.Edge; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.deploy.graph.Path; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.util.AttributesMap; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** * The Deployment Manager. *

* Responsibilities: *

* *

    *
  1. Tracking Apps and their LifeCycle Location
  2. *
  3. Managing AppProviders and the Apps that they provide.
  4. *
  5. Executing AppLifeCycle on App based on current and desired LifeCycle Location.
  6. *
*

* */ @ManagedObject("Deployment Manager") public class DeploymentManager extends ContainerLifeCycle { private static final Logger LOG = Log.getLogger(DeploymentManager.class); /** * Represents a single tracked app within the deployment manager. */ public class AppEntry { /** * Version of the app. * * Note: Auto-increments on each {@link DeploymentManager#addApp(App)} */ private int version; /** * The app being tracked. */ private App app; /** * The lifecycle node location of this App */ private Node lifecyleNode; /** * Tracking the various AppState timestamps (in system milliseconds) */ private Map stateTimestamps = new HashMap(); public App getApp() { return app; } public Node getLifecyleNode() { return lifecyleNode; } public Map getStateTimestamps() { return stateTimestamps; } public int getVersion() { return version; } void setLifeCycleNode(Node node) { this.lifecyleNode = node; this.stateTimestamps.put(node,Long.valueOf(System.currentTimeMillis())); } } private final List _providers = new ArrayList(); private final AppLifeCycle _lifecycle = new AppLifeCycle(); private final Queue _apps = new ConcurrentLinkedQueue(); private AttributesMap _contextAttributes = new AttributesMap(); private ContextHandlerCollection _contexts; private boolean _useStandardBindings = true; private String _defaultLifeCycleGoal = AppLifeCycle.STARTED; /** * Receive an app for processing. * * Most commonly used by the various {@link AppProvider} implementations. */ public void addApp(App app) { LOG.debug("Deployable added: {}",app.getOriginId()); AppEntry entry = new AppEntry(); entry.app = app; entry.setLifeCycleNode(_lifecycle.getNodeByName("undeployed")); _apps.add(entry); if (isRunning() && _defaultLifeCycleGoal != null) { // Immediately attempt to go to default lifecycle state this.requestAppGoal(entry,_defaultLifeCycleGoal); } } /* ------------------------------------------------------------ */ /** Set the AppProviders. * The providers passed are added via {@link #addBean(Object)} so that * their lifecycles may be managed as a {@link ContainerLifeCycle}. * @param providers */ public void setAppProviders(Collection providers) { if (isRunning()) throw new IllegalStateException(); _providers.clear(); removeBeans(); for (AppProvider provider:providers) if (_providers.add(provider)) addBean(provider); } @ManagedAttribute("Application Providers") public Collection getAppProviders() { return Collections.unmodifiableList(_providers); } public void addAppProvider(AppProvider provider) { if (isRunning()) throw new IllegalStateException(); _providers.add(provider); addBean(provider); } public void setLifeCycleBindings(Collection bindings) { if (isRunning()) throw new IllegalStateException(); for (AppLifeCycle.Binding b : _lifecycle.getBindings()) _lifecycle.removeBinding(b); for (AppLifeCycle.Binding b : bindings) _lifecycle.addBinding(b); } public Collection getLifeCycleBindings() { return Collections.unmodifiableSet(_lifecycle.getBindings()); } public void addLifeCycleBinding(AppLifeCycle.Binding binding) { _lifecycle.addBinding(binding); } /** * Convenience method to allow for insertion of nodes into the lifecycle. * * @param existingFromNodeName * @param existingToNodeName * @param insertedNodeName */ public void insertLifeCycleNode(String existingFromNodeName, String existingToNodeName, String insertedNodeName) { Node fromNode = _lifecycle.getNodeByName(existingFromNodeName); Node toNode = _lifecycle.getNodeByName(existingToNodeName); Edge edge = new Edge(fromNode,toNode); _lifecycle.insertNode(edge,insertedNodeName); } @Override protected void doStart() throws Exception { if (getContexts()==null) throw new IllegalStateException("No Contexts"); if (_useStandardBindings) { LOG.debug("DeploymentManager using standard bindings"); addLifeCycleBinding(new StandardDeployer()); addLifeCycleBinding(new StandardStarter()); addLifeCycleBinding(new StandardStopper()); addLifeCycleBinding(new StandardUndeployer()); } // Start all of the AppProviders for (AppProvider provider : _providers) { startAppProvider(provider); } super.doStart(); } @Override protected void doStop() throws Exception { // Stop all of the AppProviders for (AppProvider provider : _providers) { try { provider.stop(); } catch (Exception e) { LOG.warn("Unable to start AppProvider",e); } } super.doStop(); } private AppEntry findAppByOriginId(String originId) { if (originId == null) { return null; } for (AppEntry entry : _apps) { if (originId.equals(entry.app.getOriginId())) { return entry; } } return null; } public App getAppByOriginId(String originId) { AppEntry entry = findAppByOriginId(originId); if (entry == null) { return null; } return entry.app; } public Collection getAppEntries() { return _apps; } @ManagedAttribute("Deployed Apps") public Collection getApps() { List ret = new ArrayList(); for (AppEntry entry : _apps) { ret.add(entry.app); } return ret; } /** * Get Set of {@link App}s by {@link Node} * * @param node * the node to look for. * @return the collection of apps for the node */ public Collection getApps(Node node) { List ret = new ArrayList(); for (AppEntry entry : _apps) { if (entry.lifecyleNode == node) { ret.add(entry.app); } } return ret; } public List getAppsWithSameContext(App app) { List ret = new ArrayList(); if (app == null) { return ret; } String contextId = app.getContextPath(); if (contextId == null) { // No context? Likely not deployed or started yet. return ret; } for (AppEntry entry : _apps) { if (entry.app.equals(app)) { // Its the input app. skip it. // TODO: is this filter needed? continue; } if (contextId.equals(entry.app.getContextPath())) { ret.add(entry.app); } } return ret; } /** * Get a contextAttribute that will be set for every Context deployed by this provider. * * @param name * @return the context attribute value */ public Object getContextAttribute(String name) { return _contextAttributes.getAttribute(name); } public AttributesMap getContextAttributes() { return _contextAttributes; } @ManagedAttribute("Deployed Contexts") public ContextHandlerCollection getContexts() { return _contexts; } public String getDefaultLifeCycleGoal() { return _defaultLifeCycleGoal; } public AppLifeCycle getLifeCycle() { return _lifecycle; } public Server getServer() { if (_contexts == null) { return null; } return _contexts.getServer(); } /** * Remove the app from the tracking of the DeploymentManager * * @param app * if the app is Unavailable remove it from the deployment manager. */ public void removeApp(App app) { Iterator it = _apps.iterator(); while (it.hasNext()) { AppEntry entry = it.next(); if (entry.app.equals(app)) { if (! AppLifeCycle.UNDEPLOYED.equals(entry.lifecyleNode.getName())) requestAppGoal(entry.app,AppLifeCycle.UNDEPLOYED); it.remove(); LOG.debug("Deployable removed: {}",entry.app); } } } public void removeAppProvider(AppProvider provider) { if(_providers.remove(provider)) removeBean(provider); try { provider.stop(); } catch (Exception e) { LOG.warn("Unable to stop Provider",e); } } /** * Remove a contextAttribute that will be set for every Context deployed by this provider. * * @param name */ public void removeContextAttribute(String name) { _contextAttributes.removeAttribute(name); } /** * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. * * @param app * the app to move through the process * @param nodeName * the name of the node to attain */ public void requestAppGoal(App app, String nodeName) { AppEntry appentry = findAppByOriginId(app.getOriginId()); if (appentry == null) { throw new IllegalStateException("App not being tracked by Deployment Manager: " + app); } requestAppGoal(appentry,nodeName); } /** * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. * * @param appentry * the internal appentry to move through the process * @param nodeName * the name of the node to attain */ private void requestAppGoal(AppEntry appentry, String nodeName) { Node destinationNode = _lifecycle.getNodeByName(nodeName); if (destinationNode == null) { throw new IllegalStateException("Node not present in Deployment Manager: " + nodeName); } // Compute lifecycle steps Path path = _lifecycle.getPath(appentry.lifecyleNode,destinationNode); if (path.isEmpty()) { // nothing to do. already there. return; } // Execute each Node binding. Stopping at any thrown exception. try { Iterator it = path.getNodes().iterator(); if (it.hasNext()) // Any entries? { // The first entry in the path is always the start node // We don't want to run bindings on that entry (again) it.next(); // skip first entry while (it.hasNext()) { Node node = it.next(); LOG.debug("Executing Node {}",node); _lifecycle.runBindings(node,appentry.app,this); appentry.setLifeCycleNode(node); } } } catch (Throwable t) { LOG.warn("Unable to reach node goal: " + nodeName,t); } } /** * Move an {@link App} through the {@link AppLifeCycle} to the desired {@link Node}, executing each lifecycle step * in the process to reach the desired state. * * @param appId * the id of the app to move through the process * @param nodeName * the name of the node to attain */ @ManagedOperation(value="request the app to be moved to the specified lifecycle node", impact="ACTION") public void requestAppGoal(@Name("appId") String appId, @Name("nodeName") String nodeName) { AppEntry appentry = findAppByOriginId(appId); if (appentry == null) { throw new IllegalStateException("App not being tracked by Deployment Manager: " + appId); } requestAppGoal(appentry,nodeName); } /** * Set a contextAttribute that will be set for every Context deployed by this provider. * * @param name * @param value */ public void setContextAttribute(String name, Object value) { _contextAttributes.setAttribute(name,value); } public void setContextAttributes(AttributesMap contextAttributes) { this._contextAttributes = contextAttributes; } public void setContexts(ContextHandlerCollection contexts) { this._contexts = contexts; } public void setDefaultLifeCycleGoal(String defaultLifeCycleState) { this._defaultLifeCycleGoal = defaultLifeCycleState; } private void startAppProvider(AppProvider provider) { try { provider.setDeploymentManager(this); provider.start(); } catch (Exception e) { LOG.warn("Unable to start AppProvider",e); } } public void undeployAll() { LOG.debug("Undeploy All"); for (AppEntry appentry : _apps) { requestAppGoal(appentry,"undeployed"); } } public boolean isUseStandardBindings() { return _useStandardBindings; } public void setUseStandardBindings(boolean useStandardBindings) { this._useStandardBindings = useStandardBindings; } public Collection getNodes() { return _lifecycle.getNodes(); } @ManagedOperation(value="list apps that are located at specified App LifeCycle nodes", impact="ACTION") public Collection getApps(@Name("nodeName") String nodeName) { return getApps(_lifecycle.getNodeByName(nodeName)); } } PropertiesConfigurationManager.java000066400000000000000000000055331261716203600347420ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.Name; import org.eclipse.jetty.util.resource.Resource; /** * FileConfigurationManager * * Supplies properties defined in a file. */ @ManagedObject("Configure deployed webapps via properties") public class PropertiesConfigurationManager implements ConfigurationManager { private String _properties; private final Map _map = new HashMap(); public PropertiesConfigurationManager(String properties) { } public PropertiesConfigurationManager() { } @ManagedAttribute("A file or URL of properties") public void setFile(String resource) throws MalformedURLException, IOException { _properties=resource; _map.clear(); loadProperties(_properties); } public String getFile() { return _properties; } @ManagedOperation("Set a property") public void put(@Name("name")String name, @Name("value")String value) { _map.put(name,value); } /** * @see org.eclipse.jetty.deploy.ConfigurationManager#getProperties() */ @Override public Map getProperties() { return new HashMap<>(_map); } private void loadProperties(String resource) throws FileNotFoundException, IOException { Resource file=Resource.newResource(resource); if (file!=null && file.exists()) { Properties properties = new Properties(); properties.load(file.getInputStream()); for (Map.Entry entry : properties.entrySet()) _map.put(entry.getKey().toString(),String.valueOf(entry.getValue())); } } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings/000077500000000000000000000000001261716203600275665ustar00rootroot00000000000000DebugBinding.java000066400000000000000000000031421261716203600326730ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class DebugBinding implements AppLifeCycle.Binding { private static final Logger LOG = Log.getLogger(DebugBinding.class); final String[] _targets; public DebugBinding(String target) { _targets=new String[]{target}; } public DebugBinding(final String... targets) { _targets=targets; } public String[] getBindingTargets() { return _targets; } public void processBinding(Node node, App app) throws Exception { LOG.info("processBinding {} {}",node,app.getContextHandler()); } } GlobalWebappConfigBinding.java000066400000000000000000000073511261716203600353400ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import java.io.File; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; /** * Provides a way of globally setting various aspects of webapp contexts. * * Adding this binding will allow the user to arbitrarily apply a file of * jetty-web.xml like settings to a webapp context. * * Example usage would be: * - adding a server or system class setting to all webapp contexts * - adding an override descriptor * * Note: Currently properties from startup will not be available for * reference. * */ public class GlobalWebappConfigBinding implements AppLifeCycle.Binding { private static final Logger LOG = Log.getLogger(GlobalWebappConfigBinding.class); private String _jettyXml; public String getJettyXml() { return _jettyXml; } public void setJettyXml(String jettyXml) { this._jettyXml = jettyXml; } public String[] getBindingTargets() { return new String[] { "deploying" }; } public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); if (handler == null) { throw new NullPointerException("No Handler created for App: " + app); } if (handler instanceof WebAppContext) { WebAppContext context = (WebAppContext)handler; if (LOG.isDebugEnabled()) { LOG.debug("Binding: Configuring webapp context with global settings from: " + _jettyXml); } if ( _jettyXml == null ) { LOG.warn("Binding: global context binding is enabled but no jetty-web.xml file has been registered"); } Resource globalContextSettings = Resource.newResource(_jettyXml); if (globalContextSettings.exists()) { XmlConfiguration jettyXmlConfig = new XmlConfiguration(globalContextSettings.getInputStream()); Resource resource = Resource.newResource(app.getOriginId()); File file = resource.getFile(); jettyXmlConfig.getIdMap().put("Server",app.getDeploymentManager().getServer()); jettyXmlConfig.getProperties().put("jetty.webapp",file.getCanonicalPath()); jettyXmlConfig.getProperties().put("jetty.webapps",file.getParentFile().getCanonicalPath()); jettyXmlConfig.configure(context); } else { LOG.info("Binding: Unable to locate global webapp context settings: " + _jettyXml); } } } } OrderedGroupBinding.java000066400000000000000000000043701261716203600342520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import java.util.LinkedList; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; /** * Provides a way of forcing the ordered execution of bindings within * a declared binding target. * */ public class OrderedGroupBinding implements AppLifeCycle.Binding { private String[] _bindingTargets; private LinkedList _orderedBindings; public OrderedGroupBinding( String[] bindingTargets ) { _bindingTargets = bindingTargets; } public void addBinding(AppLifeCycle.Binding binding) { if ( _orderedBindings == null ) { _orderedBindings = new LinkedList(); } _orderedBindings.add(binding); } public void addBindings(AppLifeCycle.Binding[] bindings) { if ( _orderedBindings == null ) { _orderedBindings = new LinkedList(); } for (AppLifeCycle.Binding binding : bindings) { _orderedBindings.add(binding); } } public String[] getBindingTargets() { return _bindingTargets; } public void processBinding(Node node, App app) throws Exception { for ( AppLifeCycle.Binding binding : _orderedBindings ) { binding.processBinding(node,app); } } } StandardDeployer.java000066400000000000000000000027761261716203600336320ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; public class StandardDeployer implements AppLifeCycle.Binding { public String[] getBindingTargets() { return new String[] { "deploying" }; } public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); if (handler == null) { throw new NullPointerException("No Handler created for App: " + app); } app.getDeploymentManager().getContexts().addHandler(handler); } } StandardStarter.java000066400000000000000000000030201261716203600334520ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; public class StandardStarter implements AppLifeCycle.Binding { @Override public String[] getBindingTargets() { return new String[] { "starting" }; } @Override public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); // start the handler handler.start(); // After starting let the context manage state app.getDeploymentManager().getContexts().manage(handler); } } StandardStopper.java000066400000000000000000000030221261716203600334640ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.handler.ContextHandler; public class StandardStopper implements AppLifeCycle.Binding { @Override public String[] getBindingTargets() { return new String[] { "stopping" }; } @Override public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); // Before stopping, take back management from the context app.getDeploymentManager().getContexts().unmanage(handler); // Stop it handler.stop(); } } StandardUndeployer.java000066400000000000000000000052101261716203600341570ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppLifeCycle; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; public class StandardUndeployer implements AppLifeCycle.Binding { private static final Logger LOG = Log.getLogger(StandardUndeployer.class); @Override public String[] getBindingTargets() { return new String[] { "undeploying" }; } @Override public void processBinding(Node node, App app) throws Exception { ContextHandler handler = app.getContextHandler(); ContextHandlerCollection chcoll = app.getDeploymentManager().getContexts(); recursiveRemoveContext(chcoll,handler); } private void recursiveRemoveContext(HandlerCollection coll, ContextHandler context) { Handler children[] = coll.getHandlers(); int originalCount = children.length; for (int i = 0, n = children.length; i < n; i++) { Handler child = children[i]; LOG.debug("Child handler {}",child); if (child.equals(context)) { LOG.debug("Removing handler {}",child); coll.removeHandler(child); child.destroy(); if (LOG.isDebugEnabled()) LOG.debug("After removal: {} (originally {})",coll.getHandlers().length,originalCount); } else if (child instanceof HandlerCollection) { recursiveRemoveContext((HandlerCollection)child,context); } } } } package-info.java000066400000000000000000000015761261716203600327070ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : Standard Deployment Bindings */ package org.eclipse.jetty.deploy.bindings; jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/000077500000000000000000000000001261716203600270725ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Edge.java000066400000000000000000000040741261716203600306060ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; /** * Basic Graph Edge */ public final class Edge { private Node _from; private Node _to; public Edge(Node from, Node to) { if (from==null || to==null || from==to) throw new IllegalArgumentException("from "+from+" to "+to); _from = from; _to = to; } @Override public int hashCode() { return _from.hashCode() ^ _to.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Edge other = (Edge)obj; if (_from == null) { if (other._from != null) return false; } else if (!_from.equals(other._from)) return false; if (_to == null) { if (other._to != null) return false; } else if (!_to.equals(other._to)) return false; return true; } public Node getFrom() { return _from; } public Node getTo() { return _to; } @Override public String toString() { return _from+"->"+_to; } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Graph.java000066400000000000000000000175651261716203600310140ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; /** * Basic directed graph implementation */ public class Graph { private Set _nodes = new HashSet(); private Set _edges = new HashSet(); public void addEdge(Edge edge) { Node fromNode = getNodeByName(edge.getFrom().getName()); if (fromNode==null) addNode(fromNode=edge.getFrom()); Node toNode = getNodeByName(edge.getTo().getName()); if (toNode==null) addNode(toNode=edge.getTo()); // replace edge with normalized edge if (edge.getFrom()!=fromNode || edge.getTo()!=toNode) edge=new Edge(fromNode,toNode); this._edges.add(edge); } public void addEdge(String from, String to) { Node fromNode = getNodeByName(from); if (fromNode==null) { fromNode = new Node(from); addNode(fromNode); } Node toNode = getNodeByName(to); if (toNode==null) { toNode = new Node(to); addNode(toNode); } addEdge(fromNode,toNode); } private void addEdge(Node fromNode, Node toNode) { Edge edge = new Edge(fromNode,toNode); addEdge(edge); } public void addNode(Node node) { this._nodes.add(node); } /** * Convenience method for {@link #insertNode(Edge, Node)} * * @param edge * the edge to split and insert a node into * @param nodeName * the name of the node to insert along the edge */ public void insertNode(Edge edge, String nodeName) { Node node = getNodeByName(nodeName); if (node==null) { node = new Node(nodeName); } insertNode(edge,node); } /** * Insert an arbitrary node on an existing edge. * * @param edge * the edge to split and insert a node into * @param node * the node to insert along the edge */ public void insertNode(Edge edge, Node node) { // Remove existing edge removeEdge(edge); // Ensure node is added addNode(node); // Add start edge addEdge(edge.getFrom(),node); // Add second edge addEdge(node,edge.getTo()); } /** * Find all edges that are connected to the specific node, both as an outgoing {@link Edge#getFrom()} or incoming * {@link Edge#getTo()} end point. * * @param node * the node with potential end points * @return the set of edges connected to the node */ public Set findEdges(Node node) { Set fromedges = new HashSet(); for (Edge edge : this._edges) { if ((edge.getFrom() == node) || (edge.getTo() == node)) { fromedges.add(edge); } } return fromedges; } /** * Find all edges that are connected {@link Edge#getFrom()} the specific node. * * @param from * the node with potential edges from it * @return the set of edges from the node */ public Set findEdgesFrom(Node from) { Set fromedges = new HashSet(); for (Edge edge : this._edges) { if (edge.getFrom() == from) { fromedges.add(edge); } } return fromedges; } /** * Convenience method for {@link #getPath(Node, Node)} * * @param nodeNameOrigin * the name of the node to the path origin. * @param nodeNameDest * the name of the node to the path destination. * @return the path to take */ public Path getPath(String nodeNameOrigin, String nodeNameDest) { if (nodeNameOrigin.equals(nodeNameDest)) { return new Path(); } Node from = getNodeByName(nodeNameOrigin); Node to = getNodeByName(nodeNameDest); return getPath(from,to); } /** * Using BFS (Breadth First Search) return the path from a any arbitrary node to any other. * * @param from * the node from * @param to * the node to * @return the path to take or null if there is no path. */ public Path getPath(Node from, Node to) { if (from == to) { return new Path(); } // Perform a Breadth First Search (BFS) of the tree. Path path = breadthFirst(from,to,new CopyOnWriteArrayList(),new HashSet()); return path; } private Path breadthFirst(Node from, Node destination, CopyOnWriteArrayList paths, Set seen) { // Add next unseen segments to paths. boolean edgesAdded = false; if (paths.size()==0) paths.add(new Path()); for (Path path : paths) { Set next = findEdgesFrom(path.nodes()==0?from:path.lastNode()); if (next.size() == 0) continue; // no new edges // Split path for other edges int splits=0; for (Edge edge:next) { if (seen.contains(edge)) continue; seen.add(edge); Path nextPath = (++splits==next.size())?path:path.forkPath(); // Add segment to split'd path nextPath.add(edge); // Are we there yet? if (destination.equals(edge.getTo())) return nextPath; edgesAdded = true; // Add to extra paths if (nextPath!=path) paths.add(nextPath); } } if (edgesAdded) return breadthFirst(from,destination,paths,seen); return null; } public Set getEdges() { return _edges; } /** * Get the Node by Name. * * @param name * the name to lookup * @return the node if found or null if not found. */ public Node getNodeByName(String name) { for (Node node : _nodes) { if (node.getName().equals(name)) { return node; } } return null; } public Set getNodes() { return _nodes; } public void removeEdge(Edge edge) { this._edges.remove(edge); } public void removeEdge(String fromNodeName, String toNodeName) { Node fromNode = getNodeByName(fromNodeName); Node toNode = getNodeByName(toNodeName); Edge edge = new Edge(fromNode,toNode); removeEdge(edge); } public void removeNode(Node node) { this._nodes.remove(node); } public void setEdges(Set edges) { this._edges = edges; } public void setNodes(Set nodes) { this._nodes = nodes; } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/GraphOutputDot.java000066400000000000000000000127701261716203600326750ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.text.CollationKey; import java.text.Collator; import java.util.Comparator; import java.util.Set; import java.util.TreeSet; import org.eclipse.jetty.util.IO; /** * Output the Graph in GraphViz Dot format. */ public class GraphOutputDot { private GraphOutputDot() { } private static final String TOPNODE = "undeployed"; /** * Comparator that makes the 'undeployed' node the first node in the sort list. * * This makes the 'undeployed' node show up at the top of the generated graph. */ private static class TopNodeSort implements Comparator { private Collator collator = Collator.getInstance(); public int compare(Node o1, Node o2) { if (o1.getName().equals(TOPNODE)) { return -1; } if (o2.getName().equals(TOPNODE)) { return 1; } CollationKey key1 = toKey(o1); CollationKey key2 = toKey(o2); return key1.compareTo(key2); } private CollationKey toKey(Node node) { return collator.getCollationKey(node.getName()); } } public static void write(Graph graph, File outputFile) throws IOException { FileWriter writer = null; PrintWriter out = null; try { writer = new FileWriter(outputFile); out = new PrintWriter(writer); out.println("// Autogenerated by " + GraphOutputDot.class.getName()); out.println("digraph Graf {"); writeGraphDefaults(out); writeNodeDefaults(out); writeEdgeDefaults(out); Set nodes = new TreeSet(new TopNodeSort()); nodes.addAll(graph.getNodes()); for (Node node : nodes) { writeNode(out,node); } for (Edge edge : graph.getEdges()) { writeEdge(out,edge); } out.println("}"); } finally { IO.close(out); IO.close(writer); } } private static void writeEdge(PrintWriter out, Edge edge) { out.println(); out.println(" // Edge"); out.printf(" \"%s\" -> \"%s\" [%n",toId(edge.getFrom()),toId(edge.getTo())); out.println(" arrowtail=none,"); out.println(" arrowhead=normal"); out.println(" ];"); } private static void writeNode(PrintWriter out, Node node) { out.println(); out.println(" // Node"); out.printf(" \"%s\" [%n",toId(node)); out.printf(" label=\"%s\",%n",node.getName()); if (node.getName().endsWith("ed")) { out.println(" color=\"#ddddff\","); out.println(" style=filled,"); } out.println(" shape=box"); out.println(" ];"); } private static CharSequence toId(Node node) { StringBuilder buf = new StringBuilder(); for (char c : node.getName().toCharArray()) { if (Character.isLetter(c)) { buf.append(c); continue; } if (Character.isDigit(c)) { buf.append(c); continue; } if ((c == ' ') || (c == '-') || (c == '_')) { buf.append(c); continue; } } return buf; } private static void writeEdgeDefaults(PrintWriter out) { out.println(); out.println(" // Edge Defaults "); out.println(" edge ["); out.println(" arrowsize=\"0.8\","); out.println(" fontsize=\"11\""); out.println(" ];"); } private static void writeGraphDefaults(PrintWriter out) { out.println(); out.println(" // Graph Defaults "); out.println(" graph ["); out.println(" bgcolor=\"#ffffff\","); out.println(" fontname=\"Helvetica\","); out.println(" fontsize=\"11\","); out.println(" label=\"Graph\","); out.println(" labeljust=\"l\","); out.println(" rankdir=\"TD\""); out.println(" ];"); } private static void writeNodeDefaults(PrintWriter out) { out.println(); out.println(" // Node Defaults "); out.println(" node ["); out.println(" fontname=\"Helvetica\","); out.println(" fontsize=\"11\","); out.println(" shap=\"box\""); out.println(" ];"); } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Node.java000066400000000000000000000033071261716203600306250ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; /** * Basic Graph Node */ public final class Node { private final String _name; public Node(String name) { assert name!=null; this._name = name; } public String getName() { return _name; } @Override public String toString() { return "Node[" + _name + "]"; } @Override public int hashCode() { return _name.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Node other = (Node)obj; if (_name == null) { if (other._name != null) return false; } else if (!_name.equals(other._name)) return false; return true; } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/Path.java000066400000000000000000000054011261716203600306310ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; public class Path { private final List _edges = new CopyOnWriteArrayList(); private final List _nodes = new CopyOnWriteArrayList(); public Path() { } public void add(Edge edge) { _edges.add(edge); if (_nodes.size() == 0) { _nodes.add(edge.getFrom()); } else { assert _nodes.get(_nodes.size() - 1).equals(edge.getFrom()); } _nodes.add(edge.getTo()); } public Path forkPath() { Path ep = new Path(); for (Edge edge : _edges) { ep.add(edge); } return ep; } public List getNodes() { return _nodes; } public List getEdges() { return _nodes; } public Node getNode(int index) { return _nodes.get(index); } public Node firstNode() { if (_nodes.size() == 0) { return null; } return _nodes.get(0); } public Node lastNode() { if (_nodes.size() == 0) { return null; } return _nodes.get(_nodes.size() - 1); } public int nodes() { return _nodes.size(); } public int edges() { return _edges.size(); } public boolean isEmpty() { return _edges.isEmpty(); } public Edge firstEdge() { if (_edges.size() == 0) { return null; } return _edges.get(0); } public Edge lastEdge() { if (_edges.size() == 0) { return null; } return _edges.get(_edges.size() - 1); } public Edge getEdge(int index) { return _edges.get(index); } @Override public String toString() { return super.toString() + _nodes.toString(); } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/graph/package-info.java000066400000000000000000000015571261716203600322710ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : Deployment Graph */ package org.eclipse.jetty.deploy.graph; jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/000077500000000000000000000000001261716203600265675ustar00rootroot00000000000000DeploymentManagerMBean.java000066400000000000000000000050711261716203600336740ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.jmx; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.jmx.ObjectMBean; import org.eclipse.jetty.server.handler.ContextHandler; public class DeploymentManagerMBean extends ObjectMBean { private final DeploymentManager _manager; public DeploymentManagerMBean(Object managedObject) { super(managedObject); _manager=(DeploymentManager)managedObject; } public Collection getNodes() { List nodes = new ArrayList(); for (Node node: _manager.getNodes()) nodes.add(node.getName()); return nodes; } public Collection getApps() { List apps=new ArrayList(); for (App app: _manager.getApps()) apps.add(app.getOriginId()); return apps; } public Collection getApps(String nodeName) { List apps=new ArrayList(); for (App app: _manager.getApps(nodeName)) apps.add(app.getOriginId()); return apps; } public Collection getContexts() throws Exception { List apps=new ArrayList(); for (App app: _manager.getApps()) apps.add(app.getContextHandler()); return apps; } public Collection getAppProviders() { return _manager.getAppProviders(); } public void requestAppGoal(String appId, String nodeName) { _manager.requestAppGoal(appId, nodeName); } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/jmx/package-info.java000066400000000000000000000015541261716203600317630ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : JMX Integration */ package org.eclipse.jetty.deploy.jmx; jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/package-info.java000066400000000000000000000015611261716203600311630ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : Webapp Deploy Management */ package org.eclipse.jetty.deploy; jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/000077500000000000000000000000001261716203600300065ustar00rootroot00000000000000ScanningAppProvider.java000066400000000000000000000244751261716203600345220ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.providers; import java.io.File; import java.io.FilenameFilter; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; /** */ @ManagedObject("Abstract Provider for loading webapps") public abstract class ScanningAppProvider extends AbstractLifeCycle implements AppProvider { private static final Logger LOG = Log.getLogger(ScanningAppProvider.class); private Map _appMap = new HashMap(); private DeploymentManager _deploymentManager; protected FilenameFilter _filenameFilter; private final List _monitored= new CopyOnWriteArrayList<>(); private boolean _recursive = false; private int _scanInterval = 10; private Scanner _scanner; /* ------------------------------------------------------------ */ private final Scanner.DiscreteListener _scannerListener = new Scanner.DiscreteListener() { @Override public void fileAdded(String filename) throws Exception { ScanningAppProvider.this.fileAdded(filename); } @Override public void fileChanged(String filename) throws Exception { ScanningAppProvider.this.fileChanged(filename); } @Override public void fileRemoved(String filename) throws Exception { ScanningAppProvider.this.fileRemoved(filename); } }; /* ------------------------------------------------------------ */ protected ScanningAppProvider() { } /* ------------------------------------------------------------ */ protected ScanningAppProvider(FilenameFilter filter) { _filenameFilter = filter; } /* ------------------------------------------------------------ */ protected void setFilenameFilter(FilenameFilter filter) { if (isRunning()) throw new IllegalStateException(); _filenameFilter = filter; } /* ------------------------------------------------------------ */ /** * @return The index of currently deployed applications. */ protected Map getDeployedApps() { return _appMap; } /* ------------------------------------------------------------ */ /** * Called by the Scanner.DiscreteListener to create a new App object. * Isolated in a method so that it is possible to override the default App * object for specialized implementations of the AppProvider. * * @param filename * The file that is the context.xml. It is resolved by * {@link Resource#newResource(String)} * @return The App object for this particular context definition file. */ protected App createApp(String filename) { return new App(_deploymentManager,this,filename); } /* ------------------------------------------------------------ */ @Override protected void doStart() throws Exception { if (LOG.isDebugEnabled()) LOG.debug(this.getClass().getSimpleName() + ".doStart()"); if (_monitored.size()==0) throw new IllegalStateException("No configuration dir specified"); LOG.info("Deployment monitor " + _monitored + " at interval " + _scanInterval); List files = new ArrayList<>(); for (Resource resource:_monitored) { if (resource.exists() && resource.getFile().canRead()) files.add(resource.getFile()); else LOG.warn("Does not exist: "+resource); } _scanner = new Scanner(); _scanner.setScanDirs(files); _scanner.setScanInterval(_scanInterval); _scanner.setRecursive(_recursive); _scanner.setFilenameFilter(_filenameFilter); _scanner.setReportDirs(true); _scanner.addListener(_scannerListener); _scanner.start(); } /* ------------------------------------------------------------ */ @Override protected void doStop() throws Exception { if (_scanner!=null) { _scanner.stop(); _scanner.removeListener(_scannerListener); _scanner = null; } } /* ------------------------------------------------------------ */ protected boolean exists(String path) { return _scanner.exists(path); } /* ------------------------------------------------------------ */ protected void fileAdded(String filename) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("added {}",filename); App app = ScanningAppProvider.this.createApp(filename); if (app != null) { _appMap.put(filename,app); _deploymentManager.addApp(app); } } /* ------------------------------------------------------------ */ protected void fileChanged(String filename) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("changed {}",filename); App app = _appMap.remove(filename); if (app != null) { _deploymentManager.removeApp(app); } app = ScanningAppProvider.this.createApp(filename); if (app != null) { _appMap.put(filename,app); _deploymentManager.addApp(app); } } /* ------------------------------------------------------------ */ protected void fileRemoved(String filename) throws Exception { if (LOG.isDebugEnabled()) LOG.debug("removed {}",filename); App app = _appMap.remove(filename); if (app != null) _deploymentManager.removeApp(app); } /* ------------------------------------------------------------ */ /** * Get the deploymentManager. * * @return the deploymentManager */ public DeploymentManager getDeploymentManager() { return _deploymentManager; } /* ------------------------------------------------------------ */ public Resource getMonitoredDirResource() { if (_monitored.size()==0) return null; if (_monitored.size()>1) throw new IllegalStateException(); return _monitored.get(0); } /* ------------------------------------------------------------ */ public String getMonitoredDirName() { Resource resource=getMonitoredDirResource(); return resource==null?null:resource.toString(); } /* ------------------------------------------------------------ */ @ManagedAttribute("scanning interval to detect changes which need reloaded") public int getScanInterval() { return _scanInterval; } /* ------------------------------------------------------------ */ @ManagedAttribute("recursive scanning supported") public boolean isRecursive() { return _recursive; } /* ------------------------------------------------------------ */ @Override public void setDeploymentManager(DeploymentManager deploymentManager) { _deploymentManager = deploymentManager; } /* ------------------------------------------------------------ */ public void setMonitoredResources(List resources) { _monitored.clear(); _monitored.addAll(resources); } /* ------------------------------------------------------------ */ public List getMonitoredResources() { return Collections.unmodifiableList(_monitored); } /* ------------------------------------------------------------ */ public void setMonitoredDirResource(Resource resource) { setMonitoredResources(Collections.singletonList(resource)); } /* ------------------------------------------------------------ */ public void addScannerListener(Scanner.Listener listener) { _scanner.addListener(listener); } /* ------------------------------------------------------------ */ /** * @param dir * Directory to scan for context descriptors or war files */ public void setMonitoredDirName(String dir) { setMonitoredDirectories(Collections.singletonList(dir)); } /* ------------------------------------------------------------ */ public void setMonitoredDirectories(Collection directories) { try { List resources = new ArrayList<>(); for (String dir:directories) resources.add(Resource.newResource(dir)); setMonitoredResources(resources); } catch (Exception e) { throw new IllegalArgumentException(e); } } /* ------------------------------------------------------------ */ protected void setRecursive(boolean recursive) { _recursive = recursive; } /* ------------------------------------------------------------ */ public void setScanInterval(int scanInterval) { _scanInterval = scanInterval; } } WebAppProvider.java000066400000000000000000000432771261716203600335000ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.providers; import java.io.File; import java.io.FilenameFilter; import java.util.Locale; import org.eclipse.jetty.deploy.App; import org.eclipse.jetty.deploy.ConfigurationManager; import org.eclipse.jetty.deploy.util.FileID; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.xml.XmlConfiguration; /* ------------------------------------------------------------ */ /** The webapps directory scanning provider. *

* This provider scans one or more directories (typically "webapps") for contexts to * deploy, which may be:

    *
  • A standard WAR file (must end in ".war")
  • *
  • A directory containing an expanded WAR file
  • *
  • A directory containing static content
  • *
  • An XML descriptor in {@link XmlConfiguration} format that configures a {@link ContextHandler} instance
  • *
*

* To avoid double deployments and allow flexibility of the content of the scanned directories, the provider * implements some heuristics to ignore some files found in the scans:

    *
  • Hidden files (starting with ".") are ignored
  • *
  • Directories with names ending in ".d" are ignored
  • *
  • If a directory and a WAR file exist ( eg foo/ and foo.war) then the directory is assumed to be * the unpacked WAR and only the WAR is deployed (which may reused the unpacked directory)
  • *
  • If a directory and a matching XML file exist ( eg foo/ and foo.xml) then the directory is assumed to be * an unpacked WAR and only the XML is deployed (which may used the directory in it's configuration)
  • *
  • If a WAR file and a matching XML exist (eg foo.war and foo.xml) then the WAR is assumed to * be configured by the XML and only the XML is deployed. *
*

For XML configured contexts, the ID map will contain a reference to the {@link Server} instance called "Server" and * properties for the webapp file as "jetty.webapp" and directory as "jetty.webapps". */ @ManagedObject("Provider for start-up deployement of webapps based on presence in directory") public class WebAppProvider extends ScanningAppProvider { private boolean _extractWars = false; private boolean _parentLoaderPriority = false; private ConfigurationManager _configurationManager; private String _defaultsDescriptor; private File _tempDirectory; private String[] _configurationClasses; public class Filter implements FilenameFilter { @Override public boolean accept(File dir, String name) { if (!dir.exists()) { return false; } String lowername = name.toLowerCase(Locale.ENGLISH); File file = new File(dir,name); // ignore hidden files if (lowername.startsWith(".")) return false; // Ignore some directories if (file.isDirectory()) { // is it a nominated config directory if (lowername.endsWith(".d")) return false; // is it an unpacked directory for an existing war file? if (exists(name+".war")||exists(name+".WAR")) return false; // is it a directory for an existing xml file? if (exists(name+".xml")||exists(name+".XML")) return false; //is it a sccs dir? if ("cvs".equals(lowername) || "cvsroot".equals(lowername)) return false; // OK to deploy it then return true; } // else is it a war file if (lowername.endsWith(".war")) { //defer deployment decision to fileChanged() return true; } // else is it a context XML file if (lowername.endsWith(".xml")) return true; return false; } } /* ------------------------------------------------------------ */ public WebAppProvider() { super(); setFilenameFilter(new Filter()); setScanInterval(0); } /* ------------------------------------------------------------ */ /** Get the extractWars. * @return the extractWars */ @ManagedAttribute("extract war files") public boolean isExtractWars() { return _extractWars; } /* ------------------------------------------------------------ */ /** Set the extractWars. * @param extractWars the extractWars to set */ public void setExtractWars(boolean extractWars) { _extractWars = extractWars; } /* ------------------------------------------------------------ */ /** Get the parentLoaderPriority. * @return the parentLoaderPriority */ @ManagedAttribute("parent classloader has priority") public boolean isParentLoaderPriority() { return _parentLoaderPriority; } /* ------------------------------------------------------------ */ /** Set the parentLoaderPriority. * @param parentLoaderPriority the parentLoaderPriority to set */ public void setParentLoaderPriority(boolean parentLoaderPriority) { _parentLoaderPriority = parentLoaderPriority; } /* ------------------------------------------------------------ */ /** Get the defaultsDescriptor. * @return the defaultsDescriptor */ @ManagedAttribute("default descriptor for webapps") public String getDefaultsDescriptor() { return _defaultsDescriptor; } /* ------------------------------------------------------------ */ /** Set the defaultsDescriptor. * @param defaultsDescriptor the defaultsDescriptor to set */ public void setDefaultsDescriptor(String defaultsDescriptor) { _defaultsDescriptor = defaultsDescriptor; } /* ------------------------------------------------------------ */ public ConfigurationManager getConfigurationManager() { return _configurationManager; } /* ------------------------------------------------------------ */ /** Set the configurationManager. * @param configurationManager the configurationManager to set */ public void setConfigurationManager(ConfigurationManager configurationManager) { _configurationManager = configurationManager; } /* ------------------------------------------------------------ */ /** * @param configurations The configuration class names. */ public void setConfigurationClasses(String[] configurations) { _configurationClasses = configurations==null?null:(String[])configurations.clone(); } /* ------------------------------------------------------------ */ /** * */ @ManagedAttribute("configuration classes for webapps to be processed through") public String[] getConfigurationClasses() { return _configurationClasses; } /** * Set the Work directory where unpacked WAR files are managed from. *

* Default is the same as the java.io.tmpdir System Property. * * @param directory the new work directory */ public void setTempDir(File directory) { _tempDirectory = directory; } /* ------------------------------------------------------------ */ /** * Get the user supplied Work Directory. * * @return the user supplied work directory (null if user has not set Temp Directory yet) */ @ManagedAttribute("temp directory for use, null if no user set temp directory") public File getTempDir() { return _tempDirectory; } /* ------------------------------------------------------------ */ @Override public ContextHandler createContextHandler(final App app) throws Exception { Resource resource = Resource.newResource(app.getOriginId()); File file = resource.getFile(); if (!resource.exists()) throw new IllegalStateException("App resouce does not exist "+resource); String context = file.getName(); if (resource.exists() && FileID.isXmlFile(file)) { XmlConfiguration xmlc = new XmlConfiguration(resource.getURL()) { @Override public void initializeDefaults(Object context) { super.initializeDefaults(context); if (context instanceof WebAppContext) { WebAppContext webapp = (WebAppContext)context; webapp.setParentLoaderPriority(_parentLoaderPriority); if (_defaultsDescriptor != null) webapp.setDefaultsDescriptor(_defaultsDescriptor); } } }; xmlc.getIdMap().put("Server", getDeploymentManager().getServer()); xmlc.getProperties().put("jetty.home",System.getProperty("jetty.home",".")); xmlc.getProperties().put("jetty.base",System.getProperty("jetty.base",".")); xmlc.getProperties().put("jetty.webapp",file.getCanonicalPath()); xmlc.getProperties().put("jetty.webapps",file.getParentFile().getCanonicalPath()); if (getConfigurationManager() != null) xmlc.getProperties().putAll(getConfigurationManager().getProperties()); return (ContextHandler)xmlc.configure(); } else if (file.isDirectory()) { // must be a directory } else if (FileID.isWebArchiveFile(file)) { // Context Path is the same as the archive. context = context.substring(0,context.length() - 4); } else { throw new IllegalStateException("unable to create ContextHandler for "+app); } // Ensure "/" is Not Trailing in context paths. if (context.endsWith("/") && context.length() > 0) { context = context.substring(0,context.length() - 1); } // Start building the webapplication WebAppContext webAppContext = new WebAppContext(); webAppContext.setDisplayName(context); // special case of archive (or dir) named "root" is / context if (context.equalsIgnoreCase("root")) { context = URIUtil.SLASH; } else if (context.toLowerCase(Locale.ENGLISH).startsWith("root-")) { int dash=context.toLowerCase(Locale.ENGLISH).indexOf('-'); String virtual = context.substring(dash+1); webAppContext.setVirtualHosts(new String[]{virtual}); context = URIUtil.SLASH; } // Ensure "/" is Prepended to all context paths. if (context.charAt(0) != '/') { context = "/" + context; } webAppContext.setContextPath(context); webAppContext.setWar(file.getAbsolutePath()); if (_defaultsDescriptor != null) { webAppContext.setDefaultsDescriptor(_defaultsDescriptor); } webAppContext.setExtractWAR(_extractWars); webAppContext.setParentLoaderPriority(_parentLoaderPriority); if (_configurationClasses != null) { webAppContext.setConfigurationClasses(_configurationClasses); } if (_tempDirectory != null) { /* Since the Temp Dir is really a context base temp directory, * Lets set the Temp Directory in a way similar to how WebInfConfiguration does it, * instead of setting the * WebAppContext.setTempDirectory(File). * If we used .setTempDirectory(File) all webapps will wind up in the * same temp / work directory, overwriting each others work. */ webAppContext.setAttribute(WebAppContext.BASETEMPDIR, _tempDirectory); } return webAppContext; } /* ------------------------------------------------------------ */ @Override protected void fileChanged(String filename) throws Exception { File file = new File(filename); if (!file.exists()) return; File parent = file.getParentFile(); //is the file that changed a directory? if (file.isDirectory()) { //is there a .xml file of the same name? if (exists(file.getName()+".xml")||exists(file.getName()+".XML")) return; //ignore it //is there .war file of the same name? if (exists(file.getName()+".war")||exists(file.getName()+".WAR")) return; //ignore it super.fileChanged(filename); return; } String lowname = file.getName().toLowerCase(Locale.ENGLISH); //is the file that changed a .war file? if (lowname.endsWith(".war")) { String name = file.getName(); String base=name.substring(0,name.length()-4); String xmlname = base+".xml"; if (exists(xmlname)) { //if a .xml file exists for it, then redeploy that instead File xml = new File (parent, xmlname); super.fileChanged(xml.getCanonicalPath()); return; } xmlname = base+".XML"; if (exists(xmlname)) { //if a .XML file exists for it, then redeploy that instead File xml = new File(parent, xmlname); super.fileChanged(xml.getCanonicalPath()); return; } //redeploy the changed war super.fileChanged(filename); return; } //is the file that changed a .xml file? if (lowname.endsWith(".xml")) super.fileChanged(filename); } /* ------------------------------------------------------------ */ @Override protected void fileAdded(String filename) throws Exception { File file = new File(filename); if (!file.exists()) return; //is the file that was added a directory? if (file.isDirectory()) { //is there a .xml file of the same name? if (exists(file.getName()+".xml")||exists(file.getName()+".XML")) return; //assume we will get added events for the xml file //is there .war file of the same name? if (exists(file.getName()+".war")||exists(file.getName()+".WAR")) return; //assume we will get added events for the war file super.fileAdded(filename); return; } //is the file that was added a .war file? String lowname = file.getName().toLowerCase(Locale.ENGLISH); if (lowname.endsWith(".war")) { String name = file.getName(); String base=name.substring(0,name.length()-4); //is there a .xml file of the same name? if (exists(base+".xml")||exists(base+".XML")) return; //ignore it as we should get addition of the xml file super.fileAdded(filename); return; } //is the file that was added an .xml file? if (lowname.endsWith(".xml")) super.fileAdded(filename); } /* ------------------------------------------------------------ */ @Override protected void fileRemoved(String filename) throws Exception { File file = new File(filename); //is the file that was removed a directory? if (file.isDirectory()) { //is there a .xml file of the same name? if (exists(file.getName()+".xml")||exists(file.getName()+".XML")) return; //assume we will get removed events for the xml file //is there .war file of the same name? if (exists(file.getName()+".war")||exists(file.getName()+".WAR")) return; //assume we will get removed events for the war file super.fileRemoved(filename); return; } //is the file that was removed a .war file? String lowname = file.getName().toLowerCase(Locale.ENGLISH); if (lowname.endsWith(".war")) { //is there a .xml file of the same name? String name = file.getName(); String base=name.substring(0,name.length()-4); if (exists(base+".xml")||exists(base+".XML")) return; //ignore it as we should get removal of the xml file super.fileRemoved(filename); return; } //is the file that was removed an .xml file? if (lowname.endsWith(".xml")) super.fileRemoved(filename); } } package-info.java000066400000000000000000000015761261716203600331270ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : Webapp Deployment Providers */ package org.eclipse.jetty.deploy.providers; jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/util/000077500000000000000000000000001261716203600267465ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/util/FileID.java000066400000000000000000000045431261716203600307130ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.util; import java.io.File; import java.util.Locale; /** * Simple, yet surprisingly common utility methods for identifying various file types commonly seen and worked with in a * deployment scenario. */ public class FileID { /** * Is the path a Web Archive? * * @param path * the path to test. * @return True if a .war or .jar or exploded web directory * @see FileID#isWebArchiveFile(File) */ public static boolean isWebArchive(File path) { if (path.isFile()) { String name = path.getName().toLowerCase(Locale.ENGLISH); return (name.endsWith(".war") || name.endsWith(".jar")); } File webInf = new File(path,"WEB-INF"); File webXml = new File(webInf,"web.xml"); return webXml.exists() && webXml.isFile(); } /** * Is the path a Web Archive File (not directory) * * @param path * the path to test. * @return True if a .war or .jar file. * @see FileID#isWebArchive(File) */ public static boolean isWebArchiveFile(File path) { if (!path.isFile()) { return false; } String name = path.getName().toLowerCase(Locale.ENGLISH); return (name.endsWith(".war") || name.endsWith(".jar")); } public static boolean isXmlFile(File path) { if (!path.isFile()) { return false; } String name = path.getName().toLowerCase(Locale.ENGLISH); return name.endsWith(".xml"); } } jetty-9.2.14.v20151106/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/util/package-info.java000066400000000000000000000015471261716203600321440ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // /** * Jetty Deploy : Utilities */ package org.eclipse.jetty.deploy.util; jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/000077500000000000000000000000001261716203600216115ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/000077500000000000000000000000001261716203600224005ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/000077500000000000000000000000001261716203600240245ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/000077500000000000000000000000001261716203600251635ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/000077500000000000000000000000001261716203600264575ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-files/000077500000000000000000000000001261716203600303245ustar00rootroot00000000000000AppLifeCycle.png000066400000000000000000000470661261716203600332700ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-filesPNG  IHDRTA(WsBIT|d pHYs''MTtEXtSoftwarewww.inkscape.org< IDATxwxSe_R(@J{-(CY- F?DeLAP)Le{P(-#IH4}Փ$ysN4 !fgVHBB3*f" U!DB$T!0IBa&PL$ !HBB3*f" U!DB$T!0K<ˇ7z! !+Ӂ+V'O]xbŊY0*!x57'OsmN:eP:Ċ+裏X`gϞRJ|Gw^{3f _}/^F5Yl4M#>>8ߧz=P{t)z:883$EF`U%ԓ'ORR%z=֭cܿO>'㏙6mϟ7nmۖ͛70dϟ ٳg޽{zʕ+و#::0oBә$ 7K,8::Yf+4fU#GV]taܿSNwq@}{A4cbbti" d0NKEzuV~w@uJt:J(LB=vo~)͛7Ǹ:n84i?111.ZDGGAddhKfs~"##_Nggg\\\Lfɒ%` 566QFٳ={6ϟDFF̝;w>j䄗.\ <<Νۦidd$Ozɓ'*%LPfM@WZՐ(u:ƍc޽l߾(<==4i`:rH~'mFll,_zM6Iaaam33JhF $K߀X<~'Ȍ31YApp5!֭[jժ1g4MjժY"E׼ysϒ%KtԮ]kג?t|̓x]! &88Pak̙Sk:aSI͛71bg&44h<==ML0I&i̙B'_LL AAA|͛7\2s;]ǏMH(SÇܹs#CZ;w_lٲY:d=s`8;;[:4~.^7TV]tɰ^*U1ԉӜ9sǏ=~lڴɬLÇpBԩS|W[nDEE&'g}fu2Ce̘1) :?eGHU d˖-ZVJ׮]Y~=۷oa]Ŋ9qgΜTRTTzW^%""r)V> 6d<ŋ)Vݻw篿2iߟMҼys~7T=ʌ3ț7/:u/Pɞ={={6o͛7gʕܹ3 իZ. 5ڵ]ҵkWXjK&  PdI9Hoߞ… -[6jժe8P.\ƍ\vƍS^=6mJ…MNxZNW7J*Z۩\2]tB 4襧&$OM8q*Txܸqh?N%pvvfƍԬY]RhQ @\\!ԨQ6mPfMöm )T L2sxʖ-K.]R  b׮]&Z^z4k֌֭[u$'SݻɺNx4M… 'ONm6ԩcܩSԫW=zٳ4i9sd̙,\^z̕+W1/Icȑ3gܹ3#00zܹct}{Im[܋0l |iHhG=|0׮]DJlǏ7i?͞=;gȑ/_yܹnnnܺus ?~ CO33sN~?2ܐ0kJ`,%nc}޼ySLLJRJٞw s E*j׮믿nNK0NGR8{՝ աC޽K޽)\0?f̘1T^>I&ѹsgéUݻwO*UDnOɚ5+۷#D*Uڵki۶a^R 3ՋNC4ΝkDyw4 WWWO^gΝ&= z-V^M=utz:00A"Eٳ'/^ܰ]Z˟?? Hײu):t݆K?~۰Ņ˳sN5k+W(\0Gjժ=IP-Zc||||=z6mp] .Ltt4|t^ݻٽ{7-23g 5PPa8Lڵpuu޽{3p@.\Hr8y$?6&ƎԩS ~LȒ+00WZU>$lh/MoOff;Gs=\]][{ܿs*uZȖ-ECjʔ)ԪU=z:X_*3_~?~ZlIٲe6{yy~CCCׯr"gΜtDJ֬YMzsm8p*U JOwHȑ;-r4i҄Sn]ڔȓ'ժU+U1YR@====N<_3avj(ȟ? bț7oۉQ:k֬iLdBbŨT$S3KQ?>>P|Z֭KޠAʗ/oXM ț7I{vظq#sի|azj888PhQݻw ,Oiڴ).]à &sOM4PB]fgg;3׻鉗\:dM%_|~'OX454M#88CI"ٜS.Dl.N:y HUۦ4GMT!fĂDF.ߗ.]nݺѩS'HڝsE\ _?m"ct\?9es%Jn9888-[6gώ;ٲe+[I6JffO$fZEA$LF4Ln-!prriΫ# UMLL 111/+8::5kVtYfkI"Iqqqg2oF fɒ噄)gNPEMzM4d:yi^^^t:37d %H,67lJX/;;4i<1Ͼ*>>}_WXI"yt޵kPlfU 7ހͳ Sމ\XI"M)%4IBicxw1z ,7{wh/]޽NW?ؽah~~z=|Y3XԸ'MRqرа!i;vtkW8u*aPEص  EURZ֬Qqc`T~mzu(QWM s'A*qC>?O>y`@ &LVr0MwŒPz޺5\}]@>jُ?ܳJ#G&2BohLj: xH=QCc`5ڷWӿ1yz>aF˖/UJcF5]Fewkdɢtwܹ5/7;sFbj} 77|dkd˦jv( ??5ݨGXdd>l4߿WXPEAT鼄vRu 6Vۯ]S%ЬYs_my8|X@UA`q׮zo6)<\myGxzRi`C\T ŊA"=$!-@SJ$_p [6Ȟ89s˦._t\Ф ^oJ9sNNpx{03pj>(S71ݫPE5ov-h4P%MsNaFUJMp6ܼ)-\5k6t:}:|ؽZ$ژH DJ6la[#ٸq#˖-ሗjCnܸ;v$*lCl!)J«WѧEDVM3k,-Z/@TT筷ޢgϞ/\I\nܐc"odoO={x\d_P<͛+S9r͙33g_5kV F'2M7nL80u=tX6MMY- xbi/fo7o^jԨ[v?|ҡ$IV"b~ `ӦM^*ܹs|'ܺu0o̘1l?|؂Q.[,1~Ilؿ?&/\K-mj%/oqpp_~_\tq ,푄*D&PhQLµk)Q OaiC"t4jԈ<{Kdc*D&˒!IBa&PL̛P-+Lǃ/ܸ}N}62H8p T ~5}:fca 4JH~N+y&,VR5y2+g(H734iyBѢx#3U ̝k(L/CΜpӦPqupԭz=_NAУd˦**]8Kԗ':Z}yjPoooH|޿RUѣ 9rPڗNgvv8|XZ5ڮ]?;!Kƍн;ԯ W{6l ]5SccaR6mR۶mecm#GT rpXZw-] mڨc,:8ZߟХ $֓' ZU0oC]3ҹС0j8:¤ICJU+=<2i W38sFM|qywo5*ΜNK_~QϞUQ#`,:-y|Q2W *޿7uq?U-=ڸSd QjY|:WVOiIKȸ8-hhOw5G]jTn\fFj] {{'ˋS?kUh,^;v(]ڸ44W ؿ_M7i1dqE4QnnnQF\z^/ɓ5<<45Դ^mb|5j|{IxppJB$۵0-ÚQxte'Oj8:jkA8Rڏ&~^r˙39 )C`̼mN0.KbSUjPEɄeTCرN PXY@Dij9 (/xvv6Nt׫iꋐp%Gkhn깝liZ@0AYǫQQeQQc+1Q$,Oj7zPlx-V~7gy?/WN%vhє^=k-8YڥA{tǺeiŊZrOO ٸ1'Oߏ ᆱ]y}a26N\9@U`Nc1>eTM UHi31o ];(X7W;o^Ľ/ᡆ$5k%TTܪ78Ջjiaʞך> _jA=]JcƨaWkN@ VS%]&oUŷW 4~UWzc>,L]{P„dHH\b=^{yӉw1gyaa]6fz

[Wii[j:ā5I>%%=Y/cGӞŔ>UejKn_Ghƿj̜zw2]E}ThPPJB$G6aB/PPygwikQ鈡D巤s*ڥK0hn_Yanݳ=E6s& f̘1{ި!$44!{;{)>*,QRk֨SGΜ y%TH*Hx5jK׮] R"ݻG߾}:a}_)V 5sUډr>Ug".^| ZiByu~r)ʔ)c-8r#Gdz8N*PHRߥ 6m۷x)cf͚ŏ?Hܹ-Mje&O-bݺu5 >"VX͛Yv-."iBngeŋ ݰa.7oޤwUHӧʷ~kP2 )Zn KNNNZ .0qD F%2 ^ψ#puueԩ'St:NJӧ1 *)ݛڵk3tPKHB@G׮]ر#GXPtB>}ܹɔ 5iҤ K.,\‰o 2{ѧOON ,N%% \r\s!K#,… `INGh\a(R/>d Η/6lϏ.]СCMXÇ3yd֬Y1vvd&i]U<ɔsgȑ(P?tc/_Ί+puut8gx̝; B|'M[|96mbݺuLPmȖ-[Xf ˗/-UaNʓ'O6mCO i۶-GSNܹs3 >777IVJJ6ƍߟ3gJo߾nZƘZ1I6*$$___ @f,HziԨ/! Ն2x`T€Dwݻw +ڰ,Yxbͧ~jp2hzIݺu>|$ 5tL6};ЦMIȳu+mڴё-[мys&҄T3\]]Yv-{aΜ9'C4qкukz2HBϕpkMӘ3b)C:rN24.U{ a֬p##ahڴ;|OT0ΟWӁ0hԫ]z=|o>B*G[pC*YΛЪ1in M7>hW^kLc׮{7٩}q}矡X1ZQ~}hnRN}|ki 5M4>6Ԙ6Æik;wCp WWgsWW>3.7OD 5}Fj} 8tȸFja //8[7UJزƌj:_>+ƌ(T(韃i"PEwSϫšhQ4In]v}޸i*i^WWM۷CPl}cwJP%"ET9(ղzM<-sȰ)>=={` $V^,accՐMLG37o͛g=zwf(pߴCM琄*رSti#h^u۫^rTuhQۗ -NL xػw@J 66#Gw^ˁ-G.]\5)="#y^:tH" IB\v֭[si,>pW* $9s&W^嫯ђa4"U~776?i$JWnlѣi۶-۷'004 UVa4" iƂ ؽ{7sΥ@UU[t)c…X:D҆jnݺEtlڴIiҷo_HǎmՒZ|96mbܹN'pܹÀ5j 4t8jcܹ3OL3a;K#^AJ6gʕ̞=%KZ:afgҥK̝;,)H{RBAAAtޝ+W?K2Q>۷cǎܿje,X̙3Pʕ+ :O?ʕ+[:$ nj9///&L g7e2aaa 0-Z;X:tKWÙr*^5Sj;7KҺ;wd֬Y|gTkfZ1yd"##:uj è I2Z LŁӛ#\nVhbn|eB cԨQm6)@at:'NdtpekTJN(3KNYwBstW|Ɍ۷)S0qDիA]v+Vnݺ_&S>K9_@(V.22?~'\QlYV^ME)SaSVСCjՊ~K2/3gN~>}3#IBB111;o7[oY:$A3k,%""[8:' ?~-ZPV-.]N!ի K.ܼy;v駟rmKfӤ JhL</?s}!z,^~?ȑ#-͒{J(ի% ֭[SUׯ_nl$T+Q}Dtb0 9q[ԩS& B\!-HB|*Vӧ9~8cƌ'՜?ڲ(IBظ+2}t]cŊ2* XwB-< BY&˖-:yl޹gϦ륂u&+Y3ȑCW.$ ص ~!bC{=!ҁN.%L ›oB @PZmlْ\SMm 'nMݼ Nz Gk^8}8BdvQxQ"#矍_Al,m׸sVķ;Ty3T 玎Щ>~\NákWȕ χ8=~w{wͩSlU]4 >5R׭S Q_//6 T˗.U^=ؽ UtiSÔ)g 6ܹ f6lPAݺy,X8pnUMwìYO PFP8v ^oRT S7yjsԶ0p ӧch-XwvV۬Y|gl/jhghi|Ƒ#e!!kmqZW4F(]Zc6E4<=5VT~MEI?И4I=O壏4g4HAn43[oiZw{94/!C41i/cƲe%Jh.u9~<Bh´Ǐq萆k=g\ѣ:6ϝS׭׸pAo4\]5]S˶npsxm;5NXT[qhy4:vTܾ͚} gg5<<4Jc7ȒŘ+u`>WK>VQƗ_ܧn|@7n47NJcBQ#Hj޼& ''cLYj/k֨i??^xO# :;4QՇǏUQ}DUU[NÆy9syzT;l* w IDAT&UM'V7U AA k' jւU{&Yʗ/1`\^j HVpyV үe:jZM_Ea<*tb} u8qu5Tpare564濻89ڵq{.5,+UUɏsg+uK5[}av3g/^}T)T҉ aacԱ}_|yӟ0+:wV?T3^|Ӏ%Ԡ UȝhQ+8fZް-+E4Xzȡ $T ||\95jukv`Hm^x1Zc5K}y*VwU=rQha+Se^{Mt)SFKCM*8;Ä jNjcǗFNțWUᓣMON:P&d͚nat~' C^%0P PsGk ~M>TUǎUM *}lrtd}Rԡ-[rIK"H!IVܹ *PjU-"$Z |8z(ˋnݺk.i0$~X-͑jEƎkr("""pwwOٵ+xiӦq-,iPl$T+={vƏox÷~K5,1rHeq,MjeH…)S ۷oW^\ta .66}Rzu bpl$T+ԩSׯJMdP;t֍ niǡf@;w&迋Cxzz~zý{h߾Çe„ TV<)Z!Nṳ3k֬a߾}̙3ǂ֭[уYfI2M'P3;;;ΝK||<#GDo Vܹsߟ%KPD Ki[?#'z Wٳ'K,9WơC:u*k׮88P8+V;wnK#/Š+XbZ$ … 6ShQK#,l͚5ݻ QFP3@0a5SpA\a̙ݻw1cUgAPm@DD~~~tڕvY:>c<<<5jC$ڈxN6l >>SV-zmp2lf_| êl\TT=ze˖L$T3zhUFϞ=2׭]U K. 2-[Z:Tm̙3Yr%Ev=̙3)[OjΝ;Y`Et8"._СCYp!rG$U~VT)VZŐ!C8|p1FU$Z1)fҽ{wڶmK1~2~Yr%nnnfأH+P3xJR:th-\LlZU]qsl3i$jhҟ-5֓L6mĊaSZ5e8:2a ,D4+jtb檙GscΥG=?]\yhooĉ׳de,%qJJJ4PWWGww7GU. o>ԭjdxx?ӅBg*ر;wHˑkLOOglA222";;p#lڴ]v 른p&&&ɡm۶I xhoo?tGa)++####HBll,tvvRSSr" TWWr$D T &&#G)--U9ؽ{7 qaPJ RRRҥK.gј 顼f<O8]΢pm}GG555ƪ5%וIBB۷oBRRKZz244D__DGG4qL ㏻ݱ10/G yWW߸q#ǓˡCB޷OJLLd0k(,DX֯&ldJKC?7lz;y$FSSO/R T;/ p4LL06E8|8c>;~\0Y:u3g΄(M*sSza*;_~ ;N*z+TVKͰw/\bx>v:x);ol ^{^ʯ^ />8vu5?yy>ȱc=}ONJ+C#2\ߐi8~/#ҥ ?^dž4J`k 6yc'CRyßRS >k3{ >Æu --c ))?+&fje={'GY 5Zel^]l07jyyk Lwv 38Xnul̤!.e$%[%%Kv/\w /?c7>8}MN“Og;h .N"ι̽[nU^YYp劝=jk{OtCmкֲeQQY֬znZ+W^=dC! O27~Z[mx47úucccoa3IO/Lo bzapum(w?D, c!.oP¶Y}}g]wK'8EE] |nBr=6ccpa< dӓR2#zzs\z* CqD*"UD# TG""(PEDQ8@qD*"UD}}?]4tY@3p sN6%"8@qD*"UD# TG""(PEDQ8@qD*"UD# TGb4؄IENDB`AppLifeCycle.svg000066400000000000000000000370221261716203600332720ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-files image/svg+xml G incoming_app command_deploy state_deployed Deploying Undeployed Undeploying Incoming WebApp Deployed App LifeCycle Starting Stopping Started DefaultAppLifeCycleBindings.png000066400000000000000000002001651261716203600362420ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-filesPNG  IHDRSsBIT|d pHYsy;tEXtSoftwarewww.inkscape.org< IDATxwxU{M@ PҐ:(i"k)PQi *H @ @ an1;$$r+g@.puu7+Y7oĂ pQܺu ٳ'mVi(O@@ZlY$"z:jjTR㺬DKfjj R R)&sDDTd*''cǎm|3&OS_=+33=zk$~~~꾏޽{K .ѱc*$ISTTTF1v5VII JJJVZBR 3331*5."F&SQQQ&RO>7~b"ճgOpttE㠠 (8}4abb*V磠EEE%2~Ey Gɖ%,,,`ii \^MQyjd2uYUV8z(rrrp̝;GC@JJ_v x Zt)nܸT!--M<(&Smڴϝ;'~ܢE q.{tt8 QH·ұ"+//*JLX1{.>rppsL&8͚5EEEI <GZƢEʼÆ 3gΈN͛0a~g񵉉 LMM%U F9R3K.^B 2?VYȘJJJL$%% puu-jx2h+#=^^^1bbcg-͛7/YƍDׯG6mPTT///q>Lq͚5 Ro|?OF刨vЏd߽{B2vhDD5BNp1\~QQQXl8jT*rJXZZ|||RRR/K{e;vL6R-Z|8tODIާ_ssIa iM|qt:_'%48mp*Uaa!n޼7opqq}jU]VJzyyaŊxgĶXqo( D.]Rh?G8>} >ŋ!C̙3s hРr9j뿲`ccBؠ 999Zua̘1b,666ĉ'иq}DT4 n޼[nj HJJ6$,JéSо}{8998r<==Km?ڵ{gL6 ]t!CJɓ'Ѹqcy+TQd.'..T&M˒D j*(Ĺsl2lڴ pvvF~MիWÁ G$ PAA?mbcc,JÆ C.] srrPPP pjVׯԩSHNND /,^ڞWYedXp!/ISpB||g/%%%ER!Q,\ .|kjw"22 +QMQyzzJ~) qAOOOX[[?QF믿Nwww4o!!!rFH¦)))ŋѮ];k/_ ^I1i$8p͛7Ghh( j4i"Oggg}pdff"66Z֜GT">>%*`ҥ,GGGxyy!::/ $$$@Uѿ~KʢjP?pZlY%ϪN:w܁Jd.QɔS^LxW(ݻ7z-QYZh!aeeUqqqyGnڇΝ;tj,iܻw FǏJ틮]J?@||<ѰaC 0@G޽{qiI&1bMٳC۶m1djlݺ111ǸqРA5k֠q޽;`Ph۶-mۆ7nq;v,lmmt:v؁'O]voL2&$$A/UVܹsk011lKHddd#GK8qBVVZnaÆIEӧO#==K.۷wRɔuwwGxx8޽-[bԨQ61x`ԩS=zH~oU7F8xzzI&FԨi~DD5]VVVK ;;;wuV4iiii5kdZ^^&L]Lh N8!Yp! ;wĸqpѣGi&|G尴IJe˰~z|8y$5kHzS@hydd$mۆw}YYYFXXvU믿_ Looo|7X|9&$$ٸz*7`QQQh֬8bHZHHN:UǏ>J%/_s疊e޽hժ<<<3СC(**9z(6l؀3f@T k׮{'NVcΝXh~!==}6oϣ:ܼyo6vDDUFLd:/^q!Bu6 {A?3T*֭['~waĉ6l`ȑ6lV\3g}\ 6͛7+VѣkڵkS"::ZRAIIIo͛cz*6m$ܹoXuĈ5A?e/&&͚5Cll,`8p^xFDD`ҥVGСCvZIwelٲEk.,^Cg,dggرce&R SiPPp $<j $Ә[l \Q I ~yhZtIT*KGX)bҥKǏ HJJBRR/J'Y*>K?S|ϭ[޽{K$VަMKdU?:w<ދƢRQ#SС^vM Q #5mTNJ/=ݻpvvUV8}4Z=õ2JfffLB}||$vkשT*Sr^a M`s-,,p9\r&&&bRw-dddp-R랜tm޽hԨN>-`߾}}6˽)|}}%'^Ԇ"3g]&i| JD(vvv_srrzhT*% aggWBuqrrBQQ_.I!*OHHv܉\zU'h\|~~~ h{ 7hiiiig.]+W~ iZܸqTQ7dաADTU4/,kh6333+AmÇ;RԢE hZɔ>kcn09rDl+5<{k׮d9sΝD D||<yK IDEEAՊS [[[lݺ۷o<S,)rqedgg(H(R`iid:?2IԹsJ3cڵq!rϗ,qAiӐիWҥKhٲ%,X OD? iṏ́ 0n8L:U,w~uhZ 4۷G`` ,Xx4m1~x!2?s ????z3gΠk׮)&k^xaڴi;v,4il?"O0GFJJ ~wCh48pz ݾ}b߾}8{,ڶm pssÔ)S0rH`ƍ@߾}%feeaԩx饗p cРAFOMMM`L"R}!,, ܹs /({d*** _d:tL~dlݺUl۲eJJGDD{p5kV\.G`` a8%S-,,"n>װaC4o\|իWcʕXlrssѨQ#=Zxb8v3fH-גC ,?,\ѤIH111AHHddcҤIɓ'ٳgѣgũSM6())A``.bݺuؾ};n߾ kkk_j ڵC^^lقLt]we4m=\qnr劘Li[ƶmې`055\;i$`jc}U-Z`?"d\_())App0[nEpp8QFHNN\xbIW^yÆ Î;rJիAaa!lllđ/ooo|h4={XjoQy&ktL⥗^BV;J3ydxxx`? kwww4mڔ#RDToّ+VThh( V 333!%%*JRp/kܾ} sΉ):$SOII9s@OOO8;;ƍ{é ___$%%Ȑq888ۻTC"N&S٘={zѢEi#|sIR1\;m4XPHJY&_ ,mԨx\*Q,,,мysxyy!)) BTym۶A&?C cM*s VurppQU's{lrIٛ7oDZb2K.BIZ>ɔa@ZטzXZZy())Azz:Ґ)CСèrֶ~-\\\ޫsԵk$JJJPf_Ê~o333˗#??ЧOq!HVffxR/0>||WDT[r HKKCZZ#jkkk1ba "\25}tqg}T٫W0V{p7xo&bbb駟#:I2vZ76l: pwwZlcFHpqqk*DD$S=]|2<<<$}䭬L &t'N\P(zjqs\\8J7nܐ\ӫW/ܹVVVf*..FvvcEL&5lmm333cEDTթ۷kRxyyW^.iiipuuZx1b gϞxЧOHVΝ`8~85k{3ft"*EPQ&鐗'Iw2)JIdmmrDDPF*Jj&&&~hҴiW_ϟ?:B%zBHFhBr'~GDT9TEAVkZT֭,6"LMM'''((((/d2aaa XZZQa2iIt:O=TEDdHP# t: QPPP*QDuB$Jd2cGDTԊdʕ+Coƽ{77ǾHHV^ ~dZc?QMMLLTi&I&#*))AQQVVc6lRRYc eTFF|M_ IIII/f3B%%%aƍ8pnݺxxxs5j7n\>'Ocǎ͛K6%"*$KhP\\̪O &J,>F:{,6m@(a믿7--ML,,,вeKٳgTLFV1o<JΝ;waaa믑~k8}cz蓪>D&&&b"$ˡP($ǏWn3Z20J%B={ڠq`ظqP"66td2!jtσj+ݻѰaCk2L111yڈѓG% QQQm۶!22.\=zǏG^Э[7@~~>1޽{/B.y:t8fX M64~bbb`kk:`رMHj͛s܏O@@lٲ>|xbۃ?6mt:]tt߿>}t:.33S"1c#_lc: IDAT~{Ws~!]ƍˌg:NS:RdL+Wڶm[mڴ='ODT7}' 6; ""g}\|Y|8#Sz8p N:С>+WD-';UuP5k#C.]3gΈ /|'?{Fbb"_~3g&MWWWmt+ѺukDFFSLիѮ];1>qQab ^\r抉B[#FKӧOǸqe{"ڵks0tP@DDN*I:vXj PTdO?+]͛7vvvPT8q4`rQ᭷BNN`ҥ{GMDDDDDړ)ÄEe-V|jժd} 7?LTO?ŧ~ ^UNPqd2tv¹shp%Ba֭1~x?;wF,nmm-&v?~\|?~ $UQ)$,c0*T*ѪU+\ttxVq2+KOR=[ L۵kpy̛7OL.]*.6|]v}=*a~t:#U&SZ/U^ߨ(q\V${3gɓ˼~:_PP\صkakk+[XX[nGl3LV\[^~e9:)~Yej)~ k?6GDDDDDZ Pŋ똘,\PcwTYkl0! oB777 v¢EuÇ߹s{ƞ={pݻf͂X 0}?@-31Z~=vڅBdggc x{{ɩٵkZ~q|ǥ@"""""Z:2+h_~T={⭷BQQ\.GppaSǎ/ 7oJ%]:uС;vKdd$ P*kRK ?L>~'Nƍh^xh4<^  $$$ 55 6p-t: 6TDDDDDTud0 *>i9w.TaXAdGϞ=.^;wC ѣ%׬Z WGx&p:>R}:t;vHj4Gᬬw^ 8PltRRR`ee_/\"""""Z2~QDt:M, QJW)믿.V۴i#GҐBOOO8;;?Fbb"j5ѠA:Q}6g4o\2uU{'OLLDBB:$&Rcǎ}v...pqqyx ڊ{mqd*̛7+W_o˗/7bDDDDDDd L w///l&LDDDDDQ,ܾ}[&27t:PkVzYx̘1PXo\z׮]=0nܸJ}.FMK|geСC_K@\|[lVJGŎ;j7GDDDDDQJUVn:=99:t W %>Ul"""""jL2j* <* &L_@DP^pĉǺqq1]f͚=$zZZj55j&SSL)SHMMC%&&vvvgII n߾wnnnuUjݴН;wp-uΝaoo LL&Nnݺ7oĉaooxzz+Cxx8눉7t @H,,,vvvhڴ)pRѴiS4ixq1>ANN&M{{{xzzM6qҥ'_2ڦaaaׯ@T"<<ZGŋ sss۷ݺusqqAzz:ʕ+hڴK۷ann@dW^FkРRSSy,}₻wΝ;'djj [[[ܻwOr=z#m0c |>777ܹsVk){PTdzDDu7%""c0Ȕ?Z^z駟Ɯ9s wwwDEEI7o=6m @Ht4jwFxx8d2 33Gܾ}cǎFL&?deeaHKKc806mڈdž4 w'Obb{DDrss{);;;޽٘;wH>;++ aaag}{/ԈdJP r\l}6fϞ-0qO{^J۷oGѷo_\\\-Baa!`̘1xakk>}HZ&S0|lقcȑb%|ؾpB666xwT*K?\KKKÇK/aٲePThٲ%}QիQXX<]Vl߲e ʼptȰ}ȑb! }J%Zl ?cǎĥ߿ (?a+Xŋb{PPd2rrr$5 (  Xz-4lÇG~~>%@bbZ瞓TzKyF%ˣ_SSSt:\rEӸqc8++K7ot.k$իbߡCjhrJlٲ]vGi&_DDDDDTL&&VVV ToĤB&>ѡmۊ8W(WΝ+j9:: Z #b=Il+,,n´iʌ5-- r/oT*|DDDDDTԃZۧO{{{BR+%%%󜜜$#Me믿CڵkصkW8E){vF˗/ʹn/^LO/,))wݻI&?#ر#=þ}$oc֬Ye^ӺukJ&4ϟ^zXt)6l}-oյkW@||||x <Ť$+W@w%"ڄ#SDDTq6_|!$3怳30o~+`cXڴ.! 4OA O=%$pÇ Iü0epܴ)BB!99fׯK[ؽ^/<݅Dq(!۷DpC!C~HDDuG9{Vqu^7l|( ?۶ }ƏCo|5!9rr&N4F^{~_JXdf̜)$WskJǸ{7gBӦ Iܹ%@~rBz@qS>|SH(/dQh0EDDuG~"]R"$p萐T=>GzMK #Q-[ =t /YY@TT1P?Iad꯿&&+$OB۶mB  ´gc|<,LKΝ'RNݧ_ ium~uI_GeɓdRگS'a$J5a}R/}ݱcٯo*}-ܼ)$pcشݻQ{#̞-$_܀{$y볈N`2EDDBQz ]Dyi (ez8kWewsJ(޹s9~C?j%0!=hڕ[SṘ/""U8͏ڷFfSv =ԓݯ];T;wJoۥ uգ8 }}$ݿ}{ǏoNuO,-dsݺ׾Hc$":#SDD$pPwoa ,^ L,5zC  }WHF.,=µp !j텇ݺG%ijgvP.ݽF?hPQ#B!L3Խ;м0ORZ9?^8x0`a\P>ظQN8}:-T{p!jLH*$D(0{Lde~~J+~%=[[!!Oˁ!YTHf%$ܿ]9S80]KSJ/K3g #IzB uM pڵڵ hܸg0rǏ/}nX!\@ 7>]HBBDkⲟGDDLU\&_: Ҟ=wsg!ۺQ Xh 1L? >]zMLQab"z<{eOXOF""`2EDDSՖȨ{QDDDDDDdLU)""""" `2EDDDDDTL*Q-K"g0 ?+Lj_ 3>|mKKcF)7FD""2nKDT `׮]ؼy3ۇBS!qzME=}v4i;2N3vDDT }$ғ8{nȸ_x7ft!+GDTý;8t<==Krj5C^~]E:g\?< 1vhDK,-[e˖2+= >-€C{oOז#*,>`njQ]&(#:6GU7[;,LMM?ܜhPuW_}&M2v8TOd3N캽0W?8N$e\Ñ+`R jBO$SExh<1ބK~$cȿ_Oua6v܂`cD~}+)S;rss1|pbɒ%01l:ng fAZ V  y&ՒL=RtjRr?,:{/lG7vvꙃbe"L%%%:u*~zXZrD*br[xbps NDCEv2ȪU[p\)d2znv 1#]RZd fΜ}1*'wߡ{իRSS1719j"^i)6n6 fp17?\Z]#SL Caxt7K7cCu\AA&LBQd۷oG}-[UVtx& h3P=t%=ˏ,©ƿZY#S3bG{&RT+8Z:c@Kx֪a޽;:t5k0"4h֬Y_|0v8Tp;%`H(cBT3cNBZYeɔVDGg@TYcBuѯ_?,Z&ڵk}êUr_^ZaDd<8Y-ϫdRNyU#*L&CcΌ2v(T}\E57"""~z̜9ռ$E鸙fάLƥ01Ecf8Ttf<C&jD'f1vT`ԨQÇ/8vvv Cjj*F"cD̙HY5C!BC/8Y-ϪdlY}<W0}\E)~g駟ƽ{"g2"aTh4Xۮ1m/ĦM//eĈĀrJt4ܻ23jހL%hpHUoFS/ًr rS9lD^=Yӱo7YhhKNv ٱà9Szm~(\NZK.ؽ{7 ?#Gi4DFFSNbL>}jnebQ3ks O^DY:7G3ۤj/qdԆ@L~7[}XIU}s3D[mPs :FԩSѿÇcvf͚8uLHʱw_D ) P3~iW #FZ:`ƱO鐕;ncݘi 5 Sv*='p|왷 `/i*n]HƖ~õPU;w{Au;{~Q?F\0vX' _d=O(oHz <1tQ"BJ\2%pqCӘnl>s`+t6>3#8lwnýE# ;_}~ǢGy:$& ÖFbbu16O@աh;GQn!1"V\dߢ;l2STcã*?&M'NzBK~A(A:ҫ{ i?l$_ZPBFd ^ Y1|s.=b2JJow&6:œe~f^Zty'AZpS9lR.\Josz?],+/ASWfCZCX2lK|sᾂL]ߏծ4ڒCJ*N\ZQ)c 7Rq]P.'wȼΗ'+Fs+Ɋ`Ÿvc)OUDD_{6nܨʏREKK駟Ҷm[B~G 6 #Eſ[_s0~-+TVpf Å g8m5-]ѐjp}lyo=Pw;-e~۴>Xz`Cf>8;2'+}HOO{QS,$Ϟv [;fXg\ΞϷ48K֮)8Cynr8R]QHK]C߽l ~ns<_GHvmb'񉥇I KDɝ5aǛh6%s_*;6~CN-;xp.&ڼc:s&/G%aϐhJ叙~"þoL͘Gjt{r 9).TseEމ( ﹶq'+:VSE9̾6KCG]uB=|NvnoRm4͢c[F$:֍#?9<'W"uǛ04dw>̰™ߏ+=s;?{l}o=_*_MY__?]v~P)QP8o1~k;:mȉű_΁Ϸ5U4M3+ry?`dkBq^1á{q?WS\۸#+zү 6CFAm>rơ{PyٝR*J˙X9bOE3'xvo$?Kء! f& VϮLX9"? o)M O4\X/ Ѧ1m5UU|~[lOcوY9a)sC).Z~ R\r2M @뛦Eœ8^隸 gxЇsJb1!3*K+~h^aH%7'jrc t޻}~'u$gNB6Q!Z݇,|,VbW_քTPTUQ:e .YFeHOѣի%% o[n=#єUt%x_~z^X Ֆ7:|$&6or{KTϮ>dE׭m.o>kwEꉎ^&fTUTƍHH!ȉ vyRWXijѹ Y "Fp;-> TMth7'(U.ފVc`iPk/[Mi3#9H@׻GT5ŵ'~=B -kSNaH؈oɛJm_aHsEM] dףXcReSkXlHKNvfC[r귣Z_E9`he`mZj*TkhFsӐD co:ݳ4诱S3's(#/9iJ,IبJ]~j_evwD{:Z.Ʃ3MrRÒpk״!zchR{\ l06 oPFv/48iG};Y CHDUE}Uihba@󊉩HFğWpTztӖ_GHQN!^Qv ܚ*PB O{{{>cn޼ӧ?~<}ųkVsdRZD mtPRcێ)>r]8'.JY0ʊdDVnw?y js2eE26#cZTUTQSM{yJUĞimT9R9Y{E ֘cSt|f@ZD f h|%g>zmk[*$x9 d")Ck#n-\'vfݔTUoքskNէQaޏ˛;ۑ$+5M |%bS9gW^ct}מ3Ӂ+%2(+;.ajInRW_"xd[G-eY1v1WȹpnJ=N =o U;SVTF$Zhڝ_-eE2 Vlܘb1>~)C`.;EeylA4; J^+'T4̤05 63!J2*TPЮ];ڵk?ՇR ֍/Th&;./ρ2bGqbal}uд7:؛/h5Z[tKY2{x!}v)y15cXzX_|ݧfalxk%}>xtseB.Sxԉ(-k$Jyv/&ʄqk\.$ /(hIc*~=  CvBUYp׽hH5v/r}vnn_߿R`5u5:ޝ]nFM]M!&"ǨXzXsnܫ@GE*C]y=i'ot tfMWY1 9$vvWO+\/y[dś:6wC_-}m>87#?'z*JWv\j`OZ&-^lÍ#׹)7O#hpR;n zNK+7q"ƳT3nk8rn) {qe%CnwOvUί;M~J.//$.`ܕ=56%/%t΍6ۧ9i9[sĻ% , yapEm-]#w7WCB *Tjj8tecY^ mF{V-܋6c†W)ڨk3lK잽w_C]Ct$]=1ql}ǗɈƱ] e]/Ph`lg9'u*Տx}46O_ǂn_Ԙ$2v-._QU^nx ;8{B;?Ώ7!Ą0 >BР`%g/׌tx Jl}[P>!ZiAG[Zط}ݫx|3l+Ok^'n0'Gy*KsCdhk<YYJJuâ*‚oBY?>ϸשX8)R^\F~jjjj8*< hI}*e%bnGv\v +XmEfB)=P&JN-;U$dcnU {Sâ>WB~>v&O;vɓOߜ1UB?CqjLLSV$'޽pWZeBShThseL1m56>T$y$t uWI_1G`{]UM*7n۪P!1Q|Fl~O n?sNJbKaܝ|5]W*ߏ߾||o1}~=o;Ty22y;H4%|yQi8RWu;l"\%/%B}"gWL|V6TU.rrh1q㸵ӆ|#7!C RS]CW…BN-?Jd`Xr Y2=FTw!ՠ8}vQVXJ qmAtNǐEq^1?:fmA]Cₗ 05۴Zg6:OAs|[mnrF|?SD *IǏàAO   ]`hӦ> a~dU+iiЭ8°4$r?!:Z٭8-KPR{@`[/i%˃޽A&456omEbH%|}ݻ! IDAT* <=E3g.uT&&uaȞЫsyVVn"\Тj[}{1/;&{?fp℘gOU;wNCycGF/ڐp1Ç7bcE55U>:%g_W{TVTV>yc&::cKnblLʕ$%Z2҈;k[ BMu .Kk7\Zt971u4ç?R BVlgW":Fz D)'ok[R=B9*f:E׷{NUy%W_ ``s&MRÒǫ/UD EVtX[QV$#94> 7\H\q% ݊;Y\]}PPHFHs=T<3U[\)W9 z&; lď b98eĝjWZSoߌG/¥;/-ϡ{))8;C߬ e2V]L06Ӵ7oFq{|I&tR*+ђR]Y͒afLɦcQYuuã'5-M^\2qgb80/.[,5dϜmK40w'g eª)p~iF4ы'bhšG+e".r>:wԩ5Z#G`@>aWk'L{ 6o)ɓEb0 h 8}}8{6|u55b1v8NH95UWTsłij+aSU%vbQbY O@76lC9V'yiܽ;SwL&>۳fAf'@Py ~%l(>Mrs[N::x\׬F!0i /$?S15o.ǨQ`G0q"Ggscx#<r9llX 뷗Q^3<F^El}IH2nD`ק[0w`H8\.g﹓YC3' S үTP]go,\-)+sH%(ZIqnN-\(+)35< @G5rǡ3TWVSo(-G^SK={Rq vRVAMu KH Y:Nv4X>5j'F6TSH4%XyPxyI9,D\/QSSϞBSW12PN,9LFHr9q6N]EAmrnĀφbj&tC]t tIh7ł ȌJCV(3ty֜RVw?Ȋɤ#[6N[ͅ14To4n\GG/ibnT%2p0]-FN\f΢wSU^ɖ븼iWEnm~*e2zN'Wh"NĎ7Q[31bH9Lޅ:hzˆlIq1|m xMRR~/CzQyS-YR9p\*᝹jl.<>.?$% QB<(v}@ŏ> ꡡbby$16u"C^{`sxtw Z~]YkܡP( qn/sbg:as} :tsEaT7b mOa4bXF_CψAiiklYݼ"?ldJI]ZqT &DŽ ںDX x+F#W7Qh䄍=w?WqhN):L\=~߉C\x '^ lFG#O#;. RZj[s **5%lO@nB6V^xuS`HDZD |iά R'}۶ gOq,#Q OKK;#C^\̽]ԵUD:u&Q ǏѹskV94mh->dC6ڢeꮻtI^Eߚw*EG FرBT;+?U%%"W~ALd rLZHr|yVTl&%Djx2NTWRS]ŞՒs7qopD xd̜ͱ !16 b<#bd9cN`2=QTKJX/2.|f\|NWa}[.61 '浝ET5"R;"ʫPP]#6r ._Oïo ZzqX%ku3~A:ccoF!ђY;"mccvRU7c#Z~Эk@FT@YϽ' 8!gDHC+#sɊP0 Ԃo]tSD\ЂĝFo~=֟a'9A~5Bqq|{e/F^]B] єPV$袃ݏ5?βm.ddƩTUTxQ(5!}ܺse%s xo= j/TTpE ƌ^{T4/)aĉ155 h{qq9 r {S7N+W^+|/YY ~~B.Vȑu .04 j=^ _yqpƧ *.wmdL;XY{P6n|(>׮Kuk@xnެS]Ls=xzbb"jڍMc`?XcF䌖"'+22Ć^rxy_³0N^FBkKGTϬ<7ƴzU<;16"J"͜hI5%rW#z͇¥7#?%Shq:]ȎoGqjᢸgdKqnJrhhhbhcLXnBs02Rs\r@u9A1€2k[LM=WH Of5rʫȊI5mwK+7ή:|/PJ wksjj\.ɜcjT;l}nXz<7}f5559Cj9DSW ׶ȫkjڑ?EC@wbfG#r2.u{#[qL.1w;TUTۻg( ZLMRSȄS0w&cxk *dɊk}"fMq3Ү&FFFd͆DXKw+ڌˋ')bw5԰$͚v܊ɠȶX?~5XA>J2h ;!;Шl6N!'a\UP> ZQ܀ǵ;v ZJJė5]U=1@U= u>m5#`@3űDKPE"ڶN"P)Qбh91 `_X}i{A{/&xz||g._{B]eщ#GB(ATTU^k֬0 ߯;_w{ aK nXzx!-N~oTP!rl>3nDG"ݵSa  ȑ)S`hmi8"6jW.Xfg߿-ż0ilh$@ZNYK׻58WnBgg+I4%Ֆ*YY1UI.TG'۴>U%m0&JR5u548XoETЫR]#]:OQ04۽ТY1TxVոjhh~zzHHRR*#6ܬcGf2c{kJǨU+L ~(LŻ UiӠI.A6mJ{hj A 55p;Wxx}c>],>XaybP?DoP]h!~ nxJ5SB'>^k(+3'N5텚]-. _󒥥0޽EWcjTaH-\(B0g[o9I/7'l@1̚% O<*..b.7Oxzŋߟq6ئSm?K/ uF{_]X a)*%>Bf/jkt"#s֠PSbAѤ C`چyGԩ(5K N޸lY4Hxł%,Lχ}Q1jP]chDXܹ#$77!@l$D(VW?YA˅M"7AlIb|wW䚘P^~~OSfaPROA͉PPC"jrb ȣ--> yHΨ4/ VYFe5hkTSկ4zU(̕/߲^SV&vz}w++}A<0|0DEXl05~^TF^"6.,1{WR0>YcvϢanCyI9:??^JPSC>bwIf27D С ^!u}8{I drdIۜ?pf&ZDKJ#c&y/2 G%;.+.*sx>y |pn::|~6i) ù~04$()$xD.'E`t "OHrjjruͩ!l%*jkHGҥx,4GO OVzHMŅcȉFn-[NW#p$h$N-]^l['.qfN em~}hJHD[J8zcKT5*J ܠȰG㶗 nFL[!jhM8Ũ$ &~!ff"a+V$ @H7lIq>i?_~H]b*ׯt>xgP&=G'ܭZiq[\*}xNqfmeOF.EUE1Ǣ"7)/'5<7qJM$L fGCSB½dHꓴӁ22k)-)eɎ"*ANv+/b N|[w8mt ;.裑H4%x5O@xIə*/)үRZPUS˴nr/chm bk), *,$Jq80_T=k+#05,$P"Q E/|GI~1M GB!|,N{Z%{yYbU +!Sc BG(N^α!Xۨ&7155GM]5DG-%/)m3EAɖFCSª K11΄oBELwqk5Jldd'͉"=*JS?ݹq ֖3fǥBŝ Xb:C7;:%j^2D%%b!tCf?bjjjXzX K>6e71wdӻkɊMVLN-\ȈJ/SS]CQv!M`bAYq|DKJ~J.[k?^IqN!N-\(+VzN0PYV! Bv-l|g5r pU44%ƻ?v&ğe[+1s2/:B E? WK2&_·O N$]!Љ45r~ƳTWV#ՖRQZNv\55'q+6 c;)aIXzXSU@aV;$R tt3 c(/F%eL9F|?nko!v5G߃ew&Rxx5%dFgPQRo@*TX[?9Q '푺##1jiCк[&;; K˖-Yd U"˙>}:ϟg IDATկQWBO)_xtJYŹEJ%djIH`:lyo=M;{s'6nDFG#OVlM BFŽ'݃9UVCIVQ節s "ή>_ ZnOI]H&j%1dHG' , ī/6>vlzw ]})usWKBDސ#X7eY jRMd׶Gp #ۢJ=O<7ޔ+tڛ>e9 I S'sL8{.%)OSÓﴈ샜ȌJfٛ9[`@FT?NРx1C[*1v٫kŷ le%bxk:MNݱljZO&/F:o5ԩA.. -"_a@_Oű55TUExGUmIg>"|w(| *LL8eJ.]bԩ*JJ^~eٴiZO֛i!Qk)aI-rŪ5NhCa8< f.N) %֤hS1=QT>wJ)-"Y)MC#B/o+SÓ p$+& 7!~PSUMUY%-]1u2igoOaǢPSSS-ZrD<-//Ğ9X5y*"Ϗ1(<< #q v%r"MK_iۉIŇ'%, '[x!4v| ׶ۙ`jɭ\X.o NkelYy#ԫ)4G'/LUR$*M ayŪ5Www&v%\$efHna^KwkB^ 94C3gb2ne疮$&`a&6Ƥ^MVR :sWJ~ͽ c_GpPo͢EPUQhJ6F(EFdwL(uMM,ݭ12"u;ˎԢ odKq]K[1⸝G86sZyn3#;?L8C6sr^Lx{f 6ƶu%4n˖ʲJ*dw4צ 6TWalAD\_q}TU* ˴dwJ;3g ڌ툑u≢1{6amjIu8abA C.m}7nӦMSzm~\27\I3^&F8u5?>KԹ,w/ƪ+qm#_j긷$|W("1s2 @Gw^FC <:z!Ֆ̙Winsi9d2yǹ5ȼA׷{5HsמHg7_,ܬAv!jWQ_ %S  Аhp.MM\V68|A_&b[_;"E`iKTHwaQ\_ǿ,ibET {{-آ17FcݨI4{PAL\\1K9񁝽3;Ι۔ʩ4\{NgvƔ+&d)!^x)=?l~9uSjU_100`„ lڴ+@B^0Olx,~;Ƶ=ޤfIpM/;Bd`fx.9?OfZC+9j^x}%GR9r;8Iq/|s}~5C<)'W*OYae_N,?Bjs5RSzC.oaeq;82Ww]{eW{lSzх;%+WRJ4oޜ7$jƏϙ3gؿ?VY(xz?y-f`Pc<0O '59ZMtpIqIŦxBБBk_?cGZM'iX:VgpO) IW{ND?"%!\zEl3f*).8@zq,ȺqiX*m޿1%.:}R)۸]V?\O\|˾Z՟.e1$&e_xnɳh9]N꿄kG¹mx#ޜ勱M&R]u+ū;cbǒs93k)ZUJ03i$iܸ1۶mdɒoI(%%biiɦMd )QlOs-hQȒ*m^0[}%7DJb ǮŹv)aĎXd״-XwTv\q"֜$oadfLbL%땦쟵pP瞻 jb`}|l!je_SΣ" ,2u]LRl"J}r@V#*(ذr(́SB1|`PcjϹ6Kn$$o@;m8*ŽdY[](V;A\vIMYIș,*`Tؤ7GGt*U$&D_?Đu#Va_сDm88qaYNijh, Y{W }u6Ԫ+l]2I #/>{'=-]cbyNs öt{6:LNH :α ===jtCZJ~7Wt3[clnBjNJ<᱘T֧yG mݻ!wx#wT2>v!TlQ8㒈"#Ga^BkEmfQ aoPD.ԣG+F6mXjuuHBb]tAL2E<"=-?E-m۾`F)t"]h01X" 5>?tĮLQ/=E!K>_ͦfU==νSuE=z.RC >kgM!?kbhb@wMB*Q^~TlYN(TD-C7֏-Ҫ+^0Jd,viw?)go*veɌ{He(T0 `Pc5k X5vB".bi)JpOs59Fջ*k ^xuk^ٽ(dI}ݻ;#ow(җVCN"k4hЀ]v1l0l٢pӧ4mڔ]J"%>d M^yٗ&#[IZJ7\$%PՉ;XW<摷veku)W1g59IjT@0njS~UNSsLR;DBt<+z-†3\UuM&x5ghbc$Dꄃn!1L80oltyū;SG]Sӵ[~iOހ< ʣ|uD##c6]9Ř'-9+{Za^Ђ0ߧ˟^4(C=Ep՟;nB幾כЇO?ӷvZgeZ:*ɕG֩ uéVI.OHKM#%1")QZU(,0ʏ5tɑX\0kAo. ...;v;?ptBұcGON۶m8ii~F&^B9 UËTjUo?mgJ_E002 ."|6,﹐:c]4?'V3JI$ ZPƭV"59Tbb(^÷aI_HǪ5J$NԊ5#CO鵔 74 208^ꍩ)CPOG/fCL3 põcMVO,l=)6̘ӫڱ&5xQG/[K'xoW/5%_|NUz*Q8lf#:EK] ']JO]*\ʖiw^۰+S%gcN=88z*jtCzZ:KmRmi79K[Sй0NҔ qUC)B%m5ߕg/fCCɩTi[T_}7/Pӧ+W]N^\x*Dy8(WQ_#l hZxd tCC9jR{ tgW . \z|ߕO&csdLM277\↳XOŖU4-Z_z.zce$]VvvCVᇮ1d9veRv)9c^R ʰ\J-Mwlu$>ΚkkZǑ#4*_#WsMUvՙDM(IW˺ǡjq1 )VcC\;dQ123[9hN>s :+3K.쬱-]?t.΢OԨnq)U|ppc]6XסIIKau;NrZf\v7bnnDpaK6oLrtzNV;:s!i)9Gd|8}IdXUY;>L祾`u9fJ+;+~ɛ \kvJ[Cy3gΤcǎ4i҄ ]$r3qD8 jIPۏ?o&TrV<,^gjJ$'%r惱q,i wEp^VV_JO>}:͚5ڵ`f޼y,[#G`o/MM#: !xZkW,76 kKV*pUtÚ7oΆ իu8"QL0'N~uE\W#21gѺEGS˦G+o{6]vfaoA,5=MkHMJbjtTT2uTVXpD.Ba˖-:$XXP…[Wubbxq65?J}/Ӓj˩oUM;ǨRχɁF1ї =-Z#GsN&Mcƞ'..v’%KP+`mŊ>j:KB .駟|rׯpD6tQFŦM(_yԍ ؏hzC~g]$rv\]倳̪<%~$BחΝ;3qDwpD6qFf͚Ν;)Vy\:9wf1g:p,PK`o툁P*<.(_Er,-+*9$I"s4oޜ': ̟?m۶sNYCJd+\+Ħ`鄩C9H:g$&P֪խkܶ9]4.I"JNNfȐ!xb }\-ćM#3{V4iwaq-*qiqE *TpeE Q˦$SB}w?M6aan,F2n"cT )-B__C&5d>!;zI&M |?{Ξ/[D .]*B*@~={6-[ʕ+Gd05kF6mBL!b˖-AG!|2ad'..,gǎO>lڴI)!&$B\Ԕ-[FnHLLuH <97mرcٿ?*TadB!^$ɔBR*9sNf uH-;v 6p9.\ȯʑ#G(VB"3%܈#prriӦlڴҥK:ar IDAT$'O`ȑ4k֌;wpLMMuBI2%y@֭)Z(;wf4lP!WعsEۛX\"BdSO!WWWW_}uxIzz:SLv=7nÇe"!Ȇ$B<#G3c ]#^n:nܸi:J!VB9 >Kj u `H@@f=d8::.8!J)!z-]=Iu(yY\T*ZlСCiӦ M!kH2%yR Uׁ$[[u4,,yWUDCI&I+B L !DTJ -Z@|*֭S(T(yW+Cf0g;Z !b2Bw 'Lx6oD XH4٨}M!;B!_n0aB7FEcWVg]u9NV!ٞL !pll`rT BM(XPX>8wb"- ۷CPrA !BϞ>ApTs炻UΫW3m U2.k`xL{Ghbݳ"!$SB!>, ?e[PTZHIF@It܁a * mSZw7װ~=+ɓRU+8~w2g*+OeC1%Q{me\% ~ #FzۄB>JPVʶ֭@6 vRZ7VM2qqnLV-P RZB)L !`*%Qʟ_V8;+Aor|YӧŊ|z2>ń) $V&M ~9%}{|NN1=1K!D!P!pUZIu;vdg;r"#jزEvW٢D}p!c sC( \e<0k2~E tI9s6:6!L !x˖޽ʴJP*dud;VIjՂ}%8X7psU(LALRQ1cEXF2=Ν0gszzJZ˖J :(Ӭ߿ySY;/`Z(_^uCB!r=E7BK/w15U]9++Kҿ-dahQguHyc,ZH!xKO!r8Μ9'\x-B:2o{>_||<~),Z###]&5$B\ 2ӧOkYXXL+^\LSϘ1}yftB7n~BK*U+WRzW>l2lQ1yd&N^^^G!ĿdJ!r%Kc 4x߾}ٳoM6lݺAvZ]#$B\ˋO>=zh9;;$9Dٲe9~8֭cܸq:$!/dJ!rNʈ#?X`7F__uaaa[fϞ=T*ZnMTTCBI"~:GRq)*V@ڵ:u*uq]T*f͚E~pssG! !@fB\#--ٳgi&VXi ///\]]a ˳ΥKׯ?#m۶ʰBL !D.p]܈ٳFZșWѣG={6ӧOu8BI2%9Zft҅~YfallDիWK"%/^Ǐs BrrCB\O)!BCCܹ3vɓ2Wffflܸggg<<<uHBI2%ضmhԨݺucƍ(P@!`L8tBZ2fJ!H ={˱uH"}6ݻwgG!D#-SB۷]$lٲ?~u1n8tB*2%DLL cǎח~ GGG]$rtk]Ɔ uHB+H˔Bdԭ[*UpAIR5k ]$L !%$$0i$.]ի)UCܥKׯ? )tN$]!r===UƺȡӤD&G: èP`戉Cs1l0رcQ8BBBڵ+-Zou8" I al}1I &5=Ua ;S;\ѵX7>)3}GO ځWE.E]Z“15otӒ(eQ WjZW]s*YUuh"INNfڴi=zUVQB]$dFIdd$k֬!_|Ir/k(iBj8wƬ rM']Lb4a< @/YWgn?j:PA'PƶE {+ tȁ%FG@O"|΃0ux`:?qhύRRS]KV}NolLŧ3Ʒ,{2mhQ*C]%r`6^ZMDL(-mL`"B_`> f[ǩ̜9۷rJU ?Yx1u޲ Lh)?a9=qng;Sʎ>\!\q#V]3l6vӓit>{1Y)e 1m*ž̻;G!\֭[4hЀXΞ=+vطoǏgѢEG11t<W m$R"[92&0ӏRgKܘL_Oe1]#N|`SoLJDzz:?3ݻwgܹOKKM/^Ǐs BrrC9w7RҎV;y }jlڕedp!?X€:#5J|t% y<׍x]#r?ϟ^zI7233cƍ8;;AppC9ԍx:32}垌2,+$SQ)Q Ї.(pDբl{MwuC9ZfŴmۖiӦpBd*L<'ၗW>~P"Pfa4)JvjD>SKfLzM2u40fqru("ӣ}lxj," yxyyq9<<Z-c_dgew,;~HrJB['ufiwW>FZJZ֟T*j|%YŲe8u+3f 9*]%',9YrskO18cI?FYb'?z̅g Ͳ:bCBzQ_(˺g@>7gF}Va<Xc)A~kz-YYvdzz/-q b7ǮLQnOGoǷqSYV;Wu]P9^.OD@8t5-(ݨ ŇpZJgYlB!rkkkΝ?̉'ذa[l!<<___e2 tU3w:עNv)B(^ Q'W% [fY,ER\s %!vT2$&}^H1BV~.s]2ϜC])U4<8s75=bۤ 4GOt5m n5'U*bM'\pM2w܆5dJ!J;>|׳i&fΜ]o>?N`UĚ!Fb`X3:Ҕ#5)008OHKIP)[쬵HO")6 –$'pV*Q8S#=-JEaeYu'-u)hL-M145"AO@Jb Iq>cח,`3ƮLQ&i)i^{]rְ\LEģ0m%=%18@pWaQ(Ct+ذ)\6Su> ĘD+:`hyиXoa[(_wR\O|DA8h7{|= lK/[.er'p]>Ki9,IwcB/XblK/> KVJTP$hޯ%F>sbc ]٢~1Oaln (Q&aB_YglX &A'i\==WB!;ZlI˖-Y|9˖-cJj1z*ko赐gY&@rB[vN;067!5)ԤT mBŃS) ~d7x k.?ugazbbXqO})M 6,csz.@4eV^&&LAYu]gzOW^C=w-VO9/&Sf8L-M 6±Z,FvZg.n<˓[(dIY\[2˧ǨtՋ4vLȁؐld؆Q8V& ;eQt)i}֔ kɎr]2>TxL);/ d, S"b)Z6hb&2p܇ӫ< (T1&ise: MHILO255X~~dIvɠ?Gh^̓l5JMx Ǘᯑ7PN2a ̺S~9{8ayy=Đ tעr? %8 F&>4JSci8ăr IqIXY3b8ke5=~뻈!L/?}4'R+?-5fmsn݂@04šDuGO.^~E@?. ]3j(߿,- nބ 03ggpp;wFn\vOn߆i\ikxL' \R 0|l WXuū&rIqx.9#S(^ݙssfjG4v-Cq4:3l5Oscsb>9tꗦp+.Qs-]"Qy:/2U.?_^i ر~<8{5(QE|TlY;ƱO Uƴ |ʨUbb>Kshm' csRRIIdމZqidQTnJ(6]n~gVîi[tgTT+M*6R9/fCV?}&IZeu#X%𿻿`bi X'W7fcjeʪ3յkf%qki<9?-kKOP35f5#V2n;2չcfʢ2ono߈íy7ߍ{'o3Lά9NLǞqnZg$?pXZͲ󉏎gԞ _Aןp~L߮u Ĵk?B܇7cWZ$fRkWf?^7GAB,K~D~=+vGǵulA+92|>[iI=U *T6mOdIp|[A\l g.߇e˔/!Dw"TU@ǎдrW/r oa_غ{ڕaGo_#W3xvőQyLL敱q,@J])QVi5. bdfLqm006$$p]2Чn߆TP#S5$6V}#LiY2b7GcT\2ЧF:Y/SLv$s˂CYuXYS񓪨Uj 8 $_{" W'ZOEaKdet5~E ѵFf*iK( #77Au7q\2cYS+rd> MpP?hoP@NVS-MlŲ }͗mRFC=@6ո7cKcjeF_ivRdį%떦VwUѐ&狤oωeG72֘nsruG4„4Wtͷ%_z/HVUh>5wDrB ykRNVyj (hA_J׾[7Ç3ʤg֭R/9:tPZCa IDATN57`r119٫~ow(l4(vz*=jtCnu}­C9 FN|dmu~>e#N֚Z м污E%橲!iZ+Q+jm2f@c4#µ9.Y|a$$k S(Q?k/QPŎ ifO˹,3zZݜ^hOMNA猱b>yQ_(I dyZϩ yz/Xk7u@rY2.zq]qK[{>wck9)C Jg~?y1eˣhti QTP$ۿWyg^"S|_=Dz^SRv;/hzz]s!uOzZ&A{OТ*qDFhPђ2Ч[9޷a  2&lkE>М̞в$R/KK]Zl<<<2. {J lݪ~< uk ++[/mkknJwºu-Lb" ǎJZiw9Rb.]Zy>* WZ+Cڭk }RjUh\Src7Df(^͉ _}W_{lz=sv#~j] \r#xugz{y!Voy*W&SϙXRe*B 1e>Gov}?dmOqDRv_e^z҃*%v2Gc^'웹ˏdrjR ff_kl@+'W}m2\|gkK{.q>L 9# HKQE!Kʸ*WƽT>zTIZTbώj좵J"쵆i< ")} |3E)=-Gn`mn:A^w5L`f  :FO#7[_"oOx*C ^z_B4Ͷ[^Ԉذm]SaPϚrr `MyR$=޸v(Q^u*eO g@ћt>|, HMԔ+R( 4Ϛjk0}$Iy4﹘+om{GÆʝSOETIIP-vwFw[Jֿb/ NeLM߯@I>%XÜ9u|JR5m̜"jn8r2|H9?qB鶨V/ḋ6$x{У,_$V+.^*Y0db$bp$lڤsMKunڤL\sgJ˒c1Ly})=yRI21P9 l^y[Dv܂\W}`E}Ϟ:^}1)݉rFa\қ}K{gqcuGor5\l/Ӹ|5@14 eqߔV ZPe.l8C?< Z]HIЌ=~žn CCjtytۻY2HjWM,*Zo'N+jhVKJQ{Ldq~ri8 jrsJ<}_uq.#?9윿Ă{$mȐ-aޘpq=G}O.Ej+BOs`{GRozL[6CzU[?ݨڤgvi61g9dv|K;KT7[mG~{s3b*\^$9< 4jy5FJ$+ FTENMek{{Oҥj ٽWe&Pf峳SI*͙4I=vq:;P#Cݐ FAiJx_fmiBjN, ~}\>UUϜ=Of:j gRRѫlֿuwbcET55jJʂ5+gqZK`]:Nid;d>o!U=mڶzbbaLsί14̈́ Ys@g1`pr2sx_M<;EӮ6~7l6ozNQ7o&&|p&(9nvf6WF3f?3i.v2~ 7ʀ,n\{VWG15crTLuy&jкNz8TT痙:?+} ƻsYc s~⒩'/;=~be8UsАKX08ޞ6, fT̬sh=Gd8x8q.ӥAՈV~'Rnӷu)9xl NZ k]Y7}%;$+-*x93ۈ5N^AVZ&v#g`وol7 K{+/X:o_kNͷ7΄l sFՈś".(cQ:ǵ/SV;I^+_і,Tb#G~>oeS8/QPZ,#Fwjəf2֭?X ~4d}/АkFf_^3|S=_w*mpa[2wtTBCu (~.PύwLjFlD3<&N=eKi.̆ w(5irfgK/~D7_m_k/w= ;+Vվ^9Q τ1Dmj(Hb4d&;yP7FF.{w gfr14T#>/udJegݼY0SUn,XWu?/z\QJ T9u  B%&&] l|djB|VOU6o=gQ} z|._#J\ߚ=eX$SUtK7;*6@uzޮH3P`I?Ezx4H/ڀrffmN"-׋^~y[ݮQNl\c~E_sWy%S2cScm_Wz3oN#֎4EKmv~<Wdzq/GbǸVNOjT󘵣u;146fkY9|֎}C(ls4*05U;&5E?.MEP* 33MO?W%;{czÇUg}{%5[TΝ0m7W +lr}{Ӫs_ۃ_ɩZIaBMcm{Sc^xXQ?7΃s?}犾z֭X$y!ʳ'2aڬbԺleܹ;ΰX[pbb(JzBGgb~Y%5f@5ocUUȆFFToEr9xP3ajYTRܡujaV (׎WƎU{L٩y}~7wWǹK kk~ӭo*D0c:Gܕlb4{̮otݶ 㜜OZZ΂qkѢڴBTsBwO\ ?4=G ([Ӻ9"w۵RM j"oj5Z .Ąx;;zYC㬬BɓՍI *tI'_Jl|}U)*R#`*Gk ^_j_~JU|qvV-kք%K}{W!CՅ+/OB<~..j饗zHNVMc ǜ3GnkރԱ*c*̛: fnW=̄/ Ғ &&<52sf۲83+S!:MƂ^'*]-eJCrL E=B~ 7Άi |2>+012 xDWCאFT͜LR>JO&~BSR֥ *NV_Nnj|ejP5koU7C*I-\bdUbv޻_ZjVV7<DT#XR|.Diq fSvNJR<;J o]V̜f z>/n0婙+[Μf owkn‰9-k|#~7z[%2%RVafͷR;=4.8C~ϖ߽8k' _olvnv{v?GrA;H/e~BreP3hʕH e} : 1Ç3(*72RM6<{`nnX[?MNmC}SsՠZ{ 5]RVVe# WuT1xC%Rk֤ΡCFGSwZ +h^̙{:^mBp˯t5 f';MNentu1 126֪̈́_NYs38a%jz6dw;~ f& Y4JW jNVZ&;ڂ1Bx#՚zh..9 ̘-IiL^HOJgu1ns\2m&tz3oB]Y_lh65~ifBŦD]:߫S!>1SK34S!x=[Tm$-jMszq|Lי.jw}BZ|*#bۿ܌ύk{ݪ$Dij."΅դ5Zdpv%)&#۰Ñ * xi ZuHѨ=M͐!뫓HD]ĵV%O{vj=K7zׇ $&1j썹9?[H^ fl%12Y=߽_uKU8;?R:~_nƹ+/|> ƬKKqt!nĚNvzCٳh;fx6F_L: f;BfJ6ˌX6\ާK;t֎QN!96壿:7g}${s z6_^qfTkꍕ57N׺M֪5ة~&ʊ'nfjKHJjE:L:ۨ"5~up>Rin;/;aT|%SS棐Ƣ_2u%>ǞE9Q̬̩ӵ-GE,d9٥Ѓ+Im[t:5296V[wwWBQZ||TcHXyzD2S3 'VBNvn{-Uc` lv0#viɆ '\݅[qdgdZї100 ME[ =Lɝl\L,LGޮD] /'O;Jp"Άaj7{@>MڗݾW ;Bߏrqy~dgalnfN՜qL`l]88'8#104KD^g>ו(~ !yyfbbaz{D_mu1PWuA&deblj v>cD_i{f2S3~*Sߢބ 7T͔֜u D'vtN=&(G(4yK˒cTzR:Q"th4D^rSKSm \P07;shq des5JA(TٹD_YIY,p %/ow~`c4/G1LRn}SPfg5ćT&Tu"|6l6m01')*_#r$+Hj &5C{s5goF8tU~:)o>_P7CIJ?b3w5\Tzf6tW9t0|3OSloMDZγ8Vub-TmEbdhCCN@WWΝKƄB{gkt5r<?SW%i4f :r3jˑx6ѵ:r-kq3$}ģ'7i2PwAxW K0UmCS9_IKHcg'՚zn*vcX;Zy>J~Un7gkFZD_=W`o}ZAx6)9vOŐ=_AjMMFr:6`jafVf4М*l7"%9a'CN.9kJ{|cM_ImHBf5IN$Z4.046ħ]mR =̾wR߃\݅LwhyD%S!ǃڰ`ᩡ!9z"-jC}2PgCWLܶxVM&m|K;KVMXµr3 HOe{n Ʀl7B^Ҟ8y9yFb5yyjĥ:oeF.OfJ~?2DZ8y.jY/g6& .HzbUVeq bu#R3<@Bwo@pll|<|@ЭaR3IFƀ7cI.y! GHrl[_`dl~:MYѵ8_bٰV~ؠ^l߾,K4zsz"+@f5ZW e}ЯGi86mD&M9r$! !3Cc#ƬMz S*X / O.n1S-;L/ x#oL:LðvҭhBSm5Ҡ o:%U{fsiyrr\C#C~ ;218P B-K;8d`s:s>8z>NXNǭBĹp9Ʀ^늛o%nW(TCsi͌\5.y k'bn>]u)7ʤĩ)PUV֭\ڜK{kG,/G|X+ Z_0ϊ}4|Rt"v%12^Kyz5~vdl wϵ\\߾vUV#=) [ `x!>k-.8l/{k\[?3f$lu)Xl~l/fLeȢQ?}}~z9‘#G:u*cԨQn' IkjI&T^=~}RW;c> }]hcScjޗn  _Ĉg:y _a5 Q3evn;_F V:_{7;LHK&.(ëxmBzdA310Ɇ*Un?zK IOb5g0ڝp7cqPo)X9XgCh7sZB'|IiiiX{ooo-[x rss1b6;QRɲ2179;2t#l̬͙}f,IlRBO̔FɆ|5\y6mIOJǧznaWɞ>ǴcAyT[ݑ4ЌE}ڜa?kG:,>3+sjH? Wq(?+-~X9Xt{$;3~Yᵮ,|s 67^֠nφ,>K{k^0l]*hGk #99lK|Mݞ146&qIS1PL^KM7{iPv=SK3jĭЛhQΨڜ}ыMn? _}G,*XQ K;K^ʇ/ Q[XS̬V/"_Tpշ2#/'#?dɰ{ k'6WθqX`UTO>gϞwy_zz:nݺXhA_"vuJMg?}"щ7h#;pk7=IN;Xy? Eة,fP?]Ԝ0vm?1LJFEl^ࢢŒzz$'R] vESX<իcҤI8rsg}Ds $|=22}"oƲF+hLٚiSk婑Ù'9 mw(D*35f֮G!< 7TX7xSSS,Y¨Q8r䈞#ш}?I&;QԵGT }!9DFR .d CSBnI2XT/~+WfVfұTܺJ#C#SN͍q1`6lѣ?ʕ+tؑ9s0`}#ʠ K &>~B_gxΠdv:9w8)ĖMEECFFFo;Rŋ㉕AzĴi]vxzz>(%>f۶mXZZؼ-+$^G^,l#m~'e6ʷ?vo}S 'n뉛;fiM<Fí8n$ ]\2'8xbbbիÇgܸqGRRRӧڵcƌG<\㏈ \ILXz(I9I3d愻ѣR/LRTb)T={w8BQn$!3###.\?;w&!!A!ٳgڵ+ ,DJ!$SBQF̘1cҶm[BBBx 8pfZjpܑ2?!(c˄ Xr%w8 i&~m6l؀BrI)!(.\/ȼyҥO˗pB6n܈Br !x|}}پ};zƍ=Z!'g}Ɩ-[رc666G!5B2,%%ӠA>}#1FÌ3~:VL! !D' ( ?$::aÆD9ȑ#IJJbڵH !D)dJ!8###{|||ҥ I#www[ OB)Brdʕ|lذwwt< T"%e ,,bNHHW^ L8\A!nL !D9{n&Nի[nRK-2Q6@ `^獈gϞL>مB !DڵkC?pDΝcɒ%l߾M61a6mڄBQRBLNpqq /v-;$q;C^^ӧO'::ŋ}vB;0h4}![DggE/Bh(@*(mܹ~jp15j}ٙ3gH"%e !SʱR%xTTHhЭt 5k*=Pn.t wnU,MǏ#ޯk֬Y:ЩS'lBnn#BdJ!D<Μ@HH4zFMf5DDyˑX'O2gvv)TȚ)!+- ɓSǫWwUk4/CH}ܩCTlۦf,-aChF:5??ؾ]%o=z#?Pt)xݿko_k8zT͘9:B]3g|nkkСC7nk~BQ2L !(]*q:srr\J+V+WǍdA\y>ƌ/8Ǽy*!uK%_5/[,H&LEԱ*o AjUyF%..omۖ7@oqhذ!ƍcXYY=k !(ҀB!R@ Q|<1o_hZ%4*S\ ^x[ >L%?&&_V/l*2vvc{@@_}@LJ / |kWw #__Zټy: (B}fJ!D{ UN׹j0py{$%[.kkU~WLXHЪUA"tN0lܨdL)PI22=t]$BrHB<t)ڥڊ*2ޯ}M?T5dgrZ3%>f]''`ֶ-l`yyB<L !x U}Ӧ{'S['`nUcll<ssQ%jEYZ7%23n|!xjH2%Qm猌TDNVIҘ1*عS*=VɺVݺp.U~~j4 !x*Ț)!+5UGxQ%5vuHYY!ĪS'XN%//D[7%KT~ҝ)mUW`Pz9;\0>֮-ؗGUVQEhjf񠉜B$SB!J)x{v澾`kfy~P]Mnnsgk|S,|[[Dj.nGJW47u0|X5HNVKUK[$ х)H[*8EF=l5TJ*6nP%+kTeo_.H !($B#OdJ!/)B!BdJ!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!BdJ!b} wB!JX!;pw O9sddS!+F#B!Bd0L!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!BdJ!B!J@)!B!(IB!$B!B)LX 0IENDB`DefaultAppLifeCycleBindings.svg000066400000000000000000000770211261716203600362600ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-files image/svg+xml G incoming_app command_deploy state_deployed Deploying Undeployed Undeploying Incoming WebApp Deployed Default App LifeCycle Bindings Starting Stopping Started StandardUndeployer Removes ContextsRemoves HandlersFrees Up Resources StandardStopper Disables Incoming Requests StandardStarter Allows Incoming Requests StandardDeployer Allocates ResourcesCreates ContextsCreates / Adds Handlers DeploymentManager.png000066400000000000000000000631661261716203600344020ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-filesPNG  IHDR{a1sBIT|d pHYs''MTtEXtSoftwarewww.inkscape.org< IDATxwX{bAX6,iS)&Dh]{#Vl@R0`n5<>gΜf9sFV!Z@G!$-!ZCB!IK!֐%Bki:!INt?;}f(De$IK tϿģ=q$''333̞8rMϟ;L)B֠ApvvsssLMM^:1{1g>3;LMM+Fǎر#/^t8BQ}۷hyݻwILL`ĉ;wDz_|ׄPNzѣG  iӦ̝;We 2vXڴiC>|8qqqҫW/BCC}ϼy W^`ԩ2uTϟO ܹs3l07n'|BQQҎZfܹ???t֭[>cǎG^^֭+Ӟ ?#))) 25kpU222V~~> "99=zp'55իWмys$L2 7n̐!CrM~mbcckkkeFzz:{:~8)))L>J}Y ϰ!33S9Ç( ח֭믿\___ny<<)_wtO~FXBTjO4ﯔ%%%)fj׮?[l@GGGXi*`l+W={ssseHѣJ*-kѢ7|CVVTJ7.s]pE$cnn^JϣPe[iۥ]tt$i=_;!m˔:j^>G sw<{'f&ry:Pgpg:NYX wi7qc/DE{줕N||>>Hqa!j> HGWJ5O8S8l6DaJQA>I'ڵ5j%׈ڴi6055AJ;EGGLqq1}ڵPK͚5hժ 6ѣԯ_+W)7n...ʶ҄TDbbbS^=T*J jԨm8w\vJAWWWAi]]] i޼r]L<T*zF=}bIin. GKѯfNT승DLmȍûQw,\Vc:Ñ4>KCꓛJqQ!gaԶ_Ia^$qO@teP GSOv2ԧ OӲdaoo=UVUfa۶m,^#Gt=iիW%K0k,rssԩ2'x$DČz}<Rp|tVMc^w٘I4}sv~0KWP*mB6lHÆ v}}}}F#4iԩS144O?-Wnbb?=7oEOOܬcǖy]NԩSlVTӇ>}<صkצve ,WW!#`lewΟ#+[OƳU2u-ݪ1bO1'}& GCtcF\ޱ]j>z A}3߻ ߰_!;6p*[rez^/ЊKDzʥM@=ݺZxj*͂'sĠôihݺS?!_q~J[6zYmi<[gXկ~}_ޙ_oޡ'7,&ly.ϖ(., fv}2F/~֝$7klU򳡹%60$/3oGHZjZY9gOfͰy|!* e<{ zyAȘLlՅ}G5K|`SJWچ|{6).*DGWOɯ1 GOU'( )ǎS&;dggӡCeɓ'gT*2"ٳڮH&"";;;Fp3Jwj)L>!;y9FV87@C/q4e߹?Q\X7ahi;:z׬ρo.h6f;J^VHv|!y!*H]%KJrr2zqƱj*RSSIMMSO?eʔ)@}`Ba?R00J6;bY2: QTvmU:yi܎9N+8nH31Vqez}ɧt &v4}c"%Tkӝ2qoWMxgH>`bJ ޥ'3k+ndeC6_CڃCeѢESPP3x8y$PPP>YYYpu6nիWϏ-ZPn]={p<==W+V 77(3鲲Xf .\ ==UҢE 100Pںy& IIItޝ"=c"""p  (YΝ;ѽ{w"##| B;GM㔥ZD3g($ZnMxx8|u]^*##~ӕJ,ӷo20k,.^ȝ;wޘJBRdey777̔֠ApppPeee% Bه[Ξ=/2 bҒ FFFڲj*GoħV~:>>>* w-sWpttTnTB* J*L,Xnݺ>sBCCٶm5jԠo߾riv +)1:|777lmmٳgAAA̛7[[[zmvΝ+Y1WY}נAt x"/^ʊ@ZlY&}Gll2Y̯ҵ ֭2Dhee͛7 חK.)Ê])=WJ&] !Ŀ?i"ׯСCiܸ17oJ>|K@uqqeXtEh???eB={6 ] >+11d]ƴipww֖{)3ydZqq1UV+s/}$e1mll8t;KMM%11wwwwfnnL0)=ի++ŗ&ҞBi~Hs-SvoooJ櫖͚521!<ܫcҪR ݛeHttttݝ-Z(uJ<$,,^zѭ[7y>>jwqU8p -[E=Ol?+,6{rAh׮ J82OGG`t)Y-%%EYYf@ɚ۶mc\xʻᆱ^7]iJVpvv&** YJ!?^x{{SF Ο?ϤIXt)\t KKKM(6My/..FD)dʻ3boe߾}ersscݺuӡ=gή^j{,窧uA۷VZt}}}M&Fdggckk˃kBh*i !Zv-}ȑ#$!v t !EQTTDxxC⩐%sҥK@ٛfxN(϶KII)scJϩp%iݾ}7j8"!$-!SvBGG 6 xeddA5prr"66{,S߅Vϡm۶ƞ={055%2223BI9TPP#G011֖(nܸȄx22<(sn,YDaTHOK!֐%BkH9'DB!IK!֐%sNDB!IK!֐%sNDB!IK!֐%BkH9'״DB4 0WQY9>~ZΌ}=ҳrl]eHtutx`G+mWwu ZJ]~|owa/dֺ}[3vPG68͚'a Am˃>I/@j]l?'úr>> ;+3 7G S]1w~dXg֋`ێ`Mz޴nP;)N^g᜼tAuY @VN,s7У?Fxk=8I):XSV3mnFڞXU{7K A w}2Q16x6:3c$R3{'/]`s`ȠA Ԅ[yGԘ~>eڛ}/(W{ȷ*]e w!~)S8v![4n;!nomLjy7iv!C;7eXM#r1{}#5ВؓRr$Zfj8z.~^ݯ=!-?7UkIMϦghݠ+v#S78{&~^U]M_3cCR152@_7e{Pj$IS^Wǝ]Q+P2 |UW^7[M֦XMJFbcijL@-r璐°.MRboֽ l771$#ÉB<IZBT%ۏ$oUn>ȷ (Ɲ{dfbgi΃| xNO"R?#}]g;+N%* D05[z,Ex>hcdpN68Z[?}/J $䚖M?s,~".]Og?n}ZZL`E[y7mG$Zy:R,vnʼ_i6m⭇9v!,qT#؟9|fnf5{Oc>BTdkAʔ7S+|dp -eݗlk @]/W\W`T7ڒ a_fh{Tm=Tvu}1uiͻ |!N6|0_T?]>}><ԤP|GONV՚BJN,\x~..]LxX4NoiLZB4xF䫏B!ÃBhАMt>'[K#r%Y6=]:xn󴄨@8::T*_/uEVX~PD%'=-!*Ж-[(,,ĉE$-!*Pxx8EEE,YDӡ\%D`߾}iΪ IDAT D$-!*ș3gHOO 55{i8"!$-!*ȲeHIIl޼Y $i QAv؁JА.]鐄zz@Qff&4hЀUr9N>ZB<i Qoߎ ~8uꔦCBI)?~x G&vA!*!Cʕ9::ҫW/ D#CzZB!$-!ZCB!IK!֐%BkHB5$i !Bh IZB!$-!4(33Jǎٺu+&Lm+!*Р5kpB/^Lff&`dd&&&$$$Ǖ+Wx뭷筷{{{Hg$DŒ%nݚ%KOVشiXXX ޽'Nge#Fйsgjժ AAAXXXhlx0///ƏϬY(7|aÆ1sL233r'''tuu)..~A !rMK Z`ɉ+V0tP͙={6;w`޽DEE= SSSrss;w.!!!J2y$IK 4h$==YDvZN:)WfϞ=曌1W_}{{{M6qԩ5{a``CH CCCڴiCϞ=' ;;;LLLڵ+4h(R|!AAAXYY=s]v%//~Sp-}ϟϮ]m̘1x$i !*777978v8.]JVV_ff& .իJYvv6ќ>}l8w/&&&bCDD6l넕$-!DӪU+"""qnnn޽{iݺ5gΜ9X[[ӣG%̙3F$իWӣGzUؿ?Ғ/͛7SXXHhh(ǏP\$i aDGGsEMRi}hѢDEEE`` ˖-ښ3gPTTÇ۷/]taر_(W^d]v%$$'flmm3f Fڵk<LB4hm۶%==mҭ[7zɺu4899Ʈ]:u*̜9{{{ 022"$$sss뇃Pall˗IHHW^JU666V+U\\}a~'j֬ݻ;w.7oV&i3vXGGG*B 2O]pAݨQ#aSʃޱc:66V)=zzꌌ u'O蟍 .Ǐ_,11Q}Yɓ'wU^gdd׮]>|H}uտ:%%EVէNR*O<^hz-[o޼lkٲ:::Z} <[$%rQ"##o9qD/^}ۓ@_~رg~R UTQ^,,,ٳͭ/={===:uT{*!IK *,,DOO`tuu;w.@k)dggcjjJjjA~o__g>btɓ'qrrth%И~Lj#Xx1CKTb<-!`ooυ PT3tPM%*1i !4f͚jlmmiҤ#$-!F 2###\]]ӓ $i !4]vؠR t8%(lmm155gϞGTrתU+tttpqqt({q~1D?P6o7/BQS]AYMQ +*RsMG!AV,_\D8B[7k /KQ't]Ut%tt MG!ABIXR%BkHB5$i !Z.\OԜ/Zu?S\T\:íG;ֱ",M$nnK!ēъq2Nzbv|0\_Kcځ6iRjInFc%i"_yi ^ƫ& Š38S#PEob]c[T*馴wzLT7q.lXo څ`U>Sw6|i8 -@&˷ά32*qp4ys"Ʀܽx+撛q{TĞ%!L읈ۺڽԦ];=vVfEVVNJ>dSenX{Ti]j;6^5tWPabcO*=wSbYXnAm! Ⱦ[Ey "?+$uq1jSohѢ#9iwY@_I:y}cSngNTva_#3> ~!YorS} z}<} &polYDžSãEǒT*~}-oLQPN,~?Z鿽 )P ;N]S}/@F IwY0~/~Dq4QU!>37abTo,k n:{:PPXG675'DѺu/4RfdmG^߿ToĬk㖜]5>>z V!Ejzp7>?IXzj&ڛ^k?'71W t{;o`jl惧Y8?}41VȪLjq!V'^Idpf|2;* kv3cnsh)gݾz1UӑԨK~FVIWt'#1W惧 ŕ{N+INI+#$4vG{,]Z|:27G>ũK02ԧwƼѷ=V&l?Ø++`Hf'CU|\ͯa`Ǧ|<;::*߂.'/qlbo]Aj&h𠩝.Kgs%yT*T)uPi÷\;kQTxƝ~ L0sr-{v>L4C]g.%׿~;'˶*G#*Vf8uݢϸL~ !ez?b k^_և!ROlbB2Dzl];ben9+wE)uE'0mN??`39wwo.~YkW9 xedd?`}p!f;Uf}0J9>ߪJ,C4cC4/s^Nj4 k}=u sJ&Bfd3uv:7K̲QTZG]=9q~;NJz}á݈^2B=NN^o}294Cvx͇N$IcҢMъMdkXDuI˧Ss|!/3-+)}JG\ޱ̛ \ #%,9w71çS;ǍNߗX'|C䦧~-#3> 3*xwCql]Eნ?mϷۋ\ݳUA^%f<.\Oa^.I׸PK<[Vf&d_PGU #N^ wNU{jksSzjHUygckkyү}v$Wzaކ}'+; VwZ{ǝ:^YѶQ-vFJ: sWoҿ[Qru`x2{Kewl:x;i A~eWwuEz1l-3 071bhe׃/0snT}'{.V!o_j5[ѭE}J%)%}@m:7x~hԒxx–wjzF8 Tf|*~ †p:L M6C}m43 !Y%?2sv ttY ڎ8Ĭ]CQMPk@pJ1v_jJy6PԢ?y vv{dk;j}o Wl)M|ԩ)::%[=]<]~Drm޾IM{05=\*ypN`(w췡Bꮎϖf^/Jٛ}6tMJ2GfƆnX%mze]Q|p#:4˕Cg.z1 >am[ CC ~6eb2m[s<N*u>A_^>iext/ Qʬ=kuGwo2e Mao)KFYgjtG.t]?fYrG{ U\6ǥaG!*Bn~_-BXԨsWruR),*؅x6weZ)WFU'"N^GoIDRrZ_D۷+U4{dĈܼ0/mx l>3Xnۜu{0:5)bմnX/!$>UF)K)jyVHJ !-RJdca9Я}#VˢxTu jWP6ᩴʦѽ|~eH,͌IVaZ*VYϡ_@ v, P_q`HNn}6.ww^Ȼ?t;ɞ)ۇum[1yRz G浯02ЧSJj]>O/CԦQM>¿̶`$l`=z1 soY0Iw\k^ϛ>-v,N(IF_z.s%[K3uj-~ ao%Xƞ#ޥZn=6Ls(@߸<J42?y(zR|+WTTVu~oX{tu0cX$steжq-jyp?'|l=|3cCe*;fԭFjU8x&Jŷo/L | /u`cQ5θ:(۫UqZ{l,. <G^AUf[YWUO_O _Jwq{kVw{{ר'vV񢠰ܴ.}4#NPboLPȀ:^XP]@_O z`cn k1zɸ]05;q5_ԄNMbma *vwRtJ'kCTyx,ȒAc˰?s *#5kpw"3߯ɶg C +J=<(De_W }sW?n$<ǐ y[@*\cxIOҒBh IZB!$-!ZCB!IK!֐%Bk}ZUEoY!/OglHϯ'%MG!*3kwi(? ÃB!$-!ZCB!IK!֐%BkHB5$i +,.WVPC*^QBMX<$i Q X|ZkO0y׍r~pXy}V~8MWh7 mg_r#=q7^y#IZBT8p% ۮ?NM,Wnadž?N&ώK6m&@OWŗ]=}q-S +bQ~:z;3@;/573?v,.ڻbiۯӹ K:Ү=V~Q1;/Ӯ5s&L*LH˔jT;̾tlLy Lx{9ujJ{U/Rs6MݶʶݱcpcGհp2.gP\&ߞw^.G$1=7 '~ſ@lF=C-e۝|:Adzi Jc bKԫbFC73-ȱr @SO ifFh][tT*Fe8RјV?FzEjƥ3$\*ߞTWp(>d-HOK ZcmNjd5bcR_/+9}1cM>>KiteƎ prJ.$p@ul?SlLdJV'dQқ;ɲwxEj\~+#C=:1P2yr&)kǭ|~EFJym̌L} k>@-'\?9ʼ>gHtM<,Xq.U XvpUU*j'>y9؝ǎ%:9C-'S, Uq~>&X̙G3%3, ]WRrq27P֟ÑRVޘ;Y{ _%D9ŵ{yy%3j9ӑ[JҺOJvvKVzVP29y5߶`ge, (VzXSO D eꒄZֈf8ye'pl?.ܺORFr=77%K>9!}r!*#Ё9aʟCjr%7Ut5ne332"?4#8+:u mi 99w+moLV o[PL e)!՘7o&r=-iy,<Ī唬$_BhpΞ4mߖٵ:Vb|)AzrI/հBδjjp#N`LPp? u`p^Zu-85M?7b.h[n۷UZe r9w0~m9k[۸_ʊ 101m !ĝhpgZ5IKJ%ȯ[ԊqK),qøq;S']d?X0a$r%EELו +?&35HN=:qܜ{*L#f(#f`dYy*ťe:X%euYzS^Q#h?Ҳ8@nANx43FR2ٕQ?oKسFMl&/|mۓSJr"nC<7MzV=Ц/>Ĕ6 <׳&@vzK~F_ϕ;ݵ8Jr)Ĥ7n_,\O#pO//C3{~5MAGoUMR3s1was~Bm#OFV^*T975 ȐXJɞOj]cQ?o'61|u ag> es^ O7Veբ۽lJqlF60w̜+ahdDsҶs*uiO+--!ly]8PZ\̚ۏL7^-h32ƀ+w`hfFf,KEż:ORz6}+7nsU 45Rk?/P9B~"Z{q.>g毤i&醥 .mv%bͼ:hȔ#3vcljUص[|CCAjQTX@IQ!&f3=7*MolgϕQX4B73obg4g^)L0#UJ)*dJRZ))ȣR4{ ܬ-077¢sss vsbhvQǿ3鋿Vmu#8Ck|,9'`om'zV*ϋZ3~l߂]xu2 Eд ?ʞrn: 7psezLy+1_g>X2 WX_X-վ܋ ɼ|#z :qYX3kL?BńH[cajaѸ7*_^ߵU|e([\'ƦdmZ kٳv Gu o丛}ޝ:Ʊ׮ģ_ƫ%=f s^+wRƉKxm8 +mSEyyrssIJJiz^^eeulZYݏt W3sq<ϻp9b\`C|D:k O~$lJ8Hi[)j |kbtnCf.|NN\rF-XUogh<ơ8.&$sVٷ]JbC|$ K.OhM#sLbjV"!9VR'FDU>Ld\"O_ɐGۢRx59X~Dİk m_IH*ŗ7O{BQ|j:I#:y4 -ㆱ042"WH>: ҆>-(*(ށxsοHO|5k̒ZcUc\YZ-U]^^UN/((@q*hԐiy>̍XkL{lo] JʨbQs-=e@^߷9l$Cϛ4ϿU 슯;94sagBށ{Tw\Z*Yy.°^x8Fy}7;E1w69` !-rf%;Ħ9tfs40oPk}\ղ5bR 0:Rwbq?4ٛMysw&}lP'wgnӮ[/6]L&+-[GJ=5J6LZR"V1pl՚O_ )[?z_Z>(T* N}U|]:@ay?f5hלYctSτ`myӲ.7zװidNfeajfkۼJ/'#'wC|{:X{elz^^mnޟ|Ҳ*MI;S硪㛝WV nM.߲2sT_/2r (.)khOMӊ7#cMTtl^rO̘1qH7FadMn;ٰ7"7fmcZ{:*,͌ D JEaBE?S'zV9IKV`UiFفc^bt҅8VZ_"""0445-J}h⣪jonۜ[me,5t{x}vSk;&촻F 4օ["o܃aum}\9p&ɋS7W)+bnC~an|R: ;+Kw }ճo}08$<ͺ)4#mTYLtt4۷oСCBU4uCzCǧpv a?H<ŒTLlȽ/[ZUqaz}h]޿/v1=EB}7Hh=D M$<@Zt$v;fd8mFS[tJ q W!X:cPOOO cǎriܸ1фNӦMo|lu=%MWӺ=zs4:爫:53&Mq'%#<p@7i1`$/G[QAWik?8'ώCy[hTzV^}8=#sK| ǧp*H<o;#uX0 y9%Ϩ:yP_uyTh "((8ƍGQY vE`` cǎeʼE@u0=+X5m' N r 2RJŹc7.fcX_:UC9$H9?~߈ 6⩯32l.28]}hqH>}_"|7nq#kbo%Fcwoj!OT_RoU*Z)Zc[`:ZX;I L]?/wBCC֭}W\EC]BNNgٲe4j ,-uIVTTP\\̇~+:&8 TacK+l}Zu1طl?[i7n:kIqB_}h/14cw> & Z'~h₍/V:G֧5r:Mk`Oz߉imGM{ڍݛ-Lڥ -LpmG ]fӦM?Ғҷo_B۫mQp|FNȻvkm;:= -qݙ6mӦM#33-[0i$f4ir! 8::ָ\v1nkPOnZ@w|8LNgVMEY)jCd_ڎ.Fqnܼ8i퍲Z2bZ7azYI'y>o}_6f<{ ԜV;!ns 0j9r_|.\LJCtGߵn`; .̆ZviӤٳBuUँ106a[i h1`%y7h_BZK:|Llh;|?N. !Wb}+_5&xOS*/F.e5CKwUw'lwڍStt4?#Ύ|W ;rG8 s4:p *On%uTn.QP}Ͼ6'Q|||1c)))τV aРAFp0(((`̜9lvJhh([)--5bԦSysLZ`ffСC:t(߿?K.Ghh(:tjr02V4ݸ:ťe?C`v_&Q v"$зEJf.>nsG,[cmm5fff8::NfrT xQVcQ}uݓ?1h4tޝuϜDFFi&-Z C gϞ: +cn><\t@Nk>>bMe^u qU}$'_#+*|Ԛ`bdHAQ f,d'.w(~ow`Jb׏#G:Z![vKxC~xZ-.]ԩS:u ___edd;'Ofʔ)T*!O ?/~Ʒ'ckeE_г} VnKZVve_RZa6aGs"_ }NtoΩ J1}~+1_g>X2 1he^a1cQtwW]LHW1MfԘYc,&ȸD| Sc}͊Tjj*jOOOڵkرc9<;v젴MԩSo\=(}xUZj8ȐJkԨ#F`D >;_n!15- Rg\r_Jrf;gAy.'_Fr-[rgez2???Ͳ~Epbk*mLl"eD_M\|~3c#zM]̀mh̅ɉ WQRVΨ 훝8ńd_WuM+*eϹKIzޜDAq ]idUILY$$g2rJ:\tg&1Wv,;2q<=%CmJw`9|f/JKK#""SNq 111 }u ƆYf1mڴ{royʩkrj^o]8p&6\8pF?ofۉ ~WmqYN]™ث`jlHĐ!CpppۤRn+ uΈIh 7iey_<}+Et 9+~ޚϷ쿣՗/KJ9ʺ[ױYuIDATi_Kߎ1w&T*OJx:ru9' fϘ8#02РR\TN]':rkӽ] 7uRzcu0,X`A}WBrW1hVWmo^/ (*)Ҍ°^wl厕)#v7$w ;+ rlťe,0{k =ߥR\}X[x`O';\͟B:yboMfN#;VE9Q-V+*hjJhքw'?cu?UFGםZ7 -n;ٰ7"7fmcZ{:*,͌ D JEaBE?S'V&}D9meCxW'FgPg_:>Wʏ2ҶvPUv+4skʁ3L^фJAIh4*,3_VϾCu+'x3{F.^I̘?K}Wޥd0oHJ֊5sE4 ZB<,Leɋ]N'>UxDT!=ZB!C.KaQߵ 2jN^ T3EP_5u@BK<\R.ܢP@E. !P -!!%B1$B(BŐBZB!C ;[w-ăĪk jIBK4\V.!D!B(BŐBZB!CBK!bHh !P Vw%+yPQ^ߵ JM ZB!C. !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBZB!CBK!bHh !P -!!%B1$B(BŐBlnbTIENDB`DeploymentManager.svg000066400000000000000000000514311261716203600344050ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-files image/svg+xml G incoming_app command_deploy state_deployed Deployment Management DeploymentManager AppProvider File System Jetty Server Handler StartStop WebApp AppLifeCycle Binding Binding Binding Binding WebApp DeploymentManager_Roles.png000066400000000000000000000526061261716203600355430ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/org/eclipse/jetty/deploy/doc-filesPNG  IHDRD~sBIT|d pHYs''MTtEXtSoftwarewww.inkscape.org< IDATxgxڀlz${MD@DErQ!t*zIH#޳ɖ$,I [׵W63oymOj5"""H"" QDDADQDDADQDDADQDDADQDDʑw Q(I"uF d8`#RAwEP@$ @*Ji"FB525~8=-HSQL 0 DEi}5E`q `O>C!ZcK<#^YARBaa(&4vB 3hhcL. "QF;b,,LVɄAE %(^Ď`,|C If%dh iim. \6̘ŊTAqcQK5:FDA1jRB.ZфAN* ,ALI/PEϯ()2Fq""uBVPBQ3XTȉ76F{DDtBx~5g0 n(' c0I&7BDP 'a0IYi05 mϐE һQެ>rlEda<W~AE)lӫJY9/''ޒ\z`.@y&s2rh9㖖 l=ZࢢlW!KzU֘ٺjJlD6CSEns2~q7/[&܁qdN6؂N}5oj]7/:?3g֖&ANf<.H鬛|,W!;~KQj̹ϓx3&mInhD`+WNӜI|>8bO|Teno]wo_DsfcnDaRWEAko(VFOmͰ: C}T^=Te˫?} s{>ѯ~Gы dAcHK*Z.(o}ُU&sHroRgm#cէymNo\};KI!&,˘tcraP:WҘɍ itҼGOԷREXb`I]yUh+kǑYJ6IeQ*rR(%S<:MHwPW@PUMA^)NR H$-,*TՇV2D=?蔑uNSϠ O5fJ5ׄk/)VZDfjZwoj\WʍL|*arxeJMVjkAGz:97B{jق7bje6vOĥSɜ*[;Z1l|jiKL첆Gm5 NuE2Q:D|jRbՂdsx ѹxk:Ȣ94,,$p 1ȟ\8XOxU]Wg0.^6zU2)-B a'/DKle1rkRqvf䤖_c}-]f_'Z]|;[[I:yL3jikUZx.uӻ~ҫdt}^u\ZյE't[l0.$mPN-4ƊNI<.=""F?:X# H90#N \ WD"mQAk }q +0 \Nf?;~NtK{POdP={ CA^)}p}}Ea0JJJJj 5s)׌B %{$@e#hܙKϑ?cj͗\JƻvR_JFJM6,̟'|FJ!62]TJ&).;K<キVC\d6^~# ϫN J^I[2\=[RN= *q 7e'q9܊GVYͰF*Ц'-1ᣓ`Z*{(^`q ׌A+4]WI}#N,$ۗT5GwrbOGgI/Ѫr;rR\]Y{ 冢 @~N5x yue&Y6IqmziK$œ\۸1l|s\m,ap=0Ѐj坤%hYcfhJJDeVTAߑM { “Ru!Ӗym"G(Mڂq϶.pDLN<|퉸*y5 'W,)*(NWκuH?#]|43x" Jͦ5{n^v+ljXi@7K˯z|^zZڊ&R hs>{xsB^MT8h:o9љ 隭Ѳ;Oљ?D^o *Cۺѹ/N%T4 ӾĴw9} DVk(?ab5)#,:xOBtJgf~C= ~XQ} A|M 4oƯ3rRjy&=$ ͆U&ieJZdǣ5jS;22S8?؈lܼxtZ[l]-Ei":W֘J/&#&AN;"-16+k -wT$).R>vuq;MxJ|z T^DMyEA54kq>YEDL=0NjGN""FG&_oa/F]qeJbV+}. *}w)>LD"R3</OZx0 g^BOq R/8/1JYFx0W8{@kεa/q19$fo^v%)l&uED ővWPA.Hf=*JXDNHb4 XCL@?zI3U"2YXTTIr9'Td PdCWB. OŽfflP("BPم9dsð{BOݙ)7aJ)Na%@l9Nujn$X`G36X[MjP5 䤢 Rqtx םgnV)8 Qc#x!a j kj*oVOZPCÓu1q ^@XXrӧOwSDL( :pԩSS C)))E!vRR # C rK nP)++^ƍtޝn "!M6i#Fll$rL3(P+U;RKBV9ư/Ȭ ,/ָPEUˈcPP) ;r͎*`~h# H!%+Jaq GCRU{=<&D5P.aAa&dT_K`ɜ7d ]`Prv{R|IaS{4ՎNJ_Pp:+)8IzV`u&% ʊʪ0dDAYɽ„~4e ɐ^4/܈_"2. +՜f?LyK>~M1/j?6).ވb mNM`̌OuC-^Ôwą >n;aLW6>fPZ+?s.s΍x0:e6u͋fǑtx<͉ Y׿<}Oˁӗ1^ :mOsmo\ p1,RʪjT+5C{uf3eCJ*k[! aϽOLB2k(7Z?]ImxB*}tOk^<y)Ckdѳ=q$J܁?&9=13wqj0h%{QT,gŦ<=n(VжESy?ZуY|wOչ?w=V}~:=;7HL :!OWgKHoੱCe#^ ,ֺy]@?66ogi_b^qX[Y̑sW5PuHաU0W#nѫc^e ff`gpr!͚bme n.0CEPVdȴ4ǮEƒNO݂W#'ܛxȼeb܌ʼnşAvPJ"cxƓGƵXR3Ifh/D =zil?|&ӕf#e޼xZ6ac#66:ScX ~:]D.mKEWy{m+5o0^ktoߊtyBIHܮXEWl4vr]Tz ݐ@i>{s67 +ww]Z9KJg{TjIiZ5V2-&գ_)luOC¡5mޔsĕXj#3 ]Nۜ\8l_jNjKSZ`㜽R]br x N/-YŰ(^^sn`pFr i&Qq4zirkaCXeqpMN0~x_l0?-^CBJ:e "boc)1y@_dT*5FCXt<pW+c`F놼:}3e锼&: C-F |0q.ӳC>}h2 ¹—'&)-L[5x:zK KG߯b߻bԀZC(4^HqEd C`\*/Nz\&͚%_5݅W11h/b5LxSJZG[P+ɣES?~ul_Z ڵykUiݼcd@z>ͪw2spuFT͊9x{s9vfN(VzNF)(ɯS:|9n=}ݿh6s܂BY;P$F֒mH7aPY}y+U_UiB{k N_̕p *&Vms&KZ"ɫBwn`aS;YqO7Vmo3w %=+@6"D RNՃՈ[zM>a6W݄Af0 ݅u ?8׎ Av^!.Inr)NMvj_Q/}Ȟ㡤dd '.`\e妽c wN] `t{̹Z N_μ2^G͹љsk! 8d{erkL`Cxƽ5疬7>sdK Vnd•,Z~Om]^֏[; o<V4Y0¢Y0,āiOFv.!z*|]B "^zwާ3?O}9c}lYU[;W;P@Zfe 2E%r2spqr%U<' ; v+K@?jJ%}}nl/[S0`>SGR29zTA9Y/+7eޛIZs6si4 ɞ㡜Flb*+7a>("cm6`3_RPQ; o];kT!mظ-'n<9fYtmBs]@]3i~^/?=7Isrݙtgڷ Ǐ^荋c K+D'$¤QZ:o܈l^4/~̜<ޝ>a+U_4_IAQ ޜFfB_=GIL0ceAͷ1pZMFP^j\s~]MVg+8 8q&̻CW_C<04lY·0c7|CEfM =Y뵂=75r3߾r~t[8z m8s3\һ uA A=/ȬiHSk v /!?˙Gٱiٱzi&" <"li^2k»`Z#$-Ł@"Axd_Op7\^BQy`wES?Y^Ĉl#6̸bN6LAFtrCw`%"" "RxQ/ņ bݴ( wd IDAT D7n{;?h$äFH>rckDah,hK=PH۷_{p * (KpXE9s B&bY ;)%m C*ga3έhBnc HAy6Rr87мX;G3aC0^ViAK ipCñnp4^o/ *D Aa&@G.QDt<u! غ@~,00(JbdU< t v$&t͉>[1p{gB*^ӡ/ 6™F-RDmF 7Zq1" bߍZ!DD p}ъ30G 8}ʉ_)e0dBtsVÁo - SKu."bTpt `0$^QEDmP J '~5rr|oeq}YD6Q"k;+ ĉF''à vȻ}qV\!=YH%XZԃiI~]?a(ɇеze՗}aYvbǯ s5)N07[_^? ڎ7L> f+.vΪ2¿WRiv\5 \J 7U!Ә'{p1!6XTeJ5/ysjW锶PdDn$QPJ+o{tkSgr8x2<Ҋ-S8.M'c; 2{k x;dO?<` ,?@''\零T7{cPŖpV p;/-X{62F|)72n~ lY=ktz.p3:t&嗲H,ٛէnsV.vLO'!,˩쾞{3ۅNlf1ǡw Bwa(ʂ:g3 tm{vsJHΕDL^y Q͙7cQÿ|8F*ʊ Vn`3_gO 1/G+t֌w^K{ndP 699Ǘ[#gXM}KijnZBhn6bWkȶB`|A.|wb+9߉SYy2Asfj&ﳽ/ i¾nt&Ŝ4x=8ȴެד.*c.ڂƶbK]RQVjnH}'K ywmˏcY~Bw{+>BSFvQW]&6TŒ!|Z~)[/h`.HǿZ/ӎ'{i|&2[ Uj5_{##Wq`vx9ZQP3+/YX=S xmP ]ޠLŷ#zT&<54YCDE1! WbjWםK"9Wk#-͗tԤ`l0gK]9XxhjAHO*JWk\_.+XQ}e@SeO[y/T\dGк :8qy -* ^3{}fV;pK;|[ciuf2?S۳J)2~P3~|?-On%$f%8zWmTV)7tb.*m'܅eְ5I57{<"BElK)j n宙#\KtbJ?V neq1!B{-4=BK/{M*z{k ؐ]LN ,~ 滪JO#:} O{ҵK=rNQE+HpmZ, CaF"(|rOìTU'[f򸅴r\uzN*hY,j.!A>uOjnyT8XW^S*e*6>5ǪvOe6ffj(icג5s 5O,y}YeՎ~Zڻ綖r+R%gbsBX9TM*x5uosWNFgdS=*N6W{mxyBBY]}feQ|y1t|^u,AR\`i!a3q'w痲dlIRExJO%2'_nS;kuR H/eDgz} @$^DDj ;+ zcH'Iɓk=WM{ KůƌiBJs)VLNq;9mŔ~MVF1y% h C8/GkM}UR =p+u⩞MG^cw ä( 98 DѵI >>V 2%\O'1DK&J]'kj"!* 6= lCje0<ăS1لjQs+.υj0"ăjewC/}<]Dtj`ntlRCuג@&0'h3e 晻VC-?יi.רJ[`ο<rRk}BZ~)J ra6L[ub^OSwLNu{Zsf|'@xnDG~ YHĉ-4$e0nZw,<,8&V_z;l+[Sgz`-CRǙDU؟Cu-]?%OΈϐ[]Ppy% | p@\ĭ"YtNc=_ 394SJ'xQ0cHP6NjKXV?,$b3{#F˴^,O+z|uR.璜W_ 7z2nYRqQn$F:qYٲjb~6lэ>χսp-2)ˍ^*+O&pf&BO+_z~)8`6u>IyM0 lF''.cPʔjyHZ#[9),UReS>D3HE,1RB|v_ I/EPi@X t||ظN.PhTLUFfU;Ħ¬8Lz {zBF:sp|'$QeRզ hYi{`_|~p51ߜHJEC~٪sW\,r|moQOb5C͙Ps\,yb`Pnc ͍jb@o'ӱQU#[p )n7 a`޸콑Nnw!j+_gkr>uwJujك[bgIlf1ۮVK;ueϼr*ϑOO3W>!AX;(+L"~.ft]Ӎ#kymн.l`ەT.Vgu _gkmeZ zuI׹$]Ig eDR\d˫-4sMGoa1"ă٣c!PPqVt T?kh0P% Cֻ͙f#;4ъ}7 ajgy!XJ9}xe@S,-$ʕɕK8 m]hS]`P+wӻZ>JMbN Yex9Yh]QSk1έ=@8ʴVq^VLhGe1'v`J/7Y,軵z^{4.+aX7{K>ۚ1U C7LP†/|Q Wp2:˩V2{d(,mV_n_]DĨ'ݰŠ"DDtoPFX(!L) RÆ s ћ3I'1C&`;jf'"b0ݟ6(yh1`DDICIg\W1CnjZH Fİڸ/ZHqiRĘFRa:+{6U׽2_G,R>/Ds~Xyy4"6{'ԇbHbaJ (ɽw:ذG]:y dz"/2cή0X!L3<?K gLaq ʽnaڷ bsKV(8u){Ƞ^&m/ <& ?~=_}'9{5w'^:VZ/1_WF23Qg3G'BRx4g3op8LtB`og} | wkkkxt{e&S)!~76vbx߮:so~fՂ"=нg(+aYMq@Pz-fLEV#amyˆ6u, tâڶ{|N ^MnP ]O[U La ށZѷpY,߸=+qvgXt8|ܜML\a?N7G CVe-}'ļYӈOFTەl\MN!ixVa,,yF:Z18FLB2|)(*&*LPjfUqYsZxֱ1f0: }D^Aeb)v^ F0S$u & j^J%'. 61#TOiXevftYaa(ʺw#2mȬ/0W'?Lf}` FLfARZ&M /Lz~'ۚYGX̝;wnS :/&F̂B<<͞u{V:;"@ m[Đ^`S<@zwkHK4t-2-P@멕[u׹wޖ'FjD,Gyo SKJe $$܄'tc֚g/s#:N5yV*tn12̺2zJz#+NSҫmKڮQ0R G `y2 3T~]y1V ZfҬy|oڵxE +"&%:92a} $!%UҦYV89ƥhtoY-*.[<6WnRZp i,\ՙa)~ ⓸ޝqs9Tܖ6u@ 5jr wܜ),.I_ȯvWȴ#h, ڼBYY8?\ &V" ;ѷ+>$fj>nӾeFs*Y,{'%20`=e-3٪˹x#[SI *.Q"/e}zCN\΄7>s{#"6[qv1ojf6s~}psv$Ό)ԨuWLj.(JޝBpiov:؟^xW'. !!%&,{2Wg6:Fb3k+~s+zwf !vP-ov2{zVk)5n$X3^,(*Ζ;_XU $yS}8s%Wޠ&>OjMS'܃t΢/ni N:gKL <&A־^ޢmIFTE m#{;_@BךNG߯ɁNmп[{jW@bvXnUyK֐GBtP܂tˣ+.zyegCwMÉML/s%<ŏlɣqur?=q,o uf/\˓Gk8Vq5/J'FsMR¦}'X.."Q=KH 6=Ό?j>itlv!OPH$lS t0S3yX=3H#/ߴ&ڵE뇰f8| &#|Lrֿ[;R)2{}Ilb*~^gǑ!wUl3iR24,-et?5W'츴y >N\AώAx/x9q:} a`Ϥhpbwհ|JH}xjMqqt`}o{U= 6sDmF<a;agc%λع ^2 Р)P_I'B7僗,Ż 6+cOh`_13>g]\Fgs0quv,ނƈ閝 8{@mkǑZ/ w@ipevmBƢAYy_]o|F `8vQK70( uYXSp`>a@DA(L/ jd크â{8޴UT;ǘ +VԆ鄡(Reu""z!T~:baPC>؍9<8 16!u=6j""հk M^F¸EA1Ec>' sъ'!}ъ30^RNd>Fpa'Ajx?I& paHFGTßCÄ!/TPi(u_TrנEDJA(;G0i( }+˄zW9=~Sk֋S럸KPpE6<:dwb6HhP("/h]^G[ *E ( $ BVBI'$wr'C$w'眜33yܹww=E %?d;ش"Uzd|IWPЎ[*r1dkjٺGj8ذ7.hj=iYֶl5"&ZTZ7?q9S#qu:}2zl)Tx[0.Cz܆aNS=nyqxp֗r X9^|=8~*A~mqH=׫Aۚߝ K^䘡QMQ[V͵ƒ]5sșϐ{*B]QuB] ~]$}/^ˀЖۣVw} Jbf`y W[g_I~>…ڞ(+"tl O-.$@`@A 0o/\Ei$%";B&Un?H7sh3KpK) )-V]l-Lz}YE$R|<av-4o7z}-^vlۄvWwD ܿT v”ӯq VJytq̭ F<97Mp*,4~OJfѾ\.NZĦ>e21hh6 *܃YOD,9) a¨`Hlmx)X\"<ؕy:N%ޠPx*Cύ7+q 7=kLf pJIĭ .瑒YD/bx5=˛_3'G^+q*dribH*Us;UXAb9+1,ڧz_#96a ղU(3;}md۾dwUo1}e ޚ 9\jkxӼitƨSڊs{ q襌0H sW^^7w8R3c ow?:ug(יf-zaDh;9Kji#ncи4?ʌ*uϝ= V}ߋP?7)UgIw?9=>`(L ;R/KYs82ndKVdɪ3}R9IWn1_s5'kfOdjeoZ\¦ˡP geoc4~`ܡa@J\/wvgpsA#`:rXa f 9 <VV3x/#L7ȖfYODV+Йh$ȺY^_q\vl|w>>oan@\ vJaxa]v5.Hszocf5DTV鹔ZHo  n&2ZlCяm{9;==Šmy$0(C~Z:q^NvɵFy!xn0bPө.̜ܟu;.pXiY0x`BW`QH握zw|)F $O]q0 Y >:œ4?; 7gocXuuGH.HB4j lqr.)++q;شֶ]GiMc8F[YW`D_|osW@'DI image/svg+xml G incoming_app command_deploy state_deployed Deployment Manager Bind LifeCycle Use LifeCycle App LifeCycle App Provider Manage Provider Process App jetty-9.2.14.v20151106/jetty-deploy/src/main/javadoc/overview.html000066400000000000000000000003131261716203600243420ustar00rootroot00000000000000 Jetty DeploymentManager API Overview Short overview of the API. jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/000077500000000000000000000000001261716203600222145ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/org/000077500000000000000000000000001261716203600230035ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/org/eclipse/000077500000000000000000000000001261716203600244275ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/org/eclipse/jetty/000077500000000000000000000000001261716203600255665ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy/000077500000000000000000000000001261716203600270625ustar00rootroot00000000000000lifecycle-bindings.txt000066400000000000000000000000221261716203600332700ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/main/resources/org/eclipse/jetty/deploy# Default Bindingsjetty-9.2.14.v20151106/jetty-deploy/src/test/000077500000000000000000000000001261716203600202355ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/000077500000000000000000000000001261716203600211565ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/000077500000000000000000000000001261716203600217455ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/000077500000000000000000000000001261716203600233715ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/000077500000000000000000000000001261716203600245305ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/000077500000000000000000000000001261716203600260245ustar00rootroot00000000000000AppLifeCyclePathCollector.java000066400000000000000000000044301261716203600335750ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.deploy.graph.Node; import org.junit.Assert; /** * Binds to all lifecycle nodes, and tracks the order of the lifecycle nodes for testing purposes. */ public class AppLifeCyclePathCollector implements AppLifeCycle.Binding { List actualOrder = new ArrayList(); public void clear() { actualOrder.clear(); } public List getCapturedPath() { return actualOrder; } public String[] getBindingTargets() { return new String[] { "*" }; } public void processBinding(Node node, App app) throws Exception { actualOrder.add(node); } public void assertExpected(String msg, List expectedOrder) { if (expectedOrder.size() != actualOrder.size()) { System.out.println("/* Expected Path */"); for (String path : expectedOrder) { System.out.println(path); } System.out.println("/* Actual Path */"); for (Node path : actualOrder) { System.out.println(path.getName()); } Assert.assertEquals(msg + " / count",expectedOrder.size(),actualOrder.size()); } for (int i = 0, n = expectedOrder.size(); i < n; i++) { Assert.assertEquals(msg + "[" + i + "]",expectedOrder.get(i),actualOrder.get(i).getName()); } } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/AppLifeCycleTest.java000066400000000000000000000160261261716203600320340ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.deploy.graph.GraphOutputDot; import org.eclipse.jetty.deploy.graph.Node; import org.eclipse.jetty.deploy.graph.Path; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; /** * Just an overly picky test case to validate the potential paths. */ public class AppLifeCycleTest { @Rule public TestingDir testdir = new TestingDir(); private void assertNoPath(String from, String to) { assertPath(from,to,new ArrayList()); } private void assertPath(AppLifeCycle lifecycle, String from, String to, List expected) { Node fromNode = lifecycle.getNodeByName(from); Node toNode = lifecycle.getNodeByName(to); Path actual = lifecycle.getPath(fromNode,toNode); String msg = "LifeCycle path from " + from + " to " + to; Assert.assertNotNull(msg + " should never be null",actual); if (expected.size() != actual.nodes()) { System.out.println(); System.out.printf("/* from '%s' -> '%s' */%n",from,to); System.out.println("/* Expected Path */"); for (String path : expected) { System.out.println(path); } System.out.println("/* Actual Path */"); for (Node path : actual.getNodes()) { System.out.println(path.getName()); } Assert.assertEquals(msg + " / count",expected.size(),actual.nodes()); } for (int i = 0, n = expected.size(); i < n; i++) { Assert.assertEquals(msg + "[" + i + "]",expected.get(i),actual.getNode(i).getName()); } } private void assertPath(String from, String to, List expected) { AppLifeCycle lifecycle = new AppLifeCycle(); assertPath(lifecycle,from,to,expected); } @Test public void testFindPath_Deployed_Deployed() { assertNoPath("deployed","deployed"); } @Test public void testFindPath_Deployed_Started() { List expected = new ArrayList(); expected.add("deployed"); expected.add("starting"); expected.add("started"); assertPath("deployed","started",expected); } @Test public void testFindPath_Deployed_Undeployed() { List expected = new ArrayList(); expected.add("deployed"); expected.add("undeploying"); expected.add("undeployed"); assertPath("deployed","undeployed",expected); } @Test public void testFindPath_Started_Deployed() { List expected = new ArrayList(); expected.add("started"); expected.add("stopping"); expected.add("deployed"); assertPath("started","deployed",expected); } @Test public void testFindPath_Started_Started() { assertNoPath("started","started"); } @Test public void testFindPath_Started_Undeployed() { List expected = new ArrayList(); expected.add("started"); expected.add("stopping"); expected.add("deployed"); expected.add("undeploying"); expected.add("undeployed"); assertPath("started","undeployed",expected); } @Test public void testFindPath_Undeployed_Deployed() { List expected = new ArrayList(); expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); assertPath("undeployed","deployed",expected); } @Test public void testFindPath_Undeployed_Started() { List expected = new ArrayList(); expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); expected.add("starting"); expected.add("started"); assertPath("undeployed","started",expected); } @Test public void testFindPath_Undeployed_Uavailable() { assertNoPath("undeployed","undeployed"); } /** * Request multiple lifecycle paths with a single lifecycle instance. Just to ensure that there is no state * maintained between {@link AppLifeCycle#getPath(Node, Node)} requests. * * @throws IOException */ @Test public void testFindPathMultiple() throws IOException { AppLifeCycle lifecycle = new AppLifeCycle(); List expected = new ArrayList(); File outputDir = testdir.getEmptyDir(); // Modify graph to add new 'staging' -> 'staged' between 'deployed' and 'started' GraphOutputDot.write(lifecycle,new File(outputDir,"multiple-1.dot")); // before change lifecycle.insertNode(lifecycle.getPath("deployed","started").getEdge(0),"staging"); GraphOutputDot.write(lifecycle,new File(outputDir,"multiple-2.dot")); // after first change lifecycle.insertNode(lifecycle.getPath("staging","started").getEdge(0),"staged"); GraphOutputDot.write(lifecycle,new File(outputDir,"multiple-3.dot")); // after second change // Deployed -> Deployed expected.clear(); assertPath(lifecycle,"deployed","deployed",expected); // Deployed -> Staged expected.clear(); expected.add("deployed"); expected.add("staging"); expected.add("staged"); assertPath(lifecycle,"deployed","staged",expected); // Staged -> Undeployed expected.clear(); expected.add("staged"); expected.add("starting"); expected.add("started"); expected.add("stopping"); expected.add("deployed"); expected.add("undeploying"); expected.add("undeployed"); assertPath(lifecycle,"staged","undeployed",expected); // Undeployed -> Started expected.clear(); expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); expected.add("staging"); expected.add("staged"); expected.add("starting"); expected.add("started"); assertPath(lifecycle,"undeployed","started",expected); } } DeploymentManagerLifeCyclePathTest.java000066400000000000000000000117531261716203600354670ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.lang.management.ManagementFactory; import java.util.ArrayList; import java.util.List; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import org.eclipse.jetty.jmx.MBeanContainer; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.junit.Test; public class DeploymentManagerLifeCyclePathTest { @Test public void testStateTransition_NewToDeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); depman.addLifeCycleBinding(pathtracker); depman.addAppProvider(mockProvider); depman.setContexts(new ContextHandlerCollection()); // Start DepMan depman.start(); // Trigger new App mockProvider.findWebapp("foo-webapp-1.war"); App app = depman.getAppByOriginId("mock-foo-webapp-1.war"); // Request Deploy of App depman.requestAppGoal(app,"deployed"); // Setup Expectations. List expected = new ArrayList(); // SHOULD NOT SEE THIS NODE VISITED - expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); pathtracker.assertExpected("Test StateTransition / New -> Deployed",expected); } @Test public void testStateTransition_Receive() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); depman.addLifeCycleBinding(pathtracker); depman.addAppProvider(mockProvider); // Start DepMan depman.start(); // Trigger new App mockProvider.findWebapp("foo-webapp-1.war"); // Perform no goal request. // Setup Expectations. List expected = new ArrayList(); pathtracker.assertExpected("Test StateTransition / New only",expected); } @Test public void testStateTransition_DeployedToUndeployed() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); // Setup JMX MBeanContainer mbContainer=new MBeanContainer(ManagementFactory.getPlatformMBeanServer()); depman.addBean(mbContainer); depman.addLifeCycleBinding(pathtracker); depman.addAppProvider(mockProvider); depman.setContexts(new ContextHandlerCollection()); // Start DepMan depman.start(); // Trigger new App mockProvider.findWebapp("foo-webapp-1.war"); App app = depman.getAppByOriginId("mock-foo-webapp-1.war"); // Request Deploy of App depman.requestAppGoal(app,"deployed"); JmxServiceConnection jmxConnection = new JmxServiceConnection(); jmxConnection.connect(); MBeanServerConnection mbsConnection = jmxConnection.getConnection(); ObjectName dmObjName = new ObjectName("org.eclipse.jetty.deploy:type=deploymentmanager,id=0"); String[] params = new String[] {"mock-foo-webapp-1.war", "undeployed"}; String[] signature = new String[] {"java.lang.String", "java.lang.String"}; mbsConnection.invoke(dmObjName, "requestAppGoal", params, signature); // Setup Expectations. List expected = new ArrayList(); // SHOULD NOT SEE THIS NODE VISITED - expected.add("undeployed"); expected.add("deploying"); expected.add("deployed"); expected.add("undeploying"); expected.add("undeployed"); pathtracker.assertExpected("Test JMX StateTransition / Deployed -> Undeployed",expected); } } DeploymentManagerTest.java000066400000000000000000000074311261716203600330700ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.util.Collection; import java.util.Set; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.toolchain.test.TestingDir; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; public class DeploymentManagerTest { @Rule public TestingDir testdir = new TestingDir(); @Test public void testReceiveApp() throws Exception { DeploymentManager depman = new DeploymentManager(); depman.setContexts(new ContextHandlerCollection()); depman.setDefaultLifeCycleGoal(null); // no default AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); MockAppProvider mockProvider = new MockAppProvider(); depman.addLifeCycleBinding(pathtracker); depman.addAppProvider(mockProvider); // Start DepMan depman.start(); // Trigger new App mockProvider.findWebapp("foo-webapp-1.war"); // Test app tracking Collection apps = depman.getApps(); Assert.assertNotNull("Should never be null",apps); Assert.assertEquals("Expected App Count",1,apps.size()); // Test app get App actual = depman.getAppByOriginId("mock-foo-webapp-1.war"); Assert.assertNotNull("Should have gotten app (by id)",actual); Assert.assertEquals("Should have gotten app (by id)","mock-foo-webapp-1.war",actual.getOriginId()); } @Test public void testBinding() { AppLifeCyclePathCollector pathtracker = new AppLifeCyclePathCollector(); DeploymentManager depman = new DeploymentManager(); depman.addLifeCycleBinding(pathtracker); Set allbindings = depman.getLifeCycle().getBindings(); Assert.assertNotNull("All Bindings should never be null",allbindings); Assert.assertEquals("All Bindings.size",1,allbindings.size()); Set deploybindings = depman.getLifeCycle().getBindings("deploying"); Assert.assertNotNull("'deploying' Bindings should not be null",deploybindings); Assert.assertEquals("'deploying' Bindings.size",1,deploybindings.size()); } @Test public void testXmlConfigured() throws Exception { XmlConfiguredJetty jetty = null; try { jetty = new XmlConfiguredJetty(testdir); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); jetty.addConfiguration("jetty-deploymgr-contexts.xml"); // Should not throw an Exception jetty.load(); // Start it jetty.start(); } finally { if (jetty != null) { try { jetty.stop(); } catch (Exception ignore) { // ignore } } } } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/JmxServiceConnection.java000066400000000000000000000102131261716203600327630ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.io.IOException; import java.lang.management.ManagementFactory; import javax.management.MBeanServer; import javax.management.MBeanServerConnection; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import org.eclipse.jetty.toolchain.test.IO; /** * JmxServiceConnection * * Provides ability to create a connection to either an external * JMX server, or a loopback connection to the internal one. */ public class JmxServiceConnection { private String serviceUrl; private MBeanServer server; private JMXConnectorServer connectorServer; private JMXConnector serverConnector; private MBeanServerConnection serviceConnection; /** * Construct a loopback connection to an internal server */ public JmxServiceConnection() { this(null); } /** * Construct a connection to specified server * * @param url * URL of JMX server */ public JmxServiceConnection(String url) { serviceUrl = url; } /** * Retrieve an external URL for the JMX server * * @return service URL */ public String getServiceUrl() { return serviceUrl; } /* ------------------------------------------------------------ */ /** * Retrieve a connection to MBean server * * @return connection to MBean server */ public MBeanServerConnection getConnection() { return serviceConnection; } public void connect() throws IOException { if (serviceConnection == null) { if (serviceUrl == null) { openLoopbackConnection(); } else { openServerConnection(serviceUrl); } } } /** * Open a loopback connection to local JMX server * * @throws IOException */ private void openLoopbackConnection() throws IOException { server = ManagementFactory.getPlatformMBeanServer(); JMXServiceURL serviceUrl = new JMXServiceURL("service:jmx:rmi://"); connectorServer = JMXConnectorServerFactory.newJMXConnectorServer(serviceUrl,null,server); connectorServer.start(); this.serviceUrl = connectorServer.getAddress().toString(); serverConnector = JMXConnectorFactory.connect(connectorServer.getAddress()); serviceConnection = serverConnector.getMBeanServerConnection(); } /** * Open a connection to remote JMX server * * @param url * @throws IOException */ private void openServerConnection(String url) throws IOException { serviceUrl = url; serverConnector = JMXConnectorFactory.connect(new JMXServiceURL(serviceUrl)); serviceConnection = serverConnector.getMBeanServerConnection(); } /** * Close the connections */ public void disconnect() { IO.close(serverConnector); if (connectorServer != null) { try { connectorServer.stop(); } catch (Exception ignore) { /* ignore */ } } } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java000066400000000000000000000054141261716203600317400ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy; import java.io.File; import org.eclipse.jetty.deploy.util.FileID; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; public class MockAppProvider extends AbstractLifeCycle implements AppProvider { private DeploymentManager deployMan; private File webappsDir; public void setDeploymentManager(DeploymentManager deploymentManager) { this.deployMan = deploymentManager; } @Override public void doStart() { this.webappsDir = MavenTestingUtils.getTestResourceDir("webapps"); } public void findWebapp(String name) { App app = new App(deployMan,this,"mock-" + name); this.deployMan.addApp(app); } public ContextHandler createContextHandler(App app) throws Exception { WebAppContext context = new WebAppContext(); File war = new File(webappsDir,app.getOriginId().substring(5)); context.setWar(Resource.newResource(Resource.toURL(war)).toString()); String path = war.getName(); if (FileID.isWebArchiveFile(war)) { // Context Path is the same as the archive. path = path.substring(0,path.length() - 4); } // special case of archive (or dir) named "root" is / context if (path.equalsIgnoreCase("root") || path.equalsIgnoreCase("root/")) path = URIUtil.SLASH; // Ensure "/" is Prepended to all context paths. if (path.charAt(0) != '/') path = "/" + path; // Ensure "/" is Not Trailing in context paths. if (path.endsWith("/") && path.length() > 0) path = path.substring(0,path.length() - 1); context.setContextPath(path); return context; } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings/000077500000000000000000000000001261716203600276215ustar00rootroot00000000000000GlobalWebappConfigBindingTest.java000066400000000000000000000073551261716203600362370ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/bindings// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.bindings; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.isIn; import static org.hamcrest.Matchers.not; import java.io.File; import java.util.List; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.PathAssert; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.webapp.WebAppContext; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; /** * Tests {@link ScanningAppProvider} as it starts up for the first time. */ public class GlobalWebappConfigBindingTest { @Rule public TestingDir testdir = new TestingDir(); private static XmlConfiguredJetty jetty; @Before public void setupEnvironment() throws Exception { jetty = new XmlConfiguredJetty(testdir); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); // Setup initial context jetty.copyWebapp("foo.xml","foo.xml"); jetty.copyWebapp("foo-webapp-1.war","foo.war"); } @After public void teardownEnvironment() throws Exception { // Stop jetty. jetty.stop(); } @Test public void testServerAndSystemClassesOverride() throws Exception { File srcXml = MavenTestingUtils.getTestResourceFile("context-binding-test-1.xml"); File destXml = new File(jetty.getJettyHome(),"context-binding-test-1.xml"); IO.copy(srcXml,destXml); PathAssert.assertFileExists("Context Binding XML",destXml); jetty.addConfiguration("binding-test-contexts-1.xml"); jetty.load(); jetty.start(); List contexts = jetty.getWebAppContexts(); Assert.assertThat("List of Contexts", contexts, hasSize(greaterThan(0))); WebAppContext context = contexts.get(0); Assert.assertNotNull("Context should not be null",context); String defaultClasses[] = context.getDefaultServerClasses(); String currentClasses[] = context.getServerClasses(); String addedClass = "org.eclipse.foo."; // What was added by the binding Assert.assertThat("Default Server Classes",addedClass,not(isIn(defaultClasses))); Assert.assertThat("Current Server Classes",addedClass,isIn(currentClasses)); // boolean jndiPackage = false; // this test overrides and we removed the jndi from the list so it // should test false // for (String entry : context.getSystemClasses()) // { // if ("org.eclipse.jetty.jndi.".equals(entry)) // { // jndiPackage = true; // } // } // // Assert.assertFalse(jndiPackage); } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/graph/000077500000000000000000000000001261716203600271255ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/graph/GraphTest.java000066400000000000000000000103641261716203600316750ustar00rootroot00000000000000// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.graph; import org.junit.Assert; import org.junit.Test; public class GraphTest { final Node nodeA = new Node("A"); final Node nodeB = new Node("B"); final Node nodeC = new Node("C"); final Node nodeD = new Node("D"); final Node nodeE = new Node("E"); @Test public void testPath() { Path path = new Path(); Assert.assertEquals(0, path.nodes()); Assert.assertEquals(null,path.firstNode()); Assert.assertEquals(null,path.lastNode()); path.add(new Edge(nodeA ,nodeB)); Assert.assertEquals(2,path.nodes()); Assert.assertEquals(nodeA,path.firstNode()); Assert.assertEquals(nodeB,path.lastNode()); path.add(new Edge(nodeB ,nodeC)); Assert.assertEquals(3,path.nodes()); Assert.assertEquals(nodeA,path.firstNode()); Assert.assertEquals(nodeC,path.lastNode()); } @Test public void testPoint() { Graph graph = new Graph(); graph.addNode(nodeA); Assert.assertEquals(1,graph.getNodes().size()); Assert.assertEquals(0,graph.getEdges().size()); Path path = graph.getPath(nodeA,nodeA); Assert.assertEquals(0,path.nodes()); } @Test public void testLine() { Graph graph = new Graph(); graph.addEdge(new Edge(nodeA,nodeB)); Assert.assertEquals(2,graph.getNodes().size()); Assert.assertEquals(1,graph.getEdges().size()); Path path = graph.getPath(nodeA,nodeB); Assert.assertEquals(2,path.nodes()); } @Test public void testTriangleDirected() { Graph graph = new Graph(); graph.addEdge(new Edge(nodeA,nodeB)); graph.addEdge(new Edge(nodeA,nodeC)); graph.addEdge(new Edge(nodeB,nodeC)); Assert.assertEquals(3,graph.getNodes().size()); Assert.assertEquals(3,graph.getEdges().size()); Path path = graph.getPath(nodeA,nodeB); Assert.assertEquals(2,path.nodes()); path = graph.getPath(nodeA,nodeC); Assert.assertEquals(2,path.nodes()); path = graph.getPath(nodeB,nodeC); Assert.assertEquals(2,path.nodes()); } @Test public void testSquareDirected() { Graph graph = new Graph(); graph.addEdge(new Edge(nodeA,nodeB)); graph.addEdge(new Edge(nodeB,nodeC)); graph.addEdge(new Edge(nodeA,nodeD)); graph.addEdge(new Edge(nodeD,nodeC)); Assert.assertEquals(4,graph.getNodes().size()); Assert.assertEquals(4,graph.getEdges().size()); Path path = graph.getPath(nodeA,nodeC); Assert.assertEquals(3,path.nodes()); path = graph.getPath(nodeC,nodeA); Assert.assertEquals(null,path); } @Test public void testSquareCyclic() { Graph graph = new Graph(); graph.addEdge(new Edge(nodeA,nodeB)); graph.addEdge(new Edge(nodeB,nodeC)); graph.addEdge(new Edge(nodeC,nodeD)); graph.addEdge(new Edge(nodeD,nodeA)); Assert.assertEquals(4,graph.getNodes().size()); Assert.assertEquals(4,graph.getEdges().size()); Path path = graph.getPath(nodeA,nodeB); Assert.assertEquals(2,path.nodes()); path = graph.getPath(nodeA,nodeC); Assert.assertEquals(3,path.nodes()); path = graph.getPath(nodeA,nodeD); Assert.assertEquals(4,path.nodes()); graph.addNode(nodeE); path = graph.getPath(nodeA,nodeE); Assert.assertEquals(null,path); } } jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers/000077500000000000000000000000001261716203600300415ustar00rootroot00000000000000ScanningAppProviderRuntimeUpdatesTest.java000066400000000000000000000135551261716203600403040ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/providers// // ======================================================================== // Copyright (c) 1995-2015 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.deploy.providers; import java.io.IOException; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.deploy.AppProvider; import org.eclipse.jetty.deploy.DeploymentManager; import org.eclipse.jetty.deploy.test.XmlConfiguredJetty; import org.eclipse.jetty.toolchain.test.OS; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.Rule; import org.junit.Test; /** * Similar in scope to {@link ScanningAppProviderStartupTest}, except is concerned with the modification of existing * deployed webapps due to incoming changes identified by the {@link ScanningAppProvider}. */ public class ScanningAppProviderRuntimeUpdatesTest { private static final Logger LOG = Log.getLogger(ScanningAppProviderRuntimeUpdatesTest.class); @Rule public TestTracker tracker = new TestTracker(); @Rule public TestingDir testdir = new TestingDir(); private static XmlConfiguredJetty jetty; private final AtomicInteger _scans = new AtomicInteger(); private int _providers; @Before public void setupEnvironment() throws Exception { testdir.ensureEmpty(); Resource.setDefaultUseCaches(false); jetty = new XmlConfiguredJetty(testdir); jetty.addConfiguration("jetty.xml"); jetty.addConfiguration("jetty-http.xml"); jetty.addConfiguration("jetty-deploymgr-contexts.xml"); // Should not throw an Exception jetty.load(); // Start it jetty.start(); // monitor tick DeploymentManager dm = jetty.getServer().getBean(DeploymentManager.class); for (AppProvider provider : dm.getAppProviders()) { if (provider instanceof ScanningAppProvider) { _providers++; ((ScanningAppProvider)provider).addScannerListener(new Scanner.ScanListener() { public void scan() { _scans.incrementAndGet(); } }); } } } @After public void teardownEnvironment() throws Exception { // Stop jetty. jetty.stop(); } public void waitForDirectoryScan() { int scan=_scans.get()+(2*_providers); do { try { Thread.sleep(200); } catch(InterruptedException e) { LOG.warn(e); } } while(_scans.get() _xmlConfigurations; private Map _properties = new HashMap<>(); private Server _server; private int _serverPort; private String _scheme = HttpScheme.HTTP.asString(); private File _jettyHome; public XmlConfiguredJetty(TestingDir testdir) throws IOException { _xmlConfigurations = new ArrayList<>(); Properties properties = new Properties(); String jettyHomeBase = testdir.getDir().getAbsolutePath(); // Ensure we have a new (pristene) directory to work with. int idx = 0; _jettyHome = new File(jettyHomeBase + "#" + idx); while (_jettyHome.exists()) { idx++; _jettyHome = new File(jettyHomeBase + "#" + idx); } deleteContents(_jettyHome); // Prepare Jetty.Home (Test) dir _jettyHome.mkdirs(); File logsDir = new File(_jettyHome,"logs"); logsDir.mkdirs(); File etcDir = new File(_jettyHome,"etc"); etcDir.mkdirs(); IO.copyFile(MavenTestingUtils.getTestResourceFile("etc/realm.properties"),new File(etcDir,"realm.properties")); IO.copyFile(MavenTestingUtils.getTestResourceFile("etc/webdefault.xml"),new File(etcDir,"webdefault.xml")); File webappsDir = new File(_jettyHome,"webapps"); if (webappsDir.exists()) { deleteContents(webappsDir); } webappsDir.mkdirs(); File tmpDir = new File(_jettyHome,"tmp"); if (tmpDir.exists()) { deleteContents(tmpDir); } tmpDir.mkdirs(); File workishDir = new File(_jettyHome,"workish"); if (workishDir.exists()) { deleteContents(workishDir); } workishDir.mkdirs(); // Setup properties System.setProperty("java.io.tmpdir",tmpDir.getAbsolutePath()); properties.setProperty("jetty.home",_jettyHome.getAbsolutePath()); System.setProperty("jetty.home",_jettyHome.getAbsolutePath()); properties.setProperty("test.basedir",MavenTestingUtils.getBasedir().getAbsolutePath()); properties.setProperty("test.resourcesdir",MavenTestingUtils.getTestResourcesDir().getAbsolutePath()); properties.setProperty("test.webapps",webappsDir.getAbsolutePath()); properties.setProperty("test.targetdir",MavenTestingUtils.getTargetDir().getAbsolutePath()); properties.setProperty("test.workdir",workishDir.getAbsolutePath()); // Write out configuration for use by ConfigurationManager. File testConfig = new File(_jettyHome, "xml-configured-jetty.properties"); try (OutputStream out = new FileOutputStream(testConfig)) { properties.store(out,"Generated by " + XmlConfiguredJetty.class.getName()); } for (Object key:properties.keySet()) setProperty(String.valueOf(key),String.valueOf(properties.get(key))); } public void addConfiguration(File xmlConfigFile) throws MalformedURLException { addConfiguration(Resource.toURL(xmlConfigFile)); } public void addConfiguration(String testConfigName) throws MalformedURLException { addConfiguration(MavenTestingUtils.getTestResourceFile(testConfigName)); } public void addConfiguration(URL xmlConfig) { _xmlConfigurations.add(xmlConfig); } public void assertNoWebAppContexts() { List contexts = getWebAppContexts(); if (contexts.size() > 0) { for (WebAppContext context : contexts) { System.err.println("WebAppContext should not exist:\n" + context); } Assert.assertEquals("Contexts.size",0,contexts.size()); } } public String getResponse(String path) throws IOException { URI destUri = getServerURI().resolve(path); URL url = destUri.toURL(); URLConnection conn = url.openConnection(); InputStream in = null; try { in = conn.getInputStream(); return IO.toString(in); } finally { IO.close(in); } } public void assertResponseContains(String path, String needle) throws IOException { // System.err.println("Issuing request to " + path); String content = getResponse(path); Assert.assertTrue("Content should contain <" + needle + ">, instead got <" + content + ">",content.contains(needle)); } public void assertWebAppContextsExists(String... expectedContextPaths) { List contexts = getWebAppContexts(); if (expectedContextPaths.length != contexts.size()) { System.err.println("## Expected Contexts"); for (String expected : expectedContextPaths) { System.err.println(expected); } System.err.println("## Actual Contexts"); for (WebAppContext context : contexts) { System.err.printf("%s ## %s%n",context.getContextPath(),context); } Assert.assertEquals("Contexts.size",expectedContextPaths.length,contexts.size()); } for (String expectedPath : expectedContextPaths) { boolean found = false; for (WebAppContext context : contexts) { if (context.getContextPath().equals(expectedPath)) { found = true; Assert.assertThat("Context[" + context.getContextPath() + "].state", context.getState(), is("STARTED")); break; } } Assert.assertTrue("Did not find Expected Context Path " + expectedPath,found); } } private void copyFile(String type, File srcFile, File destFile) throws IOException { PathAssert.assertFileExists(type + " File",srcFile); IO.copyFile(srcFile,destFile); PathAssert.assertFileExists(type + " File",destFile); System.err.printf("Copy %s: %s%n To %s: %s%n",type,srcFile,type,destFile); System.err.printf("Destination Exists: %s - %s%n",destFile.exists(),destFile); } public void copyWebapp(String srcName, String destName) throws IOException { System.err.printf("Copying Webapp: %s -> %s%n",srcName,destName); File srcDir = MavenTestingUtils.getTestResourceDir("webapps"); File destDir = new File(_jettyHome,"webapps"); File srcFile = new File(srcDir,srcName); File destFile = new File(destDir,destName); copyFile("Webapp",srcFile,destFile); } private void deleteContents(File dir) { // System.err.printf("Delete (dir) %s/%n",dir); if (!dir.exists()) { return; } File[] files = dir.listFiles(); if (files != null) { for (File file : files) { // Safety measure. only recursively delete within target directory. if (file.isDirectory() && file.getAbsolutePath().contains("target" + File.separator)) { deleteContents(file); Assert.assertTrue("Delete failed: " + file.getAbsolutePath(),file.delete()); } else { System.err.printf("Delete (file) %s%n",file); Assert.assertTrue("Delete failed: " + file.getAbsolutePath(),file.delete()); } } } } public File getJettyDir(String name) { return new File(_jettyHome,name); } public File getJettyHome() { return _jettyHome; } public String getScheme() { return _scheme; } public Server getServer() { return _server; } public int getServerPort() { return _serverPort; } public URI getServerURI() throws UnknownHostException { StringBuilder uri = new StringBuilder(); URIUtil.appendSchemeHostPort(uri, getScheme(), InetAddress.getLocalHost().getHostAddress(), getServerPort()); return URI.create(uri.toString()); } public List getWebAppContexts() { List contexts = new ArrayList<>(); HandlerCollection handlers = (HandlerCollection)_server.getHandler(); Handler children[] = handlers.getChildHandlers(); for (Handler handler : children) { if (handler instanceof WebAppContext) { WebAppContext context = (WebAppContext)handler; contexts.add(context); } } return contexts; } public void load() throws Exception { XmlConfiguration last = null; Object[] obj = new Object[this._xmlConfigurations.size()]; // Configure everything for (int i = 0; i < this._xmlConfigurations.size(); i++) { URL configURL = this._xmlConfigurations.get(i); XmlConfiguration configuration = new XmlConfiguration(configURL); if (last != null) configuration.getIdMap().putAll(last.getIdMap()); configuration.getProperties().putAll(_properties); obj[i] = configuration.configure(); last = configuration; } // Test for Server Instance. Server foundServer = null; int serverCount = 0; for (int i = 0; i < this._xmlConfigurations.size(); i++) { if (obj[i] instanceof Server) { if (obj[i].equals(foundServer)) { // Identical server instance found break; } foundServer = (Server)obj[i]; serverCount++; } } if (serverCount <= 0) { throw new Exception("Load failed to configure a " + Server.class.getName()); } Assert.assertEquals("Server load count",1,serverCount); this._server = foundServer; this._server.setStopTimeout(10); } public void removeWebapp(String name) { File destDir = new File(_jettyHome,"webapps"); File contextFile = new File(destDir,name); if (contextFile.exists()) { Assert.assertTrue("Delete of Webapp file: " + contextFile.getAbsolutePath(),contextFile.delete()); } } public void setProperty(String key, String value) { _properties.put(key,value); } public void setScheme(String scheme) { this._scheme = scheme; } public void start() throws Exception { Assert.assertNotNull("Server should not be null (failed load?)",_server); _server.start(); // Find the active server port. _serverPort = -1; Connector connectors[] = _server.getConnectors(); for (int i = 0; _serverPort<0 && i < connectors.length; i++) { if (connectors[i] instanceof NetworkConnector) { int port = ((NetworkConnector)connectors[i]).getLocalPort(); if (port>0) _serverPort=port; } } Assert.assertTrue("Server Port is between 1 and 65535. Actually <" + _serverPort + ">",(1 <= this._serverPort) && (this._serverPort <= 65535)); // Uncomment to have server start and continue to run (without exiting) // System.err.printf("Listening to port %d%n",this.serverPort); // server.join(); } public void stop() throws Exception { _server.stop(); } } jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/000077500000000000000000000000001261716203600222475ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/binding-test-contexts-1.xml000066400000000000000000000044741261716203600273740ustar00rootroot00000000000000 -org.eclipse.jetty.continuation. -org.eclipse.jetty.jndi. -org.eclipse.jetty.plus.jaas. -org.eclipse.jetty.websocket. -org.eclipse.jetty.servlet.DefaultServlet org.eclipse.jetty. org.eclipse.foo. java. javax. org.xml. org.w3c. org.apache.commons.logging org.eclipse.jetty.continuation org.eclipse.jetty.plus.jaas. org.eclipse.jetty.websocket org.eclipse.jetty.servlet.DefaultServlet /context-binding-test-1.xml /webapps 1 /xml-configured-jetty.properties jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/context-binding-test-1.xml000066400000000000000000000020501261716203600271750ustar00rootroot00000000000000 org.eclipse.foo. jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/etc/000077500000000000000000000000001261716203600230225ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/etc/realm.properties000066400000000000000000000013711261716203600262420ustar00rootroot00000000000000# # This file defines users passwords and roles for a HashUserRealm # # The format is # : [, ...] # # Passwords may be clear text, obfuscated or checksummed. The class # org.eclipse.util.Password should be used to generate obfuscated # passwords or password checksums # # If DIGEST Authentication is used, the password must be in a recoverable # format, either plain text or OBF:. # jetty: MD5:164c88b302622e17050af52c89945d44,user admin: CRYPT:adpexzg3FUZAk,server-administrator,content-administrator,admin other: OBF:1xmk1w261u9r1w1c1xmq,user plain: plain,user user: password,user # This entry is for digest auth. The credential is a MD5 hash of username:realmname:password digest: MD5:6e120743ad67abfbc385bc2bb754e297,user jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/etc/webdefault.xml000066400000000000000000000567711261716203600257060ustar00rootroot00000000000000 Default web.xml file. This file is applied to a Web application before it's own WEB_INF/web.xml file default org.eclipse.jetty.servlet.DefaultServlet acceptRanges true dirAllowed true welcomeServlets false redirectWelcome false maxCacheSize 256000000 maxCachedFileSize 10000000 maxCachedFiles 1000 cacheType both gzip true useFileMappedBuffer true 0 default / jsp org.apache.jasper.servlet.JspServlet logVerbosityLevel DEBUG fork false xpoweredBy false 0 jsp *.jsp *.jspf *.jspx *.xsp *.JSP *.JSPF *.JSPX *.XSP 30 index.html index.htm index.jsp arISO-8859-6 beISO-8859-5 bgISO-8859-5 caISO-8859-1 csISO-8859-2 daISO-8859-1 deISO-8859-1 elISO-8859-7 enISO-8859-1 esISO-8859-1 etISO-8859-1 fiISO-8859-1 frISO-8859-1 hrISO-8859-2 huISO-8859-2 isISO-8859-1 itISO-8859-1 iwISO-8859-8 jaShift_JIS koEUC-KR ltISO-8859-2 lvISO-8859-2 mkISO-8859-5 nlISO-8859-1 noISO-8859-1 plISO-8859-2 ptISO-8859-1 roISO-8859-2 ruISO-8859-5 shISO-8859-5 skISO-8859-2 slISO-8859-2 sqISO-8859-2 srISO-8859-5 svISO-8859-1 trISO-8859-9 ukISO-8859-5 zhGB2312 zh_TWBig5 Disable TRACE / TRACE jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/jetty-deploy-wars.xml000066400000000000000000000017221261716203600263760ustar00rootroot00000000000000 /webapps 1 /workish jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/jetty-deploymgr-contexts.xml000066400000000000000000000027101261716203600277750ustar00rootroot00000000000000 org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern .*/servlet-api-[^/]*\.jar$ /webapps /etc/webdefault.xml 1 true /xml-configured-jetty.properties jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/jetty-http.xml000066400000000000000000000035621261716203600251130ustar00rootroot00000000000000 0 300000 jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/jetty-logging.properties000066400000000000000000000002161261716203600271470ustar00rootroot00000000000000org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog org.eclipse.jetty.deploy.LEVEL=WARN org.eclipse.jetty.util.Scanner=WARN jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/jetty.xml000066400000000000000000000165271261716203600241430ustar00rootroot00000000000000 false https 32768 8192 8192 true false 512 true 5000 jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/000077500000000000000000000000001261716203600237105ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/foo-webapp-1.war000066400000000000000000000100541261716203600266200ustar00rootroot00000000000000PK lS; META-INF/PK lS;Df}META-INF/MANIFEST.MFMLK-. K-*ϳR03r,J,K-BV+$xRKRSt* 3R|RxJ3sJYٙ]lfz@;xPK lS;WEB-INF/PK lS;WEB-INF/classes/PK lS;WEB-INF/classes/org/PK lS;WEB-INF/classes/org/eclipse/PK lS;"WEB-INF/classes/org/eclipse/jetty/PK lS;(WEB-INF/classes/org/eclipse/jetty/tests/PK lS;/WEB-INF/classes/org/eclipse/jetty/tests/webapp/PK %kS;j mWEB-INF/web.xmlSn0ﶁ BIP=Ѫ!7ـc[YC$<*iݍY`J#2bCNL*:"/OAXqZ9;,KV1Sx82@.N ҳ{pYbrK\cXj*Lk'S,L"\=M_V2|8؈+l˙VR>蟥}ÌdP+ ;R*VP`ʫL~)=#=6DlsxefL:*3%f٧WZ))N#ZňSrш4q\~mr+\r ע*RUDj~PK lS;META-INF/maven/PK lS;'META-INF/maven/org.eclipse.jetty.tests/PK lS;4META-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/PK jS;`SJ7;META-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/pom.xmlTMo0 Wh9m[IסC(~ɾN"3:Y2$I(qܬ. G'RcV8 (3:<*zv`DMPK lS;w7Fb{BMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/pom.properties OCgE vvp *on0[=0J#Po.}݂6Rv\| C+P;Pa6_Yc_mJ\PK lS; AMETA-INF/PK lS;Df}'META-INF/MANIFEST.MFPK lS;AWEB-INF/PK lS;AWEB-INF/classes/PK lS;AWEB-INF/classes/org/PK lS;AEWEB-INF/classes/org/eclipse/PK lS;"AWEB-INF/classes/org/eclipse/jetty/PK lS;(AWEB-INF/classes/org/eclipse/jetty/tests/PK lS;/AWEB-INF/classes/org/eclipse/jetty/tests/webapp/PK %kS;j mRWEB-INF/web.xmlPK lS;I8@WEB-INF/classes/org/eclipse/jetty/tests/webapp/InfoServlet.classPK lS;AbMETA-INF/maven/PK lS;'AMETA-INF/maven/org.eclipse.jetty.tests/PK lS;4AMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/PK jS;`SJ7;&META-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/pom.xmlPK lS;w7Fb{BG META-INF/maven/org.eclipse.jetty.tests/foo-webapp-1/pom.propertiesPK" jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/foo-webapp-2.war000066400000000000000000000100551261716203600266220ustar00rootroot00000000000000PK lS; META-INF/PK lS;Df}META-INF/MANIFEST.MFMLK-. K-*ϳR03r,J,K-BV+$xRKRSt* 3R|RxJ3sJYٙ]lfz@;xPK lS;WEB-INF/PK lS;WEB-INF/classes/PK lS;WEB-INF/classes/org/PK lS;WEB-INF/classes/org/eclipse/PK lS;"WEB-INF/classes/org/eclipse/jetty/PK lS;(WEB-INF/classes/org/eclipse/jetty/tests/PK lS;/WEB-INF/classes/org/eclipse/jetty/tests/webapp/PK lS;$6mWEB-INF/web.xmlSn0!RJU%$zU{CncGYC$<*iݍŴt5! $6Ufח'zO@AempRƹ|yYcXp8"@&2I ҳ{pgXb3K`Xp9f 6P/+F ><:{$-lLb_\52xe-"^wJGn ڛ]) T+tG +B6.ӂw qRߦ;zfhE. ^yY)`Rxu>3+hڿ*@ʓ@]V%bDmsYsxֱv3gѿ?9(LVj{]PK lS;p @WEB-INF/classes/org/eclipse/jetty/tests/webapp/InfoServlet.classSn@=7Ԥ!M);)Kfhy P jJxs!ƞx>B\/HH4|̹.3x\@<6qBs?lfnA(W=~\m^.N-G,{*"(G駄t1 dvݱ$#?6#;bYy}B!G)֧B@Z'/[!;v_HM8k&xKi퉗l4e΅d'l BmAuBO"F<@C=4u;T5Q iٖr LlmBa}=کƩʙ66 ?a5$9ts|tZ<^a[DϽE4yF<f$j`>p/\6`ױe^`Kٖ} %\f[(5cVQa0+ ~>;*ΐNu ٯ*FSgFYkW<\SUM'RPK lS;META-INF/maven/PK lS;'META-INF/maven/org.eclipse.jetty.tests/PK lS;4META-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/PK kS; >;META-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/pom.xmlTMo0 Wh9m[IaC(ې~ɾN"3:Y2$I(qܬ. G'RcV8 (3:4 #8Xgz8fܡ:Še4Ihǵ@2ub|%28zOnb9^6z> Ia9nge&G*!C>PV跍ye#lzhUH0QtDƵZ2ᢏ~<2q!w1k#*}51=XAv%8>4mhwXqw9%DωэSC%5>*zv`DMPK lS;H|BMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/pom.properties 1 НS@m@xͯbl v*nnԩ h3e;cB4qb[t_7SABC*fu/kM+PK lS; AMETA-INF/PK lS;Df}'META-INF/MANIFEST.MFPK lS;AWEB-INF/PK lS;AWEB-INF/classes/PK lS;AWEB-INF/classes/org/PK lS;AEWEB-INF/classes/org/eclipse/PK lS;"AWEB-INF/classes/org/eclipse/jetty/PK lS;(AWEB-INF/classes/org/eclipse/jetty/tests/PK lS;/AWEB-INF/classes/org/eclipse/jetty/tests/webapp/PK lS;$6mRWEB-INF/web.xmlPK lS;p @WEB-INF/classes/org/eclipse/jetty/tests/webapp/InfoServlet.classPK lS;AbMETA-INF/maven/PK lS;'AMETA-INF/maven/org.eclipse.jetty.tests/PK lS;4AMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/PK kS; >;&META-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/pom.xmlPK lS;H|BG META-INF/maven/org.eclipse.jetty.tests/foo-webapp-2/pom.propertiesPK# jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/foo-webapp-3.war000066400000000000000000000100551261716203600266230ustar00rootroot00000000000000PK lS; META-INF/PK lS;Df}META-INF/MANIFEST.MFMLK-. K-*ϳR03r,J,K-BV+$xRKRSt* 3R|RxJ3sJYٙ]lfz@;xPK lS;WEB-INF/PK lS;WEB-INF/classes/PK lS;WEB-INF/classes/org/PK lS;WEB-INF/classes/org/eclipse/PK lS;"WEB-INF/classes/org/eclipse/jetty/PK lS;(WEB-INF/classes/org/eclipse/jetty/tests/PK lS;/WEB-INF/classes/org/eclipse/jetty/tests/webapp/PK lS;SoQmWEB-INF/web.xmlSn0yBIP=Ѫ!,`V֐5 JcfggfwpZf*8@ XJ3} Ӹ5NJ:g'E3x/-dJNH/=;q %&5V9;xl~.ǬĴrB9*$Uc e%ӈѣ'r␍Ia**qZd^kQ[M$Nd[ qRwhSSXr34?h#AeY)`K{zw:כ[Yq!!oAmVbDIvܑ9@>B\/HH4|̹.3) j˸{ǃ6Q3P7 +H?P@v]'Ж=YrOSB2XVzʑӏCZCrN>Rs#[yRSeY-]ۗ-u/&{։5A%K6 @~LBZqw6`q:!' KJr g#ir`i!C}w S(bIZδlK9Z&6!lTTcL lx6 ?a5$9ts|tZ<^a[DϽE4yF<f$j`>p/\6`ױe^`Kٖ} %\f[(5cVQa0+ ~>;*ΐNu ٯ*FSgFYkW<\SUM'RPK lS;META-INF/maven/PK lS;'META-INF/maven/org.eclipse.jetty.tests/PK lS;4META-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/PK kS;9;META-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/pom.xmlTMo0 Wh9m[IaC(~ɾN"3:Y2$I(qܬ. G'RcV8 (3:<誼R  `fX1\im`2:Mhǵ@ "ub|%2/8zGnb9^6z> Ia9nga&.+!C>PV跍{ye#lzhUH0QtDƵZ2~<2q1w1k*}51[Av%8>4mhuX{pﷱ9%D/эƉԛF KHqk|3T$-6PK lS;y |BMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/pom.properties 1 НS@m@xͯbl v*nV:ue;cB4qb[t_7SABC*fu/kM+PK lS; AMETA-INF/PK lS;Df}'META-INF/MANIFEST.MFPK lS;AWEB-INF/PK lS;AWEB-INF/classes/PK lS;AWEB-INF/classes/org/PK lS;AEWEB-INF/classes/org/eclipse/PK lS;"AWEB-INF/classes/org/eclipse/jetty/PK lS;(AWEB-INF/classes/org/eclipse/jetty/tests/PK lS;/AWEB-INF/classes/org/eclipse/jetty/tests/webapp/PK lS;SoQmRWEB-INF/web.xmlPK lS;eJ@WEB-INF/classes/org/eclipse/jetty/tests/webapp/InfoServlet.classPK lS;AbMETA-INF/maven/PK lS;'AMETA-INF/maven/org.eclipse.jetty.tests/PK lS;4AMETA-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/PK kS;9;&META-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/pom.xmlPK lS;y |BG META-INF/maven/org.eclipse.jetty.tests/foo-webapp-3/pom.propertiesPK# jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/foo.xml000066400000000000000000000005541261716203600252210ustar00rootroot00000000000000 /foo /foo.war jetty-9.2.14.v20151106/jetty-deploy/src/test/resources/webapps/logcommon.war000066400000000000000000001326071261716203600264260ustar00rootroot00000000000000PK l/; META-INF/PK l/;Df}META-INF/MANIFEST.MFMLK-. K-*ϳR03r,J,K-BV+$xRKRSt* 3R|RxJ3sJYٙ]lfz@;xPK l/;WEB-INF/PK l/; WEB-INF/lib/PK l/;WEB-INF/classes/PK l/;WEB-INF/classes/org/PK l/;WEB-INF/classes/org/eclipse/PK l/;"WEB-INF/classes/org/eclipse/jetty/PK l/;(WEB-INF/classes/org/eclipse/jetty/tests/PK l/;/WEB-INF/classes/org/eclipse/jetty/tests/webapp/PK Z9<4_6'WEB-INF/lib/commons-logging-api-1.1.jarpݒ-m۶m۶m۶mmm{wK}=W*Y3ƨZU  -LVTEVRN^`6!f5w Xl|ܐg ~+L0h'BQ*Nu%wa0uPiϹƲx% R9<V`>g"G^',o]/Z >X D64,=Z?"4bvUF}r_>*3;84RUKA-<Ϳ_FRXTNYeGKyk~ݩL,4(VRÎ,9 a 7'CMUj!6gO~*ֵ\?uRK֭rmÂ;[[gٸdW mVȅTw.m:{wgrzhGw_ow}Z?yep7X7+:Zn'AkG,ȶdYb-Rn~IpSmrEz?F.Ujϫ*Ƒz(|ږ=ބҪ35B@1[orX|)՝(Բ ^]!P hGW1+e9a8 6ܴƷlM$"ISH%!04Gkqapk,k:ڌ[jϻI~ / ǜ<:[I6:L_!Kt,C:% d4F<[r 5p}lB# (S/2&Ok}Γwb)zx^ h_`v,6hCRBk٫ք?Yˢh-:Hjt5aN$1[T;3?79r]o +|?ģr \SgPQ~j!.-Y(_B*\A?pN(h%hn%4͕mN)o; "z|eYALTN |V)6]^a-P3y|debÌL'UE2S $`v 3Dhr r|~M;VyG;p, 9aCS4 \(!_yw?p+5wrv Z%6D ~7`V 0Ro1 cq2\ <>3/N? |L Q`Pv£&-.!{A1hhE(rܐ3alx?)Z^7ƍ covxc,R kb 7Ϛ6h z8*`mqeH4-ШAy*'&BB-fc̞9Tg`'NAЪ`e%3U:KI?/[=ͯ4|?\?$:xo&8m gp6ҼS#jC>gP-k6QH<T 2 A";4c"mU~OK/5 拼"8friR(a(pɋ+K3!z8Rj絑2 f^o qI0F~ w<@ܨ5n_n6%{}qGZ?PvM4%ch3^5}1]K ,~d^5Ziel+"v a!RGf^ #=(UQSgXd) ıˁ+ .!rBi 1], K;il1G'"f{'moiӀ{ܾ|&Ld?#:Zs/8nW%(Z}R@;=OCBF3bO4FkGk8b0߱w<ڳazX6/ql>R8"֔؋H: ~ z"zygK)`S) r6^bHxqkFR)#?eFI]u ,x'kMͪ@ʱ݊Cp,qy+CeiyZﴍmv_qaG9:OF]9߯[!G!l`5+O1 VY [B{򸆉k6wkK/ lRnW30WSYq\JdH_f ڿl!e,9p8cfG]4$mRej[@&Z)20X)xد 1*#RԢ E\%eOR]1L虝/k1jQVQc~Z1RG.Lh^IƼlh (w9E KBF1CrN3%n :hc]b-d$Vʴ,7,}֋|]M((f1Cc{'Opl#FBe&.By]41)\̄(wJ sH 5V[X$F7_F]@%HRt@ pI 1I!O|dLjZ:! C: ny„/7 o Fڊv?s̍8U™^^IzPq@"2bRd5E}+櫒EPʖ`bgж3zKjw̯ AW65`Z24,f!I D:=b:s0"A [Wt\ ^7+Sn3FaQnV` -&aw+1 ] WFS8͞R_|W瀤.ԑ. BIP(8!*4kk+2(zGJEz2?Y_xsK%; ⸐IEz ݎT[ Sk3n#&bvp"%7_rZAs0eKeqXg)0h6;^`A+aL%}՛~0遾0g"^V?AIUgJ`PFD!2jJW.p,_˰Bߌgr7ųBΩw( ΁&H4R@MQD5o6]eB-RjY%>4pH <VafF1 /1Q\kfp"uVU2ח96e}Q_UG BZ Ubʌ[.  MMsKdw9N.s;~t?`cՌ'Ec=1=4T{xH;KĂ#'uUB+DGP!#6ʂ'!!kedI^X1c31Xh~4HE̲_jڇb/Ŵ6cV-ߛӿ|/;SktdGK/^{|x^OpQ=3:KXIaWBoV9lK%^q(X gM^)qy1w$FxG_7ࣁ70 琼A(kNK : ,f4KT=z``YfJ[45~c KΊULsH{({d\DE%!TJZ4 )H*1\ohC ᖤ_xɨ?!gDD!>qs ^uSQh$2RN5ҭt5RE j(&X2Q+TV"!nr.qÕ'˕'yř5Pk ˹ g|FMJ86^9Ƹ8T$ЪMΕ} ֺD9i_g(4Eu83qSqO0cXdSΥ"o' 1tM3;To]6z֤e ( ˪\Z fX7,5ߟYz ѥ͓0R7K8 C.N( Oͤ*;]VOm#y"A.vdFO? h;&4b.o-Ar .>Uz9ƆPo>(kw];=.-Q^ !U0bV+@rK>#=#l{z@MvI덹Íٞ]m/ԏ'3h@6 ~=q\l%qEVkgMq֮_q.:cj=\gKcpܮzYn.,Fx^_ BtoYBv0R[3o!8$ _^;{PJQ\xw6pv۽NW߷#ϡ޵ ϱ]MfCL"\ulNdW4{BJ1lRWͽwZ\"#%7c^#>mƸPA@Dm~ܓ$? %WǺw)t@6A`2J^iTZxc1s MAY^~"ήuCDjGMN9BSHp?!(S<Դmˈ/,7*Zd~JL!G3-ChiRA3 2B93)P)|Fsp!eH6[Cq򨕶g1pedXMlܒ{8DhXя'1ժM("&LQB{$eD\h8bq9cƍbo#s7)k%)8N-Lj ~ ī8Qsa|, |`s@k$cW[\<`c;>AE\ ȲTWD1_njO<,?Gi1 s5P!K̐29@28-!|K!%t2̮3 | ` =G2~vZwCY-HD9e a3̿R\8o8_-.Xb[ SKx6j`#ϮY`a׀ 04MD/r?HY9`C p{[0CJ8= ,ƲQu8$P»Cׂ,8w7g$DQӗWW6v++jkTU*ȕFu F!q/ *`WUt<{rCs}])hƵzղ,[KP{-]K=e y+ĀdѓMKLF 5yx Z~w6=|V_X gwa>->qiio9z`D{$aJV+*l/n.+l/,je ϥ-h.LUkQF)ZZ:d[Çi!-roڮ&u E1 #KIj/j9n9QPr"ѩuP mضŏZSqYGRL05{T3ܗG%Nt 49tm)4R ?T͝eq*jc?N8Ozsi: )':Ä$>&o"|h6e G+!WQUWZWThkʹQv%vfэl#b'4S=;`}WEРuAμ(\v|*Zj>b {z#eKyhzTY5#^VSh3%agzKt"$;lKȤ>*4PX]ndX??1#O$oGA[4q<@l3W"8kLjEV¸ !a͕4<-Ȥ|l_\Q}r5";'+K#2@ ~9q6q>^KQ`Y^Z -/8xeUeum"v}^qw/5Is 6`(V H捫R(n3FS9c3NԈ柆xQٸ/sL)zat"]vq#\^+NqUMԆ^!\©beT|jHmZ sbg2Tr'kU) b9ް9GJ H8b9G pMp eBUD 2LߣPPc9.Ir GC-C6llef+  BznL.R~bo1} }K1h3 -`2§(1,W*$y`Rs5, }) >,oTRnó~1KuAIcƌfnXV)-&31DBZR.tGYpO.ALj#XTf ,+hFB׍p/L~{ )m )jȔdeBP+g]݈Aϡ[*멚qV#!YZL[=_f̊}* Kn) x/_ 'h%=Qy";ҏ*v2զ ""?\PtDTr>PR/֭-ݱ}D̔19 2 j,o{L=4pLWIYl,F6w4~HZGF9KҐpU .#(Nꢩ?℣l O*QvC ZďGf WĹҸI>;B1DfJ !|xPlL/7 %vm{ 2Jkm: |k\"2ebǟ/[ZMs'&eĄcSEh?M ٚ ,j&3cd,9Cj7On&㬨GP7eOcoQz+QXIT ,Gp`C)7ȄQ6u0.ayGv\OáC h-,.,%lkm+}RJw!훧4ߨYc|.!oCAD?- o4[΀+~"Z|}?Hϕ{%3L~O Iw!3}yĜܶpugWVL%[<8X ;xY Ķ_=tQ &e5I:E- eeD!\d&2}h^|eC|5Hf19R"~zmlqFmہ︁snǴPcY :P2P1;#^Ck[ | [Gh8tA,m9 ruH`BA-QR5r&g/Au5zQ$ly׌N{o@w)&n/TH>@"h + >XZ +tNZ[\\wY9vfMs**'ۓ5"p%F/Lpa 4gnğ<xKO &2yrOVfJeu; Sa7!3)MU*uW?`s<¤Ue̡̧T>M3!7 LNT5>P?jBBQFd;lWpbQ6S6kSYrvW ~J,MFޡsT)'$;D5S.HQPөW/^K!SNVz"J@IZ[\im Uی.ݙsBWoG)ۍͥpI}9aUoL%nL nRGDN6I(DL AHO2RϰG@9jz,9 7hЌ:Ɵd q^t@ygʔ"YR/H<ՍJWȢI^NrTc,-w([ΆYb~gS̜uНkdȅC6ҾآM= Z%=GMF@S\#vnʀ>HS$T&H&L*i;9"0,T9@YcVĔ,X 41]<|LnݨDR EԖ3ϓjBn9áߋ>*52iմo >Ȗ`Ig-3a WV_ ٖ!/mu6tMCmƪP-.͎p[@oYN#%Țe ĨȜ K`#Q<ܰsu͐e)+*(h\#Y):Ekma>d..έ v?d8k,THl猴RYrS[R^.= o=Q$Dz†gt>D$RrrE )Z%g -e Nt=,"w LKrnZ΢^ECjfp+8NyS-և~FjWq;^8B0иu}r)0ᄎiqM R $`yIؐ|>0p傲TNE73+XX8".yƍ YZa`.,-2H/3fyT vƩ OP;Y<և`ڦ޻jjVm:G.AØ^{qvƔ4fz%n@NFd=^3)L(əTh6x'c-83TW,Uu%{֌xfJU>ɽ6]x#]0Z&ܠG0u{WKČ="C6.D~VeF"kZbZveπH-x/:|B#$(˶t۳'ܓ'[n/-]< FhzSyS)=S]"MiS74՝]Ԅ_{>ʐl9#WASJ07`{Aݖ>h7+7r =$~7`p~2)»s/^7pp~!XMt8;kށ7[7/n/c L$ٳ3ta"vM{!Fz;.F{4LlH~2hvݵʷ];}h/ =s^T>;na} Em@)z>TgQȕ(˖tj'g"UWHKZH|"iIx<X3[\hD9QҜzd@Y` dYQDAGϬaXHh뎕MP$@cx2H^Rb5~'+-%f.Io83 M۰$ׁZN}rNH']eӂcƐ`'Nc~A ◁XZoa-~F\:G;jzF7$[= A_+# W,C`a]25 \U8yNA9M͸9Nwm]YAj5ȈxAU{٦mIS)_%-E; tM׶m۶m۶m<׶m۶m۶ow3lҩʩ9u ublb8l]ر&?" b`{gCDnّ NUٹgžxa^qzAS *z:'*zk=J]Zժ=ܐc`'w2ojt yn^{my5+w M!buݓ.qݭġW#@fL bPj:7i*xoUmc+Მ;Gug'yG cOl- 6+|EWrulwdsxn/ϭ]u؋hq㾣< ש*&s44P0)xdIs}LHa/"w[(}|j?wD/b/P/~)g߰V+?HroX/w=k~}6/"Bľ$T„Fh vH$LcJ% Xѵ^XbyK3I_J]('*E%ȖQA4C7(Mm.gld.nNo9 S 9TPzջGY.1`ŁT1'Ey?vDpO=ëlFU(,50כN^j(5@HhV)"UJ7"^=-^務&53z7&ڰֆYjF&[3ܞ,JѶZ3 Eܧb,YLÀN< %P^/s .@L aʆէa0dSXw/ek4dws4 CBӌJm"491F_kY1D65<&0-d{YBI0{2RwJ'wz3 \X$%G鯏D8RZ_05br) ( h1Z(XxlQn`6+!Q@iK_T:59[-Ob7B_tA-Qmnϖh+3pţ+PNrم42e jey1h_ wSOh,9Skmx(˸e=s\*):G涎"t*1 M8KK$&vAZh^W@<*ׄ#1OOy{m o 0[ Roӭ~0#mCKZg_97{gX Z>"e+Xg gV@6;:$SB&3"s >Am7g-sw6Zlkf"'t<퉧5EO_Ȯͫ"GoRzQ'%2g6$;wDF],RUPwF|߄s=̢8Q$T")=6e^?Y#+.w39S87n03" ORlesO0cHuv1F"9ʰ!G]<1|0Fx``% cb?@q|+42}Y0t}0wb?u'=-J [@1geM͌~Ou0gXeLҒ((9Cń&4Rو  YM̼/("#j-RjIP@L4y^N+T~z>= zdgwBr(9EvG*US8jS=Ft .G! XS_TZ2Fpۚ>R^g##EȑU;}عht$w#m#} , e,F ӚhC^S?"]&#s";ԪЀ-__j|)\Ͼպ0(r@[MA;\ݫ~}B&#5h=zqLݾW=iKŊ$NFhU~)iX[`G)D;3\Nik8Fĉs~dEm$moz[M.ɋ9qD4- Ф7Ը:BhC ȦXb5m#1> n||F{i g;Yg4͐/*6@~6ﭬE˧laL3$5Gx^~W ,FuDa\?# ~Y4LjO`.K` Lhũ&lSiJ4I - ^_Z'g>i[>v(8Ymg(=%Nc;|'T=bz$ʑw.maɎہl!g}A͌Gʗ4E0Xp55tI8~"j#1s,D)+kr<rr=CP~QJ6BI6%j]3sTc4Q+PZN`?+'7[jl~6?tdXY,[=LnfOVM/U;u<.#?!+θG 5P'] Ƚ69ߴ %O&N_{C< p?a`Ү"6oy'C%ktPSw#?Kxv C_jF 5Ve=7l & ۷dsdN dJhCꌊ`[m;٠q)9qgszVj !S"nAӱW=C4~((#Jufv t;=wIш/<i%F zǤ])@3XL]]Agi`?M>`x-eb57u?&UV[e^?8n2 )o^RX!_asMpD жr0@ =h2.wpgbDwpth狌iǘc!g=! Hi0/)91př2ZcOկg*T&1ZFb{ˆ̙SMWSmp$4ܕ8g >$yݸe|yJ]mHSB(6Pa*0p|es]b4,=Uhd;3KٜWl[/[@zdh21QCa8R_L>OKe^+nK!vgYhVtQ,z^RF,9ke%:6~yj! ҵ"SS*TL;ȺO%R.f<X''g\~QE)4ocەUA8Ɋ8i(0~m}`NJ\l$?^Y*J%F cƛE*lmԩKŠe&!J-X dvڄi-P= 8WISdGWƍ8'AuEBfĠ+fT~{m")1p#1jS1)@|C6#E=FjCP~HAk4˔ Cl47Wڈ7+|SYF.6U,m6sYHn#M&;^nʄVePOV9 ܕ>vi3۶M#g+ =fml67޸O դ(}%2e {IjNmJOeY܈8DlFRC; /<./^>YQYWtnےv*9& oDK=D~aw>h335 _t(&`ٖ4VtJWb3|'qm@U[Y^nGxh>)bh*QW0ƀ`HD~=5=D1[qP2alliD.臱[3穲s3{M1k{ہU5120*m{;1!ddpmavWǶXȯ;/xZhwٲl= ~aBvuE'G@"q52(ҢUzA +V`qDNmr}@"Q"Vw0QEM!p行*ܢA4AYU$ZKM!JJQ(uHY04F69ucK~7mevOnAk{hIӼcd.4EM],铑 YFk&  Ah^bLvF}J[ghjggTHKKmKjU_ʍḻ,CVWmV :|0ǡ.Ezqkl+|ؒkv n &cj(cS K%R-݊cVٹGnޒcY;0X޹GS,ʃ.nőG\ۅؗ0ۅ1ׯΘĘؘwIl##n;_Q_ّ:k bUnnhU K:nBm3?I 8 B'5G=B(yB9p+.C^SlzBInr,w2="4N9L9 @Wr~~Ӓ3a޳9'X2c,ok'Kv u~~G9?ڐ}!ADBOD e]97EK'HO-95OMdBfŘlQ1fF,iF b,8- iLҘ,X8,vڧB{߲'ӓV;MBO|ջiCN4®t٫]Czq:x:9㎐t! CM SIJ%̒c-Iӝ^09Hu l#UE 41u;ظE (E1jϑTHOr1橑b9D m(~UAwhHoE¶$ԏ09 lD+egxd 'ލ-W `Ŷ.%†lNPA""ʓ7%B#dz `4F|ePS{,9P=@'@a&wboI:,lk ^5a&& Ѳ`ߌQHB|i6V+$7)ۢe_k&Uynp0&K>CL56r"@憱'+[Ijn~#EniF?0),=CEA}=ҼTŶܫ mH![Tn+8*[,3N3WAją&QܶAwiLħv[e)6F*DLg´sLaaʝc}fEuP\2vԆ렍}#hȰ(hXCPJãjfn %Yq^vkQ3M::[^w^LȬ|fI~}\t_]+?:<+k2 ,CTPѹPV3U;%RzNYsy E*gdW3F!ckJCR[,\DŃ&9CEy(o[:!GLbo4*ס)8k ǹo߲7Pt<ȿv|=t8SP^ieYy["m\`yhzB1LҒB5O>V8y(8Ґ 'N/"Ad5.og7@ox ]dr*55iث.=Ef|Ȱ\/bRd2rh@i l=/)fYu7l" C[vAK'h'|訄-LtI /vkNMlG]T번&j!lʵSYYJSm5tH\jo䄭ZJ/#k"dh$[v!j5S\m 4H2ߩ^}{ɫm5ؑakl`,5O}c/~T6 4m?r(g1N*H϶mg008aJ~@sUG kZ,w|hKdOؖ6ga5ո-w좶D_]?.džڳKs͗q|¬,,cv {z <7$ԭs~Ѱv?_rbcM4Qco`#h!mY` 6^$;v]DmIc4 'h Ѝݿre $!)[!g -h "uͭ @RH)*zmMxj:.=kT"˕ݙd#DA\>EDY-B h=nſpS}$kRwXк  ATHzSPz7~bJ R&_F/[7p4W28A! #&qVL 6{kI=Mښ .9+Ћ("V}:Y\ҵ55ͻY;Wy_D|i7~AdfXovZ}RO^Z]޾sV9Kk*vV3|@TmvaTA|N%ˈa5zx3)l-1:\=T.2ĺ-bczDY3L~Bȕ'3dz-(٠}2sN7M2aMdS-uerZ 'gf?,~$ZWQ;phucq}S`1.it7%0e۵XSR&dg`SalbA$dL54w]p7Jb;c}30b֕GpA0AS%grQB۽F7ʼnvD/*42,.ՐHH@KRۃF3 ۞G֯P>yR'2_`J{*^e1Lk\8 )u8c)c*|JS5VW!ioKv_!,2D0#kz-X9yP EP q4 M0S]-\M9T-A|Yyc]&;= Dr"A)f7epϜ>6G-޻9~݉msebXT+bqnmCPe&kb@Ja)45d?"pڼU!ʒչi\)r_"3' _kK{ʹzOxߛ߿m1ftARHǹ2Xhjk)iنo#]σKL^@%!1seʡE0RZ\&2t$w6ЉF's(Ծ~dPJhvM^K6[5f <4*Hc_摙Qzfذ=7#I"@|1rRj]JOSfu%!'% ^E񣙳g)N4#Hh {{%")TѯI ̤)m"jT2bD`N!*{ϏM6yU7;*ߛ#s6EvZ!P֧3tkA;r K틨ѳpV/ lOe\Fw^ XG&rL SED^hM7iD瑮*eJ$Bb"tiKx&vYPpG*;hBl_䝭{'zÐ;kJ젮InAW8KLhF.s9pbVLnVrVɳs ёF{iwc5u )Ec0ut)<u)5 ||*T{!X3+~pMTl?ԓii{f)0 ϣqX H;  vvc ֵy|3?> ysrj抂J"Iۡoa$'{<֎u|.7"2uŲ_9=!drDІi0Y_0#ZY6P *e_3)=_W~TCnsۡ[Ķ]VdN]W`A$]%eQBĞCjZF76>[GաZVu;8raȊZ|R`nz]7[2'$grʣ,3Խcm߰_U~ll= 5O`'ksWy~ؕί`v~㽦S _fq) 5r*jh)BퟤUIz5ZULz-zӲQc6*HJZ+/[kMq-M~U-RN1[UT-j[9k@ CR\C&^' E 1,SNTFꩢd; ;c_zL[B ܱגAe4!E@F!_?X+8Jn 2`YVh/(21* I0b

l^灱!4qK{HI'|"Ma& z>b>k}пt#^:2ĻZa탤M%1C OC-.86+?KVt+(8 ˗ͣ!ZrJzi]ca%(N;,$k#; traKM00wmíO!7.MFi!{M>r˔H:q&7yKK*AHEF%"-:* ܂y0t+R9Xk>H#U `=\.#>熣CzLuž /PĭAG!r cR {>nJ̍"`+EV` V/o07en TjdXKIG 4a5ZxҘ@`= ɡ_Q_f,z4pRZ58@._-H' yr[f1&uw&ɒÌuL0(;}GHNl \犃b..WBѽ+^j3bcTƩw}(ɰs(,V3#3!Q饵2;+6g hrVī;Tz8GaPxb\Tz儛r,W 7nIIFюjq]sٮwn.xP[jAo+}[|PюB7z+5>gCVA(& C21C3uPIXR' < 83 ZK$ Brn2 Vj_п >׾bH yq wF'lfc+)-=K'~1"".'Jc:pR_d|#d i-mȤ, _EvOX[Ky/ꚰ6pCK4$ks?жo~b Oyhn('^~?D? .?(wjl8i>^P"X]A ["Itpuw$$ɚά8Ǣ1VW.%֏e;n39@% )'ߒgz'll>5ha *ˋj@0[+d xM# bh`)QMZ".r.J6v\aV(#69ؐg(:aaf0g vJB{T |b 2^'|T 5 xxG;s쉟^ZR gEإ',{PxM0mA)E6M͍Zfߝ,d<ٵ|ΌmVE}駘&]HqbKg΂,0f3=@}`@5Y/O]X>"fN7ziסs@cˉGC^mĀ2${;jjڊ-.seی,KR,s0rSY  ?{ww57`>hFj=Z$?o[T,٧0VVpJ՝!S=z%|AϛBO?q~DVv56irȔ0MUxY 8K߂s&%tѻLpxHL*q*[wvpJ0w$ߘ;i N1 ROE{.l4Uvņ.v48MlN[E`}tk ct̸+Hse;Dc0=h&] }HSuFsO=BqMVB!>@D^_U1M-Z\='$C R(_OPGFo/1OP_$vdzwӐړQؘʋoOb=%w֦Ċ^ī=B ,d3~'Q@ovF~鶧twN:&?hm?Y?r ?\K*w4!)ʶ#_vj)!Nިv`/l&l$"KDUI.RTԏ0^sϾ8&QlӜ~ XEQB[.GÊ f-pͲhVyW{8)[a "ﶉE+/_!'qcu676z Y5˦,W vfj2 ^̔MDѣUE ƶn !S>U\8X8"f ̓ XX^q94!b:_n4y;һ亗 tٖ9H0# }aT\h*p xx*ِųvjƄ12W0VnC5YOiIiAdfI8H  I<Hy !QɑOM@XhݸEl.AX^kZovu~ No@0y~:}M3SߓJI$ƈgX6+~v`c([?V7:0.j,q Š[u)ŬYm\ RUm;MRW(uHImRȶ.Vzw?/zu-H*ϰu;?.}:eѧ, ?,>3y)dyU$=*ffi _<;< 4g!'UsBG|$y^x}6C3 E6Nէ-%禞bZ榜rZY䥝|&\%"m]^&Y+[[,IreԘn:}1_+pw[-zl a?#}e}Rͧ?ב[>C}ͩK^HyΜ%uQ;c=c}.ړ_[Pq]Rw LzMwM"]Ls _̉[̒NVlQjJLq%zN0h&W@$Czi0HCg1_ԑf‹} ? A4.7p6읹j\LpCsA>MP>ȧ&^gw" :: aUnZRLLkE{:MSUWY"(=AςKQ>l3Mi߱ ˽ۤ 9 +5N{Ia!{1`(r, Ŋ6A*Bkyf3 CHFjB,;!j(f@2p Qw$U@ Khd=tŔa2Zam%8S_[Vjjk-0Uv6.GpeTXc_RSkkͥdU_iE*-D*mmlARᴔ5]"imiIʢ"KhVgcLtX9b99YE"aw+(OUFni4k`i0!Tu0yκ5{>;HoBQ+Bŀf5&əiWR O4CX=0yC 逕l=2\sSEx#@f#jų@*f4nFQJm/N-h\HU*adP-Y2C`is`A\*l$ǡHZ!%kYL, Pv$"\8,#t2 22mV.,|I72"PA-gA؎g}rNdr 5a1byd#TupB4QYei%YpJ| <"hK;Д{"ۍ"哬ISYLJWFZrĘ TٲY5=;3 Y4NBT ċqALD*MRU*Dj=UGњoZQrFJN}ѩ+15$V꿕vQq%[n 8 .[p 4Cp Cp! /ν_&w:OQZUwծ[L0:=OoU@oT%j wX͟$,kFȀinMϪ@XFVKkuoyaMa3Yg:om RBB$wH_Y>WΏ(O9aշ9`bSD=uOw6v20âfUUO$%}YUy )kQzH׻X܀%k,;[( A :>KVGT:UY,wc,9pLI?jYyBq4gڡ0x'hOy!|op #Ĭ04%ђ{b,T<434J^GC/Ex^2/'mS"L:2&ExXoN8t K^Q:N wԠ\!3^܂+s \C}*3&U#tf-q!JA0ʤy bh+q^Yn})p8hML~O2$A/<|`UE _2.I)];LJ+* mNsD-LIA{%9>mb 7':0yt|ní!'22K { tH}gHwr}Ts97C/#EՆlc;aaVw#*+<(F5dsM(JB?OOJT߹&(O9Q14PG8_)͢։=t޷ݟZ55 ffqoO,o@WWd0灂U(/bHUN3: sxm|&üDHuZlDI 㸛vEYX. #ʩD]q*tt<|> Jm{ ʌı([%jl칏![\Còc,̽zRm~(ҷĻA7CŨJ3g_A,ڄ|ri>2OʅAH +v/ .qI.ͯ: R6ge,&5W!˹%ʗ7` Ft>),Rʋq*;bn_* p-VL⥋ݲ sҨ7߰{cZW`O?e,蜌f?Եΰ ۳8m -de]Bn:+n3"-A9-@P'Cr@4+X6{|@u96JZ]/u8v1ÅЊɰ5e|/oQ-+)ﴴg5]{ )jP/SR`c]5vЦ!Յ_6^!Iu9%N4|@[~@91JQ Rlܹ6Z[nCҰŵJyrǼW^&sTݣηZy[~q=6ɘCQgx]a 6`UƩM)9s.71eq`AZ1^t Z#crx4 `Ċa=ՌBgW,Y-9'H]Pb%|q ʔnpo*lϔsߓ QrWoCS͋^Id|>D .H6#0SP_pBh R掆Fcg͠:;bm ѫ B·\zhll :.}/whx?W*b>߈7Hvnٻ-թ%Ͳ,DIzzJ v|=ifgf7X! N '7}(8ׄnJ8{5s|v'-(_:fTUƼvi}IRa#'߼)Ϫ"99L%! : L~M^Cd燪łg_aB6ǂk7D0GaE5+;%Hu͟s$۪Pe&dNF%0vûF NxGOHC |7CN.ۖb.K4K<:6S5!*)%k(Z/aϝN%g\ڴjP-!Hi#t*IG_ߢ~gIu`\c(PJ"[$@!VI6< >ێ0Wk%t`6fa?pG'=_+ *v{TB3: 8~V%gŎcS_U^ uK`ְ$j˓ _\,\(|3dhy%p`U:!a-4&WU&|O}LG8s2+8 1/wkS036v@-ܾopg].Q3:|:g}%mkphX&ë8}ej*km͹0cݣ1keŭg\5B Lh)4Q(2|]bX[T.1?{L_x2ƧNu~4H;'Y5DJOQd6fMHi9_8VcJ vM^FE__/])`Q.}ý8|,^~XNBئ3H.A-dW] LhLUfhT VeVVj ړ+ͪ}V&H(fá{[ӷCh K:%ddCFN!y't*[XLX8޲Aʹ.R\JNE_G"e 0faJ詈VUXQ)z>7͔zV9`zb1oB]ɷUMJFPy2je(oٟcwz=zp/qL͗:+="[Qrro  +f6vrՃB7.IB0}׮ lٺxHZhZB{ @ж4ݗhߓfX2vKʳnMR`~QB " G2h6kG#5|{sY(9z0 \c_L598ZҫKZE S|Լ Kz@q>OBJz$D`{wb){Be.N5XD.5,PXː}dOHKk ՏbKȺ!n(_@*)SS(N7Ly2y r "c:Gnۯ],_~U=󒭎JN-t^߬Ϧ>j*K׻RPq%o GKy~Ng,:{ݹ2Ɖẖ΀dB<}XjcQ}e䇪.>Tn$'^q_ 0&F~e>V5߰cX8]BRԨ'},a/T*ĠUV^1>=I4>m\ נ ?+6xߕt; );~/'1 vüzصxݨ^iiɰW7Ҙ̀uE աDڦ'FiPM]Bk ^-CikcDZ7/@w;F^X%P\<8|FGw8NǷy]{">k[&Rб/rpZZLlfg:aJp1 ecL[W)eC`}r=E|CǍn>Pr#47҄0&0|ϔ;Y>DrQl{qal+E%@"anG{`^c'-MaOLXEp riž, yMG&֧E2`#tTL8;\%ڛ1]6fo?NHA# Ɖ/;Di^bKw"Z&^Z|۽i'|di}kDU7﵊DؒRㅧd,L|a>¿>lA 'NhφJ{TQ 2WxO.YzBC}ଟnFg=IfĶk{m2ĞU ε OX嵞ٿ4܅] R;LՄ}Ji(l\Iq1ljjAK&Q^K.J9)rϽ%#4#uNYYʂudܰk_5~_z= }JQ0.`yK}0hI֊D!v0Ҧ0[+ҶWZP'70VN!Y:y۲ FM31 0@+wR.Rj3ruOak'\MR~@dj(CId5L>mLBbTÔVs=qfia+EOu9Nqtv80X V+)Eբ]ZG,chu&UR 7^K)4`%ڼIm$cGqE\xLlJ<'Jy~g($<:A]JH_`/1ݩN(WՖ33 cQHm̅/x68eTa6(؉Kh*"x*'aу ymuf4 Rb¬oDal3K& 1D( Ԅfnjl+3ǚlڹhW o>59}@ďRA -8SiN= D\~f4gX3C'ݒr2OФRfStWs&JcNWKhjE]f4Rvg-ު߾?aܱPLnjUSyzptQ*%T{qMI( 3Xgpb5 kUeǭx=i9 qזor(BNy<0ǓYXD0]7PT;]o4 tK]yܟD'+nC{ {QGG:@yqU&i ~;:10TK_?VQWQexLZB9i1qqfGDŽ?J?_ ((` ?_] w?@/K4?4x&Пm~KiA-l㳋Vo7ruKt~(~|B oD1kS[{CG1?q6rMgo:J@ctGH$c4_E96t:IU4ǟTD`a*'ܨ7?ct_EsPqb5 0ާ??F'Zl*/CBk8cϫ|o~ӆģ$/Vp7xr)=y)3z?)Iea~h)ǜJPK Mt(;bMp7WEB-INF/web.xmlSn0ﶁ JUJDL#ljIx$--G`ZA*3!!DYLHb2=6WB69/˒,+>Gm6֐J Zi" ȝd5,RJ`n=8b cFc|E+ݴh|aߘMx^+̵S#S:K'uif:;*A;Е@B{@B*x/umj +ն -v99i8F/\ƭӱN}\ͼq}%NˏD_03 h5o׈[۝Su;'Z(6n|i>PK l/;oLyCWEB-INF/classes/org/eclipse/jetty/tests/webapp/LoggingServlet.classUrD?"QTB uH)!ILR峬9y^bx(=Yd\ډgw{۽ݽ_';8;>.>>qobgfoiUu_T064Gb$L@Ema[WQEE8e2J֑8n("m$ b|RXx}z\綔dY @E~PZW]pDrwpܑɁ8R'(JÒ)^ĩtCWT86˶LNBkfدe?uӱk!51M6dlp$G`R͕qnmȓ\8)rJcS{4]E}=pOD"{FÔWRyb?'Լh3,n ק|"WKړaSxZ%Cjs_ꖙ'wkf=iڬO1ki ;ZLu] 4i$Qg=Ex Yd-bxN\Wo yD8ぅ3< +J_PG)̆Dt \-B_&3Fx'y!C26+5]@0B%Ύ澯^|K^+N^ { >]H3 1`vf j}D9@& 7?PK l/;META-INF/maven/PK l/;'META-INF/maven/org.eclipse.jetty.tests/PK l/;DMETA-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/PK Mt(;APKMETA-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/pom.xmlTn0+XڃD;Mnp)̔~},+j0]gKFgL *hH *y:~_~ > A&ȀR2}㚩bE-G~ʊ|>s.%C4$!Yn!VfayB\e(d̂L4`yYńd+ о q!j .Eaܖ+)8 9zpqDK'l7oZ DŪkb.XqȬocm1t݆PWJTB$Bxnl&;)q*C痳Eաن*= P./4U7梔ysDމatش n7W{!ΈؾhL]X@ƲV11>9WYg^܌<:ݍI(CFƞ rjUI4p܃Ђ&3mŚqaRf>ŠE U pt3cQg!"vfѪ#$gd2!3ȭfRkvvu7w%kzLJa.o1tRH/eySGm|{5'T>PBꢾEMWZqQm˖l|Y飴IC}hkP@xs/RmS=>=tmi0+cf3sZ JWxY+|tM#}ym&0*a_T>=}mػP?PK l/;fRMETA-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/pom.properties K 0}N1uB?Ѕ+uQ "͇dZ_uEI0UjG醮&3;0exh|\^T̤:d. ̔NdzYw c[0z×\PK l/; AMETA-INF/PK l/;Df}'META-INF/MANIFEST.MFPK l/;AWEB-INF/PK l/; AWEB-INF/lib/PK l/;AWEB-INF/classes/PK l/;A=WEB-INF/classes/org/PK l/;AoWEB-INF/classes/org/eclipse/PK l/;"AWEB-INF/classes/org/eclipse/jetty/PK l/;(AWEB-INF/classes/org/eclipse/jetty/tests/PK l/;/A/WEB-INF/classes/org/eclipse/jetty/tests/webapp/PK Z9<4_6'|WEB-INF/lib/commons-logging-api-1.1.jarPK Mt(;bMp7 WEB-INF/web.xmlPK l/;oLyCWEB-INF/classes/org/eclipse/jetty/tests/webapp/LoggingServlet.classPK l/;AMETA-INF/maven/PK l/;'AĪMETA-INF/maven/org.eclipse.jetty.tests/PK l/;DA META-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/PK Mt(;APKkMETA-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/pom.xmlPK l/;fRĮMETA-INF/maven/org.eclipse.jetty.tests/dummy-webapp-logging-commons/pom.propertiesPKjetty-9.2.14.v20151106/jetty-distribution/000077500000000000000000000000001261716203600177125ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/pom.xml000066400000000000000000001012371261716203600212330ustar00rootroot00000000000000 4.0.0 org.eclipse.jetty jetty-project 9.2.14.v20151106 jetty-distribution Jetty :: Distribution Assemblies http://www.eclipse.org/jetty pom ${basedir}/target/distribution 1.0.1 org.apache.maven.plugins maven-resources-plugin copy-base-assembly-tree generate-resources copy-resources false true ${assembly-directory} ${basedir}/src/main/resources org.apache.maven.plugins maven-antrun-plugin generate-resources run org.apache.maven.plugins maven-remote-resources-plugin generate-resources process org.eclipse.jetty.toolchain:jetty-distribution-remote-resources:1.2 ${assembly-directory} maven-dependency-plugin copy generate-resources copy org.eclipse.jetty jetty-project ${project.version} version txt true ${assembly-directory}/ VERSION.txt org.eclipse.jetty test-jetty-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps test.war org.eclipse.jetty.tests test-jaas-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps test-jaas.war org.eclipse.jetty.tests test-jndi-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps test-jndi.war org.eclipse.jetty.tests test-spec-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps test-spec.war org.eclipse.jetty test-proxy-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps xref-proxy.war org.eclipse.jetty.example-async-rest example-async-rest-webapp ${project.version} war true ** ${assembly-directory}/demo-base/webapps async-rest.war org.eclipse.jetty jetty-start ${project.version} jar true ** ${assembly-directory} start.jar copy-setuid-deps generate-resources copy org.eclipse.jetty.toolchain.setuid jetty-setuid-java ${jetty-setuid-version} jar true ${assembly-directory}/lib/setuid org.eclipse.jetty.toolchain.setuid libsetuid-linux ${jetty-setuid-version} so true ${assembly-directory}/lib/setuid libsetuid-linux.so org.eclipse.jetty.toolchain.setuid libsetuid-osx ${jetty-setuid-version} so true ${assembly-directory}/lib/setuid libsetuid-osx.so unpack-setuid-config process-resources unpack org.eclipse.jetty.toolchain.setuid jetty-setuid-java ${jetty-setuid-version} config jar true ${assembly-directory} META-INF/** unpack-test-jaas-config process-resources unpack org.eclipse.jetty.tests test-jaas-webapp ${project.version} config jar true ${assembly-directory} META-INF/** unpack-test-jndi-config process-resources unpack org.eclipse.jetty.tests test-jndi-webapp ${project.version} config jar true ${assembly-directory} META-INF/** unpack-test-spec-config process-resources unpack org.eclipse.jetty.tests test-spec-webapp ${project.version} config jar true ${assembly-directory} META-INF/** copy-lib-deps generate-resources copy-dependencies org.eclipse.jetty org.eclipse.jetty.orbit,org.eclipse.jetty.spdy,org.eclipse.jetty.websocket,org.eclipse.jetty.fcgi,org.eclipse.jetty.toolchain,org.apache.taglibs jetty-all,jetty-jsp,apache-jsp,apache-jstl,jetty-start,jetty-monitor,jetty-spring jar ${assembly-directory}/lib copy-lib-websocket-deps generate-resources copy-dependencies javax.websocket,org.eclipse.jetty.websocket javax.websocket-client-api jar ${assembly-directory}/lib/websocket copy-lib-fcgi-deps generate-resources copy-dependencies org.eclipse.jetty.fcgi jar ${assembly-directory}/lib/fcgi copy-lib-spring-deps generate-resources copy-dependencies org.eclipse.jetty jetty-spring jar ${assembly-directory}/lib/spring copy-lib-monitor-deps generate-resources copy org.eclipse.jetty jetty-monitor ${project.version} jar true ${assembly-directory}/lib/monitor copy-servlet-api-deps generate-resources copy javax.servlet javax.servlet-api 3.1.0 true ${assembly-directory}/lib servlet-api-3.1.jar org.eclipse.jetty.toolchain jetty-schemas 3.1.M0 true ${assembly-directory}/lib jetty-schemas-3.1.jar unpack-spdy process-resources unpack-dependencies org.eclipse.jetty.spdy config false META-INF/** ${assembly-directory} copy-lib-spdy-deps process-resources copy-dependencies org.eclipse.jetty.spdy jar ${assembly-directory}/lib/spdy copy-annotations-deps generate-resources copy-dependencies javax.annotation,org.eclipse.jetty.orbit,org.ow2.asm javax.annotation-api,asm,asm-commons jar ${assembly-directory}/lib/annotations copy-jta-deps generate-resources copy-dependencies javax.transaction javax.transaction-api jar ${assembly-directory}/lib/jndi copy-jndi-deps generate-resources copy-dependencies org.eclipse.jetty.orbit javax.mail.glassfish jar ${assembly-directory}/lib/jndi copy-glassfish-jsp-deps generate-resources copy-dependencies org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain, org.eclipse.jetty org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el, jetty-jsp jar ${assembly-directory}/lib/jsp copy-apache-jsp-deps generate-resources copy-dependencies org.eclipse.jetty,org.eclipse.jetty.toolchain,org.mortbay.jasper,org.eclipse.jetty.orbit apache-jsp,apache-el,org.eclipse.jdt.core jar true ${assembly-directory}/lib/apache-jsp copy-jstl-api generate-resources copy-dependencies org.eclipse.jetty.orbit javax.servlet.jsp.jstl true jar ${assembly-directory}/lib/jsp copy-jstl-impl generate-resources copy-dependencies org.glassfish.web javax.servlet.jsp.jstl jar ${assembly-directory}/lib/jsp copy-apache-jstl-deps generate-resources copy-dependencies org.glassfish.web taglibs-standard-spec,taglibs-standard-impl true jar ${assembly-directory}/lib/apache-jstl copy-jaspi-deps generate-resources copy-dependencies org.eclipse.jetty.orbit javax.security.auth.message jar ${assembly-directory}/lib/jaspi unpack-config-deps generate-resources unpack-dependencies org.eclipse.jetty,org.eclipse.jetty.websocket config false META-INF/** ${assembly-directory} org.codehaus.mojo exec-maven-plugin setup home process-classes org.eclipse.jetty.start.Main jetty.home=${assembly-directory} jetty.base=${assembly-directory} --add-to-start=server,deploy,websocket,ext,resources,jsp,jstl,http java setup demo-base process-classes org.eclipse.jetty.start.Main jetty.home=${assembly-directory} jetty.base=${assembly-directory}/demo-base --add-to-start=server,continuation,deploy,websocket,ext,resources,client,annotations,jndi,servlets --add-to-startd=jsp,jstl,http,https java org.apache.maven.plugins maven-assembly-plugin src/main/assembly/jetty-assembly.xml gnu false package single org.apache.maven.plugins maven-pmd-plugin true org.codehaus.mojo findbugs-maven-plugin true org.eclipse.jetty.orbit javax.mail.glassfish org.eclipse.jetty.orbit javax.security.auth.message javax.annotation javax.annotation-api javax.transaction javax.transaction-api org.glassfish.web javax.servlet.jsp.jstl org.glassfish.web javax.servlet.jsp org.eclipse.jetty.toolchain jetty-jsp-jdt javax.servlet.jsp javax.servlet.jsp-api org.glassfish javax.el org.ow2.asm asm org.ow2.asm asm-commons org.eclipse.jetty jetty-deploy ${project.version} org.eclipse.jetty test-jetty-webapp war ${project.version} org.eclipse.jetty test-proxy-webapp war ${project.version} org.eclipse.jetty jetty-jmx ${project.version} org.eclipse.jetty jetty-monitor ${project.version} org.eclipse.jetty jetty-quickstart ${project.version} org.eclipse.jetty jetty-start ${project.version} org.eclipse.jetty jetty-servlets ${project.version} org.eclipse.jetty.websocket websocket-servlet ${project.version} org.eclipse.jetty.websocket websocket-server ${project.version} org.eclipse.jetty.websocket javax-websocket-server-impl ${project.version} org.eclipse.jetty jetty-jsp ${project.version} org.eclipse.jetty apache-jsp ${project.version} org.eclipse.jetty apache-jstl ${project.version} org.eclipse.jetty jetty-plus ${project.version} org.eclipse.jetty jetty-client ${project.version} org.eclipse.jetty jetty-continuation ${project.version} org.eclipse.jetty jetty-proxy ${project.version} org.eclipse.jetty.fcgi fcgi-server ${project.version} org.eclipse.jetty jetty-spring ${project.version} org.eclipse.jetty jetty-cdi ${project.version} org.eclipse.jetty jetty-jaas ${project.version} org.eclipse.jetty jetty-annotations ${project.version} org.eclipse.jetty jetty-rewrite ${project.version} org.eclipse.jetty.spdy spdy-core ${project.version} org.eclipse.jetty.spdy spdy-server ${project.version} org.eclipse.jetty.spdy spdy-http-server ${project.version} org.eclipse.jetty.spdy spdy-example-webapp ${project.version} war org.eclipse.jetty jetty-alpn-server ${project.version} org.eclipse.jetty.example-async-rest example-async-rest-webapp ${project.version} war org.eclipse.jetty jetty-jaspi ${project.version} jetty-9.2.14.v20151106/jetty-distribution/src/000077500000000000000000000000001261716203600205015ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/000077500000000000000000000000001261716203600214255ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/assembly/000077500000000000000000000000001261716203600232445ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/assembly/jetty-assembly.xml000066400000000000000000000007041261716203600267430ustar00rootroot00000000000000 assembly tar.gz zip ${assembly-directory} ** **/META-INF/** *-config.jar jetty-9.2.14.v20151106/jetty-distribution/src/main/assembly/jetty-src.xml000066400000000000000000000005651261716203600257200ustar00rootroot00000000000000 src zip ${basedir}/../ **/** **/target/** jetty-9.2.14.v20151106/jetty-distribution/src/main/assembly/site-component.xml000066400000000000000000000007451261716203600267400ustar00rootroot00000000000000 site-component jar ${basedir}/target/dist-src jetty-distribution-${version}-site-component target/site/** README** VERSION** LICENSES/** jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/000077500000000000000000000000001261716203600234375ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/README.TXT000066400000000000000000000031451261716203600250000ustar00rootroot00000000000000 JETTY ===== The Jetty project is a 100% Java HTTP Server, HTTP Client and Servlet Container from the eclipse foundation http://www.eclipse.org/jetty/ Jetty is open source and is dual licensed using the Apache 2.0 and Eclipse Public License 1.0. You may choose either license when distributing Jetty. RUNNING JETTY ============= The run directory is either the top-level of a binary release or jetty-distribution/target/distribution directory when built from source. To run with the default options: $ cd demo-base $ java -jar ../start.jar To see the available options and the default arguments provided by the start.ini file: $ java -jar /path/to/start.jar --help Many Jetty features can be enabled by using the --module command For example: $ cd mybase $ java -jar /path/to/start.jar --module=https,deploy Will enable the https and deploy modules (and their transitive dependencies) temporarily for this specific run of Jetty. To see what modules are available $ java -jar /path/to/start.jar --list-modules JETTY BASE ========== The jetty.base property is a property that can be defined on the command line (defaults to what your java 'user.dir' property points to) Jetty's start.jar mechanism will configure your jetty instance from the configuration present in this jetty.base directory. Example setup: # Create the base directory $ mkdir mybase $ cd mybase # Initialize the base directory's start.ini and needed directories $ java -jar /path/to/jetty-dist/start.jar --add-to-start=http,deploy # Run this base directory configuration $ java -jar /path/to/jetty-dist/start.jar jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/bin/000077500000000000000000000000001261716203600242075ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/bin/jetty.sh000077500000000000000000000353351261716203600257160ustar00rootroot00000000000000#!/usr/bin/env bash # # Startup script for jetty under *nix systems (it works under NT/cygwin too). ################################################## # Set the name which is used by other variables. # Defaults to the file name without extension. ################################################## NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//') # To get the service to restart correctly on reboot, uncomment below (3 lines): # ======================== # chkconfig: 3 99 99 # description: Jetty 9 webserver # processname: jetty # ======================== # Configuration files # # /etc/default/$NAME # If it exists, this is read at the start of script. It may perform any # sequence of shell commands, like setting relevant environment variables. # # $HOME/.$NAMErc (e.g. $HOME/.jettyrc) # If it exists, this is read at the start of script. It may perform any # sequence of shell commands, like setting relevant environment variables. # # /etc/$NAME.conf # If found, and no configurations were given on the command line, # the file will be used as this script's configuration. # Each line in the file may contain: # - A comment denoted by the pound (#) sign as first non-blank character. # - The path to a regular file, which will be passed to jetty as a # config.xml file. # - The path to a directory. Each *.xml file in the directory will be # passed to jetty as a config.xml file. # - All other lines will be passed, as-is to the start.jar # # The files will be checked for existence before being passed to jetty. # # Configuration variables # # JAVA # Command to invoke Java. If not set, java (from the PATH) will be used. # # JAVA_OPTIONS # Extra options to pass to the JVM # # JETTY_HOME # Where Jetty is installed. If not set, the script will try go # guess it by looking at the invocation path for the script # The java system property "jetty.home" will be # set to this value for use by configure.xml files, f.e.: # # /webapps/jetty.war # # JETTY_BASE # Where your Jetty base directory is. If not set, the value from # $JETTY_HOME will be used. # # JETTY_RUN # Where the $NAME.pid file should be stored. It defaults to the # first available of /var/run, /usr/var/run, JETTY_BASE and /tmp # if not set. # # JETTY_PID # The Jetty PID file, defaults to $JETTY_RUN/$NAME.pid # # JETTY_ARGS # The default arguments to pass to jetty. # For example # JETTY_ARGS=jetty.port=8080 jetty.spdy.port=8443 jetty.secure.port=443 # # JETTY_USER # if set, then used as a username to run the server as # # JETTY_SHELL # If set, then used as the shell by su when starting the server. Will have # no effect if start-stop-daemon exists. Useful when JETTY_USER does not # have shell access, e.g. /bin/false # usage() { echo "Usage: ${0##*/} [-d] {start|stop|run|restart|check|supervise} [ CONFIGS ... ] " exit 1 } [ $# -gt 0 ] || usage ################################################## # Some utility functions ################################################## findDirectory() { local L OP=$1 shift for L in "$@"; do [ "$OP" "$L" ] || continue printf %s "$L" break done } running() { if [ -f "$1" ] then local PID=$(cat "$1" 2>/dev/null) || return 1 kill -0 "$PID" 2>/dev/null return fi rm -f "$1" return 1 } started() { # wait for 60s to see "STARTED" in PID file, needs jetty-started.xml as argument for T in 1 2 3 4 5 6 7 9 10 11 12 13 14 15 do sleep 4 [ -z "$(grep STARTED $1 2>/dev/null)" ] || return 0 [ -z "$(grep STOPPED $1 2>/dev/null)" ] || return 1 [ -z "$(grep FAILED $1 2>/dev/null)" ] || return 1 local PID=$(cat "$2" 2>/dev/null) || return 1 kill -0 "$PID" 2>/dev/null || return 1 echo -n ". " done return 1; } readConfig() { (( DEBUG )) && echo "Reading $1.." source "$1" } ################################################## # Get the action & configs ################################################## CONFIGS=() NO_START=0 DEBUG=0 while [[ $1 = -* ]]; do case $1 in -d) DEBUG=1 ;; esac shift done ACTION=$1 shift ################################################## # Read any configuration files ################################################## ETC=/etc if [ $UID != 0 ] then ETC=$HOME/etc fi for CONFIG in $ETC/default/${NAME}{,9} $HOME/.${NAME}rc; do if [ -f "$CONFIG" ] ; then readConfig "$CONFIG" fi done ################################################## # Set tmp if not already set. ################################################## TMPDIR=${TMPDIR:-/tmp} ################################################## # Jetty's hallmark ################################################## JETTY_INSTALL_TRACE_FILE="start.jar" ################################################## # Try to determine JETTY_HOME if not set ################################################## if [ -z "$JETTY_HOME" ] then JETTY_SH=$0 case "$JETTY_SH" in /*) JETTY_HOME=${JETTY_SH%/*/*} ;; ./*/*) JETTY_HOME=${JETTY_SH%/*/*} ;; ./*) JETTY_HOME=.. ;; */*/*) JETTY_HOME=./${JETTY_SH%/*/*} ;; */*) JETTY_HOME=. ;; *) JETTY_HOME=.. ;; esac if [ ! -f "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] then JETTY_HOME= fi fi ################################################## # No JETTY_HOME yet? We're out of luck! ################################################## if [ -z "$JETTY_HOME" ]; then echo "** ERROR: JETTY_HOME not set, you need to set it or install in a standard location" exit 1 fi cd "$JETTY_HOME" JETTY_HOME=$PWD ################################################## # Set JETTY_BASE ################################################## if [ -z "$JETTY_BASE" ]; then JETTY_BASE=$JETTY_HOME fi cd "$JETTY_BASE" JETTY_BASE=$PWD ##################################################### # Check that jetty is where we think it is ##################################################### if [ ! -r "$JETTY_HOME/$JETTY_INSTALL_TRACE_FILE" ] then echo "** ERROR: Oops! Jetty doesn't appear to be installed in $JETTY_HOME" echo "** ERROR: $JETTY_HOME/$JETTY_INSTALL_TRACE_FILE is not readable!" exit 1 fi ################################################## # Try to find this script's configuration file, # but only if no configurations were given on the # command line. ################################################## if [ -z "$JETTY_CONF" ] then if [ -f $ETC/${NAME}.conf ] then JETTY_CONF=$ETC/${NAME}.conf elif [ -f "$JETTY_BASE/etc/jetty.conf" ] then JETTY_CONF=$JETTY_BASE/etc/jetty.conf elif [ -f "$JETTY_HOME/etc/jetty.conf" ] then JETTY_CONF=$JETTY_HOME/etc/jetty.conf fi fi ##################################################### # Find a location for the pid file ##################################################### if [ -z "$JETTY_RUN" ] then JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp) fi ##################################################### # Find a pid and state file ##################################################### if [ -z "$JETTY_PID" ] then JETTY_PID="$JETTY_RUN/${NAME}.pid" fi if [ -z "$JETTY_STATE" ] then JETTY_STATE=$JETTY_BASE/${NAME}.state fi case "`uname`" in CYGWIN*) JETTY_STATE="`cygpath -w $JETTY_STATE`";; esac JETTY_ARGS=(${JETTY_ARGS[*]} "jetty.state=$JETTY_STATE") ################################################## # Get the list of config.xml files from jetty.conf ################################################## if [ -f "$JETTY_CONF" ] && [ -r "$JETTY_CONF" ] then while read -r CONF do if expr "$CONF" : '#' >/dev/null ; then continue fi if [ -d "$CONF" ] then # assume it's a directory with configure.xml files # for example: /etc/jetty.d/ # sort the files before adding them to the list of JETTY_ARGS for XMLFILE in "$CONF/"*.xml do if [ -r "$XMLFILE" ] && [ -f "$XMLFILE" ] then JETTY_ARGS=(${JETTY_ARGS[*]} "$XMLFILE") else echo "** WARNING: Cannot read '$XMLFILE' specified in '$JETTY_CONF'" fi done else # assume it's a command line parameter (let start.jar deal with its validity) JETTY_ARGS=(${JETTY_ARGS[*]} "$CONF") fi done < "$JETTY_CONF" fi ################################################## # Setup JAVA if unset ################################################## if [ -z "$JAVA" ] then JAVA=$(which java) fi if [ -z "$JAVA" ] then echo "Cannot find a Java JDK. Please set either set JAVA or put java (>=1.5) in your PATH." >&2 exit 1 fi ##################################################### # See if JETTY_LOGS is defined ##################################################### if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_BASE/logs ] then JETTY_LOGS=$JETTY_BASE/logs fi if [ -z "$JETTY_LOGS" ] && [ -d $JETTY_HOME/logs ] then JETTY_LOGS=$JETTY_HOME/logs fi if [ "$JETTY_LOGS" ] then case "`uname`" in CYGWIN*) JETTY_LOGS="`cygpath -w $JETTY_LOGS`";; esac JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.logs=$JETTY_LOGS") fi ##################################################### # Are we running on Windows? Could be, with Cygwin/NT. ##################################################### case "`uname`" in CYGWIN*) PATH_SEPARATOR=";";; *) PATH_SEPARATOR=":";; esac ##################################################### # Add jetty properties to Java VM options. ##################################################### case "`uname`" in CYGWIN*) JETTY_HOME="`cygpath -w $JETTY_HOME`" JETTY_BASE="`cygpath -w $JETTY_BASE`" TMPDIR="`cygpath -w $TMPDIR`" ;; esac JAVA_OPTIONS=(${JAVA_OPTIONS[*]} "-Djetty.home=$JETTY_HOME" "-Djetty.base=$JETTY_BASE" "-Djava.io.tmpdir=$TMPDIR") ##################################################### # This is how the Jetty server will be started ##################################################### JETTY_START=$JETTY_HOME/start.jar START_INI=$JETTY_BASE/start.ini START_D=$JETTY_BASE/start.d if [ ! -f "$START_INI" -a ! -d "$START_D" ] then echo "Cannot find a start.ini file or a start.d directory in your JETTY_BASE directory: $JETTY_BASE" >&2 exit 1 fi case "`uname`" in CYGWIN*) JETTY_START="`cygpath -w $JETTY_START`";; esac RUN_ARGS=(${JAVA_OPTIONS[@]} -jar "$JETTY_START" ${JETTY_ARGS[*]}) RUN_CMD=("$JAVA" ${RUN_ARGS[@]}) ##################################################### # Comment these out after you're happy with what # the script is doing. ##################################################### if (( DEBUG )) then echo "START_INI = $START_INI" echo "START_D = $START_D" echo "JETTY_HOME = $JETTY_HOME" echo "JETTY_BASE = $JETTY_BASE" echo "JETTY_CONF = $JETTY_CONF" echo "JETTY_PID = $JETTY_PID" echo "JETTY_START = $JETTY_START" echo "JETTY_ARGS = ${JETTY_ARGS[*]}" echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}" echo "JAVA = $JAVA" echo "RUN_CMD = ${RUN_CMD[*]}" fi ################################################## # Do the action ################################################## case "$ACTION" in start) echo -n "Starting Jetty: " if (( NO_START )); then echo "Not starting ${NAME} - NO_START=1"; exit fi if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1 then unset CH_USER if [ -n "$JETTY_USER" ] then CH_USER="-c$JETTY_USER" fi start-stop-daemon -S -p"$JETTY_PID" $CH_USER -d"$JETTY_BASE" -b -m -a "$JAVA" -- "${RUN_ARGS[@]}" start-log-file="$JETTY_LOGS/start.log" else if running $JETTY_PID then echo "Already Running $(cat $JETTY_PID)!" exit 1 fi if [ -n "$JETTY_USER" ] then unset SU_SHELL if [ "$JETTY_SHELL" ] then SU_SHELL="-s $JETTY_SHELL" fi touch "$JETTY_PID" chown "$JETTY_USER" "$JETTY_PID" # FIXME: Broken solution: wordsplitting, pathname expansion, arbitrary command execution, etc. su - "$JETTY_USER" $SU_SHELL -c " exec ${RUN_CMD[*]} start-log-file="$JETTY_LOGS/start.log" & disown \$! echo \$! > '$JETTY_PID'" else "${RUN_CMD[@]}" & disown $! echo $! > "$JETTY_PID" fi fi if expr "${JETTY_ARGS[*]}" : '.*jetty-started.xml.*' >/dev/null then if started "$JETTY_STATE" "$JETTY_PID" then echo "OK `date`" else echo "FAILED `date`" exit 1 fi else echo "ok `date`" fi ;; stop) echo -n "Stopping Jetty: " if [ $UID -eq 0 ] && type start-stop-daemon > /dev/null 2>&1; then start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s HUP TIMEOUT=30 while running "$JETTY_PID"; do if (( TIMEOUT-- == 0 )); then start-stop-daemon -K -p"$JETTY_PID" -d"$JETTY_HOME" -a "$JAVA" -s KILL fi sleep 1 done else if [ ! -f "$JETTY_PID" ] ; then echo "ERROR: no pid found at $JETTY_PID" exit 1 fi PID=$(cat "$JETTY_PID" 2>/dev/null) if [ -z "$PID" ] ; then echo "ERROR: no pid id found in $JETTY_PID" exit 1 fi kill "$PID" 2>/dev/null TIMEOUT=30 while running $JETTY_PID; do if (( TIMEOUT-- == 0 )); then kill -KILL "$PID" 2>/dev/null fi sleep 1 done fi rm -f "$JETTY_PID" rm -f "$JETTY_STATE" echo OK ;; restart) JETTY_SH=$0 if [ ! -f $JETTY_SH ]; then if [ ! -f $JETTY_HOME/bin/jetty.sh ]; then echo "$JETTY_HOME/bin/jetty.sh does not exist." exit 1 fi JETTY_SH=$JETTY_HOME/bin/jetty.sh fi "$JETTY_SH" stop "$@" "$JETTY_SH" start "$@" ;; supervise) # # Under control of daemontools supervise monitor which # handles restarts and shutdowns via the svc program. # exec "${RUN_CMD[@]}" ;; run|demo) echo "Running Jetty: " if running "$JETTY_PID" then echo Already Running $(cat "$JETTY_PID")! exit 1 fi exec "${RUN_CMD[@]}" ;; check|status) echo "Checking arguments to Jetty: " echo "START_INI = $START_INI" echo "START_D = $START_D" echo "JETTY_HOME = $JETTY_HOME" echo "JETTY_BASE = $JETTY_BASE" echo "JETTY_CONF = $JETTY_CONF" echo "JETTY_PID = $JETTY_PID" echo "JETTY_START = $JETTY_START" echo "JETTY_LOGS = $JETTY_LOGS" echo "JETTY_STATE = $JETTY_STATE" echo "CLASSPATH = $CLASSPATH" echo "JAVA = $JAVA" echo "JAVA_OPTIONS = ${JAVA_OPTIONS[*]}" echo "JETTY_ARGS = ${JETTY_ARGS[*]}" echo "RUN_CMD = ${RUN_CMD[*]}" echo if running "$JETTY_PID" then echo "Jetty running pid=$(< "$JETTY_PID")" exit 0 fi exit 1 ;; *) usage ;; esac exit 0 jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/000077500000000000000000000000001261716203600252735ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/etc/000077500000000000000000000000001261716203600260465ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/etc/keystore000066400000000000000000000026101261716203600276350ustar00rootroot00000000000000jettyu500 +*~ F9SNu\<KpI| pF#?d#1BWI2@\n].I )-UOmֹXqn}j𥢞…,C@8R>lzlōrcZ05' ͞'qgږG0>K=QWv Cr؅$JP)\ԃSrဍW9+}(ti&$Vo&{+Ġcie&t@cF;r'*3FR)@R2p+:0C-/ipF(.a B84fUG1ܠ}ZhE3 'X^s`a\ ,9YxYb@,yAR.dU mn(T߄[΂Y\z.څ-_5<)ɦGW 1boʔo5#͞uU(x~e"ݍz<D珮!F9Ήei(ڠ9:k}sm" =ߧ x{0lcmw>* H'^^Ip)K$-A1U^k3.g_4/rхPs NՋ4]J)$KusřBƱf3 9ĹX.50900I0  *H 010UUnknown10UUnknown10UUnknown1$0"U Mort Bay Consulting Pty Ltd10 U Jetty10Ujetty.mortbay.org0 081107042836Z 360325042836Z010UUnknown10UUnknown10UUnknown1$0"U Mort Bay Consulting Pty Ltd10 U Jetty10Ujetty.mortbay.org00  *H 0 -%]<7e@o~t 4HaORH"%HA/w~ҽ'+D 'АHsʢ;=N@IҾ3SМ>dKRyʹ5,kr' cYf} q0  *H u;Q;7KwcEq삠y.95-l@hp1(1>*jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/webapps/000077500000000000000000000000001261716203600267345ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/webapps/README.TXT000066400000000000000000000005251261716203600302740ustar00rootroot00000000000000 This directory is scanned by the demo WebAppDeployer provider created in the etc/jetty-demo.xml file and enabled by the start.d/900-demo.ini file. To disable the demo, either remove the start.d/900-demo.ini or issue the following command: java -jar start.jar --disable=demo For normal webapp deployment, use the webapps directory. jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/000077500000000000000000000000001261716203600275175ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/images/000077500000000000000000000000001261716203600307645ustar00rootroot00000000000000jetty-header.jpg000066400000000000000000003115711261716203600340040ustar00rootroot00000000000000jetty-9.2.14.v20151106/jetty-distribution/src/main/resources/demo-base/webapps/ROOT/imagesJFIFHHJExifII* (12 iCanonCanon EOS 450DHHgThumb 2.14.32012:10:12 15:16:57<D"'d0221L` t|   6666660100R2: 2010:10:31 06:44:202010:10:31 06:44:20@Bp@BDerived from image purchased from FreeDigitalPhotos.net Order ref. aia15ehici under standard terms http://www.freedigitalphotos.net/images/terms.php/Anu+Hhttp://ns.adobe.com/xap/1.0/ Derived from image purchased from FreeDigitalPhotos.net Order ref. aia15ehici under standard terms http://www.freedigitalphotos.net/images/terms.php 0, 0, 32, 22, 64, 56, 128, 128, 192, 196, 255, 255 xmp.did:C9158643C1E4DF11B615EE5EF88601CD 6Photoshop 3.08BIMPprozac1 and jetty projectxDerived from image purchased from FreeDigitalPhotos.net Order ref. aia15ehici under standard terms http://www.freedigitalphotos.net/images/terms.phpt:http://www.freedigitalphotos.net/images/terms.php standardnprozac1 and jetty project 0 XICC_PROFILE HLinomntrRGB XYZ  1acspMSFTIEC sRGB-HP cprtP3desclwtptbkptrXYZgXYZ,bXYZ@dmndTpdmddvuedLview$lumimeas $tech0 rTRC< gTRC< bTRC< textCopyright (c) 1998 Hewlett-Packard CompanydescsRGB IEC61966-2.1sRGB IEC61966-2.1XYZ QXYZ XYZ o8XYZ bXYZ $descIEC http://www.iec.chIEC http://www.iec.chdesc.IEC 61966-2.1 Default RGB colour space - sRGB.IEC 61966-2.1 Default RGB colour space - sRGBdesc,Reference Viewing Condition in IEC61966-2.1,Reference Viewing Condition in IEC61966-2.1view_. \XYZ L VPWmeassig CRT curv #(-27;@EJOTY^chmrw| %+28>ELRY`gnu| &/8AKT]gqz !-8COZfr~ -;HUcq~ +:IXgw'7HYj{+=Oat 2FZn  % : O d y  ' = T j " 9 Q i  * C \ u & @ Z t .Id %A^z &Ca~1Om&Ed#Cc'Ij4Vx&IlAe@e Ek*Qw;c*R{Gp@j>i  A l !!H!u!!!"'"U"""# #8#f###$$M$|$$% %8%h%%%&'&W&&&''I'z''( (?(q(())8)k))**5*h**++6+i++,,9,n,,- -A-v--..L.../$/Z///050l0011J1112*2c223 3F3334+4e4455M555676r667$7`7788P8899B999:6:t::;-;k;;<' >`>>?!?a??@#@d@@A)AjAAB0BrBBC:C}CDDGDDEEUEEF"FgFFG5G{GHHKHHIIcIIJ7J}JK KSKKL*LrLMMJMMN%NnNOOIOOP'PqPQQPQQR1R|RSS_SSTBTTU(UuUVV\VVWDWWX/X}XYYiYZZVZZ[E[[\5\\]']x]^^l^__a_``W``aOaabIbbcCccd@dde=eef=ffg=ggh?hhiCiijHjjkOkklWlmm`mnnknooxop+ppq:qqrKrss]sttptu(uuv>vvwVwxxnxy*yyzFz{{c{|!||}A}~~b~#G k͂0WGrׇ;iΉ3dʋ0cʍ1fΏ6n֑?zM _ɖ4 uL$h՛BdҞ@iءG&vVǥ8nRĩ7u\ЭD-u`ֲK³8%yhYѹJº;.! zpg_XQKFAǿ=ȼ:ɹ8ʷ6˶5̵5͵6ζ7ϸ9к<Ѿ?DINU\dlvۀ܊ݖޢ)߯6DScs 2F[p(@Xr4Pm8Ww)KmCCR X !1A"Qa2q#BR$3b r%4CST5DEUc6s7FWf g  !1A"Qaq2B #R3r$Sb5CEUVcs%46DFdt>u ?SG^_t/)$#2c\,G6&#"5RKj"GdG# Ip냴KghүE\zr)-BX㏌XybńWJ]zh0`h7 g N RpFtH}sl)^EqbK!C&Ȃ= RzXqpVA|&X9Á{M?dݿ]gFTǐ#|]lH<aڤu&GN>0cPnՑa>sLP|V>̂m͢A$_7kwCsx1 Be\OKbr;~*TS3?y,GqaKG7Oc T! Zy?လ- Ia~BYT oy7 s$pnjB}"LZdԓJ;XIG!kZZL{7)ؘ/`"LzAka;S ̜W%Flqج1nD6O2X0m%<[bh7]A6嬈 &ZT38b&<[3S%#Aacl<("HCM0p UF81 (hvѕ́`} DldQhasaNH2L&"D %rsB͌٣E>8)h6k :wYf^h",sr4`Ab<6í`-ҼBP@$&y牴y}x!9O$10"G8;Z,q"8-tysqJ7"<& _ Lt\YdX H'lLF(6 0Өan OR < t ,t%]@AA (crÜH'( tMWX )prfa"#' }xklP>6zt3uymtd! /9[2 q ouaap*@v:I7phd2@dTemLx=oy %QS;xR/:6'8 zy& 0Hmu@KC<| p>an_=3!klJ9(MKmQu7ZDpGwϕT99hnhg)Z$n$ 8"0Fd_,ZD%j J@ %uɲJ hͅ׹b(3;ˮ::Jv0 Jyl,;W#TDr8LƎ\SݙrA[RdLOqP>*6mV^=nX2TsZ3K+j m< 70ڢ7 "$JVr*6)+&H%JO ̾akᵻP&St0fbvVrS+P̏?/\LJ(ER bs:sG/})bx|\o-̟ųx@ МoZrH) #myXXњJ1`~B#rhb޻DDM1MP rz1VY{ My.AsB-In[_(uC/'LOCa$MEH(]`+Tm~h"j/bPL ĕ=H#4E{("bLjrX{"'\ (;`p6#\!8ȵZH@$^l :)iz~X &\PK_ӟl.?ܘ"Ns~Fʝ,@ 7#sL5\E9)77:(5H=Ly'^服#nE.>za)^64݆ 3Z>ZHwL2OӊɞI]Czn)xxHFJ44,3p/d]-.:A X}Ln/F4zRGt0`LESe!IG0<k\(6P zb֨6'c#}1SX|Ds:I)++I⚰:E>@W__^S窰ҵ G@WKr ։;i 718]0^+1k=ddtٴn S'ϡpQ.9 QQ+ n7lrbb8¥%Xh%N2myA ѰRo8r Ö$ &u9>!s}q !qq }V)DxLq4 \ f%$E$DG2,av+өBvO0&i#ܢi__^J$S۞O_Pb/p4fݺgbZ*:B@XGL#-hh]a$2XrĘH0zLܛ. (׺wMA({`OýuaZnBO!` 3.pg#̅Km!@Sƙ#aJXc*Rb =}#ñ8XوaUAQ qc{ŝ+H7 K{T4ycQI$AN6 Rd;xfly=,fh[.!w *9 YVT7@(%C >րʧ|!/`! GuGXi3Bjɛ I3ſpPQ_l$-ţ}ږ뎀 |鍉FVZq %SDG&sRأ[.Mgc ǀC)b$uSmy:(VҒoi't]]0=@s~ꀐfdM<~$ѡM y9rNop&)an@$>S7渻av(H'm0Hƅag:Yv¢f1K2#!FA0AhDӰj([h.$0>8ؕجtQ:H(q&4 7ܢo~Ya؜ꍔ{'Hou5&-70d8; }hպQmAeL^SJlpA²Gv)SGB|[M C1*93L p؋$Z 6[$7Svěpbo|46@- ,VAG|E[j$'rӦ+B<ț޶BhA #ӋI\7B 4?.O0I1Jhì0c`o2c$T鏁 6}crAP|^ĉM"s^<'Lt ,K`~3? IЅ"[8L P?{n6>b\Il.灪mS=fdYdE$ō8XNG}3MEc33ez4 Ă:]| YX)Y?rbqfILOĉL<%$3j* ":Nq&Z'A u"_53? T"`!@ XD^ߎҔ|(\H 9"\2i)pC*2Fm&$|FQPd$~Q9)L$s<`t[mQ[8a;D$'akzqOň虶)ߘ焤^8PM VMGrBg%Qk>SzkhIMZ\@rpGpIbAMU|sL

`5!9i\3b01oAsM&A o35 RY3 5@&ۭ(h$5(e7*:\r&:}тkNWTDm1 4kɈqX(A2LZD5p5HpD4XLXs??< 9#';7hVذ" -FldT*l "| El$*;du0 n s{#¢&FZ۫|3x]$ʛ*hv)>s{WO[a`U&ujbGX=dF+]Px+F2-3"c ?uV44O)hHLZm*%׺)aZɂ`H:O˜|6cNl!$py0L{}_WIUTUPH & XUAH + r 1#bmu.JZZd&$NJ]VQ3 H" l8 N]E}+ (t6yx`|=J&P/T2ٖJ$A 7EU7 ]Us$HG'^aCrp,r޼3:ؕ}C@̥^wtFexi&%V-e_QTj-x6@iGԻP=]vz IrލpvOq% $ĉ}8m"`FsM6pKPD6L,sn:=S= {L .xR g;e3BM[?bjF-.EAe\+XXUZ TNNBmD&Qb+Zi*%8spJWKnm#{s[9O6cb4[=";*bh.G4l,v;ќq?ї֏KAfv[vHzgzk4lk(I'0_yo+(qrddQPϑoQ {[s4x|T!12#t8.+ycT]L(2\]}MKS Z֤7)JkqjWUmZspdmc E`nnO{j< zKeO#iL).fLƼKIXmVmWGmYOI+sc7 v0{<[ˈ$O[uƋ#E6tۄf'758I~~rtrυ;8aa,ѾoJw8O$8#|veVf[O5=_ձ.ضB@$ 1<8E^mi{4i9eQF nHdz발rC_'<`cb3t͕-6UTcC理up6j hi+ $ gk;Rdw,iqyk4j\@IGgڜ;cyjN*h3X {kHimo=?Ilm c7/! h P/sr ;UEJ 79,*`!y%9:BB3ux+SB'pI132pU8i ap̫;bēӏqGb:)`'$ٔ?@GŽbܵG8|0$ftNƯ„Ǻ3=LpTXcz~`|0 `hr0$ #D4&wglyCsHƞ8e,4*TyI&#O!@%=90:m1&)+aT(DlTb@#'mYI$ounUjU,Ov>-Uָ kDrf/i4$PdRչ#kDuiFRCpU IN*9L` 'cuhR-S1kz^;AI%bH 7ZI a(?Z>hиU딙*KysLBjrJEJux=؜BaOAPWNd$N+ȷ,3͑ƨ`%OWBƁ/ēd+5 q{>E8_59>S[ ԭ!hS9MzҤT%A|q;~O`͎sIdA[ !|i6#)Zdz\DZۥlA.ɨFe9n_t4?ljqk.;@iE5qc|4w}c",ab{h"dLq)9}vкګ%v+ku S%R6Cд'w=΂છf>ky-k#kp[/O'ܺrոm'1CG48F!,2CMCӖI.c\ׂAmRiMQNT⪲ Քx@ $ySy>{s@ Ēr-:ۗJwU>EMs؋Xִ]s0 k@$`R嚒zwPSʇrlŶCΥ>4R 8y_ loH!FwG1sX6B9I].+rW`8TtKQ6E 3#ieLq{1.qKhCh(BR$pmH3-LϨϑ'R{7_*:l>* &၍6 45hw\3QY[fNhh%^oXE?sƓ(|Ew)IGl}C-e' ;55ҖJ+|Ws.K=yMil>о,G|.$!_gLț~6-/4,6@@q̺nPBBD1blj:qUA$isXǹ٠+x!.;)aPnXem<ア~Y`dlޑcw7\⏿}y[kM [ 68-GO@oncc1F!9)ԠXA$,%a5P;sVwI<,M;'+vc6];.ذy iX.s:0g=ւIMqL؀cRI1M'j:@W!)#2.Ye9 ]PБ%~XO>=k/KGT ;yJHͮ^R~[NrG&4۸9ɺzЖm1E@7aI;%,8QRA>ߺ_ÉvGaFIyc 苚emEИoǧ9wcF73:9l#Rdߓr_{&.Ќ*ܝ!X>C{p3or=S-'"RH1T;KOT RIA7~ͨ:6(rԘ6&1-pIl px)ǞL^ /+lGQSlk#*  ~t¯;+r)5BJO!r(ٚ;pzzq¯Yf@ 1" ',"WJJLq2*2ndnjLBBz߀sJTb"F%Lə3K A%ng:|n&:1l"a-%XDĪAnL8 -Вz_ v]&\.tF|H2yzb3V᪁ןPHJn` #xU-l(2#s>&hmާ Q/Ygʟ*q"m+ei].ؿތhBܪiڂӯվ:v:F{른˫m#AD,ut4 c[U !uUjZ%VA'UK5/'pC͵U{dԦ/(H6<\̵3LnJٮ+JڄG!*HY׾j%]V)]~'IR*J 93s9FT+AQ$c[[滒"P:Igc[w9jHKaFL@P$tk#R#Q o)WF@z|q.ZHB3lǩ(SFDY{A33tvhV2U.x18]mhNGA#3k&xE>v}oz qR`6!D*#S)c-~ o`aHS~JHoĒ®ݪ<*%I$ ۈ8;+56ꕒ Y]q"K/`Qv1oE{>e9B#7.,`8L$LGt"ײ#bp7Rn|͉3#m3~4Oqh1Ф#. KP"Q{ Cqi(Eu!R$ ~հ$f:tI\udweR})GwQQԤRj3RsTgYccd3r[l_oc<6Lʉh[8#WՏk /^/_Wٯg*49~a t㹵PpR REs,_+ܾi6K7iy2 Z\hԉ{>2˴nS@Fj)y=<]i%4(m!0oR˕S1~QvyG6Hcci-ldg܍Ɲƴʾͻ) h*LN:i'x};$#y7III/$.m&SF[!yޑ;\𴑛${ǵ*;QK\ƻlv؜ ܂'Jd#k#aٍ֝/e7tQR(r.ͳJIeʬZJhRP6Ø}f+_=L6t81$| h1#epݧtT",ֹh&h:?=_ze`^XJ#(GXj.{4R9_BKک)jVPt)H'FТ,gW(斦\"űJ:U.|16TK7̷6ߛڏodZڽIULdCO+j_Ӈ̶o6בy:S=^k_GFDg(j/"ʙ{{%Dp;[nl- xPXeci ZX6}ZIqn 㒟iCo6wh╔YqpDxWID^ U32*,GK̞̚v7 Q09 Af6c|rG'YF); `dsA#KZ0wGUﵟ%SKb)kE 9֓WMq}A0hpV?k묧71_#v/QeE/zf}#ERzȵNwY]%ֻIk*Yni49[5{M̟QKkOiV=i:&c6άFYWůKGXxO ឺLGh -Jغ?Q5U VϋkcrE3ly/,J i"Ƙs-ra{zKAvAj5o٩ri TghCǹIol):aRǿo+_*ѻC(z+*9Ub=Vn eBc]W5vx֚YvhDy09 )S6Ml/uY9ǩc/f:2s=Y//ieI$N_JxVF>SqR`h/IgHכűį/G,rˆ-p<iCL6'siJvI]^M--:b$q?^1UKkxo.Oe w"p@ȷ[2[M'?Be);g{CZ2,ĀbSrcL/rIxV*R7q5d$?_z7()d B,&5ϙYU'w0枉R I$%@$ab|̯\ݧhn, p3smU؏IhТ6LJ/9*hR@ ]f" #lY',a":un…݋" &%4< xT`țO8ZJ 2m%^{x?}YଡÙkY3jĭ@DZ~E28aka j"f=z>8U=Ut3DE3#.n7[Y#jms8 #@Kji+t(}ğq!>8{*RW*' 5_,GUR%"d\tGr8OU5`*Q]Q$ 2;tDU~7uNH L6WI5%&rI;bz[|0$ U(&BO6oXJ9hv8ͮI?0F+b~J$ M Ѯ.:%9or6 l:!Y D[TM3NOVD$8VߚC)ӕkdDrL69" ieK &DO"+R6)SH}qQ!,4rw8D$rӰPlt0.<Mއ xLA ߎ+M9 /%AJ x6`$Gke~7=x?~ wk&۬D7S<zD? uRl` r |$es遙fÒ(ƤYF1ftNANTbHortv_d-q#oa3Z[k?RX~սh}cAY]ITRFV_e~TOdӆ[m+RPUJJ@Fdhˍ$ `H@H5Si](kk wCָxrNK>]Uq;2몳ѝj)څT:%RI=ΆJE@4 cCZ?b OŪWjk&y+YdF{>+e:K[fz0˝C M}U2(rgs0P_xjPDA$]rO;((;ckn ?A٧ǩv֪pu 9TSS8?cf \:s ^.x`7Ҭ|=sR g%N\Xkc۟d%Ʊ|x6"'8@ھLU =i5ZH; 5q݈ov@vkuJ FWN,YU9XmvO>gv^'gQ+$ansq/~KOEf),aP@HVzbG4m'j-į}}\k}g뵖ciB9h6Bj=l7&O2RѶHy,ޅC1Ņa8GtI+K/$lUF1@/>#S,y_&eg :JVhiӲV";a# |Zꓽ$t={|+?atxKaܧ8"oր|5Ƕmu6hsSUeI,V2l٧BO;?7y3 tuK /a>2m^mm^BʌJ&aظ?֊v7hgwA)j_VOGI>6[=Uc4Gp.ɣ'ok`6l̈́YTGNko+ax1<@URe9}Ee[,ښ6nά$qM}Cɣc{ hѭhvKLpr#Ղf}'5u{+Zu*A4<҄z.RXh\-!K'o^B[ p_ZyKyfC#k*>8ɿ3L#3#9ڸYcmRn)HXh'_F<9>hBʹѐMvMcei^MD]fŶC0kL:oV+ }L"|D/$&}y- ;)a)".$㟫sAvX|lhٌj*@ O r+n)_O5bA*bu:K-'k*A~m8`ڄY*Y!Ͳw ) 0Sn¢s;j01`mY&W3j*p$%2Q[ H(%S+6QRT R\$[XQaj.1(NV uhy;@8JYU0HtcYSU)% |? >v^ZCK-yh7'>F|&? 4P wrv\fT:cBfG2IqańDO:|q.|+BUoxNl"38Sq 7'qnI ׂ85γ䏛gT%4N$ 0i -mKS>jVRT("M1a' >0̭ueΚ+iy!kZaJm"4sEa X[kMMj)yAGC\v8R}?'65M-|6H2+ie}=Nt'o%Q _Vֹ҇v;ͷzF~TvIO.0s&Xx"ٓ;X?^Nv͒e aD;@_SvDMvoY\m-&T`DȉorV{7rfU iqQL.q|B❄ pl?:=߂34ΟiaJ.!D$l3x2*ټ&3A5um6\ViЪJR6R"":jeꏚn5 GsMGp_ܴv}qXeASS@60=,qsi͎ƭvuf`rhQ%}?Q.KJkFB]~-6PРO8x7p,!('+G0m6xmv6PaE$܈.4MB+.]1;[Ex\%%mp8 ՅG*H0뮭b%6֨Lf6Kq[jUKH Gd}p'W96(5!J2<2<`KԑuG@@,cZAH߄ iԢGvmSpk]4MJʖ> =*XEle,Qɞ6 IPѩOK3ҏoK+'o0q j]bB-.$$\>V`Yf c]Q!TttuZNJP`Kq<$G@ж W}? aL Ecr SU%)("v-~ J٤Vb;<q}KH[V=C%tw ]ɞ]xf OR9eDwS[2\  -K:C@&P3=#"zqwWnG$}تm+NFArz|*x#t2V@ѕ )Id 22 %F:!9霂i6Fjrx= ndq &v NC?I JNd?@jR泀Ctʹ6 |톛L%$55 #`ꊃJ_YJ~oᆲ:s*%LqG1Syj?T}0nx2 DO߉jтwj ǾB#{4z8% @PdۀO|+{x&Ym ,yGB1!![E/>)'&s?눗&rr1'akD 0B.;dXptfwUSzI>v#snĖ̕;MUufwT> [AI>e> F #6:⓱ -?_H&'ڮ_Yަ٪*c~cZZ}N SoOУP9V\ ! \mP0_~xpx4Qv“ƶCUX~GvƁ.fBo ҕ9i\f_rURjε5pAR[(,{SErATU Nf2wmm~K^Qbf~A+צbScKx>5]?gd?e&OW/Tf u/ov.;$s#r[켦mGu=d\XZ"|Zz?Mɉ|7u\>a#繊杽kQǹwDݯ"g-1c_;[]7}|=SL~ƿ~VO:}T@Oc{">"}wujg\ho'wj(ɩrR+ޭ5~KwB ;<_&QraOGWdyg-1%ɻuyYz)G)W5;[>h((I/Jc{i&z=6rsao5`"i$}%6$.ʲt-^{QWU jP2oU ?>qSk!it ,yA}1>XMdܨd,fpRIyiXQ|r%fzϳÀڣ0l[Xrp)*ó3gXN+793"o9%e|K[{Ҷe3V|Fv}T{Ѳr\B 쎝4wR2Ƕ2fT6ˢK{4IA & fWH,Ne9kHJ5uuc)x.]F{bD|ƟW#ٷa&܅ȽP,bU ;x4ic^o_(~١]Gr(-[#ּE.: O^pN=LJy%n2!`ގG+Hv3b -*#FhbƚbP-Ǚq^dL.y[5:.[KAeN}1>]1GՆ>Ӵ=5EXAa1bũ_[t6>}~'i&k(`n̅*]B@،6 ڪq^0nɯ$Ge0'@ͦo6/䭨4!D$ds>< ~Sh߽8Nd޾vA.Ҿ;ՙ<\<~JK|qCܠ{328s~u~'ǻUC-.?[<$Xzr)"oD=^.knWVs)$*0y d y-({m7xa|~|bNLߛQ2Hw֌2gz qRS>+i̲'vʂR|:y^.J! ؊{ K:l gE4 ;;w%L*˖` ϒ?o䵟ӯw9 U*-~\*۸!֝ ސʇyǑvv %3\&8lc ]fO{wJ,N:X lJif~~]m,)_2ABKh T&=,Ǭ^ a蟛QScA"ϋp:n_K&$.ĹobAYʆ>v<\r6;]6GIh@\|m4AxJ(}v#G6VarS#s#$FnyMm]"jfO !#T::z,FC/cu W A$ǽ:i8Z$̓|.i1{CZƴf\2@h[˨ڦl8@s*Lvl1O2sp4#c\/#57!`ޙ}"+}Ac67qoLлArUeYPI3I=k ۰1] t,ݬˬ #u^|KO-ٻlKå0$? p̒0X.GILʖυA&Dyǒf}Xra!kl g:e:bcg\R1u]H8‚_y<\, h`q1K&ES vR۶3ϧ♊iASP*.%@ϑ3煝F8q җ\(}#>.c>3cncp 2M%k_jI}MMl;BdxO_Saoh.RQ&Ht8.]KE\B7G ;2WU&>AB7 h1Y%B њU<]+aCH)ca<<%))"t1M_r3M\$i5=;?P* <˜2e )6uSÀ$AIA<bg^^KJJ EF*eXܯ$p,iWܿgPI&H?Sϵ mʽc3d_biB|dIRVplgbEffV[i& bF$7e>B7")Z> f&RQ0SԞ%!SP.Ur<v}UzEU.MR:L8*P# |bAT՛mep-9kjM5**ڔr2 mMmc pnu2rl[MʀT1a3xy[Dr#9|,:MT xhY }My=$)䥿*}>1]&F.}7zwŽt<28g5R2VlMʛvBiH?n&OEl eNXؔ "x\NgHŒg /"ت^sU$&@xk#nHj$${)2@`cŧkb?Flڗ@FF7ӥ%g`9+bSפ(LsHIErhJE2 MvoRJv`9k?hqA8yrEѱ*P~X+KNCyS 1("pXNIne'ЃTѢ츐vx1 x+a Kj.L"܌5tHNN_q@x9@d.(qpnzlNs'qZFգ2T/aw!A*# RǗ ㉜kLiƥD+D;'c kL)F@(UPtX:D!|Ժ<Y=:L%^qĄqqZt4~ESd,}9ƹ;Ty ?`gb:x?3z4[B2XzA ocj Yi o# 1VGw@7yYy5q_GZ,A'Lj=oSE) 5CVښ(n=ajclFFE:{Klsqp/ǖ'dy!w$eK]#baáI,͝"X6fۖ4zC9r,S [I&|VcbV yNlT*RAGVb/) =mF虽o/=VN%*?c[S2D4BU>s6>xl#lM1>|WJu̓Sԙ%FQV}eFԭT2ٖmIqHA$v'X27B\Z1ì8ă]y ohmnHm$u1%,xױώXV9Dl(>ݐesM:RylEcU1za7T'CeH~֦<O2klyEXoW!*Ƙ nuUoIٮjmU&QBkTf[i}LY m{^ 1i{EM[+"dg<ԼYݖ+.bm#R`T)kh%16'&,/{E5)ByNJ$ FmǾ"ֆr=ergD9hh$.I i'v[?(efGCIWRDȧx*w&e$l'ܢbհ|Fi*Wd{Az'[u'=rT=IOV\ !}$Bd-S l9sߜFbË{J:.5%&^rUy5m+MVQ:۬aV@QP1r{vq?c|M=ŎHt\;x^I+hbH ڹ NNf btm:z#|b7@ypk@? /Ki|=Ka rUźd[  yzx6YO0|;#~FMXsTL7њu٨ޢm[ʕJ7w-*">Vǹ{UbbBC*ln0z1MgsqwJ˦;7'|xFY%I]3i-O4G)eY<5 h܃:ϲl-+@Cz$O <"|ۍĶ+i I'2"O7+N9o1sHDWY56g&cS DM#~hV}ۀdocrK wٛ]+:*Ud*ߧM**UM[N򐲄 +q)$INIųRAq<7m[/HbgMFzq dmHchw6Y\o;!ә`#1^i> Sjix#{MmPih bιnPE\  q|fxbkhfsq{B׆-{Qt2T*,fjS-ի! Rl '{cUɃh#j3Indhl9h 50}{"rױ<{MI[_a|qm%=MDsQY$qvs6@N}:yjdEL|u'Sl{?OA{.S0po }^$IrIIZ$48 J/!ԚTa._j Ӟ13gh9? Dh?ᵜ$^@Ԓ__(9&+]55G&RL)Nvk kZ\@hˣڤS8٧inBi+u՞qchKmuX3#xExF&Ê%u{9ȯ%vHب0*;K;ƸKUU'd2!;\MYTҔr,д@g.=+{Oo_N98(6chucfnwup_hWkys bMQ)7eP:f:1Fi$w+4X!K%))\x7n76,4oV,LYN8-VfL7/9J &gJA jZ:cKTsbC/ UBbnaCy~B$mSoQZ{nFxrK]X'bakEcgaU cx'թ%є[.nRf$|鏥N澚u%M"BO-U1tR+UL˭,As3(T|ZGW2K %3z\U]->ilDc V\YȫZ}7TT>@_#1]$$׹dfla*L?R챆=R ;aD7]UFl,u-$ 0q0{cJ7@P3Z IaD9L`nǛ(6Fe96 te)Z" #〜IZGnYm4,6syp'״ n,܋G*mL歩iQz\iEs$=-G/\{Rn~yqb!Uf6b#LDE*T ֿ@~iwJT ]ු`%6>nmGT5#uCp$H#QRTrc#+3e "71ÌW*H%!>|PiL8S<,{(TU<qgf2+H0)ZR&DwU+/ suN{WwTdmPS0P><_t0 eUrIpZb3+BKZ ;Tz|iC.~l+P51̼ivI %Jmܕ$=-i!4* _7B|eN&E)I uK [TS>[8]ٓ }1R?m`Դ|%(af)""dJ#Uv]ےB@IUĔٯb EF^PJ""JXG h),$Ե:GqJ[;/I: UnRu bzDm!GRi$:*R?xawq94Z JKKHrANJ4YÆ+->ʩC;=]j,15)RlJ"OB\N&|oWTUAQD/T#I`p*gxŘoe%lF^\*PH6G3q(еo أ'\` 08ןpHxY0Ù_RMIS!U^G'#PL5 l ̟ Kb,n@%ZFK9;R{8n*{ʌL4dfIL "D)@,#EeOs'iJ Kv $pf0)TWTͩ*Hnj.|6~6UVu%T+ĥu;T& _ÑꪥWo7X!- OB0 niReq9M{$zɦkՊ+t({8  7ZTʒ$LǠx(6!F&OHWoi{KʚAE1e8^PFԦߺ#-@%r{:X}^/g$DSS y/6=sWTzj<һ!$9y& u$H+i6fZٓ`#Br:^}%ym66L:RR)w9T`7Xq6yuUv {4GDUNye̸Ri\H7&!34S=L-u,0[}ؐ\/(~Ѽ: toES  ıIKI~sZKMfM6`\ r#HtM:ֲw+;]2+/r ܓA1Ia&ќ?k w\3b8Mnl>)UVnԳI ooaeE[/QMKBBC]y[DĨSr~ OUz2p>K*Onk0mL7B25ۯ)+fQSן]@y?){y`;]j.Ƕi+X6P:KӻAyG){w{_;]k-P*8*n8{$  5@,)ikeyi${sKx8{- 3Z즡'zq<Ł&@'8U*(ks/k;a9_t2ۮSy.~'1WK-g1tMqLs$cI;F̻GjE} / l V8O!>{?oj׽W0&a U2;>@–-:5"A )*r 7z ȫ0a{^jkT A pړ%5d2A3F]tr4I$a,qi;s\QdY;5-a7B<'GXI=s"8;!h/k\9ؤp\7.#" Uj~8g Թ k WGe qX%-S뱜& &3~\B˜xܞ} A'3WL26U;c`ZڳZqAzuZgGOG/?o#UooǵW>͵l!uU}~E{52,ayeZʬwuH+Dm䟓Mp>(ni^&m|lYAP۷sx@^rwڳGb6h*GS)("4tǟ)",m+2Z^{pWLz:ywkME9Y* ϖ. R,p6ũQyjG\m%'ء6KQW.v KC2ul<罹~ji*gWU1KDwdU<6Jx"nj,cbv€xw`:[͎9{7Ffvr@Nywq-.qc., нjZ6Tӯ)IQ)&=frl_"skZI9{㞋j<)- ZwwdyBONW6bpzSsQ+˥:/o?";,3f╃b&nP֓h7a19+;UMj~]H^ʴוRPSn5)'Gv (IC]3vD~3X]`֧ 0lnwT \rvA% WDM[INB6Z/c5Ձ^XDvvE X8?,rUy+ 26[4]H*0L.1T8!a84lԀRLgmPTfvT[5 EP()p G+epPnb6Ĵ0 kO#nJ XOHB]+ Si~O}NEQ(u sl[p(#6;7+-f>S߄$4OEV[x욑@# Ǘ07 &"\/U @E)$c G)IQ4uaIT <zDaCRﲫ_M?+ue+}%D @"׹RrmQ=J5rƮ3gV-MG)F% f§ ǛU>T2i- JO-\fbnkW*kL>SYqp!bB<:<' eTv5#_EU_ƨ pGpI &#\. A~'ϫͼVfuSFI4Z&Cjo`e;H@ːm6 +v$|z32X*iv\oQBNajҘdhVHI!k"3 q!ei$Uʷy;+|P_BeP,JԓN*p nc.n(&77Qdݬ%LYQ"u!163+ N#0flV!JSlm[@<)pnzK4-ގ.OŬu Ҧ贮yYYқ* p2Z2;U12 fop(lMXi@y sfrMF2Epd "(YAdɼDӦǑ GԜGИ߈PfJ2`bo'f OAz-a2Y[&[_ 9 䫥FsQ.< H 8fUlr V nH7pP njPmv|Kw:sUnec͋ _3>!I4jU59A=NF/oePG'Urڧ( 2BDm# G ՠqM3s,[tvOD-%q9=>J<)%k+]KLB#aGUj!6en6n&/wORnפ୭-#\ouzZ,L BR>K;ro#n"ln}z.jy{ 5Q2Yi-hn\tyc}fmdg&WW8}n aCl Xbjlk ]vv ׺-Xj&|OWN_y ,?gftGyc?06$3V9 :m/iݛ д8gVd#ƿ06$3V=-0!UeTmG;P%AM9fphy9Գ0z24iȏLgJ;|EQgN4ìV^eyZ*\.# #^q,hcI,`%9ۛ;7G^TrI-m~ c|Qs xyld^&ȀFϝi HL-ɓe+Vl`8KEDF'¯V8v, seٳf 誦mb'8ět)6u5WjEG]6eS{.8.I`#ySǨED5-ɭ h dc@F/rA$dv}9̉tzGI59XkZڪXkSQֳN/*,4mmܵ$I8vg͍]H/B9(@"3c$3$E?A LɝS 4#y?"g)IN 9AX!ĸV$|GnfҦ(\yiJ\qQ%!1&tSSn26,;@s%dtsJH)R7OQ,``4avhV2*V M ,HUqlt7Qk5޹-b­ 1"b.RiMr|ƞֵ) Dqbu4f?_\fi_T$\A>WXdٱt .ꔂNVI)ObšWw9VW`BE Щ\?ԳЦvBU:rl!9,D=d:vea)#@J(B\Q}y_,5IlZf6 +H1`NJlv> *X* Kbl$^oϯ5RLҙI@2f&p(~+Y']R+ yGiҋ_41Ukwn $M' PȯyBZl`pDBhFDTY)j^u7Oϙ=>KC4-W(B#39 + H5J? URU9;J$XDwʻSW:L9?|a qX"VJNeHJ$O7.#3M ME<[glp'IAV%@%L r1c*6GxZldxO)@_)q3(HJ-#z"I!hR&Hg'jksK h(R`n}łmp%配qo 4MJ[H8#A<i+R":EkL2w3"9UjQRL+! Ky+2O*JI|6X*m~R-ϸ/Hci\pGmACY&Téy˜hNEQm 9/fY<w\$n ynCACs*'G{ݻd39&T$ȴ@+zGvcy7}'dVPSlډ < |X6V1Vc䗽XCzrG' I-jPL9kaGHI~8؞A%<[.4)j/>@ql=ċoN%^$:op:#G$7"i0>8HZRv^b\J*y XiU5x ou]Uh+t)La}tTep2P#JelI&g(&@^W!LG,c(Jo5tŌrKe<ɻ.%$~|jLVG#ARx4w)m %@G[>ixEHjvW!B`xu4̅`+, O&t"Upخ? ;NYܠI=ILqT_mP˭ty3܄Ёdcw. {-urtu 3'$YńRJcf-Bªh7R\h <]`ټ9;.4NR2Z &_)'Ĥ+°jshr[6)@PVG[omtO^6 lܻb3ZqiԔ'tfU&niC˽v6ZJ/>p{9;͈*}dB}Aږ^OAe}x-'bn`?[*2KM/&OWl=Rm_|hrwV; r)S+} Z_ROpn #9ӝVOhGLA >}ИE#ur4Lr=m|$w^DL3dRUaT"n)/tRnU4JaPL Ep&qh!$  ٔ[_ږﯽ~dsb"9x0&c D)w+2l9 wv8MmW(J ܲ.AGL,~ 'KaeI|%CBaA5r(p'b_jzL:u5NCt^ԗ*)| 85G5!޽ʚ ©O$I8 2%S3|mrO\a91zq=zx[icG`̪q^XÆs:-oX`FQ{[ 3 _$5^9Sjyڗ*ZLJ۬zT;f xseZEBH ܑ_11QbL_m6r;v6-m_RԚ` 0@B"g:mw=v,[m8q WFg(6?El$V/my%s|bR\DR"QfnɽEZRLTQӎGTSmV!Vo$ϼ vi[S βz9cK]JꈣѤ[U4ڝS6.˂٨\EyGכYo!krRULBYES8C`ߘ Fua}Y7o v%$0 "GM{a0H/؎y 8X#1V:t#m*c< s=HRbd#u֓=J[.> pp2~W1각ޭԝ)WPt! gwI MEɛyt`IeQ2z Odz AC=rRh rS?|-RQICHjwpDP?7~e|4؁[t$Kgdۛb6?TWmN!!s#kb@Ua$n*]"dA$,=H\U^'Z =b0f% AV!R\M%Au ϼhʐ%pk|ֳߕAB@ dj<yEKVD)kq0 /ޕ.=sѽB^ Dm3~'΂z!i0g+f9o~@E+R=}1Sd+}eaI/7\'g++"V^f Q ǘmKC4GЀG^0<dII~XLw$u&}g ]V55z GF W.! Ur†ۭYCCV_%S>9Fl3> V X|ńxmG:?;1YFoF8 yԪ^f* qMn-x*}VhWHZ(4fDCqZ3BI Dp("E_&%PIZ5׹IC$I 2@Rr2^ھZ=L005A25R9J&`Dܔ7%rUxtɒd~17 VpHC'51E7͘!2DucWF|FG^H*U`.etvidJh+T+5"*S%5ZziG~ E2w}"Z?zSS_iԥ~ T&xfQj:T$q't`jtH{RL ]be96KDTRxdB8{x'zMkn$υS$+03V89z~­[V kW U1@H)r={:(s0 -Zs7@?BRE(>":c`JO6w`^}M&+8% x|Mq$c[bƝ2m("ELs6Q)P#(ޙ< stP4(sP6s` ˣ@M˞JP ֒2$IZuVP~N A/n:&x0Eġ*4#,(% 9$ GZD}uSb8jBBOY< :Sc>ak]WMTD,|W"CkXv:GWeY8*E-l]W,iHhT3>"I6AemO9޹[7)ҹ Ug+QUO"J:앲r̲n1j$G[)PT9)f9Ώ3r ڴp<|Ek{i[H]SU'_R 0gƯ6.X!D+uNud0&#w5W[qH:'cXFA0tPD[*TZV f:pWZu*DOw bNY԰UݝT(+uL @XцUyhZ5ѹ_Zv5Bu:+_ JDGE3\,2pѕTV-Q45*?xLqs ą^AJ˶6rnAS\y:*;)#';ޱݤOXilS4ƶ}}IڵNhUH- ;\;ei ` ط(X \/Uwn&dP tЈŅ1I*]t: ~HBf݉:\O+klgTMr9 -)M9nݑ ӕgH:ԁS9 ;2O@l$*mnDu#U/k{!rFOР,f~ O(*$ FH[7+ͅV¥[y?(+aW1\-ܶvUN۠Lylb|V r+*#䯔4 yLq^T;Efݗ;}|R"#[@`]4-1iSf]6H[o$rVD6& av%W_hH Jgla;[, 0zw,A01Rt47N`^ fhx XdU)Y$pfnYTP$:IH&k: :z\T }!`R>7\*Yӷ;"V ۛb*qQ6%^ITR4"%ԈqSSʺJBClֳ[I#H1JLƲMWHskӴz9^ږn<*RS AXָun<>ꌒ}ڈ(*ٕYZ@O1*s>x3kX ^iTWոVsrfsCB V(kuofOܔ2uGR7j`-/nE=ܗdTOQ8ΕRTz9j-ܥ/1=I9!ѓS*dPB|JQD$zJ:itSg`3FIQP[T-̄e/g]VdrJNO (-SgٺROICӶ6 %[o >="ne]Q & `!B~o.׺p& #wcH2+Δ40d|&08 P]8-Ɯʠ~ڛ~/牜~ |,S$<]}RMH/@R`ئ-6joBK.Y aՒߎ5~Cp__\Tg.A.0<ⅸ? LNi2~8 G|,HkUNцd9LHtZ,p)Jq+ &qƋA[&О4̴:Iz'..RpӱN<`RȞH>x5b^C[i=3S”k Ȉ Aq(͘T )&:`NhLl'5m)`Osoܚ`.G#1}s#lϊE]dn6iMT6d"’n,|)1bϏ,# jY5XM6 E+8ⰺQj'y xj*g( I^?4o7r9]kܩ dH~c5na7?ڭ{mG)၇"-➲ӎ$BDr [^a:Lt-Q24O@ `Y}:^ m8" >TLΙB|Imf?qJ|8Ir*rT%yP ڂ>cay=S-})}U4Zȴ/F҆d nC: QEH# ee6OHj2@i+880kIrєr" &\ :v"lHB<6@6GxCq6BH ;հ3he\i?E+UN}EM6:9JX/".JIpZK ]p8KݫUMV3G_bIz=BvGq`Fzji~F  R݋yڽ4$_%mZ O%=x,ʝ{- 3iHB6?ǁFљTcs>5,s')(aHQD_"R~:z=Sk؂z{6Fe\}ekh!DHi(I&nn**E\R95tFVw{]K,$۔+]#"V%R"Ѻg?GP䉕R1 |D_~8\K#P^=P -YOtDvT1@;漉r} kk+IksZ U3@ɝb6lMt1ݷj]\fO pA,BC; `gfŐb!?nrnúUmNOx&2TꟶU᜷2ipq!$HH>E>x%CTnZ;^ЫSA)!L$AYmiYعz|>[zzhW7tj:- _X%=]"Rd2aHێ!˞8s'}A;\tUxm7JBgK2!g76M)#sPv9K&\e}.aZ9WUʗJTL.@EN*&l,.EOɾ,r +:fPd%bn~l>L'5rYS 0nqVm܎cO97Α-Lͩh% {$1}L>_@fW#$7!b}~}c[ i@`Her@g#es+:>f: 1Hi9*$9kٛۉqśkY*12( jT7mOA=px{i 19/ ݠfZG$;m1M؜)br oګAz%;lM]U9n(Z$xv! qւ T^ lI(7Z&\Yrٵ3ch PnWLo rPV;*+,I[1Xc\HXhV&BЪe9p 0iu֑}/}ZYhTB@T  M=A}sNmzɛZ} ė p͔_bwJd4ofuNA0.7isWv]~M фEC&L|olC4NV5jxRP |k65  q!gu$0O'juX{ӆQF( Ɲ|"QCPM㬒}q6QJ PT"+hy;I[5FXJ(Qe<RFGؑVOO(yGǜD"6 oM(ńBgb;:&#f7bGQ ݉Lr\$wx &~j8kԀlFVڇC|IA =VQm-UbbK堐c#o87V]D@S K.L-թZ9 h 1}SQ"劤 LIHLIiʶ6Un]Ce;E7gv1nl^ GrVl֑wmnH3  UOH6;r!h7؋,Χ=c2MR p ) '2i]Am*gl<9K2>,gbv#6D#Zn)~m#QLqMs>@e@$o[]Ɩl9Lg3x+:iGD+>V뀙澁n{(0a %7؛e;(FJf=R-[tʮ` iq(Fֹz>VAڠRpeQ,)UUQFre4N8DQ "'p s8 *cOJF:BԴ+F,lmM$(RJw+e%qHfwKz<0n>% 94UjX*u2=b'=^=B;0:Ha>H0(* <d*v} %kʻs'}G6x38:XAy6j۹^ԩ (X{b&&_wX$u\c릾:vb]mJ#wc|Zߵwnlb;ɻ>i?"]RA`Tcx<۞ RīSi̪JMB:( |3ꪇ؛<mȓo_Eݧ;4t$k/h]jYdh oM~TUg/tn8X4xNOXn%X,;>o2*UPɃ.q#ɶh>Q7XwnpwrgsanMCt2;xS04M,tŹbzv^[;txYtz6~ #Wb2<6vrVJG 4:\( 0v6L߂T!FjdWU;=?^)"e[ 1{g r|l;T"oURijyODR. chzd3wJ\sZL5Kq65pRyAW3mG_vN֔/\k%J$S*[3xsATmh * i|sGٶq%ARPAWMC;kA ޟs>FRs0x$G  u &Zr!ZЗ )B]*,/ ѥ cO,L:c5l&fOB 1IҪ Q7 f_}W{Dv~v1n̛H&(BiFʹ>i(%vKUUT2KG;,JUQZT_ t# ʂlact+@I*jz!œrV̲chHD\@awIHˉt#-d T6;i*D E$L1E-#? .ڠ:s*@R6<|0vЍdVNAĠ^l R,4FPf)ɯ)%K+jGu6ZrUq>eW3&YPJy m*gf}n2ЂI[j#D/Jrb9L:J]K#$\X AKTYJѓ&!( 05lo-@MrbPxu&Yr9UJ"ob Y'Қz*g< $)&"ks(gjV]Lgcpe1fTEEQѥQ=FPXR$B*AVЭ XgP8b{7ZԜ?fy mߒԙf,7Wޤ,H<YD3$(*ߑZ?QsZp:lH2,= cx]iKfTHZ}UthH Qn*|{cjΊF&F'p}#~4K{T / <%M6Uc!I&|q挮sDe)HycGtB gt%$I#X]lRӪ ,%! XQL ` :ѭjt, @,LrLl߁B4Z  ٗS nC:Y2o1C)y.R%rOֺ?rVW\9rpAO刚-tF/H$5LVڇ1] HѠx͐b&Oh"~Z\l:1O8f?]q#_heױ™vio,,'rG5osnE+ooN ½Z*H` jDʤ[ܲHQ>@z@&Džmt}{@Vvg3(i} IL@' zT0iO\kޕ.Z(2S,~Nܬ|0o/ޚ2TK17q$’mUs,|U;7AsJK~hJ$%* Wɏoͦ+fw_7hBB)}ԣ68''#2EپV)қNwm:D Օ-;EIhfTͲ=d5^J€ zGnO ĜIa?[E[ָNӾ!CE\Fuh#I;{{3Fq'ŭ1^5KV_٨2uu&ДjV~bW9騡]dF\Lrn~=AB Y[22S-fL>UV8dIAH j266tb<'uá+n^䫎L%?ޘqdy>(@,7h"D6G4Pe[J$)s#h.TMt A4R8SfoDD\b.IcݠMrܳ?!y/?51pSeUG8hٳ7o'FR-srA ăf<\D6ҽ][-O*6lƆ& 1}tE?dE^ۖRIy ,>j3qd!sݕ Y˲FIFI" bzo<bUiSyE>M_,|lSvKD5<ҁ O$>x@dS1]aȲiʀn"T.曋dm1n$'iĀ *w2:+_1% 6JO˜`]"S˞'̫M&dwT^[QfdAb_MӮ"F%7Ӏf1Svky{vɂ|A]:1/:.\h)QM{(R#+ Ή a䒨kRvHJ%zq PNa-܈T ǵVWT6%i O`|fo1PM`Tô~m\JQ3ڣ'mMާVv?Uk5oh(%I[i*+enX|ԟd;R{KT+0ސ @`GbgXl -cj $)SuRv;BTvD #R76f[F:"\R %?['i"iꛩkq/ J>"v2X;u=,muL3qHt6pO]Xs ̳: k!; R Nd١핋<~q'<~`J*rlɹ+RHW6ֲS;z̀"'LŖR0 %#avm5SOKN2yPzJvn@w(kz%ϯNҡL+j(G:MoT ,o$+ǘrʇ tS<$E3uT؂/j*ښu%[ZD‰4Y=YLEKw'[ƹ t 8mVnCA6gh[kB}U`ks*.9Xf1S⺩ 6P? 12pGBI$XG_ěTѝ!U2TPNH; ku {.$‰&BML+hl'%]#yӰArr0g8n* VJGIyo9ڇ̞-}QZ& :7CL e3ӤnIg,AqZG*,RNƛAmE>xp*&k!\BJxȼmKi,t@ L=ܐʏn%6:FY6K۲. [snI2v3H>OLo/ʝ 1(ԂdB)dS S̭YH'čmh'`9{8 jj~n>Ej }fei JuP|7VBB3hr$&H%<~-3$"o`SipŅQӐ@RUcF"n!sujd -X-h*|EKKJLv$?w rp?eJK6QXn]t^p(UЫi\$Aą[@lSV2 f!mSiZ G|_1cK)Uf%iM4yrbB~DjFL@w()[D %dkbTJ2| ' $;?0+MZ7~}bv3F5\Tt5gL7: et]:i X12wd*ݗ_6 >l1ٕ9x;6dJ7at*&dHLm\U`֍ -:)յ,Y G-Wakx]?M)2BOFLEV~Ҫ@HKLqW5 ̫( dnK]bܷ6U UOFU/ڋ-vJCdR,nqO=9(t%jgDPA#Mh$\z%58Ho80o6뭞i&ar6S}FBǡ)F $0߹E݅R]TѧTvxaΎ&&ӺS+jW!([+p&)"ڈc? w#IEiU?S*g^Уn03ٶ_*FDR\_-*.RoG1+7_m l-*tUpO➡Ѹݭ-Vt[rYΠ̒ZMC*~g{\sWP-[6ܔS%%$)Y3iFR2b?boRꖚPS9~y8.6V;yOT,=mo҅Mu.DB+\1`B*̤l#th(I .l_ZSa58u~g U*e$y]]3aWIO%&Vq >9gs VMv}udfT " }ps6@(i5i-(ĐG4Āܪ9T[yzJe;P^McB꘏hc?jiTLʮ. *?RT=vÊ^<0Y#.X ~^Vit*xjA(GߍҴ؁8^<)3*ӗ$ qKLlg?J/Qu>%wZ=+'͂P"DM,IR펚 VI.б5u:%u46mR:B1d2V7u%7H楩յ JE6PD8<I' g*7~$Z:ZHiu'@ 1Y@{.N[ֳ*}f-p4)&1=dV*S<[i ^o2NE:]֙mI-5SPUK*Ay8r8f '<|7%ZS{^ӵ)u(a(B<8F,c=* {HT<2춽!쏨X8R$|,ߋFw]6IODqUWk4R*<IL9 lM\Nd4C!U3:\M@۩\Dz1ԽsX; QBJ5\z놆 T>K/Q#sjILQyGf.N,i'^)Ѓ "O]i*3"TǮ5\t*%9^Ymń(Uǡ#YY''VHiA@RH|)J,g>NH"٦}u?fǜEϹtX'thp?=lglN]رs?e4,dy91OeBKH`KAhIlz}p31\)W ZJL)qg Q.Ǽé3?ݮ AC4d(c ,u lK/-V&4NVEJfm!@\kKod!n^rY!<9ڥѭ:c+m7 >p1)yS8 匮@#x1umUG< ݦhe 򞛋@3)qi ,25K-}\Lzą\- 2J:k-HܔX@ Bߐ%Xh^\$]) oZ4.^U*CBJąS΄!|S<X  SkO8 \e| 3NVEh~&wtSLZݻ$|% = 5;71ڶz8Ȟ1^唴ԚUB3P] qQGJJ晌E/f`7aԙ8@/CqS *]Y<(giLTo/"ҙAC͒i%# 6iS9CiI4.Og;+O16{/Q wJq"6 O΋]Q3bVeRfHP>6!8hn*z2J5UM;SHRpL5%nmHr`6zhmk\&+EsUoK:UL{' $>v&Ez@ ٝ~'2mJ2?`#ȩW•Xd*f-7[+K5)"Ҡv_#궪/=Ĵ%oS=0R" 㞪8;tԻ+pVUleE!\P.qE>H~_S@}t"Pc@Hܥ)g7jfJś3IA>*΍N!IQmk@% .*$߹0((y+v 4[yjqk**>4xuuGn}-5] -?jI_2pi\XR» bޛeZ<T5{ADl\{U*͗R'h1f͏'6H'~~DLཱh;T9FɆJ/t{גW`ӈB,AdzIBY9F~ks]{ukՎdq啕gx ݺ é_,)3 #XQ5KyyN'd7zl~jA%SEʊu6QrUR#犙:\I>ýY(ֲ%Nm63Lj do#!̯jXUC7SSIlx x?=};zLl7"Vݸٮ󥠨;P @6QKM&bfh5=W?" r.ԴlNՐYTiH qPo7O@N Zn!^`?bP WRuTq G:}0.7`&+R.KyVֳ-4%Z 01NѭB\No J5JYP}L{* >{fӶ䫟[^wV enM$)=D04 GV~"}URIwZq. Olbʇ*ʁiWW!.:*$Gh9u?6RzxV6)$܂f$y~TwFe Aȍ+lt:goԓ>Rf뷊2uH%7T-è-w}?=v\Xt PDALQoT MhȆ$Ԥy{w#$}' 6ZgP?چGv)6 &3 (S0J>+_*Nu+.*1ޣ!--8RD,j>A&n2S(KmO YH' 6\'WJ?fo-S*qwJ3#Đc 6o}|puZ֣+։R q RR@C+%ϲyp#e'4f6VU;/ؗ=T>OvH?@}qu'H FUşDlĭgPe0RAi ,[SI#w\z} CUqX^af!He ӜA\:kMiHҿҕj) )#ls ћ@L6Q ϑY?TR v ڔL{470(y$8/|ꪠbP$wɍDHzma[v"~&; 2٧{֙pRQbҼXG}0曆8y;H~+ $8$Kj!m䬡+p޺ع.ϛΩ Vq>XZSsk;}BشZc9q!/J* ,؝;I,/j"Š?ͪj2ڞx[Hv?i0}T'2ouɒA)~uUm!}w>kvU:cS5ш6z^-cVH6Ӌ*)OSh+;̕a'FPR*AJ;,D8Xt*3,k`-٦m.5$O;RJ>8[bmܱ0pU(eG1yJ i Jʰ詩qmږѰ\-~Q7JOTLcs)hrlf9cR%-8d'nH` \-Uf9I**P"6$'iJ$ (NVej\zLT<S_y%s~g2@P5TBJ*n{גqITz6nx*bZ$<3KHlWKj;j&@}S&pF5 |5gKzWOcޟK=d˵5=.f3&;&-hDD* :ً٤<SU\KN6 9.ckhuv" JLmù3h[`8H$v>oi.=g\۸zޗN 1譢欿Ao@m(LF9;oZ27ŰXHoYhzv thO,vXj&XJmA\lT[ !א6OL1!=!. ɘD ' 0ij,N 0I vNij0n`u=4Ei ;2L=u"k,Zz(xIA>=0Rυ%,RɓCK8)Q'5"PC)_^ 4E}D*\Qd 6!;]c#=%Qɼ6w X,{6\?LdJu㔤0E0MOE%qʻɜR PU2fľpBӻa)p2u#ee}aJf( žsu8ܯ9]}L/p|B7ك+ZT)$\RĂ>Iġ%h>FVJ~(@*Ci# ZwfScզDQW0ܬ!5|ʞXӤQ@T-I3s ~xISpO7ѓhHjNZTLBm"8b:.J訉@ʱW t )umk 8rܡ8GVWjf`I:4HP !ROC?RsKO@VR)m*n| ^pǽM;EEPA"1[4Ih'b'usmց7Q(̒5XLĵL'c5 O!/lQ>I7x.HR:R,lB301Xxud! \TTgږ| PŸ;e4g}&adTM}!cAoT -7D!ĺ1SˇP0\;5z[͵uKIH\*Y%=F}_^w>֢HԬ8kbZ*yphcrʕ̸ Cu f~2lNJ~ˀIQBtT%!FDx]m$@VWIXfP8SZ"xR?}8ۢ9I4['7tU݌Ԩk3’~0[HXq]o5D;̨Az$[P1qk)!+&+3'RnqO?%`&gq4ԛ%4,tu \I1PU Ǯ++nd6*4/(BO B + |&OQe+$1ޓ~!(B*욵iݩ9.Ūγ!jʖҩukNЅwi`xuL{EܶeM>#_%50tnּv[>ЬW^gEVfYTӇhҹ\VXS9]= VŬ)VTV棧RZDnsoR=tq su\֜ů;]X3 T@PK9Z u)`k}]_T*e=Fp¥Yrn$juf*^l:iRq*~R V1Zolݙ6KkpC!7pqmT8oZnHn+SUdVYc"^RKMYbE2.m۴;q'BAk܋iӍE-,ȋhC{9vN@$k6I=$c~TہD4-wJ i"H`uJGӬ͉jp I"H-fGuƝn͟Jv Ɋz; h:E۩G;e4v^Dm#l1bk9:$< ʖI'mPv@X,.`RשNGPv[*e*R%70@Db.4pɲ_kեDf w]l_92xqŽ H@qdю(M'w?XꞬJz*,J(;þgͻ`;G[:vif$z"`~x]p mC.DqJo`JdLqR1/7& F3e_m(R&DƷ,KM%&O3uBXR7p aDX~CB4ѓcSȊT(i=/0>DFWCER|ILQT+I,Ea>xђQ[PDR2*qd r9|6y!G:bqӈ9NJ =MmsdAR&N2%&&ZQ49)^8rFd> ʣ#&}N#ۡ"3sH(#rhbKQHyB::[q(l=(ʒ TC77\3E 667ԥ; @Dn%ga;YHnV`t"KM!FMă$$*UP8[OgG,8ETO8ܭ"3MT&=$_v*񓚊jޙ- 0b9Sˬht6&W-$5r)z9 9Z(K-@hA`$9̦(ҔKK/5-d嗒0v)Y;GI$ J%LQ@3${ 6lߖ{~-ftjji :p7 v޳EBsV[{ &K|Z1SZ 3MB&3cLQ88{ϩQm$oƦi,ɥi#šj Vc 6ވԺز)+="F,y7.?4ã7ҨG2WqmsN"(=BI'zԂgjJrLQwG=H+$-r™@MXL-R]M!7EFvLR|Q{t8L}NuLE0ҤϢF3 OY2CN:XJG*ąۚ"/YcRYIL0)#Y@O=.!\I+A0[ ޶P׸SfuM,-u8nqqƟN,f@#:~n A =ހ/ыx-~SU9zRL;\z

hK"Uk2q}D)%*ÑBb8Ԏ2ewtq]bgmmCYsq cB"g/]WUK89(i<>:ꥅT6؄fA|1&TD- #d_܅^T1KN[`- $*ˁR=G2]m0otSۂ6h8);q̿敜f9RTJ{'7OD`i̧ϴV*]X?!nXXw}]EaN68qG9WWU;,ە* y) 1+#=vx> &U*e td !3+,Sj=XuK厺KjuH(id! Q'6kncpvj|RY品ϳ#A)sΚqר$ Jl)*B(,A` FrP Oš{D GR 1⸝S& ӓR!щb%5`pG6@z1ТNtV H" 0m&D1LNio-^d$  @DDu/}z0EMBL8ƺ=!HU8z"PŠߟ?,Dғ,$O6"fuk}}|\]xAGcBku tAԻ}*"<Y)62HD< MfS,`N`yϘ#4G[1e')>BH'C7-Y ׁm,Yd~T¼E<޲1.PDMzhMՈ8ǘ beoj*`Dr,yzb&ݟ^1 2>\7fVE"V l r1DRɣD#:ֺDgRFdϜG@ ):DJtlɑ#'xM|C(YxE@ض*"ّ1kq4Rv) jʸY\D N<.ҢY2oa2ژwD&1h4\gREj/.@IGQ\ҐrRl ?jg2/s0dPxPÏ20A!6SClJP$2DGnaB+_dKKOjq. dbKIH8qj Yn$~^6.;Hfh+lE@Oi : ΖG#< H$Br(||:O،EՖ֌nIn8p[L5`d#Mv_5"1XU%`A^#?`Hؖ3Du LcfK~ Vd3m,'%:H6 ?0q$V Htm5u@<5B:xMBD_bb)X/U$*I2o{@eZARy$k@k"ѪJi=qdmCFVT{u71gb*[l^T2 uCԸ AvfDlѸdZsuJjJR'NSdnd(!D.}l?P>6dT޹RJqanLbf (y)'#in|7uMM9L&M 'l41hRTbKGu/t?})uD# inj.!,6![h3kuoIHPJԛѹ+i(ܕL_H+,2RRc+ZRdX cGYKT#3 Ơe$,4.vr߉9L%[^$tK$É?c獭%WjZ~֢8;#vހL*m^DyCv'Rf,}梑~!WiUva0Kt{XRV "8kM=ߚŨ*N52S T%:GL-ȑ5uk(\y*ZewRzc|M]Q%yhÊqR$\e6 ( zOj?#2J)ߪFN%bB|aipN)7{5TS3,/a$5IUJE-w6ox#{C|[%32 (jN*=۵&RDZ?z]`(:9D`*&c'd5r|VԴSfh*reTT<#1`F(Lz^jډZت[Zehd.{>)Bۊ4UE;l=R4o%ir'b q˪h-t6 Ziɰs<3$+>3"i?DLT%vаdH \x5m8xYv W mIroʂ-}PBxݶKŨ16:&Kzȍ2y<;JԹʌ"[B*)qƝS.)Hy(\$y2$tRs!%Uͥqr˰xdx+ϪZPR\EbPj iOmmݼܭTZSUtǂvv#|!ӿ-5s4)S4I @(biXvNnF ;b^gv&oY;BO;QJkXFIUu5,,[Q`@;Bw]{f8eigsiq!e.xeN9mUC@QC3Y3of'mh YRRN-p)j+!k\nGgEG)uDQ,6LcFD]V_f 7hTI2JmsT%eOEPqvwͅ6 8v#1N 5 Gen "W&8-ٱ1͠؃{Yon>vљnTvMHʦUg;S.NjMu€)?DWl)sygbݰ=aUp#Y, \NDn置eàfy6N^E-RRS_%>XXxجz#8Dۜ_?im/ڪ 4M NTm1AkqMOI!SţpWFDEuNgw!Sz#;3x>~֣ϨSeK:Gz[)mHqWxhd-$Y@ge6_.*Oư{]_',p-{* AShvB^C\4KLn=q bj("Z/eN6>qfSXI/ `E)Jf# phl;]DxtnMéjj;SHS-eC0Y[;{R"Aw$MVz i7lp@ um|bFZso>{9C,Ÿ1Z[uy~@9]FLJ`*@;AJvbz;[Kwohr0cЎxkLXC(c)ft&+zN8ەhА -L!70-޹& s>\g,t1NZ],MLqJ:V].z'\ujj̵ccBQJնICa{I!@c9_u=relqNVw'y~͹U#dDnP]tO.y !e귨>t)Rd9 MvMۻtT~ KfM p s7eWs߶fb]N[bʔbEVV) .fdD=s}3ϏQ9liɳdm̌mcaI`T"ks\0mg׫Mku$5YATovf˝փn6ݜxe4{$ -߹k-I}(rSTf^}g4s'AW)s?orC@)nR )Q u;!&7\5uwnkyhd2SCC&uZ:7H:UթM毶a0bi߹u)JiS`[$`p{h$[&* ^4^fMɰhJnU.b:sŕ b:g`h  O ?Wh*^6|Ȇl{]N*V?x}q{'*QR>f=IƷq'S 3EĈn*CyC'tDaKގER {eAlb'<܉׺%I M#2nMb*6&e" S+jt֨9hs-lbhTP"fs-R.Ո^`I?0j':+U'lkKޮ9LD D#bW4[ys=#qND)\q(Ύӭ}uS0 3igݦ9܈1212RƮD#4)@>vDҴ7{~S "\Ozba 3"%`I`pD;D q{| -W ssZA`Ys?5:s #C ![Eԅ2s$'O6t&Lm$7#"%q=I_N1@ "Xv rvhD?0>9`:4J3ɛOLA]tB3XEo\ `S'})pDD|D`N_|_NzךG ̛s`n#;-aD*QY b1Xݭ"+1G`޻[dJ}fs~vB+8t}$L$mceťNp?gu e$my?95^aOWAcGjrYOT'qPS3Ԑ-=/X;l'ۿUx5Aq@ >W bQqG@ 208DE_{BWy%Ai$# wCr\(HwydQPmS*Q<1(ćJk1*PXfԕ{MP(R =ږ*zG d#׹Pi19l㹦di -蘦:I-PwJFDԀ dY-fQWfa;ErHGcmww{}ʊ6ʇhtՌ9y[ՙY/s%}5 Ji5ɦyH P) $l kEh|;QJ^KIdmil|˷: !DbaUMc湓 rV>me%^ RD\ņ-`?Z}˕+֑=^F\sq,ъH!ʝu[It2G{ą8/)ABa@pp3A7yT ڊwLcˬoenR,,̜6~V߭V)%[T?6)RGi(5;m;|Nsmvvv .IdHucGRn=!Y1aqSJ[@q ¥!`-uhwʦ=GAglf۴/9ʼ9s VmJsuc -ԇ{PDJm +R;f'јhwndbYK5bviAR,`nyF6E;YɥVR9xԹ^=Iٱ$TL@|+Ş\jFwg=pL KwܸK}U芍h{A F2]tRSUPs:$Ѵ#1w1GtA Q]EA%\=ٌ}/m04#+-H=ԾA[,S`ayXڧgI}[ͻ&M}4 YNwRE$~O_97l-%{$1(j+uMRT0x[mkp"T@`9 8geOCNYƓm5ma{ 侪cř5ړNʪUmJyM,孰&!?3׀vt `^LvKgá ,Z ɮhԞ e; `;T\]Q9i,k9NٖM%?- (`Xn:c)o=Fļ3>pmވK9#MS.x!o'BJq3 ؒOn9[A,m5oܣX褙qn4) o?4Kgu;`e(v&Hsk EY~9 DO&LŤJ݊A d{q2G D#%B@t #N0rgTBb ZDZ$%Pm9"d\Yʔ6Lsh0u&y@I1"T̬KQ}xE3qCvdz^\l|L$:A!u.s-vB"<~4XۭX^n/ [">2Dn߉u[ޱ0`"^ bRrSż|4YwާE:~q7.SN@Lq z~8 x\HNcOA|@.Ѩ1ocnTqvDI#$y^X6INs tqR 0нJPN.m3"DH6{؉6)qes= ZgÈoHjذe"Y/O3{8 j*$MH3oF"reWO1=b.7bk rO>cc{-Fުdǜ8+ '\*A@f<hpr"Aӎ:@"ݱa\f.5yUnJ9JE3قq6_J[{8)8>o"#:!{ kox[Bv/j}[L˴4C4L%$e/c巡r iZc!g9kC@@' q[<n{( FCGnm{Ig+K|[4>ϒ3]X,T]dK O'u[% rWQڹ`"dtd>*~Qv5BnY}̾ӀiPR<&1 kXxlvwm}ݼU/G#Oe_k(;Z5*sZwj?J(h>94 , BX <7Uabyl|6q9݌di퇶34',햫MT2u]nUklGߍ Išpl.{:ڤBqQlAsc{5EO;?d^:=YFbWdɥg?Ͳ\/`^PV@e!,S8 ƋѺ:wEa7K6[gJ}?ee[ɘӛ,Qw5+ SD8M#uR٣ Kn}; eci2,$Ñsc[NLo~~.`Xɛ'ӷ}ɀ\_Cfh,g4p1.".I灎ejvղfdjv)R[IL& GЂI7obdک 7 Ӝךuۏ>Di|)7[_6~7K_}%@%SLd6o˾#_ uZ֕PRXITJK/$de8*JGDBLxR輪dA k.(F0 X:9v>Nf qڵi..L(⻦U8rP(Pz/Mq Tq7XSQ -~R;fODNEXj3th XJLmugE5d4 l?~6 %fQ4X"[Ӛi\QJtʏILsD(>gLDm%5︈G!7)ˏ OUb&rrr.~*t}'hW1THQҌ)ѹ/M/0>zѢ#cW8H ^Q)Y ڻ6*1T\=H~ވFO??? [uD'Cis;>8y  ^}thM;br^p (l9Y0|G޵ # '4?VGxx~3Ԣ؃yx+t>Aot:nRF_<㈜R[e??!:#Mʂ:Kc灜RhN#%hu{v)XM.ݴ\>h<== ՁN^KB4)AK?}dD5[|C:tF.2>%*HV{o%%b䋃h&*M?ai,tǛI[TQ5#x ͌`&3t싟4߰X5wyFm5`$hH:A0|J4nF 7z= E7r)#s8j\ :7Fcu[ 7ue2*1;O\L'5cWVP`Ƣ1a8IRtDG.\yđ=8!+529Q96ɲLDqF5+}  m-$Fܢ~G5UZKQ !1g=Eb?h{V-э˹d3^rlADTϪG@c!NdjR0Vk;Ԭ@A 9'75w[KLOM`yX(oS#%`?vx c9R'6"e{2ysNoƹ?XSV[o'1)[0!GO]FOD9ӋyDc\䃉Z02FKB`:~n =83t'edz&dr7PSJ㘰b&Yu+mGjb#1vm+Cm7_+R[^cJS[S)3 "51G{'qUX!_f:F&v5 LX(FB,U,%;}*d,LQ>D\ytR;Emkx-;EZkGh94Z*GdZs#r|k̫=.i  lü7NF[04ZgzA5R4nƢtږR jl3j}]Hj&*ڨu"BmQ2# -Ѻܷt jEQ#*FgcYg_d$0H.hUMk~YRͲ᷻jh]X y m@+^we Żh.ݦً)LsھӺ/YZ3^&~TT|+XK+RaJ<ɼcT[Q_iC'B}jp3Mݰ7; H~Ѿ~β>ED=^TS5047@F%J#{IȎ&vL6:p0ѻW0ՏYk'~ParT.c/T:JZpxN-tNx.zLKw0rk*) eHKm/=3VmVYG%cv ם!<Ǯv!Wگf"2uHw`7F5`C.ME;O[T7i?SX,7=o":@^8. /bN[${'$gwb=bEB- u1]f#h- R DpHOKgɳs("3Y_Sq G>͓[Lku3P5'Amy1qMF@Xs\)t@uD7#D[>:{)< ԉ3{Lak{B9Oh I7 >ktߊR`D-@I0Oo4GMU(E$ϝ huRѷ5;ut[`Fk-(SڶZJN-̞?rĈЏbT`) YfS2y081/zl!ΜtyZm F/ޡ!rQh~..>P4D;T;FزsN3$eu:sjqBOK^kƺ?h+MģJ3ZkA01LO0jTH$"qNV[v*dRm0qLϽolvySE=='Q#`ZLZ*Dʶ}'47RɣpV1Ap:z32f IbL*A&nO]hVgh̑9L\ @ FH)4 :koQm(3%Ĥ'}h3+3 w\*A*)U B"xn`fȭ}6NY"Iߩ)#[ا]l,U%V ngȿ80*b\;xЀm1AlrϽZdILcR;7 6bM/(HL_.y ˧_Rlʕ.$6Q8m{$x#2ObR盔ju2eByB"2l.C-ցm.VsP-~*q!Z36ڠq!KLY\k\Fl*#lm 8ا[ |Km*sk4G+H&GO(3,KpXׂԌG k^2 .o\s --?Un1N?`q $? ɐ9s78L۩{ #[P< GXDҀ.^nG G刚`5[ƈD&?k \=0f'b" < =*cpv=7-DF5kB-9{JAɐ1J~fDZW0qkEp¿pd `;*yDV 6#qdf:Muz&WOR~|e{!ib\A'ER6xI&J{'˯`l֍>hG"hai#DCVU?-1|ô*;m)$M7̸-YS7)"L7"gh:P.YX(R`OHg dtJ>WpeiAv|/~/̏`6W@%R[$ dU*څ&,Vp{8m,":\qAro:~*#kE- B ="v b LH@FAmZtUन ɓgT[uWhĦOpa,K~J̙{Atؾ[n-5y:~6oO_FØr$Tz6 jzdv@-%pjӀ}4X]<%K \34 ~Ħfp64>'ocw1 U .T ~ZQSjQӱj+*r Zڒ?*b)"v0[x_IH#w.9ʫNH7!wt>?oeݮ(gпUb迱R`Ad7r&P(ڔ~Z3mmpmU,*/֤qrXoaf8PO<QU2j3:zD}@Ժ8ȏv_D?q)qg9I]w`EIE6bBx HګĀohwwѼ*,u+#A_NhO6_f8\CA#~.J lY6P,MM_( =aOI8>Antq luT^Zps%CvLy AŶ:yd`0"Gb$?O^rdEHkIP}m ?Ql T9R#XvU;6XʥU8D9J| gs/Ohk;3")Nj7UC?f#݈i2Ll͞JhRuCDn:M^=|aDGX.͙X}/8)Ùk n&KJT#p\G!MX}k1H=%;slR?[ݥ7j~)~a $߄?Y Y.8]q*~9Zʘ5̫X"4[~Ai²=c*TdY;jru|rntbƮ8F3ԭ~7 eV{DsdI@~KoTo Uc`$3WljiB056{1 ]G4Pw v2 NGL]&IKtT•TΒҸ]:0*' $m-ч9 m9~K9_O` btCMDLj$J S@0j_ D Ђ:z ZyyD93,7)3rN5HeXHmϔo;>{ ^zPv/Q|ġbY>؛u%HcWlQz~1Vz?etÈ7=a'&?&k%KZA\|qyyíF 0`у4` 0hF x$e#!䴋i^7d؀:TI"}GYԕU(Ycㄒ땕SH!<ƶ\BK =r{*499r*B긒lyjma}z;*A|W V+M2cg-7%`r2F#Rá)} LW/\T2/Xxb2Uۧxnfʻ"` u@Gm ֏zo6vH\تqI*,䞃O\],Ɯ[VoeA(\ޚ:b [%\nV,P۰bdEtM8qf!nBCnCmeƉ<%jd`cmEnLUZҢf.gc1,KI$Ư@X;+B$Su2Vkbm/̦GKS6fjY1АR!*1h]]uBKH~ߎ_g)Fݒ/3ؾb0`-MĥZ;)D~`n= []yZYhW8RdQ'YG1kݽBylJ>b˘_=kAw%Wt]~34_Szebڶ8af?vy} $HG|MV4N@Cm.]N#{ŮCLW8mP E\s1řJc3d&tYK4[kvy8`6+R2D)O4S6䍥5eBPRYu5e\'yWv3\m3I#uAEA]Yj8os̨{UYB#INjc]KHjֈdu!G`(KXJ=mZC:K1*#F6Mp\_ Fck($ebr:zr545у6Ët:y0i񅏰( TU ^ '^=b AQ9 <77/(l}rk~鬚})bBI/Rx2 }0Yo )̢,#:9 "C LJ^}Fc Ml4~:e,g GNUF6IdwUbuJBcor7Tث)Sѝz ijƪ̌@u8Nҹ6X٧(cYC.O3Q52HŘJ,rPHq10hq!s2&Ԝ*--ѽ1R-p$<46-߮6§3r|DkFX x}L2aT'a6Ly;TP G&c8TʼzK|"Q;y)G(:H.֗9~*[Fe,҆Satr~C1r})%6ue=YG~GzVVD`(oS@@!xj)#Ќh'~s/?0q˰<\1E=2RT:6ɘE_OOɹ gᄀG'X>b|vf8 C3WWtrigRy-\*Z,~^c0FbnE\R0mmmr9DՙEf/9NQmM}潁%ƙº%` P&U `\;佝A}r}$lʲS@J6%:PC!}"B|+}lT}U|{RVm۟^n_L NG=3TyX! {] p]&#s]Xʚe*GM*cQcofVhؗJ߇? JQo,nn@q"]"}-fU30v&YxBP8i.7stgWlCl~6$GKF#'Xrȴd6G"r8\7$pW.PVTF^gH'P@s DO,rh$UZfu OwotkșZ-/gvp`:0.#I^Uݿ#߷"ު j*VM*"̜Oolpr^> %fI'/XU1qȻq/9ku\sYBY2բٱbٌ2e볨b4mĠ-?6ޱn7CG #+M@TV=x3y'단6xbfᤅLL<PK6,^^|H@:Iw;Ժq9|d~]}ԁPgA%_x 텬dh+M|$+%,VVOG̷j@03Y( LQorۓn}dsXw6h#]LW 9gghp(ƦiIڡqUrk\Xxo͋N jh9/XOQУŁ֋WĽ-[cnqxj3y*W/CɓVPr_a权nXrb2B9 RI'.7p\cPRtj.r0"DcIݚGUUPI' p{KgMXe}eǍ;`k :2fh$o0r$Gy6†:a嘽="`tOn5M[IYH aZ$T0g9g2exV|tA%4BV:՘;j9Y_<﹚BR ֬R^/ڿ~n9NS$Eڸ+hlhN: iL 3~3CψO߯1sW8m].KtJ*JdN$2e=2/tc+&p77˻aiVG#*.T/%ff38s<y-fz+'qkvLtya|g( qAE!2U4Ѓ#9ً죎+÷+O%.zꨤRG-D̨Bf81oFsR) VT-)ۍT$-a]%OUP>s~%$ztҷ{2X3lE/vz]֑ܮWʫQҠHNjd*wwmSp2ȌA1E, 7֤?nvyۢtWWIK6K2cyƫ8] uwQƠy?gEAɷMzEEL!ʹ(BJtL@d &]_hdETbSJ@tD둲D NC ufQtf2S]AlkDETVXj(56:z*_59::+Aq jj($⫉$ԫ*e RFx{| SY%CجBnO-5W$³&[~9eR^;3;2vC.y('6F39yHs1~ܔ mMZ{r3 `|׍(4A֘ڕyMC`g2Vs$S^R ,ڨajD@Kc&ҙ|'2{^$+ "qΧ/.RaOLKrcD6ܢuU_*0 Z 3ԢYd,PJ`lB܅yOvmjPcR![?MKOȒUF!LgMU^ˎ@e-rfJTJ6cTbnp^7-tfP?4bJ~yINin=ВRՒ%tUf=&G^JjED@!Yl@/Q7f[vpnQUgBj5˙gb,uuܥl)jZHiy%ɞ)tpƯfͳrXb\cl(48$bRFakNm w,[#2DSQ3b;e˿njt@F@ $dIȃMwF&U|fE<_J8YDL]"TU*.e$[3m}r#)3:̄bj8N.9rd!C:v;rǸ tC.ܞ=L]s(T[<C"/̧}T;jNCsG J!S,T2K.L3u{S9aNΗhLla?HP1L?;ON5*HTJ;K}-{nȰf@vf&!~>ۧ\uڙgH"sKHcZqg2\OLMМ;|f.l#vQO$&cvI⸂t'e ]DnJaSl4E,=Y 1lєe^#G#vme;[75ҢyHᕾ5M3*ij 8r-@zo xmŹ9 CWJΌExBA57I0)MR␜17JșE5U$V*yzc<^Yމ@>@kƢPsy%oG.W6ޣ;nݴ ]=$`~šO?Ǎ/]qWg ] 57YS@LȲuU*(EbM@0.͑?owzw+0ߜaKGڡ^ANib{ov؂nOݪǙN+!䣲DyieLq)v! 1LUGżI3=Ob52MBݘ1XN]8Q!\.1Ű+aU, "XkҨD۠LbDSϰ~npLe_H]'0N]`΃o=wy}][VHEV)^OQ d̖m%FYT$%StۗzMu;x /⏫ F=pjNe!nf3y#/孔P*K@)\ w6= jɎ{#i?1Y⧯A2K<*vǠGtoz1%_sFbW6~&<ISH &"! TSCQd y}PůLl$u ~“5\2͔ ɳv*vĶ}1aLIE$* 0Ա‡lIrU^lIT$uWP'@?1Qv̶4KNǪ^7zZ]JJ 9:ζO' '揀:}Dў.gd>rۋpclry3c s.Y'V^4M2 JPՖtefW~!٢PFXeuB2g(n3j$8w{{M%+:pr z+4`XUlp^~\jovEbrU>>OG !z)k|*A?yk\aw 35l(5y}x w G^r$tvAcZ?2N!8O*N7UK=HJ(h9c$R 7];Ulz'2Y_U]QM?BC-rTfzD^:1V&*R]FETk7. ضQ&sƥQE!㺆_1Jt\|bsiح/*;',naw,GqTv:mNwan!nL$> ]1-8Eu(t6+ϑtrgB<54HfvmQ@y:T4!&d]Z^J})pV,k^A3BHD#?d{͋6ɀ&v4!@ @ܴE:ET{%P  b=(kt﫼=Ȓg%N;-\$'J:m4) -%xTĚ7fjA"U`e&v;%#:QjjBudЀ7a01.]s1NHb5A1-Ʈ5+n d$6Y:#pM6VBu)Lw*yWٻ5kenSE5LIafH=rN=]VGmL-G=L|%$&}U=( qNQbeU2,r˘F]Ux0&2L#zXjcI?n=$lcBF@ aqk.ڳ^xbΑԥ!朼v)Zz᳈bZmϹvX銠"Sn[Vr]"KjMjQs$*2S#19ϟ{{qիaRztR$hUz¥A+jd}Hvf ?ݻy'ӎcJM;qpwKdqWn &'j TSvlvU *CD@<}ƼizܻC$Vc);E鷆K$A>Xkiw9w9.uZj5eh#QWUPOL2Ԙ)  e)C B~P4 *drǡcžUn"q~K>d%+8d BMk r #9;zMEtGM}|[Xvd3l44 PRs7 A1T 5Re[Ui]r."'ʿ=`wme\ ɫI539>:6i8-oYhUmpD(4Ř{Bf0a*q-dmՙ,Y@WۋiLvHT]i1z+w&ۻqSgIRYR7_HNF`+3xoO#smݱ|*=-=$T&z睵BGv9$E&+*A9=AA„ȝ8vIY $2v4JU֩U*r!UɷQ-Cn=:E*d! ߶rzGWb8] ` xLXFlٮ/OjSY¥ŒJzVW hF>1$C؋C9G9#)D\}7k%]1C)g2 ,!PoE5㮟}_7 msױ7swkXύK[oT8ǵ| umUENU#TE#f$*{q.a2cڬץlȹLao Vt:@t}l:zQ m$j1%|LEtv nHSO$2UK Åkql+0k6l#6H Z5D!@6)JKKMCM,T""UFJ Wr/W婻UM4ZIe#K1>$[>IWI523ȹNö(C.8)QGzUDp9Jw%{yW댷8]9wXe#s9̜㯘pmjhQBC-|HG V^M s9"rr)DѴ6_cp䫿qեA`\5IDَ:Lra#M#-ٔa++OU #I4HPL9r~3m+n)lu$҅]r (D'F6ߙoZ]l%u$0IhrT@9fG"xۉ9Kقy?ZSԋW&(L,pf*̚%ǻu{MpDdYN#q A7r><OH*: `- DMD,ê2GV`+ؗPOΩ$cH(ʉ#APh1Lvò:kbo/{gkFgۗte^OXVU9sɼaYEڣRf((q_M‹l'T2[}b*."q&.ˑA21h ?'ňa9>g//r߷^ӈҗЩk ^h1f0ı{(lKۄSy BKdS"rȥP9 $;v[;oV=Ҟj)U Е-f zx2`ݻb.6ME$u"SkRA#w$89[ENƲ|2T%# i*2]̒p 6NVj0!WԎLYjǁ#aєBp^'vqbK=tZ1bŢ$nћ6Ț ZA24R(jTTTU=USR;gwbK31$1$I$sPFJ<=Z0`у4` 0hF 0`у4` 0hF 0`у4` 0hF 0`у9O۾ϑ>7=O3>ס|ͭhW鸿Fz_35ro-^穟S~}1Q:+/gӗ.gjkڣG~f Welcome to Jetty-9

Welcome to Jetty 9

The Jetty project is a 100% Java Servlet Container which supports asynchronous server and client implementations of the HTTP, Websocket and SPDY protocols. The project is 100% Open Source and hosted by the Eclipse Foundation at http://www.eclipse.org/jetty.

Jetty Blog