mirror of https://github.com/rwf2/Rocket.git
Add site contents, including the guide. Add license information.
This commit is contained in:
parent
9b7f58448a
commit
b5ef6ec85b
|
@ -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)
|
* 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)
|
* 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).
|
||||||
|
|
|
@ -11,4 +11,5 @@ if [ -z ${1} ] || [ -z ${2} ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
find . -name "*.toml" | xargs sed -i.bak "s/${1}/${2}/g"
|
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
|
find . -name "*.bak" | xargs rm
|
||||||
|
|
|
@ -0,0 +1,674 @@
|
||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||||
|
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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
<http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
||||||
|
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
|
@ -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).
|
|
@ -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.
|
||||||
|
|
|
@ -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!
|
|
@ -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!
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
||||||
|
`<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 /<id>
|
||||||
|
|
||||||
|
retrieves the content for the paste with id `<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::<usize>() % 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 = "<paste>")]
|
||||||
|
fn upload(paste: Data) -> io::Result<String>
|
||||||
|
```
|
||||||
|
|
||||||
|
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 = "<paste>")]
|
||||||
|
fn upload(paste: Data) -> io::Result<String> {
|
||||||
|
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
|
||||||
|
<ctrl-c> # 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 /<id>` 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 `<id>`, will
|
||||||
|
return the corresponding paste if it exists.
|
||||||
|
|
||||||
|
Here's a first take at implementing the `retrieve` route. The route below takes
|
||||||
|
in an `<id>` 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("/<id>")]
|
||||||
|
fn retrieve(id: &str) -> Option<File> {
|
||||||
|
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<PasteID<'a>, &'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 `<id>` represents a valid `PasteID` before calling
|
||||||
|
the `retrieve` route, preventing attacks on the `retrieve` route:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/<id>")]
|
||||||
|
fn retrieve(id: PasteID) -> Option<File> {
|
||||||
|
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 /<id>` route. Use
|
||||||
|
`PasteID` to validate `<id>`.
|
||||||
|
* **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 /<id>` route that allows a user with the key for `<id>` to
|
||||||
|
replace the existing paste, if any.
|
||||||
|
* Add a new route, `GET /<id>/<lang>` that syntax highlights the paste with ID
|
||||||
|
`<id>` for language `<lang>`. If `<lang>` is not a known language, do no
|
||||||
|
highlighting. Possibly validate `<lang>` 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).
|
|
@ -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`.
|
||||||
|
|
|
@ -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 = "<user>")]
|
||||||
|
fn new_user(user: JSON<User>) -> 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/<name>")]
|
||||||
|
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/<name>/<age>/<cool>")]
|
||||||
|
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/<id>")]
|
||||||
|
fn user(id: usize) -> T { ... }
|
||||||
|
|
||||||
|
#[get("/user/<id>", rank = 2)]
|
||||||
|
fn user_int(id: isize) -> T { ... }
|
||||||
|
|
||||||
|
#[get("/user/<id>", 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/<id>` will
|
||||||
|
be routed as follows:
|
||||||
|
|
||||||
|
1. The `user` route matches first. If the string at the `<id>` 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 `<id>` is a signed integer,
|
||||||
|
`user_int` is called. Otherwise, the request is forwarded.
|
||||||
|
|
||||||
|
3. The `user_str` route matches last. Since `<id>` 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<usize, &str>`, then `user`
|
||||||
|
would never forward. An `Ok` variant would indicate that `<id>` was a valid
|
||||||
|
`usize`, while an `Err` would indicate that `<id>` 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 `<param..>` 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/<path..>")]
|
||||||
|
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("/<file..>")]
|
||||||
|
fn files(file: PathBuf) -> Option<NamedFile> {
|
||||||
|
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 =
|
||||||
|
"<param>"` 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 = "<input>")]
|
||||||
|
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 = "<task>")]
|
||||||
|
fn new(task: Form<Task>) -> 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 = "<task>")]
|
||||||
|
fn new(task: JSON<Task>) -> 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 = "<data>")]
|
||||||
|
fn upload(data: Data) -> io::Result<Plain<String>> {
|
||||||
|
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 `<param>` 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 = "<task>")]
|
||||||
|
fn new(task: Form<Task>) -> String { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
Rocket makes the transition simple: simply declare `<task>` as a query parameter
|
||||||
|
as follows:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/todo?<task>")]
|
||||||
|
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.
|
|
@ -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>(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<String>`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[get("/")]
|
||||||
|
fn accept() -> status::Accepted<String> {
|
||||||
|
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<Task> { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
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<Stream<UnixStream>> {
|
||||||
|
UnixStream::connect("/path/to/my/socket").map(|s| Stream::from(s))
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Rocket takes care of the rest.
|
|
@ -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<T>` 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<T>` 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<HitCount>) -> 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<HitCount>, config: State<Config>) -> 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<T>` type
|
||||||
|
directly, passing in the `req` parameter of `from_request`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn from_request(req: &'a Request<'r>) -> request::Outcome<T, ()> {
|
||||||
|
let count = match <State<HitCount> as FromRequest>::from_request(req) {
|
||||||
|
Outcome::Success(count) => count,
|
||||||
|
...
|
||||||
|
};
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Unmanaged State
|
||||||
|
|
||||||
|
If you request a `State<T>` 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<T>` 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<HitCount>) -> 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<HitCount>` 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<HitCount>) -> 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.
|
|
@ -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.
|
|
@ -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/<name>/<age>")]
|
||||||
|
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:
|
||||||
|
|
||||||
|
<span class="callout">Hello, 58 year old named John!</span>
|
||||||
|
|
||||||
|
If someone visits a path with an `<age>` 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 = "<task>")]
|
||||||
|
fn new(task: Form<Task>) -> Flash<Redirect> {
|
||||||
|
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("/<id>", data = "<message>")]
|
||||||
|
fn update(id: ID, message: JSON<Message>) -> JSON<Value> {
|
||||||
|
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'
|
|
@ -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"
|
||||||
|
|
|
@ -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.
|
||||||
|
>
|
||||||
|
> -- <cite>Artem "impowski" Biryukov, January 17, 2017, on **#rocket**</cite>
|
||||||
|
|
||||||
|
## 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<T>` 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<HitCount>) -> &'static str {
|
||||||
|
hit_count.0.fetch_add(1, Ordering::Relaxed);
|
||||||
|
"Your visit has been recorded!"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/count")]
|
||||||
|
fn count(hit_count: State<HitCount>) -> 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<HitCount>) -> &'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<HitCount>) -> 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<Header>` type directly.
|
||||||
|
|
||||||
|
* **The `Content-Type` for `String` is now `text/plain`.**
|
||||||
|
|
||||||
|
Use `content::HTML<String>` for HTML-based `String` responses.
|
||||||
|
|
||||||
|
* **`Request.content_type()` returns an `Option<ContentType>`.**
|
||||||
|
|
||||||
|
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<ContentType>` 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?<param>`) 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:
|
||||||
|
|
||||||
|
<ul class="columns">
|
||||||
|
<li>Cliff H</li>
|
||||||
|
<li>Dru Sellers</li>
|
||||||
|
<li>Eijebong</li>
|
||||||
|
<li>Eric D. Reichert</li>
|
||||||
|
<li>Ernestas Poskus</li>
|
||||||
|
<li>FliegendeWurst</li>
|
||||||
|
<li>Garrett Squire</li>
|
||||||
|
<li>Giovanni Capuano</li>
|
||||||
|
<li>Greg Edwards</li>
|
||||||
|
<li>Joel Roller</li>
|
||||||
|
<li>Josh Holmer</li>
|
||||||
|
<li>Liigo Zhuang</li>
|
||||||
|
<li>Lori Holden</li>
|
||||||
|
<li>Marcus Ball</li>
|
||||||
|
<li>Matt McCoy</li>
|
||||||
|
<li>Reilly Tucker</li>
|
||||||
|
<li>Robert Balicki</li>
|
||||||
|
<li>Sean Griffin</li>
|
||||||
|
<li>Seth Lopez</li>
|
||||||
|
<li>tborsa</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
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).
|
|
@ -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/<name>/<age>")]
|
||||||
|
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: `<name>` and `<age>`. _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 = "<user_form>")]
|
||||||
|
fn login(user_form: Form<UserLogin>) -> String {
|
||||||
|
// Use `user_form`, return a String.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `login` route above says that it expects `data` of type `Form<UserLogin>` 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 = "<new_user>")]
|
||||||
|
fn new_user(admin: AdminUser, new_user: Form<User>) -> 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<T>`: 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.
|
||||||
|
'''
|
Loading…
Reference in New Issue