diff --git a/README.md b/README.md index cb9ddde3..b395a2c4 100644 --- a/README.md +++ b/README.md @@ -213,3 +213,5 @@ Rocket is licensed under either of the following, at your option: * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT License ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + +The Rocket website source is licensed under [separate terms](site/README.md#license). diff --git a/scripts/bump_version.sh b/scripts/bump_version.sh index 36e42409..d91e127d 100755 --- a/scripts/bump_version.sh +++ b/scripts/bump_version.sh @@ -11,4 +11,5 @@ if [ -z ${1} ] || [ -z ${2} ]; then fi find . -name "*.toml" | xargs sed -i.bak "s/${1}/${2}/g" +find site/ -name "*.md" | xargs sed -i.bak "s/${1}/${2}/g" find . -name "*.bak" | xargs rm diff --git a/site/LICENSE b/site/LICENSE new file mode 100644 index 00000000..55b5b7e2 --- /dev/null +++ b/site/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + {project} Copyright (C) {year} {fullname} + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/site/README.md b/site/README.md new file mode 100644 index 00000000..ea5491b5 --- /dev/null +++ b/site/README.md @@ -0,0 +1,29 @@ +# Rocket Website Source + +This directory contains the source files for the content on [Rocket's +website](https://rocket.rs). + +## Contents + +This directory contains the following: + + * `index.toml` - Source data for the index (`/`). + * `news.toml` - Source data for the news page (`/news`). + * `overview.toml` - Source data for the overview page (`/overview`). + * `guide.md` - Index page for the guide (`/guide`). + * `news/*.md` - News articles linked to from `news.toml`. + * `guide/*.md` - Guide pages linked to from `guide.md`. + +## Guide + +The source files for the [Rocket Programming Guide](https://rocket.rs/guide/) +can be found in the `guide/` directory. One exception is the root of the guide, +which is `guide.md`. + +Cross-linking to pages in the guide is accomplished via absolute links rooted at +`/guide/`. To link to the page whose source is at `guide/page.md` in this +directory, for instance, link to `/guide/page`. + +# License + +The Rocket website source is licensed under the [GNU General Public License v3.0](LICENSE). diff --git a/site/guide.md b/site/guide.md new file mode 100644 index 00000000..3cd83139 --- /dev/null +++ b/site/guide.md @@ -0,0 +1,43 @@ +# The Rocket Programming Guide + +Welcome to Rocket! + +This is the official guide. It is designed to serve as a starting point to +writing web application with Rocket and Rust. The guide is also designed to be a +reference for experienced Rocket developers. This guide is conversational in +tone. For concise and purely technical documentation, see the [API +documentation](https://api.rocket.rs). + +The guide is split into several sections, each with a focus on a different +aspect of Rocket. The sections are: + + - **[Introduction](introduction/):** introduces Rocket and its philosophy. + - **[Quickstart](quickstart/):** presents the minimal steps necessary to + run your first Rocket application. + - **[Getting Started](getting-started/):** a gentle introduction to getting + your first Rocket application running. + - **[Overview](overview/):** describes the core concepts of Rocket. + - **[Requests](requests/):** discusses handling requests: control-flow, + parsing, and validating. + - **[Responses](responses/):** discusses generating responses. + - **[State](state/):** how to manage state in a Rocket application. + - **[Testing](testing/):** how to unit and integration test a Rocket + application. + - **[Pastebin](pastebin/):** a tutorial on how to create a pastebin with + Rocket. + - **[Conclusion](conclusion/):** concludes the guide and discusses next steps + for learning. + +## Getting Help + +The official community support channels are the `#rocket` IRC channel on the +[Mozilla IRC Server](https://wiki.mozilla.org/IRC) at `irc.mozilla.org` and the +bridged [Rocket room on +Matrix](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org). If you're not +familiar with IRC, we recommend chatting through [Matrix via +Riot](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org) or via the [Kiwi +web IRC client](https://kiwiirc.com/client/irc.mozilla.org/#rocket). You can +learn more about IRC via Mozilla's [Getting Started with +IRC](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Getting_Started_with_IRC) +guide. + diff --git a/site/guide/conclusion.md b/site/guide/conclusion.md new file mode 100644 index 00000000..d7d337a4 --- /dev/null +++ b/site/guide/conclusion.md @@ -0,0 +1,28 @@ +# Conclusion + +We hope you agree that Rocket is a refreshing take on web frameworks. As with +any software project, Rocket is _alive_. There are always things to improve, and +we're happy to take the best ideas. If you have something in mind, please +[submit an issue](https://github.com/SergioBenitez/Rocket/issues). + +## Getting Help + +If you find yourself having trouble developing Rocket applications, you can get +help via the `#rocket` IRC channel on the [Mozilla IRC +Server](https://wiki.mozilla.org/IRC) at `irc.mozilla.org` and the bridged +[Rocket room on Matrix](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org). +If you're not familiar with IRC, we recommend chatting through [Matrix via +Riot](https://riot.im/app/#/room/#mozilla_#rocket:matrix.org) or via the [Kiwi +web IRC client](https://kiwiirc.com/client/irc.mozilla.org/#rocket). You can +learn more about IRC via Mozilla's [Getting Started with +IRC](https://developer.mozilla.org/en-US/docs/Mozilla/QA/Getting_Started_with_IRC) +guide. + +## What's next? + +The best way to learn Rocket is to _build something_. It should be fun and easy, +and there's always someone to help. Alternatively, you can read through the +[Rocket examples](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples) +or the [Rocket source +code](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/lib/src). Whatever you +decide to do next, we hope you have a blast! diff --git a/site/guide/getting-started.md b/site/guide/getting-started.md new file mode 100644 index 00000000..9b601608 --- /dev/null +++ b/site/guide/getting-started.md @@ -0,0 +1,95 @@ +# Getting Started + +Let's create and run our first Rocket application. We'll ensure we have a +compatible version of Rust, create a new Cargo project that depends on Rocket, +and then run the application. + +## Installing Rust + +Rocket makes abundant use of Rust's syntax extensions. Because syntax extensions +don't yet have a stable compiler API, we'll need to use a nightly version of +Rust. If you already have a working installation of the latest Rust nightly, +feel free to skip to the next section. + +To install a nightly version of Rust, we recommend using `rustup`. Install +`rustup` by following the instructions on [its website](https://rustup.rs/). +Once `rustup` is installed, configure Rust nightly as your default toolchain by +running the command: + +```sh +rustup default nightly +``` + +If you prefer, once we setup a project directory in the following section, you +can use per-directory overrides to use the nightly version _only_ for your +Rocket project by running the following command in the directory: + +```sh +rustup override set nightly +``` + +## Nightly Version + +Rocket requires the _latest_ version of Rust nightly to function. If your Rocket +application suddently stops building, ensure you're using the latest version of +Rust nightly and Rocket by updating your toolchain and dependencies with: + +```sh +rustup update && cargo update +``` + +## Hello, world! + +Let's write our first Rocket application! Start by creating a new binary-based +Cargo project and changing into the new directory: + +```sh +cargo new hello-rocket --bin +cd hello-rocket +``` + +Now, add Rocket and its code generation facilities as dependencies of your +project by ensuring your `Cargo.toml` contains the following: + +``` +[dependencies] +rocket = "0.2.5" +rocket_codegen = "0.2.5" +``` + +Modify `src/main.rs` so that it contains the code for the Rocket `Hello, world!` +program, which we reproduce below: + +```rust +#![feature(plugin)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +#[get("/")] +fn index() -> &'static str { + "Hello, world!" +} + +fn main() { + rocket::ignite().mount("/", routes![index]).launch(); +} +``` + +We won't explain exactly what the program does now; we leave that for the rest +of the guide. In short, it creates an `index` route, _mounts_ the route at the +`/` path, and launches the application. Run the program with `cargo run`. You +should see the following: + +```sh +🔧 Configured for development. + => address: localhost + => port: 8000 + => log: normal + => workers: {logical cores} +🛰 Mounting '/': + => GET / +🚀 Rocket has launched from http://localhost:8000... +``` + +Visit `http://localhost:8000` to see your first Rocket application in action! diff --git a/site/guide/introduction.md b/site/guide/introduction.md new file mode 100644 index 00000000..a49a9e2c --- /dev/null +++ b/site/guide/introduction.md @@ -0,0 +1,43 @@ +# Introduction + +Rocket is a web framework for Rust. If you'd like, you can think of Rocket as +being a more flexible, friendly medley of [Rails](http://rubyonrails.org), +[Flask](http://flask.pocoo.org/), +[Bottle](http://bottlepy.org/docs/dev/index.html), and +[Yesod](http://www.yesodweb.com/). We prefer to think of Rocket as something +new. Rocket aims to be fast, easy, and flexible. It also aims to be _fun_, and +it accomplishes this by ensuring that you write as little code as needed to +accomplish your task. This guide introduces you to the core, intermediate, and +advanced concepts of Rocket. After reading this guide, you should find yourself +being _very_ productive with Rocket. + +## Audience + +Readers are assumed to have a good grasp of the Rust programming language. +Readers new to Rust are encouraged to read the [Rust +Book](https://doc.rust-lang.org/book/). This guide also assumes a basic +understanding of web application fundamentals, such as routing and HTTP. + +## Foreword + +Rocket's design is centered around three core philosophies: + + * **Function declaration and parameter types should contain all the necessary + information to validate and process a request.** This immediately prohibits + APIs where request state is retrieved from a global context. As a result, + request handling is **self-contained** in Rocket: handlers are regular + functions with regular arguments. + + * **All request handling information should be typed.** Because the web and + HTTP are themselves untyped (or _stringly_ typed, as some call it), this + means that something or someone has to convert strings to native types. + Rocket does this for you with zero programming overhead. + + * **Decisions should not be forced.** Templates, serialization, sessions, and + just about everything else are all pluggable, optional components. While + Rocket has official support and libraries for each of these, they are + completely optional and swappable. + +These three ideas dictate Rocket's interface, and you will find the ideas +embedded in Rocket's core features. + diff --git a/site/guide/overview.md b/site/guide/overview.md new file mode 100644 index 00000000..53a7ef0c --- /dev/null +++ b/site/guide/overview.md @@ -0,0 +1,317 @@ +# Overview + +Rocket provides primitives to build web servers and applications with Rust: the +rest is up to you. In short, Rocket provides routing, pre-processing of +requests, and post-processing of responses. Your application code instructs +Rocket on what to pre-process and post-process and fills the gaps between +pre-processing and post-processing. + +## Lifecycle + +Rocket's main task is to listen for incoming web requests, dispatch the request +to the application code, and return a response to the client. We call the +process that goes from request to response: the _lifecycle_. We summarize the +lifecycle as the sequence of steps: + + 1. **Routing** + + Rocket parses an incoming HTTP request into native structures that your code + operates on indirectly. Rocket determines which request handler to invoke by + matching against route attributes declared by your application. + + 2. **Validation** + + Rocket validates the incoming request against types and request guards + present in the matched route. If validation fails, Rocket _forwards_ the + request to the next matching route or calls an _error handler_. + + 3. **Processing** + + The request handler associated with the route is invoked with validated + arguments. This is the main business logic of the application. Processing + completes by returning a `Response`. + + 4. **Response** + + The returned `Response` is processed. Rocket generates the appropriate HTTP + response and sends it to the client. This completes the lifecycle. Rocket + continues listening for requests, restarting the lifecycle for each incoming + request. + +The remainder of this section details the _routing_ phase as well as additional +components needed for Rocket to begin dispatching requests to request handlers. +The sections following describe the request and response phases. + +## Routing + +Rocket applications are centered around routes and handlers. + +A _handler_ is simply a function that takes an arbitrary number of arguments and +returns any arbitrary type. A _route_ is a combination of: + + * A set of parameters to match an incoming request against. + * A handler to process the request and return a response. + +The parameters to match against include static paths, dynamic paths, path +segments, forms, query strings, request format specifiers, and body data. Rocket +uses attributes, which look like function decorators in other languages, to make +declaring routes easy. Routes are declared by annotating a function, the +handler, with the set of parameters to match against. A complete route +declaration looks like this: + +```rust +#[get("/world")] // <- route attribute +fn world() -> &'static str { // <- request handler + "Hello, world!" +} +``` + +This declares the `world` route to match against the static path `"/world"` on +incoming `GET` requests. The `world` route is simple, but additional route +parameters are necessary when building more interesting applications. The +[Requests](/guide/requests) section describes the available options for +constructing routes. + +## Mounting + +Before Rocket can dispatch requests to a route, the route needs to be _mounted_. +Mounting a route is like namespacing it. Routes are mounted via the `mount` +method on a `Rocket` instance. Rocket instances can be created with the +`ignite()` static method. + +The `mount` method takes **1)** a path to namespace a list of routes under, and +**2)** a list of route handlers through the `routes!` macro. The `routes!` macro +ties Rocket's code generation to your application. + +For instance, to mount the `world` route we declared above, we would use the +following code: + +```rust +rocket::ignite().mount("/hello", routes![world]) +``` + +Altogether, this creates a new `Rocket` instance via the `ignite` function and +mounts the `world` route to the `"/hello"` path. As a result, `GET` requests to +the `"/hello/world"` path will be directed to the `world` function. + +### Namespacing + +When a route is declared inside a module other than the root, you may find +yourself with unexpected errors when mounting: + +```rust +mod other { + #[get("/world")] + pub fn world() -> &'static str { + "Hello, world!" + } +} + +use other::world; + +fn main() { + // error[E0425]: cannot find value `static_rocket_route_info_for_world` in this scope + rocket::ignite().mount("/hello", routes![world]) +} +``` + +This occurs because the `routes!` macro implicitly converts the route's name +into the name of a structure generated by Rocket's code generation. The solution +is to name the route by a module path instead: + +```rust +rocket::ignite().mount("/hello", routes![other::world]) +``` + +## Launching + +Now that Rocket knows about the route, you can tell Rocket to start accepting +requests via the `launch` method. The method starts up the server and waits for +incoming requests. When a request arrives, Rocket finds the matching route and +dispatches the request to the route's handler. + +We typically call `launch` from the `main` function. Our complete _Hello, +world!_ application thus looks like: + +```rust +#![feature(plugin)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +#[get("/world")] +fn world() -> &'static str { + "Hello, world!" +} + +fn main() { + rocket::ignite().mount("/hello", routes![world]).launch(); +} +``` + +Note that we've added the `#![feature(plugin)]` and `#![plugin(rocket_codegen)]` +lines to tell Rust that we'll be using Rocket's code generation plugin. We've +also imported the `rocket` crate into our namespace via `extern crate rocket`. +Finally, we call the `launch` method in the `main` function. + +Running the application, the console shows: + +```sh +🔧 Configured for development. + => address: localhost + => port: 8000 + => log: normal + => workers: {logical cores} +🛰 Mounting '/world': + => GET /hello/world +🚀 Rocket has launched from http://localhost:8000... +``` + +If we visit `localhost:8000/hello/world`, we see `Hello, world!`, exactly as +we expected. + +A version of this example's complete crate, ready to `cargo run`, can be found +on +[GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/hello_world). +You can find dozens of other complete examples, spanning all of Rocket's +features, in the [GitHub examples +directory](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/). + +## Configuration + +At any point in time, a Rocket application is operating in a given +_configuration environment_. There are three such environments: + + * `development` (short: `dev`) + * `staging` (short: `stage`) + * `production` (short: `prod`) + +Without any action, Rocket applications run in the `development` environment. +The environment can be changed via the `ROCKET_ENV` environment variable. For +example, to launch the `Hello, world!` application in the `staging` environment, +we can run: + +```sh +ROCKET_ENV=stage cargo run +``` + +You'll likely need `sudo` for the command to succeed since `staging` defaults to +listening on port `80`. Note that you can use the short or long form of the +environment name to specify the environment, `stage` _or_ `staging` here. Rocket +tells us the environment we have chosen and its configuration when it launches: + +```sh +$ sudo ROCKET_ENV=staging cargo run + +🔧 Configured for staging. + => address: 0.0.0.0 + => port: 80 + => log: normal + => workers: {logical cores} +🛰 Mounting '/': + => GET / +🚀 Rocket has launched from http://0.0.0.0:80... +``` + +Configuration settings can be changed in one of two ways: via the `Rocket.toml` +configuration file, or via environment variables. + +### Configuration File + +A `Rocket.toml` file can be used to specify the configuration parameters for +each environment. The file is optional. If it is not present, the default +configuration parameters are used. + +The file must be a series of TOML tables, at most one for each environment and +an optional "global" table, where each table contains key-value pairs +corresponding to configuration parameters for that environment. If a +configuration parameter is missing, the default value is used. The following is +a complete `Rocket.toml` file, where every standard configuration parameter is +specified with the default value: + +```toml +[development] +address = "localhost" +port = 8000 +workers = max(number_of_cpus, 2) +log = "normal" + +[staging] +address = "0.0.0.0" +port = 80 +workers = max(number_of_cpus, 2) +log = "normal" + +[production] +address = "0.0.0.0" +port = 80 +workers = max(number_of_cpus, 2) +log = "critical" +``` + +The `workers` parameter is computed by Rocket automatically; the value above is +not valid TOML syntax. + +The "global" pseudo-environment can be used to set and/or override configuration +parameters globally. A parameter defined in a `[global]` table sets, or +overrides if already present, that parameter in every environment. For example, +given the following `Rocket.toml` file, the value of `address` will be +`"1.2.3.4"` in every environment: + +```toml +[global] +address = "1.2.3.4" + +[development] +address = "localhost" + +[production] +address = "0.0.0.0" +``` + +### Extras + +In addition to overriding default configuration parameters, a configuration file +can also define values for any number of _extra_ configuration parameters. While +these parameters aren't used by Rocket directly, other libraries, or your own +application, can use them as they wish. As an example, the +[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html) type +accepts a value for the `template_dir` configuration parameter. The parameter +can be set in `Rocket.toml` as follows: + +```toml +[development] +template_dir = "dev_templates/" + +[production] +template_dir = "prod_templates/" +``` + +This sets the `template_dir` extra configuration parameter to `"dev_templates/"` +when operating in the `development` environment and `"prod_templates/"` when +operating in the `production` environment. Rocket will prepend the `[extra]` tag +to extra configuration parameters when launching: + +```sh +🔧 Configured for development. + => ... + => [extra] template_dir: "dev_templates/" +``` + +### Environment Variables + +All configuration parameters, including extras, can be overridden through +environment variables. To override the configuration parameter `{param}`, use an +environment variable named `ROCKET_{PARAM}`. For instance, to override the +"port" configuration parameter, you can run your application with: + +```sh +ROCKET_PORT=3721 cargo run + +🔧 Configured for staging. + => ... + => port: 3721 +``` + +Environment variables take precedence over all other configuration methods: if a +variable is set, it will be used as that parameter's value. diff --git a/site/guide/pastebin.md b/site/guide/pastebin.md new file mode 100644 index 00000000..adf440f3 --- /dev/null +++ b/site/guide/pastebin.md @@ -0,0 +1,405 @@ +# Pastebin + +To give you a taste of what a real Rocket application looks like, this section +of the guide is a tutorial on how to create a Pastebin application in Rocket. A +pastebin is a simple web application that allows users to upload a text document +and later retrieve it via a special URL. They're often used to share code +snippets, configuration files, and error logs. In this tutorial, we'll build a +simple pastebin service that allows users to upload a file from their terminal. +The service will respond back with a URL to the uploaded file. + +## Finished Product + +A souped-up, completed version of the application you're about to build is +deployed live at [paste.rs](https://paste.rs). Feel free to play with the +application to get a feel for how it works. For example, to upload a text +document named `test.txt`, you can do: + +```sh +curl --data-binary @test.txt https://paste.rs/ +# => https://paste.rs/IYu +``` + +The finished product is composed of the following routes: + + * index: **GET /** - returns a simple HTML page with instructions about how + to use the service + * upload: **POST /** - accepts raw data in the body of the request and + responds with a URL of a page containing the body's content + * retrieve: **GET /<id>** - retrieves the content for the paste with id + `` + +## Getting Started + +Let's get started! First, create a fresh Cargo binary project named +`rocket-pastebin`: + +```rust +cargo new --bin rocket-pastebin +cd rocket-pastebin +``` + +Then add the usual Rocket dependencies to the `Cargo.toml` file: + +```toml +[dependencies] +rocket = "0.2.5" +rocket_codegen = "0.2.5" +``` + +And finally, create a skeleton Rocket application to work off of in +`src/main.rs`: + +```rust +#![feature(plugin)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +fn main() { + rocket::ignite().launch() +} +``` + +Ensure everything works by running the application: + +```sh +cargo run +``` + +At this point, we haven't declared any routes or handlers, so visiting any page +will result in Rocket returning a **404** error. Throughout the rest of the +tutorial, we'll create the three routes and accompanying handlers. + +## Index + +The first route we'll create is the `index` route. This is the page users will +see when they first visit the service. As such, the route should field requests +of the form `GET /`. We declare the route and its handler by adding the `index` +function below to `src/main.rs`: + +```rust +#[get("/")] +fn index() -> &'static str { + " + USAGE + + POST / + + accepts raw data in the body of the request and responds with a URL of + a page containing the body's content + + GET / + + retrieves the content for the paste with id `` + " +} +``` + +This declares the `index` route for requests to `GET /` as returning a static +string with the specified contents. Rocket will take the string and return it as +the body of a fully formed HTTP response with `Content-Type: text/plain`. You +can read more about how Rocket formulates responses at the [API documentation +for the Responder + trait](https://api.rocket.rs/rocket/response/trait.Responder.html). + +Remember that routes first need to be mounted before Rocket dispatches requests +to them. To mount the `index` route, modify the main function so that it reads: + +```rust +fn main() { + rocket::ignite().mount("/", routes![index]).launch() +} +``` + +You should now be able to `cargo run` the application and visit the root path +(`/`) to see the text being displayed. + +## Uploading + +The most complicated aspect of the pastebin, as you might imagine, is handling +upload requests. When a user attempts to upload a pastebin, our service needs to +generate a unique ID for the upload, read the data, write it out to a file or +database, and then return a URL with the ID. We'll take each of these one step +at a time, beginning with generating IDs. + +### Unique IDs + +Generating a unique and useful ID is an interesting topic, but it is outside the +scope of this tutorial. Instead, we simply provide the code for a `PasteID` +structure that represents a _probably_ unique ID. Read through the code, then +copy/paste it into a new file named `paste_id.rs` in the `src/` directory: + +```rust +use std::fmt; +use std::borrow::Cow; + +use rand::{self, Rng}; + +/// Table to retrieve base62 values from. +const BASE62: &'static [u8] = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +/// A _probably_ unique paste ID. +pub struct PasteID<'a>(Cow<'a, str>); + +impl<'a> PasteID<'a> { + /// Generate a _probably_ unique ID with `size` characters. For readability, + /// the characters used are from the sets [0-9], [A-Z], [a-z]. The + /// probability of a collision depends on the value of `size`. In + /// particular, the probability of a collision is 1/62^(size). + pub fn new(size: usize) -> PasteID<'static> { + let mut id = String::with_capacity(size); + let mut rng = rand::thread_rng(); + for _ in 0..size { + id.push(BASE62[rng.gen::() % 62] as char); + } + + PasteID(Cow::Owned(id)) + } +} + +impl<'a> fmt::Display for PasteID<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.0) + } +} +``` + +Then, in `src/main.rs`, add the following after `extern crate rocket`: + +```rust +extern crate rand; + +mod paste_id; + +use paste_id::PasteID; +``` + +Finally, add a dependency for the `rand` crate to the `Cargo.toml` file: + +```toml +[dependencies] +# existing Rocket dependencies... +rand = "0.3" +``` + +Then, ensure that your application builds with the new code: + +```sh +cargo build +``` + +You'll likely see many "unused" warnings for the new code we've added: that's +okay and expected. We'll be using the new code soon. + +### Processing + +Believe it or not, the hard part is done! (_whew!_). + +To process the upload, we'll need a place to store the uploaded files. To +simplify things, we'll store the uploads in a directory named `uploads/`. Create +an `upload` directory next to the `src` directory: + +```sh +mkdir upload +``` + +For the `upload` route, we'll need to `use` a few items: + +```rust +use std::io; +use std::path::Path; + +use rocket::Data; +``` + +The [Data](https://api.rocket.rs/rocket/data/struct.Data.html) structure is key +here: it represents an unopened stream to the incoming request body data. We'll +use it to efficiently stream the incoming request to a file. + +### Upload Route + +We're finally ready to write the `upload` route. Before we show you the code, +you should attempt to write the route yourself. Here's a hint: a possible route +and handler signature look like this: + +```rust +#[post("/", data = "")] +fn upload(paste: Data) -> io::Result +``` + +Your code should: + + 1. Create a new `PasteID` of a length of your choosing. + 2. Construct a filename inside `upload/` given the `PasteID`. + 3. Stream the `Data` to the file with the constructed filename. + 4. Construct a URL given the `PasteID`. + 5. Return the URL to the client. + +Here's our version (in `src/main.rs`): + +```rust +#[post("/", data = "")] +fn upload(paste: Data) -> io::Result { + let id = PasteID::new(3); + let filename = format!("upload/{id}", id = id); + let url = format!("{host}/{id}\n", host = "http://localhost:8000", id = id); + + // Write the paste out to the file and return the URL. + paste.stream_to_file(Path::new(&filename))?; + Ok(url) +} +``` + +Make sure that the route is mounted at the root path: + +```rust +fn main() { + rocket::ignite().mount("/", routes![index, upload]).launch() +} +``` + +Test that your route works via `cargo run`. From a separate terminal, upload a +file using `curl`. Then verify that the file was saved to the `upload` directory +with the correct ID: + +```sh +# in the project root +cargo run + +# in a seperate terminal +echo "Hello, world." | curl --data-binary @- http://localhost:8000 +# => http://localhost:8000/eGs + +# back to the terminal running the pastebin + # kill running process +ls upload # ensure the upload is there +cat upload/* # ensure that contents are correct +``` + +Note that since we haven't created a `GET /` route, visting the returned URL +will result in a **404**. We'll fix that now. + +## Retrieving Pastes + +The final step is to create the `retrieve` route which, given an ``, will +return the corresponding paste if it exists. + +Here's a first take at implementing the `retrieve` route. The route below takes +in an `` as a dynamic path element. The handler uses the `id` to construct a +path to the paste inside `upload/`, and then attempts to open the file at that +path, optionally returning the `File` if it exists. Rocket treats a `None` +[Responder](https://api.rocket.rs/rocket/response/trait.Responder.html#provided-implementations) +as a **404** error, which is exactly what we want to return when the requested +paste doesn't exist. + +```rust +use std::fs::File; + +#[get("/")] +fn retrieve(id: &str) -> Option { + let filename = format!("upload/{id}", id = id); + File::open(&filename).ok() +} +``` + +Unfortunately, there's a problem with this code. Can you spot the issue? + +The issue is that the _user_ controls the value of `id`, and as a result, can +coerce the service into opening files inside `upload/` that aren't meant to be +opened. For instance, imagine that you later decide that a special file +`upload/_credentials.txt` will store some important, private information. If the +user issues a `GET` request to `/_credentials.txt`, the server will read and +return the `upload/_credentials.txt` file, leaking the sensitive information. +This is a big problem; it's known as the [full path disclosure +attack](https://www.owasp.org/index.php/Full_Path_Disclosure), and Rocket +provides the tools to prevent this and other kinds of attacks from happening. + +To prevent the attack, we need to _validate_ `id` before we use it. Since the +`id` is a dynamic parameter, we can use Rocket's +[FromParam](https://api.rocket.rs/rocket/request/trait.FromParam.html) trait to +implement the validation and ensure that the `id` is a valid `PasteID` before +using it. We do this by implementing `FromParam` for `PasteID` in +`src/paste_id.rs`, as below: + +```rust +use rocket::request::FromParam; + +/// Returns `true` if `id` is a valid paste ID and `false` otherwise. +fn valid_id(id: &str) -> bool { + id.chars().all(|c| { + (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + }) +} + +/// Returns an instance of `PasteID` if the path segment is a valid ID. +/// Otherwise returns the invalid ID as the `Err` value. +impl<'a> FromParam<'a> for PasteID<'a> { + type Error = &'a str; + + fn from_param(param: &'a str) -> Result, &'a str> { + match valid_id(param) { + true => Ok(PasteID(Cow::Borrowed(param))), + false => Err(param) + } + } +} +``` + +Then, we simply need to change the type of `id` in the handler to `PasteID`. +Rocket will then ensure that `` represents a valid `PasteID` before calling +the `retrieve` route, preventing attacks on the `retrieve` route: + +```rust +#[get("/")] +fn retrieve(id: PasteID) -> Option { + let filename = format!("upload/{id}", id = id); + File::open(&filename).ok() +} +``` + +Note that our `valid_id` function is simple and could be improved by, for +example, checking that the length of the `id` is within some known bound or +potentially blacklisting sensitive files as needed. + +The wonderful thing about using `FromParam` and other Rocket traits is that they +centralize policies. For instance, here, we've centralized the policy for valid +`PasteID`s in dynamic parameters. At any point in the future, if other routes +are added that require a `PasteID`, no further work has to be done: simply use +the type in the signature and Rocket takes care of the rest. + +## Conclusion + +That's it! Ensure that all of your routes are mounted and test your application. +You've now written a simple (~75 line!) pastebin in Rocket! There are many +potential improvements to this small application, and we encourage you to work +through some of them to get a better feel for Rocket. Here are some ideas: + + * Add a web form to the `index` where users can manually input new pastes. + Accept the form at `POST /`. Use `format` and/or `rank` to specify which of + the two `POST /` routes should be called. + * Support **deletion** of pastes by adding a new `DELETE /` route. Use + `PasteID` to validate ``. + * **Limit the upload** to a maximum size. If the upload exceeds that size, + return a **206** partial status code. Otherwise, return a **201** created + status code. + * Set the `Content-Type` of the return value in `upload` and `retrieve` to + `text/plain`. + * **Return a unique "key"** after each upload and require that the key is + present and matches when doing deletion. Use one of Rocket's core traits to + do the key validation. + * Add a `PUT /` route that allows a user with the key for `` to + replace the existing paste, if any. + * Add a new route, `GET //` that syntax highlights the paste with ID + `` for language ``. If `` is not a known language, do no + highlighting. Possibly validate `` with `FromParam`. + * Use the [testing module](https://api.rocket.rs/rocket/testing/) to write + unit tests for your pastebin. + * Dispatch a thread before `launch`ing Rocket in `main` that periodically + cleans up idling old pastes in `upload/`. + +You can find the full source code for the completed pastebin tutorial in the +[Rocket Github +Repo](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/pastebin). diff --git a/site/guide/quickstart.md b/site/guide/quickstart.md new file mode 100644 index 00000000..0bcbba9e --- /dev/null +++ b/site/guide/quickstart.md @@ -0,0 +1,21 @@ +# Quickstart + +Rocket requires a recent nightly version of Rust. We recommend you use +[rustup](https://rustup.rs/) to install or configure such a version. If you +don't have Rust installed, the [getting started](/guide/getting-started) section +guides you through installing Rust. + +## Running Examples + +The absolute fastest way to start experimenting with Rocket is to clone the +Rocket repository and run the included examples in the `examples/` directory. +For instance, the following set of commands runs the `hello_world` example: + +```sh +git clone https://github.com/SergioBenitez/rocket +cd rocket/examples/hello_world +cargo run +``` + +There are numerous examples. They can all be run with `cargo run`. + diff --git a/site/guide/requests.md b/site/guide/requests.md new file mode 100644 index 00000000..4225f0dd --- /dev/null +++ b/site/guide/requests.md @@ -0,0 +1,378 @@ +# Requests + +If all we could do was match against static paths like `"/world"`, Rocket +wouldn't be much fun. Of course, Rocket allows you to match against just about +any information in an incoming request. This section describes the available +options and their effect on the application. + +## Methods + +A Rocket route attribute can be any one of `get`, `put`, `post`, `delete`, +`head`, `patch`, or `options`, each corresponding to the HTTP method to match +against. For example, the following attribute will match against `POST` requests +to the root path: + +```rust +#[post("/")] +``` + +The grammar for these routes is defined formally in the +[rocket_codegen](https://api.rocket.rs/rocket_codegen/) API docs. + +Rocket handles `HEAD` requests automatically when there exists a `GET` route +that would otherwise match. It does this by stripping the body from the +response, if there is one. You can also specialize the handling of a `HEAD` +request by declaring a route for it; Rocket won't interfere with `HEAD` requests +your application handles. + +Because browsers only send `GET` and `POST` requests, Rocket _reinterprets_ +requests under certain conditions. If a `POST` request contains a body of +`Content-Type: application/x-www-form-urlencoded`, and the form's **first** +field has the name `_method` and a valid HTTP method as its value, that field's +value is used as the method for the incoming request. This allows Rocket +applications to submit non-`POST` forms. The [todo +example](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/todo/static/index.html.tera#L47) +makes use of this feature to submit `PUT` and `DELETE` requests from a web form. + +## Format + +When receiving data, you can specify the Content-Type the route matches against +via the `format` route parameter. The parameter is a string of the Content-Type +expected. For example, to match `application/json` data, a route can be declared +as: + +```rust +#[post("/user", format = "application/json", data = "")] +fn new_user(user: JSON) -> T { ... } +``` + +Note the `format` parameter in the `post` attribute. The `data` parameter is +described later in the [data](#data) section. + +## Dynamic Paths + +You can declare path segments as dynamic by using angle brackets around variable +names in a route's path. For example, if we wanted to say _Hello!_ to anything, +not just the world, we could declare a route and handler like so: + +```rust +#[get("/hello/")] +fn hello(name: &str) -> String { + format!("Hello, {}!", name) +} +``` + +If we were to mount the path at the root (`.mount("/", routes![hello])`), then +any request to a path with two non-empty segments, where the first segment is +`hello`, will be dispatched to the `hello` route. For example, if we were to +visit `/hello/John`, the application would respond with `Hello, John!`. + +You can have any number of dynamic path segments, and the type of the path +segment can be any type that implements the [FromParam +trait](https://api.rocket.rs/rocket/request/trait.FromParam.html), including +your own! Rocket implements `FromParam` for many of the standard library types, +as well as a few special Rocket types. Here's a somewhat complicated route to +illustrate varied usage: + +```rust +#[get("/hello///")] +fn hello(name: &str, age: u8, cool: bool) -> String { + if cool { + format!("You're a cool {} year old, {}!", age, name) + } else { + format!("{}, we need to talk about your coolness.", name) + } +} +``` + +## Forwarding + +In this example above, what if `cool` isn't a `bool`? Or, what if `age` isn't a +`u8`? In this case, the request is _forwarded_ to the next matching route, if +there is any. This continues until a route doesn't forward the request or there +are no remaining routes to try. When there are no remaining matching routes, a +customizable **404 error** is returned. + +Routes are tried in increasing _rank_ order. By default, routes with static +paths have a rank of 0 and routes with dynamic paths have a rank of 1. A route's +rank can be manually set with the `rank` route parameter. + +To illustrate, consider the following routes: + +```rust +#[get("/user/")] +fn user(id: usize) -> T { ... } + +#[get("/user/", rank = 2)] +fn user_int(id: isize) -> T { ... } + +#[get("/user/", rank = 3)] +fn user_str(id: &str) -> T { ... } +``` + +Notice the `rank` parameters in `user_int` and `user_str`. If we run this +application with the routes mounted at the root, requests to `/user/` will +be routed as follows: + + 1. The `user` route matches first. If the string at the `` position is an + unsigned integer, then the `user` handler is called. If it is not, then the + request is forwarded to the next matching route: `user_int`. + + 2. The `user_int` route matches next. If `` is a signed integer, + `user_int` is called. Otherwise, the request is forwarded. + + 3. The `user_str` route matches last. Since `` is a always string, the + route always matches. The `user_str` handler is called. + +Forwards can be _caught_ by using a `Result` or `Option` type. For example, if +the type of `id` in the `user` function was `Result`, then `user` +would never forward. An `Ok` variant would indicate that `` was a valid +`usize`, while an `Err` would indicate that `` was not a `usize`. The +`Err`'s value would contain the string that failed to parse as a `usize`. + +By the way, if you were to omit the `rank` parameter in the `user_str` or +`user_int` routes, Rocket would emit a warning indicating that the routes +_collide_, or can match against similar incoming requests. The `rank` parameter +resolves this collision. + +## Dynamic Segments + +You can also match against multiple segments by using `` in the route +path. The type of such parameters, known as _segments_ parameters, can be any +that implements +[FromSegments](https://api.rocket.rs/rocket/request/trait.FromSegments.html). +Segments parameters must be the final component of the path: any text after a +segments parameter in a path will result in a compile-time error. + +As an example, the following route matches against all paths that begin with +`/page/`: + +```rust +#[get("/page/")] +fn get_page(path: PathBuf) -> T { ... } +``` + +The path after `/page/` will be available in the `path` parameter. The +`FromSegments` implementation for `PathBuf` ensures that `path` cannot lead to +[path traversal attacks](https://www.owasp.org/index.php/Path_Traversal). With +this, a safe and secure static file server can implemented in 4 lines: + +```rust +#[get("/")] +fn files(file: PathBuf) -> Option { + NamedFile::open(Path::new("static/").join(file)).ok() +} +``` + +## Request Guards + +Sometimes we need data associated with a request that isn't a direct input. +Headers and cookies are a good example of this: they simply tag along for the +ride. Rocket makes retrieving and validating such information easy: simply add +any number of parameters to the request handler with types that implement the +[FromRequest](https://api.rocket.rs/rocket/request/trait.FromRequest.html) +trait. If the data can be retrieved from the incoming request and validated, the +handler is called. If it cannot, the handler isn't called, and the request is +forwarded or terminated. In this way, these parameters act as _guards_: they +protect the request handler from being called erroneously. + +For example, to retrieve cookies and the Content-Type header from a request, we +can declare a route as follows: + +```rust +#[get("/")] +fn index(cookies: &Cookies, content: ContentType) -> String { ... } +``` + +The [cookies example on +GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/cookies) +illustrates how to use the `Cookies` type to get and set cookies. + +You can implement `FromRequest` for your own types. For instance, to protect a +`sensitive` route from running unless an `APIKey` is present in the request +headers, you might create an `APIKey` type that implements `FromRequest` and use +it as a request guard: + +```rust +#[get("/sensitive")] +fn sensitive(key: APIKey) -> &'static str { ... } +``` + +You might also implement `FromRequest` for an `AdminUser` type that validates +that the cookies in the incoming request authenticate an administrator. Then, +any handler with an `AdminUser` or `APIKey` type in its argument list is assured +to only be invoked if the appropriate conditions are met. Request guards +centralize policies, resulting in a simpler, safer, and more secure +applications. + +## Data + +At some point, your web application will need to process body data, and Rocket +makes it as simple as possible. Data processing, like much of Rocket, is type +directed. To indicate that a handler expects data, annotate it with a `data = +""` parameter, where `param` is an argument in the handler. The +argument's type must implement the +[FromData](https://api.rocket.rs/rocket/data/trait.FromData.html) trait. It +looks like this, where `T: FromData`: + +```rust +#[post("/", data = "")] +fn new(input: T) -> String { ... } +``` + +### Forms + +Forms are the most common type of data handled in web applications, and Rocket +makes handling them easy. Say your application is processing a form submission +for a new todo `Task`. The form contains two fields: `complete`, a checkbox, and +`description`, a text field. You can easily handle the form request in Rocket +as follows: + +```rust +#[derive(FromForm)] +struct Task { + complete: bool, + description: String, +} + +#[post("/todo", data = "")] +fn new(task: Form) -> String { ... } +``` + +The `Form` type implements the `FromData` trait as long as its generic parameter +implements the +[FromForm](https://api.rocket.rs/rocket/request/trait.FromForm.html) trait. In +the example, we've derived the `FromForm` trait automatically for the `Task` +structure. `FromForm` can be derived for any structure whose fields implement +[FromFormValue](https://api.rocket.rs/rocket/request/trait.FromFormValue.html). +If a `POST /todo` request arrives, the form data will automatically be parsed +into the `Task` structure. If the data that arrives isn't of the correct +Content-Type, the request is forwarded. If the data doesn't parse or is simply +invalid, a customizable `400 Bad Request` error is returned. As before, a +forward or failure can be caught by using the `Option` and `Result` types. + +Fields of forms can be easily validated via implementations of the +`FromFormValue` trait. For example, if you'd like to verify that some user is +over some age in a form, then you might define a new `AdultAge` type, use it as +a field in a form structure, and implement `FromFormValue` so that it only +validates integers over that age. If a form is a submitted with a bad age, +Rocket won't call a handler requiring a valid form for that structure. You can +use `Option` or `Result` types for fields to catch parse failures. + +The [forms](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/forms) +and [forms kitchen +sink](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/form_kitchen_sink) +examples on GitHub provide further illustrations. + +### JSON + +Handling JSON data is no harder: simply use the +[JSON](https://api.rocket.rs/rocket_contrib/struct.JSON.html) type: + +```rust +#[derive(Deserialize)] +struct Task { + description: String, + complete: bool +} + +#[post("/todo", data = "")] +fn new(task: JSON) -> String { ... } +``` + +The only condition is that the generic type to `JSON` implements the +`Deserialize` trait. See the [JSON example on +GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/json) for a +complete example. + +### Streaming + +Sometimes you just want to handle the incoming data directly. For example, you +might want to stream the incoming data out to a file. Rocket makes this as +simple as possible via the +[Data](https://api.rocket.rs/rocket/data/struct.Data.html) type: + +```rust +#[post("/upload", format = "text/plain", data = "")] +fn upload(data: Data) -> io::Result> { + data.stream_to_file("/tmp/upload.txt").map(|n| Plain(n.to_string())) +} +``` + +The route above accepts any `POST` request to the `/upload` path with +`Content-Type` `text/plain` The incoming data is streamed out to +`tmp/upload.txt` file, and the number of bytes written is returned as a plain +text response if the upload succeeds. If the upload fails, an error response is +returned. The handler above is complete. It really is that simple! See the +[GitHub example +code](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/raw_upload) +for the full crate. + +## Query Strings + +Query strings are handled similarly to `POST` forms. A query string can be +parsed into any structure that implements the `FromForm` trait. They are matched +against by appending a `?` followed by a dynamic parameter `` to the +path. + +For instance, say you change your mind and decide to use query strings instead +of `POST` forms for new todo tasks in the previous forms example, reproduced +below: + +```rust +#[derive(FromForm)] +struct Task { .. } + +#[post("/todo", data = "")] +fn new(task: Form) -> String { ... } +``` + +Rocket makes the transition simple: simply declare `` as a query parameter +as follows: + +```rust +#[get("/todo?")] +fn new(task: Task) -> String { ... } +``` + +Rocket will parse the query string into the `Task` structure automatically by +matching the structure field names to the query parameters. If the parse fails, +the request is forwarded to the next matching route. To catch parse failures, +you can use `Option` or `Result` as the type of the field to catch errors for. + +See [the GitHub +example](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/query_params) +for a complete illustration. + +## Error Catchers + +When Rocket wants to return an error page to the client, Rocket invokes the +_catcher_ for that error. A catcher is like a route, except it only handles +errors. Catchers are declared via the `error` attribute, which takes a single +integer corresponding to the HTTP status code to catch. For instance, to declare +a catcher for **404** errors, you'd write: + +```rust +#[error(404)] +fn not_found(req: &Request) -> String { } +``` + +As with routes, Rocket needs to know about a catcher before it is used to handle +errors. The process is similar to mounting: call the `catch` method with a list +of catchers via the `errors!` macro. The invocation to add the **404** catcher +declared above looks like this: + +```rust +rocket::ignite().catch(errors![not_found]) +``` + +Unlike request handlers, error handlers can only take 0, 1, or 2 parameters of +types [Request](https://api.rocket.rs/rocket/struct.Request.html) and/or +[Error](https://api.rocket.rs/rocket/enum.Error.html). At present, the `Error` +type is not particularly useful, and so it is often omitted. The +[error catcher +example](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/errors) on +GitHub illustrates their use in full. + +Rocket has a default catcher for all of the standard HTTP error codes including +**404**, **500**, and more. diff --git a/site/guide/responses.md b/site/guide/responses.md new file mode 100644 index 00000000..6a9fb3f0 --- /dev/null +++ b/site/guide/responses.md @@ -0,0 +1,169 @@ +# Responses + +You may have noticed that the return type of a handler appears to be arbitrary, +and that's because it is! A value of any type that implements the +[Responder](https://api.rocket.rs/rocket/response/trait.Responder.html) trait +can be returned, including your own. + +## Responder + +Types that implement `Responder` know how to generate a +[Response](https://api.rocket.rs/rocket/response/struct.Response.html) from +their values. A `Response` includes the HTTP status, headers, and body of the +response. Rocket implements `Responder` for many built-in types including +`String`, `&str`, `File`, `Option`, `Result`, and others. Rocket also provides +custom types, such as +[Content](https://api.rocket.rs/rocket/response/struct.Content.html) and +[Flash](https://api.rocket.rs/rocket/response/struct.Flash.html), which you can +find in the [response](https://api.rocket.rs/rocket/response/index.html) module. + +The body of a `Response` may either be _fixed-sized_ or _streaming_. The given +`Responder` implementation decides which to use. For instance, `String` uses a +fixed-sized body, while `File` uses a streaming body. + +### Wrapping + +Responders can _wrap_ other responders. That is, responders can be of the +following form, where `R: Responder`: + +```rust +struct WrappingResponder(R); +``` + +When this is the case, the wrapping responder will modify the response returned +by `R` in some way before responding itself. For instance, to override the +status code of some response, you can use the types in the [status +module](https://api.rocket.rs/rocket/response/status/index.html). In particular, +to set the status code of a response for a `String` to **202 Accepted**, you can +return a type of `status::Accepted`: + +```rust +#[get("/")] +fn accept() -> status::Accepted { + status::Accepted(Some("I accept!".to_string())) +} +``` + +By default, the `String` responder sets the status to **200**. By using the +`Accepted` type however, The client will receive an HTTP response with status +code **202**. + +Similarly, the types in the [content +module](https://api.rocket.rs/rocket/response/content/index.html) can be used to +override the Content-Type of the response. For instance, to set the Content-Type +of some `&'static str` to JSON, you can use the +[content::JSON](https://api.rocket.rs/rocket/response/content/struct.JSON.html) +type as follows: + +```rust +#[get("/")] +fn json() -> content::JSON<&'static str> { + content::JSON("{ 'hi': 'world' }") +} +``` + +## Errors + +Responders need not _always_ generate a response. Instead, they can return an +`Err` with a given status code. When this happens, Rocket forwards the request +to the error catcher for the given status code. If none exists, which can only +happen when using custom status codes, Rocket uses the **500** error catcher. + +### Result + +`Result` is one of the most commonly used responders. Returning a `Result` means +one of two things. If the error type implements `Responder`, the `Ok` or `Err` +value will be used, whichever the variant is. If the error type does _not_ +implement `Responder`, the error is printed to the console, and the request is +forwarded to the **500** error catcher. + +### Option + +`Option` is another commonly used responder. If the `Option` is `Some`, the +wrapped responder is used to respond to the client. Otherwise, the request is +forwarded to the **404** error catcher. + +### Failure + +While not encouraged, you can also forward a request to a catcher manually by +using the [Failure](https://api.rocket.rs/rocket/response/struct.Failure.html) +type. For instance, to forward to the catcher for **406 Not Acceptable**, you +would write: + +```rust +#[get("/")] +fn just_fail() -> Failure { + Failure(Status::NotAcceptable) +} +``` + +## JSON + +Responding with JSON data is simple: return a value of type +[JSON](https://api.rocket.rs/rocket_contrib/struct.JSON.html). For example, to +respond with the JSON value of the `Task` structure from previous examples, we +would write: + +```rust +#[derive(Serialize)] +struct Task { ... } + +#[get("/todo")] +fn todo() -> JSON { ... } +``` + +The generic type in `JSON` must implement `Serialize`. The `JSON` type +serializes the structure into JSON, sets the Content-Type to JSON, and emits the +serialization in a fixed-sized body. If serialization fails, the request is +forwarded to the **500** error catcher. + +For a complete example, see the [JSON example on +GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/json). + +## Templates + +Rocket has built-in support for templating. To respond with a rendered template, +simply return a +[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html) type. + +```rust +#[get("/")] +fn index() -> Template { + let context = /* object-like value */; + Template::render("index", &context) +} +``` + +Templates are rendered with the `render` method. The method takes in the name of +a template and a context to render the template with. Rocket searches for a +template with that name in the configurable `template_dir` configuration +parameter, which defaults to `templates/`. Templating support in Rocket is +engine agnostic. The engine used to render a template depends on the template +file's extension. For example, if a file ends with `.hbs`, Handlebars is used, +while if a file ends with `.tera`, Tera is used. + +The context can be any type that implements `Serialize` and serializes to an +`Object` value, such as structs, `HashMaps`, and others. The +[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html) API +documentation contains more information about templates, while the [Handlebars +Templates example on +GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/handlebars_templates) +is a fully composed application that makes use of Handlebars templates. + +## Streaming + +When a large amount of data needs to be sent to the client, it is better to +stream the data to the client to avoid consuming large amounts of memory. Rocket +provides the [Stream](https://api.rocket.rs/rocket/response/struct.Stream.html) +type, making this easy. The `Stream` type can be created from any `Read` type. +For example, to stream from a local Unix stream, we might write: + +```rust +#[get("/stream")] +fn stream() -> io::Result> { + UnixStream::connect("/path/to/my/socket").map(|s| Stream::from(s)) +} + +``` + +Rocket takes care of the rest. diff --git a/site/guide/state.md b/site/guide/state.md new file mode 100644 index 00000000..ede89ee0 --- /dev/null +++ b/site/guide/state.md @@ -0,0 +1,144 @@ +# State + +Many web applications have a need to maintain state. This can be as simple as +maintaining a counter for the number of visits or as complex as needing to +access job queues and multiple databases. Rocket provides the tools to enable +these kinds of interactions in a safe and simple manner. + +## Managed State + +The enabling feature for maintaining state is _managed state_. Managed state, as +the name implies, is state that Rocket manages for your application. The state +is managed on a per-type basis: Rocket will manage at most one value of a given +type. + +The process for using managed state is simple: + + 1. Call `manage` on the `Rocket` instance corresponding to your application + with the initial value of the state. + 2. Add a `State` type to any request handler, where `T` is the type of the + value passed into `manage`. + +### Adding State + +To instruct Rocket to manage state for your application, call the +[manage](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage) method +on a `Rocket` instance. For example, to ask Rocket to manage a `HitCount` +structure with an internal `AtomicUsize` with an initial value of `0`, we can +write the following: + +```rust +struct HitCount(AtomicUsize); + +rocket::ignite().manage(HitCount(AtomicUsize::new(0))); +``` + +The `manage` method can be called any number of times as long as each call +refers to a value of a different type. For instance, to have Rocket manage both +a `HitCount` value and a `Config` value, we can write: + +```rust +rocket::ignite() + .manage(HitCount(AtomicUsize::new(0))) + .manage(Config::from(user_input)); +``` + +### Retrieving State + +State that is being managed by Rocket can be retrieved via the +[State](https://api.rocket.rs/rocket/struct.State.html) type: a [request +guard](/guide/requests/#request-guards) for managed state. To use the request +guard, add a `State` type to any request handler, where `T` is the +type of the managed state. For example, we can retrieve and respond with the +current `HitCount` in a `count` route as follows: + +```rust +#[get("/count")] +fn count(count: State) -> String { + let current_count = hit_count.0.load(Ordering::Relaxed); + format!("Number of visits: {}", current_count) +} +``` + +You can retrieve more than one `State` type in a single route as well: + +```rust +#[get("/state")] +fn state(count: State, config: State) -> T { ... } +``` + +It can also be useful to retrieve managed state from a `FromRequest` +implementation. To do so, invoke the `from_request` method of a `State` type +directly, passing in the `req` parameter of `from_request`: + +```rust +fn from_request(req: &'a Request<'r>) -> request::Outcome { + let count = match as FromRequest>::from_request(req) { + Outcome::Success(count) => count, + ... + }; + ... +} +``` + +### Unmanaged State + +If you request a `State` for a `T` that is not `managed`, Rocket won't call +the offending route. Instead, Rocket will log an error message and return a +**500** error to the client. + +While this behavior is 100% safe, it isn't fun to return **500** errors to +clients, especially when the issue can be easily avoided. Because of this, +Rocket tries to prevent an application with unmanaged state from ever running +via the `unmanaged_state` lint. The lint reads through your code at compile-time +and emits a warning when a `State` request guard is being used in a mounted +route for a type `T` that isn't being managed. + +As an example, consider the following short application using our `HitCount` +type from previous examples: + +```rust +#[get("/count")] +fn count(count: State) -> String { + let current_count = hit_count.0.load(Ordering::Relaxed); + format!("Number of visits: {}", current_count) +} + +fn main() { + rocket::ignite() + .manage(Config::from(user_input)) + .launch() +} +``` + +The application is buggy: a value for `HitCount` isn't being `managed`, but a +`State` type is being requested in the `count` route. When we compile +this application, Rocket emits the following warning: + +```rust +warning: HitCount is not currently being managed by Rocket + --> src/main.rs:2:17 + | +2 | fn count(count: State) -> String { + | ^^^^^^^^^^^^^^^ + | + = note: this State request guard will always fail +help: maybe add a call to 'manage' here? + --> src/main.rs:8:5 + | +8 | rocket::ignite() + | ^^^^^^^^^^^^^^^^ +``` + +The `unmanaged_state` lint isn't perfect. In particular, it cannot track calls +to `manage` across function boundaries. You can disable the lint on a per-route +basis by adding `#[allow(unmanaged_state)]` to a route handler. If you wish to +disable the lint globally, add `#![allow(unmanaged_state)]` to your crate +attributes. + +You can find a complete example using the `HitCounter` structure in the [state +example on +GitHub](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/state) and +learn more about the [manage +method](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage) and +[State type](https://api.rocket.rs/rocket/struct.State.html) in the API docs. diff --git a/site/guide/testing.md b/site/guide/testing.md new file mode 100644 index 00000000..6144d5dc --- /dev/null +++ b/site/guide/testing.md @@ -0,0 +1,136 @@ +# Testing + +Every application should be well tested. Rocket provides the tools to perform +unit and integration tests on your application as well as inspect Rocket +generated code. + +## Tests + +Rocket includes a built-in [testing](https://api.rocket.rs/rocket/testing/) +module that allows you to unit and integration test your Rocket applications. +Testing is simple: + + 1. Construct a `Rocket` instance. + 2. Construct a `MockRequest`. + 3. Dispatch the request using the `Rocket` instance. + 4. Inspect, validate, and verify the `Response`. + +After setting up, we'll walk through each of these steps for the "Hello, world!" +program below: + +```rust +#![feature(plugin)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +#[get("/")] +fn hello() -> &'static str { + "Hello, world!" +} +``` + +### Setting Up + +For the `testing` module to be available, Rocket needs to be compiled with the +_testing_ feature enabled. Since this feature should only be enabled when your +application is compiled for testing, the recommended way to enable the _testing_ +feature is via Cargo's `[dev-dependencies]` section in the `Cargo.toml` file as +follows: + +```toml +[dev-dependencies] +rocket = { version = "0.2.5", features = ["testing"] } +``` + +With this in place, running `cargo test` will result in Cargo compiling Rocket +with the _testing_ feature, thus enabling the `testing` module. + +You'll also need a `test` module with the proper imports: + +```rust +#[cfg(test)] +mod test { + use super::rocket; + use rocket::testing::MockRequest; + use rocket::http::{Status, Method}; + + #[test] + fn hello_world() { + ... + } +} +``` + +In the remainder of this section, we'll work on filling in the `hello_world` +testing function to ensure that the `hello` route results in a `Response` with +_"Hello, world!"_ in the body. + +### Testing + +We'll begin by constructing a `Rocket` instance with the `hello` route mounted +at the root path. We do this in the same way we would normally with one +exception: we need to refer to the `testing` route in the `super` namespace: + +```rust +let rocket = rocket::ignite().mount("/", routes![super::hello]); +``` + +Next, we create a `MockRequest` that issues a `Get` request to the `"/"` path: + +```rust +let mut req = MockRequest::new(Method::Get, "/"); +``` + +We now ask Rocket to perform a full dispatch, which includes routing, +pre-processing and post-processing, and retrieve the `Response`: + +```rust +let mut response = req.dispatch_with(&rocket); +``` + +Finally, we can test the +[Response](https://api.rocket.rs/rocket/struct.Response.html) values to ensure +that it contains the information we expect it to. We want to ensure two things: + + 1. The status is `200 OK`. + 2. The body is the string "Hello, world!". + +We do this by querying the `Response` object directly: + +```rust +assert_eq!(response.status(), Status::Ok); + +let body_str = response.body().and_then(|b| b.into_string()); +assert_eq!(body_str, Some("Hello, world!".to_string())); +``` + +That's it! Run the tests with `cargo test`. The complete application, with +testing, can be found in the [GitHub testing +example](https://github.com/SergioBenitez/Rocket/tree/v0.2.5/examples/testing). + +## Codegen Debug + +It is sometimes useful to inspect the code that Rocket's code generation is +emitting, especially when you get a strange type error. To have Rocket log the +code that it is emitting to the console, set the `ROCKET_CODEGEN_DEBUG` +environment variable when compiling: + +```rust +ROCKET_CODEGEN_DEBUG=1 cargo build +``` + +During compilation, you should see output like this: + +```rust +Emitting item: +fn rocket_route_fn_hello<'_b>(_req: &'_b ::rocket::Request, + _data: ::rocket::Data) + -> ::rocket::handler::Outcome<'_b> { + let responder = hello(); + ::rocket::handler::Outcome::of(responder) +} +``` + +This corresponds to the facade request handler Rocket generated for the `hello` +route. diff --git a/site/index.toml b/site/index.toml new file mode 100644 index 00000000..2e616274 --- /dev/null +++ b/site/index.toml @@ -0,0 +1,193 @@ +############################################################################### +# Top features: displayed in the header under the introductory text. +############################################################################### + +[release] +url = "https://crates.io/crates/rocket" +version = "0.2.5" +date = "Apr 16, 2017" + +[[top_features]] +title = "Type Safe" +text = "From request to response Rocket ensures that your types mean something." +image = "helmet" +button = "Learn More" +url = "/overview/#how-rocket-works" + +[[top_features]] +title = "Boilerplate Free" +text = "Spend your time writing code that really matters, and let Rocket generate the rest." +image = "robot-free" +button = "See Examples" +url = "/overview/#anatomy-of-a-rocket-application" + +[[top_features]] +title = "Easy To Use" +text = "Rocket makes extensive use of Rust's code generation tools to provide a clean API." +image = "sun" +button = "Get Started" +url = "/guide" +margin = 2 + +[[top_features]] +title = "Extensible" +text = "Easily create your own primitives that any Rocket application can use." +image = "telescope" +button = "See How" +url = "/overview/#anatomy-of-a-rocket-application" +margin = 9 + +############################################################################### +# Sections: make sure there are an odd number so colors work out. +############################################################################### + +[[sections]] +title = "Hello, Rocket!" +code = ''' + #![feature(plugin)] + #![plugin(rocket_codegen)] + + extern crate rocket; + + #[get("/hello//")] + fn hello(name: &str, age: u8) -> String { + format!("Hello, {} year old named {}!", age, name) + } + + fn main() { + rocket::ignite().mount("/", routes![hello]).launch(); + } +''' +text = ''' + This is a **complete Rocket application**. It does exactly what you would + expect. If you were to visit **http://localhost:8000/hello/John/58**, you’d + see: + + Hello, 58 year old named John! + + If someone visits a path with an `` that isn’t a `u8`, Rocket doesn’t + blindly call `hello`. Instead, it tries other matching routes or returns a + **404**. +''' + +[[sections]] +title = "Forms? Check!" +code = ''' + #[derive(FromForm)] + struct Task { + description: String, + completed: bool + } + + #[post("/", data = "")] + fn new(task: Form) -> Flash { + if task.get().description.is_empty() { + Flash::error(Redirect::to("/"), "Cannot be empty.") + } else { + Flash::success(Redirect::to("/"), "Task added.") + } + } +''' +text = ''' + Handling forms **is simple and easy**. Simply derive `FromForm` for your + structure and let Rocket know which parameter to use. Rocket **parses and + validates** the form request, creates the structure, and calls your function. + + Bad form request? Rocket doesn’t call your function! What if you want to know + if the form was bad? Simple! Change the type of `task` to `Option` or + `Result`! +''' + +[[sections]] +title = "JSON, out of the box." +code = ''' + #[derive(Serialize, Deserialize)] + struct Message { + contents: String, + } + + #[put("/", data = "")] + fn update(id: ID, message: JSON) -> JSON { + if DB.contains_key(&id) { + DB.insert(id, &message.contents); + JSON(json!{ "status": "ok" }) + } else { + JSON(json!{ "status": "error" }) + } + } +''' +text = ''' + Rocket has first-class support for JSON, right out of the box. Simply derive + `Deserialize` or `Serialize` to receive or return JSON, respectively. + + Like other important features, JSON works through Rocket’s `FromData` trait, + Rocket’s approach to deriving types from body data. It works like this: + specify a `data` route parameter of any type that implements `FromData`. A + value of that type will then be created automatically from the incoming + request body. Best of all, you can implement `FromData` for your types! +''' + +############################################################################### +# Buttom features: displayed above the footer. +############################################################################### + +[[bottom_features]] +title = 'Templating' +text = "Rocket makes rendering templates a breeze with built-in templating support." +image = 'templating-icon' +url = '/guide/responses/#templates' +button = 'Learn More' +color = 'blue' + +[[bottom_features]] +title = 'Cookies' +text = "Cookies are first-class in Rocket. View, add, or remove cookies without hassle." +image = 'cookies-icon' +url = '/guide/requests/#request-guards' +button = 'Learn More' +color = 'purple' +margin = -6 + +[[bottom_features]] +title = 'Streams' +text = "Rocket streams all incoming and outgoing data, so size isn't a concern." +image = 'streams-icon' +url = '/guide/requests/#streaming' +button = 'Learn More' +color = 'red' +margin = -29 + +[[bottom_features]] +title = 'Config Environments' +text = "Configure your application your way for development, staging, and production." +image = 'config-icon' +url = '/guide/overview/#configuration' +button = 'Learn More' +color = 'yellow' +margin = -3 + +[[bottom_features]] +title = 'Query Params' +text = "Handling query parameters isn’t an afterthought in Rocket." +image = 'query-icon' +url = '/guide/requests/#query-strings' +button = 'Learn More' +color = 'orange' +margin = -3 + +[[bottom_features]] +title = 'Testing Library' +text = "Unit test your applications with ease using the built-in testing library." +image = 'testing-icon' +url = '/guide/testing#testing' +button = 'Learn More' +color = 'green' + +# Blocked on Hyper/OpenSSL. +# [[bottom_features]] +# title = 'Signed Sessions' +# text = "Safe, secure, signed sessions are built-in to Rocket so your users can stay safe." +# image = 'sessions-icon' +# url = '/overview' +# button = 'Learn More' +# color = 'green' diff --git a/site/news.toml b/site/news.toml new file mode 100644 index 00000000..bcf11e14 --- /dev/null +++ b/site/news.toml @@ -0,0 +1,15 @@ +[[articles]] +title = "Rocket v0.2: Managed State & More" +date = "February 06, 2017" +snippet = """ +Today marks the first major release since Rocket's debut a little over a month +ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements. +Much of the development in v0.2 was led by the community, either through reports +via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues) +or via direct contributions. In fact, there have been **20 unique contributors** +to Rocket's code since Rocket's initial introduction! Community feedback has +been incredible. As a special thank you, we include the names of these +contributors at the end of this article. +""" +slug = "2017-02-06-version-0.2" + diff --git a/site/news/2017-02-06-version-0.2.md b/site/news/2017-02-06-version-0.2.md new file mode 100644 index 00000000..aa17f036 --- /dev/null +++ b/site/news/2017-02-06-version-0.2.md @@ -0,0 +1,392 @@ +# Rocket v0.2: Managed State & More + +**Posted by [Sergio Benitez](https://sergio.bz) on February 06, 2017** + +Today marks the first major release since Rocket's debut a little over a month +ago. Rocket v0.2 packs a ton of new features, fixes, and general improvements. +Much of the development in v0.2 was led by the community, either through reports +via the [GitHub issue tracker](https://github.com/SergioBenitez/Rocket/issues) +or via direct contributions. In fact, there have been **20 unique contributors** +to Rocket's codebase since Rocket's initial introduction! Community feedback has +been incredible. As a special thank you, we include the names of these +contributors at the end of this article. + +## About Rocket + +Rocket is a web framework for Rust with a focus on ease of use, expressibility, +and speed. Rocket makes it simple to write fast web applications without +sacrificing flexibility or type safety. All with minimal code. + +> Rocket's so simple, you feel like you're doing something wrong. It's like if +> you're making fire with rocks and suddently someone gives you a lighter. Even +> though you know the lighter makes fire, and does it even faster and better and +> with a simple flick, the rock's still in your brain. +> +> -- Artem "impowski" Biryukov, January 17, 2017, on **#rocket** + +## New Features + +Rocket v0.2 includes several new features that make developing Rocket +applications simpler, faster, and safer than ever before. + +### Managed State + +Undoubtedly, the star feature of this release is **managed state**. Managed +state allows you to pass state to Rocket prior to launching your application and +later retrieve that state from any request handler by simply including the +state's type in the function signature. It works in two easy steps: + + 1. Call `manage` on the `Rocket` instance corresponding to your application + with the initial value of the state. + 2. Add a `State` type to any request handler, where `T` is the type of the + value passed into `manage`. + +Rocket takes care of the rest! `State` works through Rocket's [request +guards](/guide/requests/#request-guards). You can call `manage` any number of +times, as long as each call corresponds to a value of a different type. + +As a simple example, consider the following "hit counter" example application: + +```rust +struct HitCount(AtomicUsize); + +#[get("/")] +fn index(hit_count: State) -> &'static str { + hit_count.0.fetch_add(1, Ordering::Relaxed); + "Your visit has been recorded!" +} + +#[get("/count")] +fn count(hit_count: State) -> String { + hit_count.0.load(Ordering::Relaxed).to_string() +} + +fn main() { + rocket::ignite() + .mount("/", routes![index, count]) + .manage(HitCount(AtomicUsize::new(0))) + .launch() +} +``` + +Visiting `/` will record a visit by incrementing the hit count by 1. Visiting +the `/count` path will display the current hit count. + +One concern when using _managed state_ is that you might forget to call `manage` +with some state's value before launching your application. Not to worry: Rocket +has your back! Let's imagine for a second that we forgot to add the call to +`manage` on line 17 in the example above. Here's what the compiler would emit +when we compile our buggy application: + +```rust +warning: HitCount is not currently being managed by Rocket + --> src/main.rs:4:21 + | +4 | fn index(hit_count: State) -> &'static str { + | ^^^^^^^^^^^^^^^ + | + = note: this State request guard will always fail +help: maybe add a call to 'manage' here? + --> src/main.rs:15:5 + | +15| rocket::ignite() + | ^^^^^^^^^^^^^^^^ + +warning: HitCount is not currently being managed by Rocket + --> src/main.rs:10:21 + | +10 | fn count(hit_count: State) -> String { + | ^^^^^^^^^^^^^^^ + | + = note: this State request guard will always fail +help: maybe add a call to 'manage' here? + --> src/main.rs:15:5 + | +15 | rocket::ignite() + | ^^^^^^^^^^^^^^^^ +``` + +You can read more about managed state in the [guide](/guide/state/), the API +docs for +[manage](https://api.rocket.rs/rocket/struct.Rocket.html#method.manage), and the +API docs for [State](https://api.rocket.rs/rocket/struct.State.html). + +### Unmounted Routes Lint + +A common mistake that new Rocketeers make is forgetting to +[mount](/guide/overview/#mounting) declared routes. In Rocket v0.2, Rocket adds +a _lint_ that results in a compile-time warning for unmounted routes. As a +simple illustration, consider the canonical "Hello, world!" Rocket application +below, and note that we've forgotten to mount the `hello` route: + +```rust +#[get("/")] +fn hello() -> &'static str { + "Hello, world!" +} + +fn main() { + rocket::ignite().launch(); +} +``` + +When this program is compiled, the compiler emits the following warning: + +```rust +warning: the 'hello' route is not mounted + --> src/main.rs:2:1 + | +2 | fn hello() -> &'static str { + | _^ starting here... +3 | | "Hello, world!" +4 | | } + | |_^ ...ending here + | + = note: Rocket will not dispatch requests to unmounted routes. +help: maybe add a call to 'mount' here? + --> src/main.rs:7:5 + | +7 | rocket::ignite().launch(); + | ^^^^^^^^^^^^^^^^ +``` + +The lint can be disabled selectively per route by adding an +`#[allow(unmounted_route)]` annotation to a given route declaration. It can also +be disabled globally by adding `#![allow(unmounted_route)]`. You can read more +about this lint in the [codegen +documentation](https://api.rocket.rs/rocket_codegen/index.html). + +### Configuration via Environment Variables + +A new feature that makes deploying Rocket apps to the cloud a little easier is +configuration via environment variables. Simply put, any configuration parameter +can be set via an environment variable of the form `ROCKET_{PARAM}`, where +`{PARAM}` is the name of the configuration parameter. For example, to set the +`port` Rocket listens on, simply set the `ROCKET_PORT` environment variable: + +```sh +ROCKET_PORT=3000 cargo run --release +``` + +Configuration parameters set via environment variables take precedence over +parameters set via the `Rocket.toml` configuration file. Note that _any_ +parameter can be set via an environment variable, include _extras_. For more +about configuration in Rocket, see the [configuration section of the +guide](/guide/overview/#configuration). + +### And Plenty More! + +Rocket v0.2 is full of many new features! In addition to the three features +described above, v0.2 also includes the following: + + * `Config` structures can be built via `ConfigBuilder`, which follows the + builder pattern. + * Logging can be enabled or disabled on custom configuration via a second + parameter to the `Rocket::custom` method. + * `name` and `value` methods were added to `Header` to retrieve the name and + value of a header. + * A new configuration parameter, `workers`, can be used to set the number of + threads Rocket uses. + * The address of the remote connection is available via `Request.remote()`. + Request preprocessing overrides remote IP with value from the `X-Real-IP` + header, if present. + * During testing, the remote address can be set via `MockRequest.remote()`. + * The `SocketAddr` request guard retrieves the remote address. + * A `UUID` type has been added to `contrib`. + * `rocket` and `rocket_codegen` will refuse to build with an incompatible + nightly version and emit nice error messages. + * Major performance and usability improvements were upstreamed to the `cookie` + crate, including the addition of a `CookieBuilder`. + * When a checkbox isn't present in a form, `bool` types in a `FromForm` + structure will parse as `false`. + * The `FormItems` iterator can be queried for a complete parse via `completed` + and `exhausted`. + * Routes for `OPTIONS` requests can be declared via the `options` decorator. + * Strings can be percent-encoded via `URI::percent_encode()`. + +## Breaking Changes + +This release includes several breaking changes. These changes are listed below +along with a short note about how to handle the breaking change in existing +applications. + + * **`Rocket::custom` takes two parameters, the first being `Config` by + value.** + + A call in v0.1 of the form `Rocket::custom(&config)` is now + `Rocket::custom(config, false)`. + + * **Tera templates are named without their extension.** + + A templated named `name.html.tera` is now simply `name`. + + * **`JSON` `unwrap` method has been renamed to `into_inner`.** + + A call to `.unwrap()` should be changed to `.into_inner()`. + + * **The `map!` macro was removed in favor of the `json!` macro.** + + A call of the form `map!{ "a" => b }` can be written as: `json!({ "a": b + })`. + + * **The `hyper::SetCookie` header is no longer exported.** + + Use the `Cookie` type as an `Into
` type directly. + + * **The `Content-Type` for `String` is now `text/plain`.** + + Use `content::HTML` for HTML-based `String` responses. + + * **`Request.content_type()` returns an `Option`.** + + Use `.unwrap_or(ContentType::Any)` to get the old behavior. + + * **The `ContentType` request guard forwards when the request has no + `Content-Type` header.** + + Use an `Option` and `.unwrap_or(ContentType::Any)` for the old + behavior. + + * **A `Rocket` instance must be declared _before_ a `MockRequest`.** + + Change the order of the `rocket::ignite()` and `MockRequest::new()` calls. + + * **A route with `format` specified only matches requests with the same + format.** + + Previously, a route with a `format` would match requests without a format + specified. There is no workaround to this change; simply specify formats + when required. + + * **`FormItems` can no longer be constructed directly.** + + Instead of constructing as `FormItems(string)`, construct as + `FormItems::from(string)`. + + * **`from_from_string(&str)` in `FromForm` removed in favor of + `from_form_items(&mut FormItems)`.** + + Most implementation should be using `FormItems` internally; simply use the + passed in `FormItems`. In other cases, the form string can be retrieved via + the `inner_str` method of `FormItems`. + + * **`Config::{set, default_for}` are deprecated.** + + Use the `set_{param}` methods instead of `set`, and `new` or `build` in + place of `default_for`. + + * **Route paths must be absolute.** + + Prepend a `/` to convert a relative path into an absolute one. + + * **Route paths cannot contain empty segments.** + + Remove any empty segments, including trailing ones, from a route path. + +## Bug Fixes + +Three bugs were fixed in this release: + + * Handlebars partials were not properly registered + ([#122](https://github.com/SergioBenitez/Rocket/issues/122)). + * `Rocket::custom` did not set the custom configuration as the `active` + configuration. + * Route path segments with more than one dynamic parameter were erroneously + allowed. + +## General Improvements + +In addition to new features, Rocket saw the following smaller improvements: + + * Rocket no longer overwrites a catcher's response status. + * The `port` `Config` type is now a proper `u16`. + * Clippy issues injected by codegen are resolved. + * Handlebars was updated to `0.25`. + * The `PartialEq` implementation of `Config` doesn't consider the path or + session key. + * Hyper dependency updated to `0.10`. + * The `Error` type for `JSON as FromData` has been exposed as `SerdeError`. + * SVG was added as a known Content-Type. + * Serde was updated to `0.9`. + * Form parse failure now results in a **422** error code. + * Tera has been updated to `0.7`. + * `pub(crate)` is used throughout to enforce visibility rules. + * Query parameters in routes (`/path?`) are now logged. + * Routes with and without query parameters no longer _collide_. + +Rocket v0.2 also includes all of the new features, bug fixes, and improvements +from versions 0.1.1 through 0.1.6. You can read more about these changes in the +[v0.1 +CHANGELOG](https://github.com/SergioBenitez/Rocket/blob/v0.1/CHANGELOG.md). + +## What's next? + +Work now begins on Rocket v0.3! The focus of the next major release will be on +security. In particular, three major security features are planned: + + 1. **Automatic CSRF protection across all payload-based requests + ([#14](https://github.com/SergioBenitez/Rocket/issues/14)).** + + Rocket will automatically check the origin of requests made for HTTP `PUT`, + `POST`, `DELETE`, and `PATCH` requests, allowing only authorized requests to + be dispatched. This includes checking `POST`s from form submissions and any + requests made via JavaScript. + + 2. **Encryption and signing of session-based cookies + ([#20](https://github.com/SergioBenitez/Rocket/issues/20)).** + + Built-in session support will encrypt and sign cookies using a user supplied + `session_key`. Encryption and signing will occur automatically for + session-based cookies. + + 3. **Explicit typing of raw HTTP data strings + ([#43](https://github.com/SergioBenitez/Rocket/issues/43)).** + + A present, the standard `&str` type is used to represent raw HTTP data + strings. In the next release, a new type, `&RawStr`, will be used for this + purpose. This will make it clear when raw data is being handled. The type + will expose convenient methods such as `.url_decode()` and `.html_escape()`. + +Work on Rocket v0.3 will also involve exploring built-in support for user +authentication and authorization as well as automatic parsing of multipart +forms. + +## Contributors to v0.2 + +The following wonderful people helped make Rocket v0.2 happen: + +
    +
  • Cliff H
  • +
  • Dru Sellers
  • +
  • Eijebong
  • +
  • Eric D. Reichert
  • +
  • Ernestas Poskus
  • +
  • FliegendeWurst
  • +
  • Garrett Squire
  • +
  • Giovanni Capuano
  • +
  • Greg Edwards
  • +
  • Joel Roller
  • +
  • Josh Holmer
  • +
  • Liigo Zhuang
  • +
  • Lori Holden
  • +
  • Marcus Ball
  • +
  • Matt McCoy
  • +
  • Reilly Tucker
  • +
  • Robert Balicki
  • +
  • Sean Griffin
  • +
  • Seth Lopez
  • +
  • tborsa
  • +
+ +Thank you all! Your contributions are greatly appreciated! + +Looking to help with Rocket's development? Head over to [Rocket's +GitHub](https://github.com/SergioBenitez/Rocket#contributing) and start +contributing! + +## Start using Rocket today! + +Not already using Rocket? Rocket is extensively documented, making it easy for +you to start writing your web applications in Rocket! See the +[overview](/overview) or start writing code immediately by reading through [the +guide](/guide). diff --git a/site/overview.toml b/site/overview.toml new file mode 100644 index 00000000..232cd774 --- /dev/null +++ b/site/overview.toml @@ -0,0 +1,231 @@ +############################################################################### +# Panels: displayed in a tabbed arrangement. +############################################################################### + +[[panels]] +name = "Routing" +checked = true +content = ''' +Rocket's main task is to route incoming requests to the appropriate request +handler using your application's declared routes. Routes are declared using +Rocket's _route_ attributes. The attribute describes the requests that match the +route. The attribute is placed on top of a function that is the request handler +for that route. + +As an example, consider the simple route below: + +```rust +#[get("/")] +fn index() -> &'static str { + "Hello, world!" +} +``` + +This route, named `index`, will match against incoming HTTP `GET` requests to +the `/` path, the index. The request handler returns a string. Rocket will use +the string as the body of a fully formed HTTP response. +''' + +[[panels]] +name = "Dynamic Params" +content = ''' +Rocket allows you to interpret segments of a request path dynamically. To +illustrate, let's use the following route: + +```rust +#[get("/hello//")] +fn hello(name: &str, age: u8) -> String { + format!("Hello, {} year old named {}!", age, name) +} +``` + +The `hello` route above matches two dynamic path segments declared inside +brackets in the path: `` and ``. _Dynamic_ means that the segment can +be _any_ value the end-user desires. + +Each dynamic parameter (`name` and `age`) must have a type, here `&str` and +`u8`, respectively. Rocket will attempt to parse the string in the parameter's +position in the path into that type. The route will only be called if parsing +succeeds. To parse the string, Rocket uses the +[FromParam](https://api.rocket.rs/rocket/request/trait.FromParam.html) trait, +which you can implement for your own types! +''' + +[[panels]] +name = "Handling Data" +content = ''' +Request body data is handled in a special way in Rocket: via the +[FromData](https://api.rocket.rs/rocket/data/trait.FromData.html) trait. Any +type that implements `FromData` can be derived from incoming body data. To tell +Rocket that you're expecting request body data, the `data` route argument is +used with the name of the parameter in the request handler: + +```rust +#[post("/login", data = "")] +fn login(user_form: Form) -> String { + // Use `user_form`, return a String. +} +``` + +The `login` route above says that it expects `data` of type `Form` in +the `user_form` parameter. The +[Form](https://api.rocket.rs/rocket/request/struct.Form.html) type is a built-in +Rocket type that knows how to parse web forms into structures. Rocket will +automatically attempt to parse the request body into the `Form` and call the +`login` handler if parsing succeeds. Other built-in `FromData` types include +[Data](https://api.rocket.rs/rocket/struct.Data.html), +[JSON](https://api.rocket.rs/rocket_contrib/struct.JSON.html), and +[Flash](https://api.rocket.rs/rocket/response/struct.Flash.html) +''' + +[[panels]] +name = "Request Guards" +content = ''' +In addition to dynamic path and data parameters, request handlers can also +contain a third type of parameter: _request guards_. Request guards aren't +declared in the route attribute, and any number of them can appear in the +request handler signature. + +Request guards _protect_ the handler from running unless some set of conditions +are met by the incoming request metadata. For instance, if you are writing an +API that requires sensitive calls to be accompanied by an API key in the request +header, Rocket can protect those calls via a custom `APIKey` request guard: + +```rust +#[get("/sensitive")] +fn sensitive(key: APIKey) -> &'static str { ... } +``` + +`APIKey` protects the `sensitive` handler from running incorrectly. In order for +Rocket to call the `sensitive` handler, the `APIKey` type needs to be derived +through a +[FromRequest](https://api.rocket.rs/rocket/request/trait.FromRequest.html) +implementation, which in this case, validates the API key header. Request guards +are a powerful and unique Rocket concept; they centralize application policy and +invariants through types. +''' + +[[panels]] +name = "Responders" +content = ''' +The return type of a request handler can by any type that implements +[Responder](https://api.rocket.rs/rocket/response/trait.Responder.html): + +```rust +#[get("/")] +fn route() -> T { ... } +``` + +Above, T must implement `Responder`. Rocket implements `Responder` for many of +the standard library types including `&str`, `String`, `File`, `Option`, and +`Result`. Rocket also implements custom responders such as +[Redirect](https://api.rocket.rs/rocket/response/struct.Redirect.html), +[Flash](https://api.rocket.rs/rocket/response/struct.Flash.html), and +[Template](https://api.rocket.rs/rocket_contrib/struct.Template.html). + +The task of a `Reponder` is to generate a +[Response](https://api.rocket.rs/rocket/response/struct.Response.html), if +possible. `Responder`s can fail with a status code. When they do, Rocket calls +the corresponding `error` route, which can be declared as follows: + +```rust +#[error(404)] +fn not_found() -> T { ... } +``` +''' + +[[panels]] +name = "Launching" +content = ''' +Launching a Rocket application is the funnest part! For Rocket to begin +dispatching requests to routes, the routes need to be _mounted_. After mounting, +the application needs to be _launched_. These two steps, usually done in `main`, +look like: + +```rust +rocket::ignite() + .mount("/base", routes![index, another]) + .launch() +``` + +The `mount` call takes a base path and a set of routes via the `routes!` macro. +The base path (`/base` above) is prepended to the path of every route in the +list. This effectively namespaces the routes, allowing for easier composition. + +The `launch` call starts the server. In development, Rocket prints useful +information to the console to let you know everything is okay. + +```sh +🚀 Rocket has launched from http://localhost:8000... +``` +''' + +############################################################################### +# Steps to "How Rocket Works" +############################################################################### + +[[steps]] +name = "Validation" +color = "blue" +content = ''' +First, Rocket validates a matching request by ensuring that all of the types in +a given handler can be derived from the incoming request. If the types cannot be +derived, the request is forwarded to the next matching route until a route’s +types validate or there are no more routes to try. If all routes fail, a +customizable **404** error is returned. + +```rust +#[post("/user", data = "")] +fn new_user(admin: AdminUser, new_user: Form) -> T { + ... +} +``` + +For the `new_user` handler above to be called, the following conditions must +hold: + +* The request method must be `POST`. +* The request path must be `/user`. +* The request must contain `data` in its body. +* The request metadata must authenticate an `AdminUser`. +* The request body must be a form that parses into a `User` struct. +''' + +[[steps]] +name = "Processing" +color = "purple" +content = ''' +Next, the request is processed by an arbitrary handler. This is where most of +the business logic in an application resides, and the part of your applications +you’ll likely spend the most time writing. In Rocket, handlers are simply +functions - that’s it! The only caveat is that the function’s return type must +implement the `Responder` trait. The `new_user` function above is an example of +a handler. +''' + +[[steps]] +name = "Response" +color = "red" +content = ''' +Finally, Rocket responds to the client by transforming the return value of the +handler into an HTTP response. The HTTP response generated from the returned +value depends on the type’s specific `Responder` trait implementation. + +```rust +fn route() -> T { ... } +``` + +If the function above is used as a handler, for instance, then the type `T` must +implement `Responder`. Rocket provides many useful responder types out of the +box. They include: + + * `JSON`: Serializes the structure T into JSON and returns it to + the client. + * `Template`: Renders a template file and returns it to the client. + * `Redirect`: Returns a properly formatted HTTP redirect. + * `NamedFile`: Streams a given file to the client with the + Content-Type taken from the file’s extension. + * `Stream`: Streams data to the client from an arbitrary `Read` value. + * Many Primitive Types: `String`, `&str`, `File`, `Option`, `Result`, and + others all implement the `Responder` trait. +'''