1
0
mirror of https://github.com/actix/actix-website synced 2024-11-23 16:31:08 +01:00

Update website

This commit is contained in:
Armin Ronacher 2018-05-22 23:15:08 +02:00
parent e14d14f51b
commit be2e12d2d2
93 changed files with 7324 additions and 894 deletions

7
.deploy.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" ]]; then
cp CNAME public
git clone https://github.com/davisp/ghp-import.git &&
./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -b master -r https://"$GH_TOKEN"@github.com/actix/actix.github.io.git _site &&
echo "Uploaded documentation"
fi

6
.gitignore vendored
View File

@ -1,4 +1,2 @@
.DS_Store public
tmp
build/
_site/

View File

@ -1,15 +1,17 @@
language: rust language: rust
sudo: false sudo: false
dist: trusty
# build website install:
- curl -L https://github.com/gohugoio/hugo/releases/download/v0.40.3/hugo_0.40.3_Linux-64bit.tar.gz |
tar xzvf -
script: script:
- export PATH=$PATH:~/.cargo/bin - ./hugo_0.40.3_Linux-64bit/hugo_0.18.1_linux_amd64
- |
if [[ "$TRAVIS_OS_NAME" == "linux" && "$TRAVIS_PULL_REQUEST" = "false" && "$TRAVIS_BRANCH" == "master" ]]; then deploy:
curl -sL https://github.com/cobalt-org/cobalt.rs/releases/download/v0.12.1/cobalt-v0.12.1-x86_64-unknown-linux-gnu.tar.gz | tar xvz -C $HOME/.cargo/bin && provider: script
cobalt build && cp CNAME ./_site/ && script: bash .deploy.sh
git clone https://github.com/davisp/ghp-import.git && skip_cleanup: true
./ghp-import/ghp_import.py -n -p -f -m "Documentation upload" -b master -r https://"$GH_TOKEN"@github.com/actix/actix.github.io.git _site && on:
echo "Uploaded documentation" branch: master
fi condition: $TRAVIS_PULL_REQUEST = "false"

201
LICENSE
View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017-NOW Actix Organization and Contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

201
LICENSE-APACHE Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
LICENSE-MIT Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2018 Carl Lerche
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -1,14 +1,27 @@
# actix-theme # Actix Website
The Actix website template made with [Cobalt](https://cobalt-org.github.io/)
The work in progress website for the actix project based on tokio's website.
# Contribute. ## Getting Started
So you want make actix-website more beautiful.
```bash Building the website depends on [Hugo](http://gohugo.io). So, first make sure
$ cobalt -V // Cobalt 0.11.1 that you have it installed. If on OS X and using Homebrew, run the following:
$ git clone https://github.com/actix/actix-website.git
$ cd actix-website ```sh
// modify index.liquid or tutorials/community/more markdown file. brew update && brew install hugo
// also add or modify markdown files in docs/blog DIR.
$ cobalt serve // it will get '_site' DIR, so only need put files in this '_site' DIR to [actix.github.io](https://github.com/actix/actix.github.io)
``` ```
Then, get the website running locally:
```sh
git clone https://github.com/mitsuhiko/actix-website
cd website
hugo server
```
Then visit [http://localhost:1313](http://localhost:1313).
# License
Pretty murky. Right now a massive clone of the tokio website. Will get this
figured out as we go along.

View File

@ -1,39 +0,0 @@
# Build options
source: "."
template_extensions:
- md
- liquid
include_drafts: false
ignore:
- .git/*
- build/*
- README.md
destination: _site
# Site options
site:
title: ~
description: ~
base_url: ~
data: ~
# Page/Post options
default:
excerpt_separator: "\n\n"
is_draft: false
syntax_highlight:
theme: "base16-ocean.dark"
assets:
sass:
style: Nested
pages:
default: {}
posts:
title: ~
description: ~
dir: posts
drafts_dir: _drafts
order: Desc
rss: ~
jsonfeed: ~
default: {}

View File

@ -1,10 +0,0 @@
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-110322332-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-110322332-1');
</script>

View File

@ -1,7 +0,0 @@
<meta name="viewport" content="width=device-width, initial-scale=1", maximum-scale=1,user-scalable=no,shrink-to-fit=no">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>{{ page.title }}</title>
<link rel="shortcut icon" href="/public/imgs/favicon.ico">
<link rel="stylesheet" href="/public/css/styles.css">
<script src="/public/js/nav.js" async="async"></script>

View File

@ -1,14 +0,0 @@
<div id="mnav">
<div id="lnav">
<div id="line"></div>
<h1><a href="/"><img src="/public/imgs/ACTIX.png"/></a><a id="home" href="/">Actix</a></h1>
<label ><a href="#" id="menu">menu</a></label>
</div>
<div id="rnav">
<li> <a href="https://github.com/actix/actix-web" title="GitHub">github</a></li>
<li> <a href="https://actix.rs/book/actix-web/" title="Guide">guide</a></li>
<li> <a href="https://actix.rs/actix-web/actix_web/" title="api">api</a></li>
<li> <a href="https://gitter.im/actix/actix" title="chat">gitter chat</a></li>
<li> <a href="https://crates.io/crates/actix-web" title="crates">crates.io</a></li>
</div>
</div>

View File

@ -1,30 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{% include "head.liquid" %}
</head>
<body>
<header>
{% include "header.liquid" %}
</header>
<main>
<section class="docs">
<div class="inner">
<aside>
<p>2018</p>
<ul>
<li><a {%if page.permalink == "Blog-example"%}class="active"{%endif%} href="/blog/2018/Blog-example.html">Blog Example</a></li>
</ul>
</aside>
<hr id="hr">
<main>
{{ page.content }}
</main>
</div>
</section>
</main>
<footer>
{% include "footer.liquid" %}
</footer>
</body>
</html>

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{% include "head.liquid" %}
</head>
<body>
<header>
{% include "header.liquid" %}
</header>
<main>
{{ page.content }}
</main>
<footer>
{% include "footer.liquid" %}
</footer>
</body>
</html>

View File

@ -1,42 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{% include "head.liquid" %}
</head>
<body>
<header>
{% include "header.liquid" %}
</header>
<main>
<section class="blog">
<div class="inner">
<aside>
<p>Basics</p>
<ul>
<li><a {%if page.permalink == "Basics-example"%}class="active"{%endif%} href="/docs/Basics/Basics-example.html">Basics Example</a></li>
</ul>
<p>Reference</p>
<ul>
<li><a {%if page.permalink == "Reference-example"%}class="active"{%endif%} href="/docs/Reference/Reference-example.html">Reference Example</a></li>
</ul>
<p>Advance</p>
<ul>
<li><a {%if page.permalink == "Advance-example"%}class="active"{%endif%} href="/docs/Advance/Advance-example.html">Advance Example</a></li>
</ul>
<p>API</p>
<ul>
<li><a {%if page.permalink == "API-example"%}class="active"{%endif%} href="/docs/API/API-example.html">API Example</a></li>
</ul>
</aside>
<hr id="hr">
<main>
{{ page.content }}
</main>
</div>
</section>
</main>
<footer>
{% include "footer.liquid" %}
</footer>
</body>
</html>

View File

@ -1,17 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{% include "head.liquid" %}
</head>
<body>
<header>
{% include "header.liquid" %}
</header>
<main>
{{ content }}
</main>
<footer>
{% include "footer.liquid" %}
</footer>
</body>
</html>

BIN
artwork/logo.afdesign Normal file

Binary file not shown.

View File

@ -1,8 +0,0 @@
title: Blog::Blog Example
layout: blog.liquid
permalink: /blog/2018/Blog-example.html
---
## Basics Example
The words is comming.

View File

@ -1,13 +0,0 @@
title: Blog
layout: blog.liquid
permalink: /blog/index.html
---
## Documentation
Welcome to the Actix-web Blog!
If you feel like contributing further, you can fork that repo,
branch from `source`, and submit a pull request.
Contributions are always welcome!

View File

@ -1,15 +0,0 @@
title: Community
layout: default.liquid
permalink: /community.html
---
<section >
<div >
## Examples
The words is comming.
</div>
</section>

11
config.toml Normal file
View File

@ -0,0 +1,11 @@
baseurl = "https://actix.rs"
title = "actix"
languageCode = "en-us"
canonifyURLs = true
googleAnalytics = "UA-110322332-1"
pygmentsUseClasses = true
pygmentsCodeFences = true
[params]
actixVersion = "0.5"
actixWebVersion = "0.6"

3
content/_index.md Normal file
View File

@ -0,0 +1,3 @@
---
title: Welcome
---

19
content/code/_index.md Normal file
View File

@ -0,0 +1,19 @@
---
title: Code
description: Browse and download the sources
---
# Browse the Code
All of actix is open source and can be found on our github organization: [actix
on github](https://github.com/actix)
Here are the most important projects and the link to their github repositories
and related resources:
* [actix](https://github.com/actix/actix) ([issues](https://github.com/actix/actix/issues), [ci](https://travis-ci.org/actix/actix), [crate](https://crates.io/crates/actix), [api docs](https://docs.rs/actix))
* [actix-web](https://github.com/actix/actix-web) ([issues](https://github.com/actix/actix-web/issues), [ci](https://travis-ci.org/actix/actix-web), [crate](https://crates.io/crates/actix-web), [api docs](https://docs.rs/actix-web))
* [example code](https://github.com/actix/examples)
* [this website](https://github.com/actix/actix-website)
Actix is dual licensed under the MIT and Apache 2 licenses. [Read license text](license/).

201
content/code/license.md Normal file
View File

@ -0,0 +1,201 @@
---
title: License
---
Actix is dual licensed under MIT and Apache licenses.
# MIT License
Copyright (c) 2017 Nikolay Kim
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
# Apache License
_Version 2.0, January 2004_
http://www.apache.org/licenses/
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.

View File

@ -0,0 +1,18 @@
---
title: Community
description: The best things in life are to be shared
---
# Join us
Want to talk to others about questions? The [actix gitter
channel](https://gitter.im/actix/actix) is your best starting point.
If you think you found a bug it's best to go to the
[github](https://github.com/actix) directly. There are two repositories
that you might want to report against. [actix](https://github.com/actix/actix)
for issues with the actor framework or [actix-web](https://github.com/actix/actix-web)
for the high level web framework.
We're a welcoming community so don't be afraid to engage. Interactions
are [goverend by our code of conduct](coc/).

49
content/community/coc.md Normal file
View File

@ -0,0 +1,49 @@
---
title: Contributor Covenant Code of Conduct
description:
---
# Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
# Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
# Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
# Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
# Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at fafhrd91@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
# Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

24
content/docs/_index.md Normal file
View File

@ -0,0 +1,24 @@
---
title: Documentation
description: Guiding you through building apps with actix
menu:
docs_intro:
name: Welcome
weight: 10
---
# Welcome to Actix
Actix is your door to developing web services with Rust and this documentation
is going to guide you.
This documentation currently covers mostly the `actix-web` part which is the
high level web framework build on top of the `actix` actor framework and the
[Tokio](https://tokio.rs/) async IO system. This is the part that is from an
API stability point of view the most stable.
If you haven't used actix yet it's best to start with the [getting started
guide](getting-started/). If you already know your ways around and you need
specific information you might want to read the [actix-web API
docs](https://docs.rs/actix-web) (or the lower level [actix API
docs](https://docs.rs/actix)).

137
content/docs/application.md Normal file
View File

@ -0,0 +1,137 @@
---
title: Application
menu: docs_basics
weight: 140
---
# Writing an Application
`actix-web` provides various primitives to build web servers and applications with Rust.
It provides routing, middlewares, pre-processing of requests, post-processing of responses,
websocket protocol handling, multipart streams, etc.
All actix web servers are built around the `App` instance. It is used for
registering routes for resources and middlewares. It also stores application
state shared across all handlers within same application.
Applications act as a namespace for all routes, i.e all routes for a specific application
have the same url path prefix. The application prefix always contains a leading "/" slash.
If a supplied prefix does not contain leading slash, it is automatically inserted.
The prefix should consist of value path segments.
> For an application with prefix `/app`,
> any request with the paths `/app`, `/app/`, or `/app/test` would match;
> however, the path `/application` would not match.
```rust
fn index(req: HttpRequest) -> &'static str {
"Hello world!"
}
let app = App::new()
.prefix("/app")
.resource("/index.html", |r| r.method(Method::GET).f(index))
.finish()
```
In this example, an application with the `/app` prefix and a `index.html` resource
are created. This resource is available through the `/app/index.html` url.
> For more information, check the
> [URL Dispatch](./sec-6-url-dispatch.html#using-a-application-prefix-to-compose-applications) section.
Multiple applications can be served with one server:
```rust
use actix_web::{server, App, HttpResponse};
fn main() {
server::new(|| vec![
App::new()
.prefix("/app1")
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
App::new()
.prefix("/app2")
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
App::new()
.resource("/", |r| r.f(|r| HttpResponse::Ok())),
]);
}
```
All `/app1` requests route to the first application, `/app2` to the second, and all other to the third.
**Applications get matched based on registration order**. If an application with a more generic
prefix is registered before a less generic one, it would effectively block the less generic
application matching. For example, if an `App` with the prefix `"/"` was registered
as the first application, it would match all incoming requests.
## State
Application state is shared with all routes and resources within the same application.
When using an http actor,state can be accessed with the `HttpRequest::state()` as read-only,
but interior mutability with `RefCell` can be used to achieve state mutability.
State is also available for route matching predicates and middlewares.
Let's write a simple application that uses shared state. We are going to store request count
in the state:
```rust
use std::cell::Cell;
use actix_web::{App, HttpRequest, http};
// This struct represents state
struct AppState {
counter: Cell<usize>,
}
fn index(req: HttpRequest<AppState>) -> String {
let count = req.state().counter.get() + 1; // <- get count
req.state().counter.set(count); // <- store new count in state
format!("Request number: {}", count) // <- response with count
}
fn main() {
App::with_state(AppState{counter: Cell::new(0)})
.resource("/", |r| r.method(http::Method::GET).f(index))
.finish();
}
```
> **Note**: http server accepts an application factory rather than an application
> instance. Http server constructs an application instance for each thread, thus application state
> must be constructed multiple times. If you want to share state between different threads, a
> shared object should be used, e.g. `Arc`. Application state does not need to be `Send` and `Sync`,
> but the application factory must be `Send` + `Sync`.
## Combining applications with different state
Combining multiple applications with different state is possible as well.
[server::new](https://docs.rs/actix-web/*/actix_web/server/fn.new.html) requires the handler to have a single type.
This limitation can easily be overcome with the [App::boxed](https://docs.rs/actix-web/*/actix_web/struct.App.html#method.boxed) method, which converts an App into a boxed trait object.
```rust
use actix_web::{server, App, HttpResponse};
struct State1;
struct State2;
fn main() {
server::new(|| {
vec![
App::with_state(State1)
.prefix("/app1")
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
.boxed(),
App::with_state(State2)
.prefix("/app2")
.resource("/", |r| r.f(|r| HttpResponse::Ok()))
.boxed()
]
})
.bind("127.0.0.1:8080").unwrap()
.run()
}
```

View File

@ -0,0 +1,76 @@
---
title: Autoreloading
menu: docs_patterns
weight: 1000
---
# Auto-Reloading Development Server
During development it can be very handy to have cargo automatically recompile
the code on change. This can be accomplished by using
[cargo-watch](https://github.com/passcod/cargo-watch). Because an actix app
will typically bind to a port for listening for incoming HTTP requests it makes
sense to combine this with the [listenfd](https://crates.io/crates/listenfd)
crate and the [systemfd](https://github.com/mitsuhiko/systemfd) utility to
ensure the socket is kept open while the app is compiling and reloading.
`systemfd` will open a socket and pass it to `cargo-watch` which will watch for
changes and then invoke the compiler and run your actix app. The actix app
will then use `listenfd` to pick up the socket that `systemfd` opened.
## Binaries Necessary
For an automatic reloading experience you need to install `cargo-watch` and
`systemfd`. Both are written in rust and can be installed with `cargo install`:
```
cargo install systemfd cargo-watch
```
## Code Changes
Additionally you need to slightly modify your actix app so that it can pick up
an external socket opened by `systemfd`. Add the listenfd dependency to your
app:
```ini
[dependencices]
listenfd = "0.3"
```
Then modify your server code to only invoke `bind` as a fallback:
```rust
extern crate listenfd;
use listenfd::ListenFd;
use actix_web::{server, App, HttpRequest, Responder};
fn index(_req: HttpRequest) -> impl Responder {
"Hello World!"
}
fn main() {
let mut listenfd = ListenFd::from_env();
let mut server = server::new(|| {
App::new()
.resource("/", |r| r.f(index))
});
server = if let Some(l) = listenfd.take_tcp_listener(0).unwrap() {
server.listen(l)
} else {
server.bind("127.0.0.1:3000").unwrap()
};
server.run();
}
```
## Running the Server
To now run the development server invoke this command:
```
systemfd -s http::3000 -- cargo watch -x run
```

132
content/docs/databases.md Normal file
View File

@ -0,0 +1,132 @@
---
title: Databases
menu: docs_patterns
weight: 1010
---
# Diesel
At the moment, Diesel 1.0 does not support asynchronous operations,
but it possible to use the `actix` synchronous actor system as a database interface api.
Technically, sync actors are worker style actors. Multiple sync actors
can be run in parallel and process messages from same queue. Sync actors work in mpsc mode.
Let's create a simple database api that can insert a new user row into a SQLite table.
We must define a sync actor and a connection that this actor will use. The same approach
can be used for other databases.
```rust
use actix::prelude::*;
struct DbExecutor(SqliteConnection);
impl Actor for DbExecutor {
type Context = SyncContext<Self>;
}
```
This is the definition of our actor. Now, we must define the *create user* message and response.
```rust
struct CreateUser {
name: String,
}
impl Message for CreateUser {
type Result = Result<User, Error>;
}
```
We can send a `CreateUser` message to the `DbExecutor` actor, and as a result, we will receive a
`User` model instance. Next, we must define the handler implementation for this message.
```rust
impl Handler<CreateUser> for DbExecutor {
type Result = Result<User, Error>;
fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result
{
use self::schema::users::dsl::*;
// Create insertion model
let uuid = format!("{}", uuid::Uuid::new_v4());
let new_user = models::NewUser {
id: &uuid,
name: &msg.name,
};
// normal diesel operations
diesel::insert_into(users)
.values(&new_user)
.execute(&self.0)
.expect("Error inserting person");
let mut items = users
.filter(id.eq(&uuid))
.load::<models::User>(&self.0)
.expect("Error loading person");
Ok(items.pop().unwrap())
}
}
```
That's it! Now, we can use the *DbExecutor* actor from any http handler or middleware.
All we need is to start *DbExecutor* actors and store the address in a state where http handler
can access it.
```rust
/// This is state where we will store *DbExecutor* address.
struct State {
db: Addr<Syn, DbExecutor>,
}
fn main() {
let sys = actix::System::new("diesel-example");
// Start 3 parallel db executors
let addr = SyncArbiter::start(3, || {
DbExecutor(SqliteConnection::establish("test.db").unwrap())
});
// Start http server
HttpServer::new(move || {
App::with_state(State{db: addr.clone()})
.resource("/{name}", |r| r.method(Method::GET).a(index))})
.bind("127.0.0.1:8080").unwrap()
.start().unwrap();
println!("Started http server: 127.0.0.1:8080");
let _ = sys.run();
}
```
We will use the address in a request handler. The handle returns a future object;
thus, we receive the message response asynchronously.
`Route::a()` must be used for async handler registration.
```rust
/// Async handler
fn index(req: HttpRequest<State>) -> Box<Future<Item=HttpResponse, Error=Error>> {
let name = &req.match_info()["name"];
// Send message to `DbExecutor` actor
req.state().db.send(CreateUser{name: name.to_owned()})
.from_err()
.and_then(|res| {
match res {
Ok(user) => Ok(HttpResponse::Ok().json(user)),
Err(_) => Ok(HttpResponse::InternalServerError().into())
}
})
.responder()
}
```
> A full example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/diesel/).
> More information on sync actors can be found in the
> [actix documentation](https://docs.rs/actix/0.5.0/actix/sync/index.html).

238
content/docs/errors.md Normal file
View File

@ -0,0 +1,238 @@
---
title: Errors
menu: docs_advanced
weight: 180
---
# Errors
Actix uses its own [`actix_web::error::Error`][actixerror] type and
[`actix_web::error::ResponseError`][responseerror] trait for error handling
from web handlers.
If a handler returns an `Error` (referring to the [general Rust trait
`std::error::Error`][stderror]) in a `Result` that also implements the
`ResponseError` trait, actix will render that error as an HTTP response.
`ResponseError` has a single function called `error_response()` that returns
`HttpResponse`:
```rust
pub trait ResponseError: Fail {
fn error_response(&self) -> HttpResponse {
HttpResponse::new(StatusCode::INTERNAL_SERVER_ERROR)
}
}
```
A `Responder` coerces compatible `Result`s into HTTP responses:
```rust
impl<T: Responder, E: Into<Error>> Responder for Result<T, E>
```
`Error` in the code above is actix's error definition, and any errors that
implement `ResponseError` can be converted to one automatically.
Actix-web provides `ResponseError` implementations for some common non-actix
errors. For example, if a handler responds with an `io::Error`, that error is
converted into an `HttpInternalServerError`:
```rust
use std::io;
fn index(req: HttpRequest) -> io::Result<fs::NamedFile> {
Ok(fs::NamedFile::open("static/index.html")?)
}
```
See [the actix-web API documentation](responseerrorimpls) for a full list of
foreign implementations for `ResponseError`.
## An example of a custom error response
Here's an example implementation for `ResponseError`:
```rust
use actix_web::*;
#[derive(Fail, Debug)]
#[fail(display="my error")]
struct MyError {
name: &'static str
}
// Use default implementation for `error_response()` method
impl error::ResponseError for MyError {}
fn index(req: HttpRequest) -> Result<&'static str, MyError> {
Err(MyError{name: "test"})
}
```
`ResponseError` has a default implementation for `error_response()` that will
render a *500* (internal server error), and that's what will happen when the
`index` handler executes above.
Override `error_response()` to produce more useful results:
```rust
#[macro_use] extern crate failure;
use actix_web::{App, HttpRequest, HttpResponse, http, error};
#[derive(Fail, Debug)]
enum MyError {
#[fail(display="internal error")]
InternalError,
#[fail(display="bad request")]
BadClientData,
#[fail(display="timeout")]
Timeout,
}
impl error::ResponseError for MyError {
fn error_response(&self) -> HttpResponse {
match *self {
MyError::InternalError => HttpResponse::new(
http::StatusCode::INTERNAL_SERVER_ERROR),
MyError::BadClientData => HttpResponse::new(
http::StatusCode::BAD_REQUEST),
MyError::Timeout => HttpResponse::new(
http::StatusCode::GATEWAY_TIMEOUT),
}
}
}
fn index(req: HttpRequest) -> Result<&'static str, MyError> {
Err(MyError::BadClientData)
}
```
# Error helpers
Actix provides a set of error helper functions that are useful for generating
specific HTTP error codes from other errors. Here we convert `MyError`, which
doesn't implement the `ResponseError` trait, to a *400* (bad request) using
`map_err`:
```rust
# extern crate actix_web;
use actix_web::*;
#[derive(Debug)]
struct MyError {
name: &'static str
}
fn index(req: HttpRequest) -> Result<&'static str> {
let result: Result<&'static str, MyError> = Err(MyError{name: "test"});
Ok(result.map_err(|e| error::ErrorBadRequest(e.name))?)
}
```
See the [API documentation for actix-web's `error` module][errorhelpers] for a
full list of available error helpers.
# Compatibility with failure
Actix-web provides automatic compatibility with the [failure] library so that
errors deriving `fail` will be converted automatically to an actix error. Keep
in that those errors will render with the default *500* status code unless you
also provide your own `error_response()` implementation for them.
# Error logging
Actix logs all errors at the `WARN` log level. If an application's log level is
set to `DEBUG` and `RUST_BACKTRACE` is enabled, the backtrace is also logged.
These are configurable with environmental variables:
```
>> RUST_BACKTRACE=1 RUST_LOG=actix_web=debug cargo run
```
The `Error` type uses the cause's error backtrace if available. If the
underlying failure does not provide a backtrace, a new backtrace is constructed
pointing to the point where the conversion occurred (rather than the origin of
the error).
# Recommended practices in error handling
It might be useful to think about dividing the errors an application produces
into two broad groups: those which are intended to be be user-facing, and those
which are not.
An example of the former is that I might use failure to specify a `UserError`
enum which encapsulates a `ValidationError` to return whenever a user sends bad
input:
```rust
#[macro_use] extern crate failure;
use actix_web::{HttpResponse, http, error};
#[derive(Fail, Debug)]
enum UserError {
#[fail(display="Validation error on field: {}", field)]
ValidationError {
field: String,
}
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::ValidationError { .. } => HttpResponse::new(
http::StatusCode::BAD_REQUEST),
}
}
}
```
This will behave exactly as intended because the error message defined with
`display` is written with the explicit intent to be read by a user.
However, sending back an error's message isn't desirable for all errors --
there are many failures that occur in a server environment where we'd probably
want the specifics to be hidden from the user. For example, if a database goes
down and client libraries start producing connect timeout errors, or if an HTML
template was improperly formatted and errors when rendered. In these cases, it
might be preferable to map the errors to a generic error suitable for user
consumption.
Here's an example that maps an internal error to a user-facing `InternalError`
with a custom message:
```rust
#[macro_use] extern crate failure;
use actix_web::{App, HttpRequest, HttpResponse, http, error, fs};
#[derive(Fail, Debug)]
enum UserError {
#[fail(display="An internal error occurred. Please try again later.")]
InternalError,
}
impl error::ResponseError for UserError {
fn error_response(&self) -> HttpResponse {
match *self {
UserError::InternalError => HttpResponse::new(
http::StatusCode::INTERNAL_SERVER_ERROR),
}
}
}
fn index(_req: HttpRequest) -> Result<&'static str, UserError> {
fs::NamedFile::open("static/index.html").map_err(|_e| UserError::InternalError)?;
Ok("success!")
}
```
By dividing errors into those which are user facing and those which are not, we
can ensure that we don't accidentally expose users to errors thrown by
application internals which they weren't meant to see.
[actixerror]: ../../actix-web/actix_web/error/struct.Error.html
[errorhelpers]: ../../actix-web/actix_web/error/index.html#functions
[failure]: https://github.com/rust-lang-nursery/failure
[responseerror]: ../../actix-web/actix_web/error/trait.ResponseError.html
[responseerrorimpls]: ../../actix-web/actix_web/error/trait.ResponseError.html#foreign-impls
[stderror]: https://doc.rust-lang.org/std/error/trait.Error.html

227
content/docs/extractors.md Normal file
View File

@ -0,0 +1,227 @@
---
title: Extractors
menu: docs_basics
weight: 170
---
# Type-safe information extraction
Actix provides facility for type-safe request information extraction. By default,
actix provides several extractor implementations.
# Path
[*Path*](../../actix-web/actix_web/struct.Path.html) provides information that can
be extracted from the Request's path. You can deserialize any variable
segment from the path.
For instance, for resource that registered for `/users/{userid}/{friend}` path
two segments could be deserialized, `userid` and `friend`. This segments
could be extracted to a `tuple`, i.e. `Path<(u32, String)>` or structure
that implementd `Deserialize` trait from *serde* crate.
```rust
use actix_web::{App, Path, Result, http};
/// extract path info from "/users/{userid}/{friend}" url
/// {userid} - - deserializes to a u32
/// {friend} - deserializes to a String
fn index(info: Path<(u32, String)>) -> Result<String> {
Ok(format!("Welcome {}! {}", info.1, info.0))
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
Remember! handler function that uses extractors has to be registered with
[*Route::with()*](../../actix-web/actix_web/dev/struct.Route.html#method.with) method.
It is also possible to extract path information to a specific type that
implements `Deserialize` trait from *serde*. Here is equivalent example that uses *serde*
instead of *tuple* type.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http};
#[derive(Deserialize)]
struct Info {
userid: u32,
friend: String,
}
/// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.friend))
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
# Query
Same can be done with the request's query.
[*Query*](../../actix-web/actix_web/struct.Query.html)
type provides extraction functionality. Underneath it uses *serde_urlencoded* crate.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Query, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
// this handler get called only if the request's query contains `username` field
fn index(info: Query<Info>) -> String {
format!("Welcome {}!", info.username)
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
# Json
[*Json*](../../actix-web/actix_web/struct.Json.html) allows to deserialize
request body to a struct. To extract typed information from request's body,
the type `T` must implement the `Deserialize` trait from *serde*.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, Result, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
}
```
Some extractors provide a way to configure extraction process. Json extractor
[*JsonConfig*](../../actix-web/actix_web/dev/struct.JsonConfig.html) type for configuration.
When you register handler `Route::with()` returns configuration instance. In case of
*Json* extractor it returns *JsonConfig*. You can configure max size of the json
payload and custom error handler function.
Following example limits size of the payload to 4kb and uses custom error hander.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, HttpResponse, Result, http, error};
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body, max payload size is 4kb
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html", |r| {
r.method(http::Method::POST)
.with(index)
.limit(4096) // <- change json extractor configuration
.error_handler(|err, req| { // <- create custom error response
error::InternalError::from_response(
err, HttpResponse::Conflict().finish()).into()
});
});
}
```
# Form
At the moment only url-encoded forms are supported. Url encoded body
could be extracted to a specific type. This type must implement
the `Deserialize` trait from *serde* crate.
[*FormConfig*](../../actix-web/actix_web/dev/struct.FormConfig.html) allows
to configure extraction process.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Form, Result};
#[derive(Deserialize)]
struct FormData {
username: String,
}
/// extract form data using serde
/// this handler get called only if content type is *x-www-form-urlencoded*
/// and content of the request could be deserialized to a `FormData` struct
fn index(form: Form<FormData>) -> Result<String> {
Ok(format!("Welcome {}!", form.username))
}
# fn main() {}
```
# Multiple extractors
Actix provides extractor implementation for tuples (up to 10 elements)
which elements provide `FromRequest` impl.
For example we can use path extractor and query extractor at the same time.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Query, Path, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
fn index(data: (Path<(u32, String)>, Query<Info>)) -> String {
let (path, query) = data;
format!("Welcome {}!", query.username)
}
fn main() {
let app = App::new().resource(
"/users/{userid}/{friend}", // <- define path parameters
|r| r.method(http::Method::GET).with(index)); // <- use `with` extractor
}
```
# Other
Actix also provides several other extractors:
* [*State*](../../actix-web/actix_web/struct.State.html) - If you need
access to an application state. This is similar to a `HttpRequest::state()`.
* *HttpRequest* - *HttpRequest* itself is an extractor which returns self.
In case if you need access to the request.
* *String* - You can convert request's payload to a *String*.
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example-1)
is available in doc strings.
* *bytes::Bytes* - You can convert request's payload to a *Bytes*.
[*Example*](../../actix-web/actix_web/trait.FromRequest.html#example)
is available in doc strings.

View File

@ -0,0 +1,67 @@
---
title: Getting Started
menu: docs_basics
weight: 130
---
# Getting Started
Lets write our first actix web application!
## Hello, world!
Start by creating a new binary-based Cargo project and changing into the new directory:
```bash
cargo new hello-world --bin
cd hello-world
```
Now, add `actix-web` as dependencies of your project by ensuring your `Cargo.toml`
contains the following:
```ini
[dependencies]
actix-web = "{{< actix-version "actix-web" >}}"
```
In order to implement a web server, we first need to create a request handler.
A request handler is a function that accepts an `HttpRequest` instance as its only parameter
and returns a type that can be converted into `HttpResponse`:
Filename: `src/main.rs`
```rust
extern crate actix_web;
use actix_web::{HttpRequest, Responder, App, server};
fn index(req: _HttpRequest) -> Responder {
"Hello world!"
}
```
Next, create an `Application` instance and register the request handler with
the application's `resource` on a particular *HTTP method* and *path* and
after that, the application instance can be used with `HttpServer` to listen
for incoming connections. The server accepts a function that should return an
`HttpHandler` instance. For simplicity `server::new` could be used, this
function is shortcut for `HttpServer::new`:
```rust
fn main() {
server::new(|| {
App::new()
.resource("/", |r| r.f(index))
})
.bind("127.0.0.1:8088").unwrap()
.run();
}
```
That's it! Now, compile and run the program with `cargo run`.
Head over to ``http://localhost:8088/`` to see the results.
If you want you can have an automatic reloading server during development
that recompiles on demand. To see how this can be accomplished have a look
at the [autoreload pattern](../autoreload/).

292
content/docs/handlers.md Normal file
View File

@ -0,0 +1,292 @@
---
title: Handlers
menu: docs_basics
weight: 160
---
# Request Handlers
A request handler can be any object that implements
[*Handler*](../../actix-web/actix_web/dev/trait.Handler.html) trait.
Request handling happens in two stages. First the handler object is called,
returning any object that implements the
[*Responder*](../../actix-web/actix_web/trait.Responder.html#foreign-impls) trait.
Then, `respond_to()` is called on the returned object, converting itself to a `AsyncResult` or `Error`.
By default actix provides `Responder` implementations for some standard types,
such as `&'static str`, `String`, etc.
> For a complete list of implementations, check
> [*Responder documentation*](../../actix-web/actix_web/trait.Responder.html#foreign-impls).
Examples of valid handlers:
```rust
fn index(req: HttpRequest) -> &'static str {
"Hello world!"
}
```
```rust
fn index(req: HttpRequest) -> String {
"Hello world!".to_owned()
}
```
You can also change the signature to return `impl Responder` which works well if more
complex types are involved.
```rust
fn index(req: HttpRequest) -> impl Responder {
Bytes::from_static("Hello world!")
}
```
```rust,ignore
fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
...
}
```
*Handler* trait is generic over *S*, which defines the application state's type.
Application state is accessible from the handler with the `HttpRequest::state()` method;
however, state is accessible as a read-only reference. If you need mutable access to state,
it must be implemented.
> **Note**: Alternatively, the handler can mutably access its own state because the `handle` method takes
> mutable reference to *self*. **Beware**, actix creates multiple copies
> of the application state and the handlers, unique for each thread. If you run your
> application in several threads, actix will create the same amount as number of threads
> of application state objects and handler objects.
Here is an example of a handler that stores the number of processed requests:
```rust
use actix_web::{App, HttpRequest, HttpResponse, dev::Handler};
struct MyHandler(usize);
impl<S> Handler<S> for MyHandler {
type Result = HttpResponse;
/// Handle request
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
self.0 += 1;
HttpResponse::Ok().into()
}
}
```
Although this handler will work, `self.0` will be different depending on the number of threads and
number of requests processed per thread. A proper implementation would use `Arc` and `AtomicUsize`.
```rust
use actix_web::{server, App, HttpRequest, HttpResponse, dev::Handler};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
struct MyHandler(Arc<AtomicUsize>);
impl<S> Handler<S> for MyHandler {
type Result = HttpResponse;
/// Handle request
fn handle(&mut self, req: HttpRequest<S>) -> Self::Result {
self.0.fetch_add(1, Ordering::Relaxed);
HttpResponse::Ok().into()
}
}
fn main() {
let sys = actix::System::new("example");
let inc = Arc::new(AtomicUsize::new(0));
server::new(
move || {
let cloned = inc.clone();
App::new()
.resource("/", move |r| r.h(MyHandler(cloned)))
})
.bind("127.0.0.1:8088").unwrap()
.start();
println!("Started http server: 127.0.0.1:8088");
let _ = sys.run();
}
```
> Be careful with synchronization primitives like `Mutex` or `RwLock`. The `actix-web` framework
> handles requests asynchronously. By blocking thread execution, all concurrent
> request handling processes would block. If you need to share or update some state
> from multiple threads, consider using the [actix](https://actix.github.io/actix/actix/) actor system.
## Response with custom type
To return a custom type directly from a handler function, the type needs to implement the `Responder` trait.
Let's create a response for a custom type that serializes to an `application/json` response:
```rust
# extern crate actix;
# extern crate actix_web;
extern crate serde;
extern crate serde_json;
#[macro_use] extern crate serde_derive;
use actix_web::{server, App, HttpRequest, HttpResponse, Error, Responder, http};
#[derive(Serialize)]
struct MyObj {
name: &'static str,
}
/// Responder
impl Responder for MyObj {
type Item = HttpResponse;
type Error = Error;
fn respond_to<S>(self, req: &HttpRequest<S>) -> Result<HttpResponse, Error> {
let body = serde_json::to_string(&self)?;
// Create response and set content type
Ok(HttpResponse::Ok()
.content_type("application/json")
.body(body))
}
}
fn index(req: HttpRequest) -> impl Responder {
MyObj { name: "user" }
}
fn main() {
let sys = actix::System::new("example");
server::new(
|| App::new()
.resource("/", |r| r.method(http::Method::GET).f(index)))
.bind("127.0.0.1:8088").unwrap()
.start();
println!("Started http server: 127.0.0.1:8088");
let _ = sys.run();
}
```
## Async handlers
There are two different types of async handlers. Response objects can be generated asynchronously
or more precisely, any type that implements the [*Responder*](../../actix-web/actix_web/trait.Responder.html) trait.
In this case, the handler must return a `Future` object that resolves to the *Responder* type, i.e:
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
use futures::future::{Future, result};
fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!"))))
.responder()
}
fn index2(req: HttpRequest) -> Box<Future<Item=&'static str, Error=Error>> {
result(Ok("Welcome!"))
.responder()
}
fn main() {
App::new()
.resource("/async", |r| r.route().a(index))
.resource("/", |r| r.route().a(index2))
.finish();
}
```
Or the response body can be generated asynchronously. In this case, body
must implement the stream trait `Stream<Item=Bytes, Error=Error>`, i.e:
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
fn index(req: HttpRequest) -> HttpResponse {
let body = once(Ok(Bytes::from_static(b"test")));
HttpResponse::Ok()
.content_type("application/json")
.body(Body::Streaming(Box::new(body)))
}
fn main() {
App::new()
.resource("/async", |r| r.f(index))
.finish();
}
```
Both methods can be combined. (i.e Async response with streaming body)
It is possible to return a `Result` where the `Result::Item` type can be `Future`.
In this example, the `index` handler can return an error immediately or return a
future that resolves to a `HttpResponse`.
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
use futures::future::{Future, result};
fn index(req: HttpRequest) -> Result<Box<Future<Item=HttpResponse, Error=Error>>, Error> {
if is_error() {
Err(error::ErrorBadRequest("bad request"))
} else {
Ok(Box::new(
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!"))))))
}
}
```
## Different return types (Either)
Sometimes, you need to return different types of responses. For example,
you can error check and return errors, return async responses, or any result that requires two different types.
For this case, the [*Either*](../../actix-web/actix_web/enum.Either.html) type can be used.
`Either` allows combining two different responder types into a single type.
```rust
use futures::future::{Future, result};
use actix_web::{Either, Error, HttpResponse};
type RegisterResult = Either<HttpResponse, Box<Future<Item=HttpResponse, Error=Error>>>;
fn index(req: HttpRequest) -> impl Responder {
if is_a_variant() { // <- choose variant A
Either::A(
HttpResponse::BadRequest().body("Bad data"))
} else {
Either::B( // <- variant B
result(Ok(HttpResponse::Ok()
.content_type("text/html")
.body(format!("Hello!")))).responder())
}
}
```
## Tokio core handle
Any `actix-web` handler runs within a properly configured
[actix system](https://actix.github.io/actix/actix/struct.System.html)
and [arbiter](https://actix.github.io/actix/actix/struct.Arbiter.html).
You can always get access to the tokio handle via the
[Arbiter::handle()](https://actix.github.io/actix/actix/struct.Arbiter.html#method.handle)
method.

50
content/docs/http2.md Normal file
View File

@ -0,0 +1,50 @@
---
title: HTTP/2.0
menu: docs_proto
weight: 250
---
`actix-web` automatically upgrades connections to *HTTP/2.0* if possible.
# Negotiation
*HTTP/2.0* protocol over tls without prior knowledge requires
[tls alpn](https://tools.ietf.org/html/rfc7301).
> Currently, only `rust-openssl` has support.
`alpn` negotiation requires enabling the feature. When enabled, `HttpServer` provides the
[serve_tls](../../actix-web/actix_web/server/struct.HttpServer.html#method.serve_tls) method.
```toml
[dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] }
openssl = { version = "0.10", features = ["v110"] }
```
```rust
use std::fs::File;
use actix_web::*;
use openssl::ssl::{SslMethod, SslAcceptor, SslFiletype};
fn main() {
// load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
HttpServer::new(
|| App::new()
.resource("/index.html", |r| r.f(index)))
.bind("127.0.0.1:8080").unwrap();
.serve_ssl(builder).unwrap();
}
```
Upgrades to *HTTP/2.0* schema described in
[rfc section 3.2](https://http2.github.io/http2-spec/#rfc.section.3.2) is not supported.
Starting *HTTP/2* with prior knowledge is supported for both clear text connection
and tls connection. [rfc section 3.4](https://http2.github.io/http2-spec/#rfc.section.3.4)
> Check out [examples/tls](https://github.com/actix/examples/tree/master/tls)
> for a concrete example.

View File

@ -0,0 +1,52 @@
---
title: Installation
menu: docs_intro
weight: 110
---
# Installing Rust
Since `actix-web` is a Rust framework you will need Rust to get started with it.
If you don't have it yet we recommend you use `rustup` to manage your Rust
installation. The [official rust
guide](https://doc.rust-lang.org/book/second-edition/ch01-01-installation.html)
has a wonderful section on getting started.
We currently require at least Rust 1.24 so make sure you run `rustup update`
to have the latest and greatest Rust version available. In particular this
guide will assume that you actually run Rust 1.26 or later.
# Installing `actix-web`
Thank's to Rust's `cargo` package manger you won't need to explicitly install
`actix-web`. Just depend on it and you're ready to go. For the unlikely
case that you want to use the development version of actix-web you can
depend on the git repository directly.
Release version:
```ini
[dependencies]
actix-web = "{{< actix-version "actix-web" >}}"
```
Development version:
```ini
[dependencies]
actix-web = { git = "https://github.com/actix/actix-web }
```
# Diving In
There are two paths you can take here. You can follow the guide along or if
you are very impatient you might want to have a look at our
[extensive example repository](https://github.com/actix/examples) and run the
included examples. Here for instance is how you run the included `basics`
example:
```
git clone https://github.com/actix/examples
cd examples/basics
cargo run
```

250
content/docs/middleware.md Normal file
View File

@ -0,0 +1,250 @@
---
title: Middlewares
menu: docs_advanced
weight: 220
---
# Middleware
Actix's middleware system allows us to add additional behavior to request/response processing.
Middleware can hook into an incoming request process, enabling us to modify requests
as well as halt request processing to return a response early.
Middleware can also hook into response processing.
Typically, middleware is involved in the following actions:
* Pre-process the Request
* Post-process a Response
* Modify application state
* Access external services (redis, logging, sessions)
Middleware is registered for each application and executed in same order as
registration. In general, a *middleware* is a type that implements the
[*Middleware trait*](../../actix-web/actix_web/middleware/trait.Middleware.html).
Each method in this trait has a default implementation. Each method can return
a result immediately or a *future* object.
The following demonstrates using middleware to add request and response headers:
```rust
use http::{header, HttpTryFrom};
use actix_web::{App, HttpRequest, HttpResponse, Result};
use actix_web::middleware::{Middleware, Started, Response};
struct Headers; // <- Our middleware
/// Middleware implementation, middlewares are generic over application state,
/// so you can access state with `HttpRequest::state()` method.
impl<S> Middleware<S> for Headers {
/// Method is called when request is ready. It may return
/// future, which should resolve before next middleware get called.
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> {
req.headers_mut().insert(
header::CONTENT_TYPE, header::HeaderValue::from_static("text/plain"));
Ok(Started::Done)
}
/// Method is called when handler returns response,
/// but before sending http message to peer.
fn response(&self, req: &mut HttpRequest<S>, mut resp: HttpResponse)
-> Result<Response>
{
resp.headers_mut().insert(
header::HeaderName::try_from("X-VERSION").unwrap(),
header::HeaderValue::from_static("0.2"));
Ok(Response::Done(resp))
}
}
fn main() {
App::new()
// Register middleware, this method can be called multiple times
.middleware(Headers)
.resource("/", |r| r.f(|_| HttpResponse::Ok()));
}
```
> Actix provides several useful middlewares, such as *logging*, *user sessions*, etc.
# Logging
Logging is implemented as a middleware.
It is common to register a logging middleware as the first middleware for the application.
Logging middleware must be registered for each application.
The `Logger` middleware uses the standard log crate to log information. You should enable logger
for *actix_web* package to see access log ([env_logger](https://docs.rs/env_logger/*/env_logger/)
or similar).
## Usage
Create `Logger` middleware with the specified `format`.
Default `Logger` can be created with `default` method, it uses the default format:
```ignore
%a %t "%r" %s %b "%{Referer}i" "%{User-Agent}i" %T
```
```rust
extern crate env_logger;
use actix_web::App;
use actix_web::middleware::Logger;
fn main() {
std::env::set_var("RUST_LOG", "actix_web=info");
env_logger::init();
App::new()
.middleware(Logger::default())
.middleware(Logger::new("%a %{User-Agent}i"))
.finish();
}
```
The following is an example of the default logging format:
```
INFO:actix_web::middleware::logger: 127.0.0.1:59934 [02/Dec/2017:00:21:43 -0800] "GET / HTTP/1.1" 302 0 "-" "curl/7.54.0" 0.000397
INFO:actix_web::middleware::logger: 127.0.0.1:59947 [02/Dec/2017:00:22:40 -0800] "GET /index.html HTTP/1.1" 200 0 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0" 0.000646
```
## Format
`%%` The percent sign
`%a` Remote IP-address (IP-address of proxy if using reverse proxy)
`%t` Time when the request was started to process
`%P` The process ID of the child that serviced the request
`%r` First line of request
`%s` Response status code
`%b` Size of response in bytes, including HTTP headers
`%T` Time taken to serve the request, in seconds with floating fraction in .06f format
`%D` Time taken to serve the request, in milliseconds
`%{FOO}i` request.headers['FOO']
`%{FOO}o` response.headers['FOO']
`%{FOO}e` os.environ['FOO']
## Default headers
To set default response headers, the `DefaultHeaders` middleware can be used. The
*DefaultHeaders* middleware does not set the header if response headers already contain
a specified header.
```rust
use actix_web::{http, middleware, App, HttpResponse};
fn main() {
let app = App::new()
.middleware(
middleware::DefaultHeaders::new()
.header("X-Version", "0.2"))
.resource("/test", |r| {
r.method(http::Method::GET).f(|req| HttpResponse::Ok());
r.method(http::Method::HEAD).f(|req| HttpResponse::MethodNotAllowed());
})
.finish();
}
```
## User sessions
Actix provides a general solution for session management. The
[**SessionStorage**](../../actix-web/actix_web/middleware/session/struct.SessionStorage.html) middleware can be
used with different backend types to store session data in different backends.
> By default, only cookie session backend is implemented. Other backend implementations
> can be added.
[**CookieSessionBackend**](../../actix-web/actix_web/middleware/session/struct.CookieSessionBackend.html)
uses cookies as session storage. `CookieSessionBackend` creates sessions which
are limited to storing fewer than 4000 bytes of data, as the payload must fit into a
single cookie. An internal server error is generated if a session contains more than 4000 bytes.
A cookie may have a security policy of *signed* or *private*. Each has a respective `CookieSessionBackend` constructor.
A *signed* cookie may be viewed but not modified by the client. A *private* cookie may neither be viewed nor modified by the client.
The constructors take a key as an argument. This is the private key for cookie session - when this value is changed, all session data is lost.
In general, you create a
`SessionStorage` middleware and initialize it with specific backend implementation,
such as a `CookieSessionBackend`. To access session data,
[*HttpRequest::session()*](../../actix-web/actix_web/middleware/session/trait.RequestSession.html#tymethod.session)
must be used. This method returns a
[*Session*](../../actix-web/actix_web/middleware/session/struct.Session.html) object, which allows us to get or set
session data.
```rust
use actix_web::{server, App, HttpRequest, Result};
use actix_web::middleware::session::{RequestSession, SessionStorage, CookieSessionBackend};
fn index(mut req: HttpRequest) -> Result<&'static str> {
// access session data
if let Some(count) = req.session().get::<i32>("counter")? {
println!("SESSION value: {}", count);
req.session().set("counter", count+1)?;
} else {
req.session().set("counter", 1)?;
}
Ok("Welcome!")
}
fn main() {
let sys = actix::System::new("basic-example");
server::new(
|| App::new().middleware(
SessionStorage::new(
CookieSessionBackend::signed(&[0; 32])
.secure(false)
)))
.bind("127.0.0.1:59880").unwrap()
.start();
let _ = sys.run();
}
```
# Error handlers
`ErrorHandlers` middleware allows us to provide custom handlers for responses.
You can use the `ErrorHandlers::handler()` method to register a custom error handler
for a specific status code. You can modify an existing response or create a completly new
one. The error handler can return a response immediately or return a future that resolves
into a response.
```rust
use actix_web::{
App, HttpRequest, HttpResponse, Result,
http, middleware::Response, middleware::ErrorHandlers};
fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
let mut builder = resp.into_builder();
builder.header(http::header::CONTENT_TYPE, "application/json");
Ok(Response::Done(builder.into()))
}
fn main() {
let app = App::new()
.middleware(
ErrorHandlers::new()
.handler(http::StatusCode::INTERNAL_SERVER_ERROR, render_500))
.resource("/test", |r| {
r.method(http::Method::GET).f(|_| HttpResponse::Ok());
r.method(http::Method::HEAD).f(|_| HttpResponse::MethodNotAllowed());
})
.finish();
}
```

207
content/docs/request.md Normal file
View File

@ -0,0 +1,207 @@
---
title: Requests
menu: docs_advanced
weight: 200
---
# Content Encoding
Actix automatically *decompresses* payloads. The following codecs are supported:
* Brotli
* Gzip
* Deflate
* Identity
If request headers contain a `Content-Encoding` header, the request payload is decompressed
according to the header value. Multiple codecs are not supported,
i.e: `Content-Encoding: br, gzip`.
# JSON Request
There are several options for json body deserialization.
The first option is to use *Json* extractor. First, you define a handler function
that accepts `Json<T>` as a parameter, then, you use the `.with()` method for registering
this handler. It is also possible to accept arbitrary valid json object by
using `serde_json::Value` as a type `T`.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Json, Result, http};
#[derive(Deserialize)]
struct Info {
username: String,
}
/// extract `Info` using serde
fn index(info: Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new().resource(
"/index.html",
|r| r.method(http::Method::POST).with(index)); // <- use `with` extractor
}
```
Another option is to use *HttpRequest::json()*. This method returns a
[*JsonBody*](../../actix-web/actix_web/dev/struct.JsonBody.html) object which resolves into
the deserialized value.
```rust
#[derive(Debug, Serialize, Deserialize)]
struct MyObj {
name: String,
number: i32,
}
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.json().from_err()
.and_then(|val: MyObj| {
println!("model: {:?}", val);
Ok(HttpResponse::Ok().json(val)) // <- send response
})
.responder()
}
```
You may also manually load the payload into memory and then deserialize it.
In the following example, we will deserialize a *MyObj* struct. We need to load the request
body first and then deserialize the json into an object.
```rust
extern crate serde_json;
use futures::{Future, Stream};
#[derive(Serialize, Deserialize)]
struct MyObj {name: String, number: i32}
fn index(req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
// `concat2` will asynchronously read each chunk of the request body and
// return a single, concatenated, chunk
req.concat2()
// `Future::from_err` acts like `?` in that it coerces the error type from
// the future into the final error type
.from_err()
// `Future::and_then` can be used to merge an asynchronous workflow with a
// synchronous workflow
.and_then(|body| {
let obj = serde_json::from_slice::<MyObj>(&body)?;
Ok(HttpResponse::Ok().json(obj))
})
.responder()
}
```
> A complete example for both options is available in
> [examples directory](https://github.com/actix/examples/tree/master/json/).
# Chunked transfer encoding
Actix automatically decodes *chunked* encoding. `HttpRequest::payload()` already contains
the decoded byte stream. If the request payload is compressed with one of the supported
compression codecs (br, gzip, deflate), then the byte stream is decompressed.
# Multipart body
Actix provides multipart stream support.
[*Multipart*](../../actix-web/actix_web/multipart/struct.Multipart.html) is implemented as
a stream of multipart items. Each item can be a
[*Field*](../../actix-web/actix_web/multipart/struct.Field.html) or a nested
*Multipart* stream.`HttpResponse::multipart()` returns the *Multipart* stream
for the current request.
The following demonstrates multipart stream handling for a simple form:
```rust
use actix_web::*;
fn index(req: HttpRequest) -> Box<Future<...>> {
// get multipart and iterate over multipart items
req.multipart()
.and_then(|item| {
match item {
multipart::MultipartItem::Field(field) => {
println!("==== FIELD ==== {:?} {:?}",
field.headers(),
field.content_type());
Either::A(
field.map(|chunk| {
println!("-- CHUNK: \n{}",
std::str::from_utf8(&chunk).unwrap());})
.fold((), |_, _| result(Ok(()))))
},
multipart::MultipartItem::Nested(mp) => {
Either::B(result(Ok(())))
}
}
})
}
```
> A full example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/multipart/).
# Urlencoded body
Actix provides support for *application/x-www-form-urlencoded* encoded bodies.
`HttpResponse::urlencoded()` returns a
[*UrlEncoded*](../../actix-web/actix_web/dev/struct.UrlEncoded.html) future, which resolves
to the deserialized instance. The type of the instance must implement the
`Deserialize` trait from *serde*.
The *UrlEncoded* future can resolve into an error in several cases:
* content type is not `application/x-www-form-urlencoded`
* transfer encoding is `chunked`.
* content-length is greater than 256k
* payload terminates with error.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::*;
use futures::future::{Future, ok};
#[derive(Deserialize)]
struct FormData {
username: String,
}
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.urlencoded::<FormData>() // <- get UrlEncoded future
.from_err()
.and_then(|data| { // <- deserialized instance
println!("USERNAME: {:?}", data.username);
ok(HttpResponse::Ok().into())
})
.responder()
}
# fn main() {}
```
# Streaming request
*HttpRequest* is a stream of `Bytes` objects. It can be used to read the request
body payload.
In the following example, we read and print the request payload chunk by chunk:
```rust
use actix_web::*;
use futures::{Future, Stream};
fn index(mut req: HttpRequest) -> Box<Future<Item=HttpResponse, Error=Error>> {
req.from_err()
.fold((), |_, chunk| {
println!("Chunk: {:?}", chunk);
result::<_, error::PayloadError>(Ok(()))
})
.map(|_| HttpResponse::Ok().finish())
.responder()
}
```

140
content/docs/response.md Normal file
View File

@ -0,0 +1,140 @@
---
title: Responses
menu: docs_advanced
weight: 210
---
# Response
A builder-like pattern is used to construct an instance of `HttpResponse`.
`HttpResponse` provides several methods that return a `HttpResponseBuilder` instance,
which implements various convenience methods for building responses.
> Check the [documentation](../../actix-web/actix_web/dev/struct.HttpResponseBuilder.html)
> for type descriptions.
The methods `.body`, `.finish`, and `.json` finalize response creation and
return a constructed *HttpResponse* instance. If this methods is called on the same
builder instance multiple times, the builder will panic.
```rust
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
.content_type("plain/text")
.header("X-Hdr", "sample")
.body("data")
}
```
# Content encoding
Actix automatically *compresses* payloads. The following codecs are supported:
* Brotli
* Gzip
* Deflate
* Identity
Response payload is compressed based on the *content_encoding* parameter.
By default, `ContentEncoding::Auto` is used. If `ContentEncoding::Auto` is selected,
then the compression depends on the request's `Accept-Encoding` header.
> `ContentEncoding::Identity` can be used to disable compression.
> If another content encoding is selected, the compression is enforced for that codec.
For example, to enable `brotli` use `ContentEncoding::Br`:
```rust
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.content_encoding(ContentEncoding::Br)
.body("data")
}
```
In this case we explicitly disable content compression
by setting content encoding to a `Identity` value:
```rust
use actix_web::{HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
// v- disable compression
.content_encoding(ContentEncoding::Identity)
.body("data")
}
```
Also it is possible to set default content encoding on application level, by
default `ContentEncoding::Auto` is used, which implies automatic content compression
negotiation.
```rust
use actix_web::{App, HttpRequest, HttpResponse, http::ContentEncoding};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.body("data")
}
fn main() {
let app = App::new()
// v- disable compression for all routes
.default_encoding(ContentEncoding::Identity)
.resource("/index.html", |r| r.with(index));
}
```
# JSON Response
The `Json` type allows to respond with well-formed JSON data: simply return a value of
type Json<T> where `T` is the type of a structure to serialize into *JSON*.
The type `T` must implement the `Serialize` trait from *serde*.
```rust
# extern crate actix_web;
#[macro_use] extern crate serde_derive;
use actix_web::{App, HttpRequest, Json, Result, http::Method};
#[derive(Serialize)]
struct MyObj {
name: String,
}
fn index(req: HttpRequest) -> Result<Json<MyObj>> {
Ok(Json(MyObj{name: req.match_info().query("name")?}))
}
fn main() {
App::new()
.resource(r"/a/{name}", |r| r.method(Method::GET).f(index))
.finish();
}
```
# Chunked transfer encoding
Chunked encoding on a response can be enabled with `HttpResponseBuilder::chunked()`.
This takes effect only for `Body::Streaming(BodyStream)` or `Body::StreamingContext` bodies.
If the response payload compression is enabled and a streaming body is used, chunked encoding
is enabled automatically.
> Enabling chunked encoding for *HTTP/2.0* responses is forbidden.
```rust
use actix_web::*;
use bytes::Bytes;
use futures::stream::once;
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.chunked()
.body(Body::Streaming(Box::new(once(Ok(Bytes::from_static(b"data"))))))
}
```

85
content/docs/sentry.md Normal file
View File

@ -0,0 +1,85 @@
---
title: Sentry
menu: docs_patterns
weight: 1020
---
# Sentry Crash Reporting
[Sentry](https://sentry.io/) is a crash reporting system that supports the
failure crate which is the base of the actix error reporting. With a
middleware it's possible to automatically report server errors to sentry.
# Middleware
This middleware captures any error in the server error range (500 - 599)
and sends the attached error to sentry with its stacktrace.
```rust
use actix_web::{HttpRequest, HttpResponse, Error};
use actix_web::middleware::{Middleware, Response};
use failure::Fail;
use sentry::with_client_and_scope;
use sentry::protocol::{Event, Level};
use sentry::integrations::failure::exception_from_single_fail;
/// Reports certain failures to sentry.
pub struct CaptureSentryError;
impl<S> Middleware<S> for CaptureSentryError {
fn response(&self, _: &mut HttpRequest<S>, mut resp: HttpResponse)
-> Result<Response, Error>
{
if resp.status().is_server_error() {
if let Some(error) = resp.error() {
report_actix_error_to_sentry(error);
}
}
Ok(Response::Done(resp))
}
}
pub fn report_actix_error_to_sentry(err: &Error) {
with_client_and_scope(|client, scope| {
let mut exceptions = vec![
exception_from_single_fail(err.cause(), Some(err.backtrace())),
];
let mut ptr: Option<&Fail> = err.cause().cause();
while let Some(cause) = ptr {
exceptions.push(exception_from_single_fail(cause, cause.backtrace()));
ptr = Some(cause);
}
exceptions.reverse();
client.capture_event(
Event {
exceptions: exceptions,
level: Level::Error,
..Default::default()
},
Some(scope),
)
});
}
```
# Middleware Usage
To use the middleware the [sentry crate](https://crates.io/crates/sentry) needs to be
initialized and configured. Additionally it makes sense to also register the panic handler
to be informed about hard panics.
```rust
extern crate sentry;
use std::env;
fn main() {
sentry::init("SENTRY_DSN_GOES_HERE");
env::set_var("RUST_BACKTRACE", "1");
sentry::integrations::panic::register_panic_handler();
let mut app = App::with_state(state)
.middleware(CaptureSentryError)
// ...
}
```

217
content/docs/server.md Normal file
View File

@ -0,0 +1,217 @@
---
title: Server
menu: docs_basics
weight: 150
---
# The HTTP Server
The [**HttpServer**](../../actix-web/actix_web/server/struct.HttpServer.html) type is responsible for
serving http requests.
`HttpServer` accepts an application factory as a parameter, and the
application factory must have `Send` + `Sync` boundaries. More about that in the
*multi-threading* section.
To bind to a specific socket address, `bind()` must be used, and it may be
called multiple times. To bind ssl socket `bind_ssl()` or `bind_tls()` should be used.
To start the http server, one of the start methods.
- use `start()` for a server
`HttpServer` is an actix actor. It must be initialized within a properly configured actix system:
```rust
use actix_web::{server::HttpServer, App, HttpResponse};
fn main() {
let sys = actix::System::new("guide");
HttpServer::new(|| {
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok()))
})
.bind("127.0.0.1:59080").unwrap()
.start();
let _ = sys.run();
}
```
> It is possible to start a server in a separate thread with the `run()` method. In that
> case the server spawns a new thread and creates a new actix system in it. To stop
> this server, send a `StopServer` message.
`HttpServer` is implemented as an actix actor. It is possible to communicate with the server
via a messaging system. Start method, e.g. `start()`, returns the
address of the started http server. It accepts several messages:
- `PauseServer` - Pause accepting incoming connections
- `ResumeServer` - Resume accepting incoming connections
- `StopServer` - Stop incoming connection processing, stop all workers and exit
```rust
use std::thread;
use std::sync::mpsc;
use actix_web::{server, App, HttpResponse};
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let sys = actix::System::new("http-server");
let addr = server::new(|| {
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok()))
})
.bind("127.0.0.1:0").expect("Can not bind to 127.0.0.1:0")
.shutdown_timeout(60) // <- Set shutdown timeout to 60 seconds
.start();
let _ = tx.send(addr);
let _ = sys.run();
});
let addr = rx.recv().unwrap();
let _ = addr.send(
server::StopServer{graceful:true}).wait(); // <- Send `StopServer` message to server.
}
```
## Multi-threading
`HttpServer` automatically starts an number of http workers, by default
this number is equal to number of logical CPUs in the system. This number
can be overridden with the `HttpServer::workers()` method.
```rust
use actix_web::{App, HttpResponse, server::HttpServer};
fn main() {
HttpServer::new(|| {
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok()))
})
.workers(4); // <- Start 4 workers
}
```
The server creates a separate application instance for each created worker. Application state
is not shared between threads. To share state, `Arc` could be used.
> Application state does not need to be `Send` and `Sync`,
> but factories must be `Send` + `Sync`.
## SSL
There are two features for ssl server: `tls` and `alpn`. The `tls` feature is
for `native-tls` integration and `alpn` is for `openssl`.
```toml
[dependencies]
actix-web = { version = "{{< actix-version "actix-web" >}}", features = ["alpn"] }
```
```rust
use std::fs::File;
use actix_web::*;
fn main() {
// load ssl keys
let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
builder.set_private_key_file("key.pem", SslFiletype::PEM).unwrap();
builder.set_certificate_chain_file("cert.pem").unwrap();
server::new(|| {
App::new()
.resource("/index.html", |r| r.f(index))
})
.bind_ssl("127.0.0.1:8080", builder).unwrap()
.serve();
}
```
> **Note**: the *HTTP/2.0* protocol requires
> [tls alpn](https://tools.ietf.org/html/rfc7301).
> At the moment, only `openssl` has `alpn` support.
> For a full example, check out
> [examples/tls](https://github.com/actix/examples/tree/master/tls).
To create the key.pem and cert.pem use the command. **Fill in your own subject**
```bash
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem \
-days 365 -sha256 -subj "/C=CN/ST=Fujian/L=Xiamen/O=TVlinux/OU=Org/CN=muro.lxd"
```
To remove the password, then copy nopass.pen to key.pem
```bash
$ openssl rsa -in key.pem -out nopass.pem
```
## Keep-Alive
Actix can wait for requests on a keep-alive connection.
> *keep alive* connection behavior is defined by server settings.
- `75`, `Some(75)`, `KeepAlive::Timeout(75)` - enable 75 second *keep alive* timer.
- `None` or `KeepAlive::Disabled` - disable *keep alive*.
- `KeepAlive::Tcp(75)` - use `SO_KEEPALIVE` socket option.
```rust
use actix_web::{server, App, HttpResponse};
fn main() {
server::new(||
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok())))
.keep_alive(75); // <- Set keep-alive to 75 seconds
server::new(||
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok())))
.keep_alive(server::KeepAlive::Tcp(75)); // <- Use `SO_KEEPALIVE` socket option.
server::new(||
App::new()
.resource("/", |r| r.f(|_| HttpResponse::Ok())))
.keep_alive(None); // <- Disable keep-alive
}
```
If the first option is selected, then *keep alive* state is
calculated based on the response's *connection-type*. By default
`HttpResponse::connection_type` is not defined. In that case *keep alive* is
defined by the request's http version.
> *keep alive* is **off** for *HTTP/1.0* and is **on** for *HTTP/1.1* and *HTTP/2.0*.
*Connection type* can be change with `HttpResponseBuilder::connection_type()` method.
```rust
use actix_web::{HttpRequest, HttpResponse, http};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok()
.connection_type(http::ConnectionType::Close) // <- Close connection
.force_close() // <- Alternative method
.finish()
}
```
## Graceful shutdown
`HttpServer` supports graceful shutdown. After receiving a stop signal, workers
have a specific amount of time to finish serving requests. Any workers still alive after the
timeout are force-dropped. By default the shutdown timeout is set to 30 seconds.
You can change this parameter with the `HttpServer::shutdown_timeout()` method.
You can send a stop message to the server with the server address and specify if you want
graceful shutdown or not. The `start()` methods returns address of the server.
`HttpServer` handles several OS signals. *CTRL-C* is available on all OSs,
other signals are available on unix systems.
- *SIGINT* - Force shutdown workers
- *SIGTERM* - Graceful shutdown workers
- *SIGQUIT* - Force shutdown workers
> It is possible to disable signal handling with `HttpServer::disable_signals()` method.

View File

@ -0,0 +1,56 @@
---
title: Static Files
menu: docs_advanced
weight: 230
---
# Individual file
It is possible to serve static files with a custom path pattern and `NamedFile`. To
match a path tail, we can use a `[.*]` regex.
```rust
use std::path::PathBuf;
use actix_web::{App, HttpRequest, Result, http::Method, fs::NamedFile};
fn index(req: HttpRequest) -> Result<NamedFile> {
let path: PathBuf = req.match_info().query("tail")?;
Ok(NamedFile::open(path)?)
}
fn main() {
App::new()
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
.finish();
}
```
# Directory
To serve files from specific directories and sub-directories, `StaticFiles` can be used.
`StaticFiles` must be registered with an `App::handler()` method, otherwise
it will be unable to serve sub-paths.
```rust
use actix_web::{App, fs};
fn main() {
App::new()
.handler(
"/static",
fs::StaticFiles::new(".")
.show_files_listing())
.finish();
}
```
The parameter is the base directory. By default files listing for sub-directories
is disabled. Attempt to load directory listing will return *404 Not Found* response.
To enable files listing, use
[*StaticFiles::show_files_listing()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.show_files_listing)
method.
Instead of showing files listing for directory, it is possible to redirect
to a specific index file. Use the
[*StaticFiles::index_file()*](../../actix-web/actix_web/fs/struct.StaticFiles.html#method.index_file)
method to configure this redirect.

178
content/docs/testing.md Normal file
View File

@ -0,0 +1,178 @@
---
title: Testing
menu: docs_advanced
weight: 210
---
# Testing
Every application should be well tested. Actix provides tools to perform unit and
integration tests.
# Unit Tests
For unit testing, actix provides a request builder type and a simple handler runner.
[*TestRequest*](../../actix-web/actix_web/test/struct.TestRequest.html)
implements a builder-like pattern.
You can generate a `HttpRequest` instance with `finish()`, or you can
run your handler with `run()` or `run_async()`.
```rust
use actix_web::{http, test, HttpRequest, HttpResponse, HttpMessage};
fn index(req: HttpRequest) -> HttpResponse {
if let Some(hdr) = req.headers().get(http::header::CONTENT_TYPE) {
if let Ok(s) = hdr.to_str() {
return HttpResponse::Ok().into()
}
}
HttpResponse::BadRequest().into()
}
fn main() {
let resp = test::TestRequest::with_header("content-type", "text/plain")
.run(index)
.unwrap();
assert_eq!(resp.status(), http::StatusCode::OK);
let resp = test::TestRequest::default()
.run(index)
.unwrap();
assert_eq!(resp.status(), http::StatusCode::BAD_REQUEST);
}
```
# Integration tests
There are several methods for testing your application. Actix provides
[*TestServer*](../../actix-web/actix_web/test/struct.TestServer.html), which can be used
to run the application with specific handlers in a real http server.
`TestServer::get()`, `TestServer::post()`, and `TestServer::client()`
methods can be used to send requests to the test server.
A simple form `TestServer` can be configured to use a handler.
`TestServer::new` method accepts a configuration function, and the only argument
for this function is a *test application* instance.
> Check the [api documentation](../../actix-web/actix_web/test/struct.TestApp.html)
> for more information.
```rust
use actix_web::{HttpRequest, HttpMessage};
use actix_web::test::TestServer;
use std::str;
fn index(req: HttpRequest) -> &'static str {
"Hello world!"
}
fn main() {
// start new test server
let mut srv = TestServer::new(|app| app.handler(index));
let request = srv.get().finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
let bytes = srv.execute(response.body()).unwrap();
let body = str::from_utf8(&bytes).unwrap();
assert_eq!(body, "Hello world!");
}
```
The other option is to use an application factory. In this case, you need to pass the factory
function the same way as you would for real http server configuration.
```rust
use actix_web::{http, test, App, HttpRequest, HttpResponse};
fn index(req: HttpRequest) -> HttpResponse {
HttpResponse::Ok().into()
}
/// This function get called by http server.
fn create_app() -> App {
App::new()
.resource("/test", |r| r.h(index))
}
fn main() {
let mut srv = test::TestServer::with_factory(create_app);
let request = srv.client(
http::Method::GET, "/test").finish().unwrap();
let response = srv.execute(request.send()).unwrap();
assert!(response.status().is_success());
}
```
If you need more complex application configuration, use the `TestServer::build_with_state()`
method. For example, you may need to initialize application state or start `SyncActor`'s for diesel
interation. This method accepts a closure that constructs the application state,
and it runs when the actix system is configured. Thus, you can initialize any additional actors.
```rust
#[test]
fn test() {
let srv = TestServer::build_with_state(|| {
// we can start diesel actors
let addr = SyncArbiter::start(3, || {
DbExecutor(SqliteConnection::establish("test.db").unwrap())
});
// then we can construct custom state, or it could be `()`
MyState{addr: addr}
})
// register server handlers and start test server
.start(|app| {
app.resource(
"/{username}/index.html", |r| r.with(
|p: Path<PParam>| format!("Welcome {}!", p.username)));
});
// now we can run our test code
);
```
# WebSocket server tests
It is possible to register a *handler* with `TestApp::handler()`, which
initiates a web socket connection. *TestServer* provides the method `ws()`, which connects to
the websocket server and returns ws reader and writer objects. *TestServer* also
provides an `execute()` method, which runs future objects to completion and returns
result of the future computation.
The following example demonstrates how to test a websocket handler:
```rust
use actix_web::*;
use futures::Stream;
struct Ws; // <- WebSocket actor
impl Actor for Ws {
type Context = ws::WebsocketContext<Self>;
}
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
match msg {
ws::Message::Text(text) => ctx.text(text),
_ => (),
}
}
}
fn main() {
let mut srv = test::TestServer::new(
|app| app.handler(|req| ws::start(req, Ws)));
let (reader, mut writer) = srv.ws().unwrap();
writer.text("text");
let (item, reader) = srv.execute(reader.into_future()).unwrap();
assert_eq!(item, Some(ws::Message::Text("text".to_owned())));
}
```

View File

@ -0,0 +1,629 @@
---
title: URL Dispatch
menu: docs_advanced
weight: 190
---
# URL Dispatch
URL dispatch provides a simple way for mapping URLs to `Handler` code using a simple pattern
matching language. If one of the patterns matches the path information associated with a request,
a particular handler object is invoked.
> A handler is a specific object that implements the
> `Handler` trait, defined in your application, that receives the request and returns
> a response object. More information is available in the
> [handler section](sec-4-handler.html).
# Resource configuration
Resource configuration is the act of adding a new resources to an application.
A resource has a name, which acts as an identifier to be used for URL generation.
The name also allows developers to add routes to existing resources.
A resource also has a pattern, meant to match against the *PATH* portion of a *URL*.
It does not match against the *QUERY* portion (the portion following the scheme and
port, e.g., */foo/bar* in the *URL* *http://localhost:8080/foo/bar?q=value*).
The [*App::route()*](../../actix-web/actix_web/struct.App.html#method.route) method provides
simple way of registering routes. This method adds a single route to application
routing table. This method accepts a *path pattern*,
*http method* and a handler function. `route()` method could be called multiple times
for the same path, in that case, multiple routes register for the same resource path.
```rust
use actix_web::{App, HttpRequest, HttpResponse, http::Method};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.route("/user/{name}", Method::GET, index)
.route("/user/{name}", Method::POST, index)
.finish();
}
```
While *App::route()* provides simple way of registering routes, to access
complete resource configuration, different method has to be used.
The [*App::resource()*](../../actix-web/actix_web/struct.App.html#method.resource) method
adds a single resource to application routing table. This method accepts a *path pattern*
and a resource configuration function.
```rust
use actix_web::{App, HttpRequest, HttpResponse, http::Method};
fn index(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.resource("/prefix", |r| r.f(index))
.resource("/user/{name}",
|r| r.method(Method::GET).f(|req| HttpResponse::Ok()))
.finish();
}
```
The *Configuration function* has the following type:
```rust
FnOnce(&mut Resource<_>) -> ()
```
The *Configuration function* can set a name and register specific routes.
If a resource does not contain any route or does not have any matching routes, it
returns *NOT FOUND* http response.
## Configuring a Route
Resource contains a set of routes. Each route in turn has a set of predicates and a handler.
New routes can be created with `Resource::route()` method which returns a reference
to new *Route* instance. By default the *route* does not contain any predicates, so matches
all requests and the default handler is `HttpNotFound`.
The application routes incoming requests based on route criteria which are defined during
resource registration and route registration. Resource matches all routes it contains in
the order the routes were registered via `Resource::route()`.
> A *Route* can contain any number of *predicates* but only one handler.
```rust
fn main() {
App::new()
.resource("/path", |resource|
resource.route()
.filter(pred::Get())
.filter(pred::Header("content-type", "text/plain"))
.f(|req| HttpResponse::Ok())
)
.finish();
}
```
In this example, `HttpResponse::Ok()` is returned for *GET* requests.
If a request contains `Content-Type` header, the value of this header is *text/plain*,
and path equals to `/path`, Resource calls handle of the first matching route.
If a resource can not match any route, a "NOT FOUND" response is returned.
[*ResourceHandler::route()*](../../actix-web/actix_web/dev/struct.ResourceHandler.html#method.route) returns a
[*Route*](../../actix-web/actix_web/dev/struct.Route.html) object. Route can be configured with a
builder-like pattern. Following configuration methods are available:
* [*Route::filter()*](../../actix-web/actix_web/dev/struct.Route.html#method.filter)
registers a new predicate. Any number of predicates can be registered for each route.
* [*Route::f()*](../../actix-web/actix_web/dev/struct.Route.html#method.f) registers
handler function for this route. Only one handler can be registered.
Usually handler registration
is the last config operation. Handler function can be a function or closure
and has the type
`Fn(HttpRequest<S>) -> R + 'static`
* [*Route::h()*](../../actix-web/actix_web/dev/struct.Route.html#method.h) registers
a handler object that implements the `Handler` trait. This is
similar to `f()` method - only one handler can
be registered. Handler registration is the last config operation.
* [*Route::a()*](../../actix-web/actix_web/dev/struct.Route.html#method.a) registers
an async handler function for this route. Only one handler can be registered.
Handler registration is the last config operation. Handler function can
be a function or closure and has the type
`Fn(HttpRequest<S>) -> Future<Item = HttpResponse, Error = Error> + 'static`
# Route matching
The main purpose of route configuration is to match (or not match) the request's `path`
against a URL path pattern. `path` represents the path portion of the URL that was requested.
The way that *actix* does this is very simple. When a request enters the system,
for each resource configuration declaration present in the system, actix checks
the request's path against the pattern declared. This checking happens in the order that
the routes were declared via `App::resource()` method. If resource can not be found,
the *default resource* is used as the matched resource.
When a route configuration is declared, it may contain route predicate arguments. All route
predicates associated with a route declaration must be `true` for the route configuration to
be used for a given request during a check. If any predicate in the set of route predicate
arguments provided to a route configuration returns `false` during a check, that route is
skipped and route matching continues through the ordered set of routes.
If any route matches, the route matching process stops and the handler associated with
the route is invoked. If no route matches after all route patterns are exhausted, a *NOT FOUND* response get returned.
# Resource pattern syntax
The syntax of the pattern matching language used by actix in the pattern
argument is straightforward.
The pattern used in route configuration may start with a slash character. If the pattern
does not start with a slash character, an implicit slash will be prepended
to it at matching time. For example, the following patterns are equivalent:
```
{foo}/bar/baz
```
and:
```
/{foo}/bar/baz
```
A *variable part* (replacement marker) is specified in the form *{identifier}*,
where this means "accept any characters up to the next slash character and use this
as the name in the `HttpRequest.match_info()` object".
A replacement marker in a pattern matches the regular expression `[^{}/]+`.
A match_info is the `Params` object representing the dynamic parts extracted from a
*URL* based on the routing pattern. It is available as *request.match_info*. For example, the
following pattern defines one literal segment (foo) and two replacement markers (baz, and bar):
```
foo/{baz}/{bar}
```
The above pattern will match these URLs, generating the following match information:
```
foo/1/2 -> Params {'baz':'1', 'bar':'2'}
foo/abc/def -> Params {'baz':'abc', 'bar':'def'}
```
It will not match the following patterns however:
```
foo/1/2/ -> No match (trailing slash)
bar/abc/def -> First segment literal mismatch
```
The match for a segment replacement marker in a segment will be done only up to
the first non-alphanumeric character in the segment in the pattern. So, for instance,
if this route pattern was used:
```
foo/{name}.html
```
The literal path */foo/biz.html* will match the above route pattern, and the match result
will be `Params{'name': 'biz'}`. However, the literal path */foo/biz* will not match,
because it does not contain a literal *.html* at the end of the segment represented
by *{name}.html* (it only contains biz, not biz.html).
To capture both segments, two replacement markers can be used:
```
foo/{name}.{ext}
```
The literal path */foo/biz.html* will match the above route pattern, and the match
result will be *Params{'name': 'biz', 'ext': 'html'}*. This occurs because there is a
literal part of *.* (period) between the two replacement markers *{name}* and *{ext}*.
Replacement markers can optionally specify a regular expression which will be used to decide
whether a path segment should match the marker. To specify that a replacement marker should
match only a specific set of characters as defined by a regular expression, you must use a
slightly extended form of replacement marker syntax. Within braces, the replacement marker
name must be followed by a colon, then directly thereafter, the regular expression. The default
regular expression associated with a replacement marker *[^/]+* matches one or more characters
which are not a slash. For example, under the hood, the replacement marker *{foo}* can more
verbosely be spelled as *{foo:[^/]+}*. You can change this to be an arbitrary regular expression
to match an arbitrary sequence of characters, such as *{foo:\d+}* to match only digits.
Segments must contain at least one character in order to match a segment replacement marker.
For example, for the URL */abc/*:
* */abc/{foo}* will not match.
* */{foo}/* will match.
> **Note**: path will be URL-unquoted and decoded into valid unicode string before
> matching pattern and values representing matched path segments will be URL-unquoted too.
So for instance, the following pattern:
```
foo/{bar}
```
When matching the following URL:
```
http://example.com/foo/La%20Pe%C3%B1a
```
The matchdict will look like so (the value is URL-decoded):
```
Params{'bar': 'La Pe\xf1a'}
```
Literal strings in the path segment should represent the decoded value of the
path provided to actix. You don't want to use a URL-encoded value in the pattern.
For example, rather than this:
```
/Foo%20Bar/{baz}
```
You'll want to use something like this:
```
/Foo Bar/{baz}
```
It is possible to get "tail match". For this purpose custom regex has to be used.
```
foo/{bar}/{tail:.*}
```
The above pattern will match these URLs, generating the following match information:
```
foo/1/2/ -> Params{'bar':'1', 'tail': '2/'}
foo/abc/def/a/b/c -> Params{'bar':u'abc', 'tail': 'def/a/b/c'}
```
# Match information
All values representing matched path segments are available in
[`HttpRequest::match_info`](../actix_web/struct.HttpRequest.html#method.match_info).
Specific values can be retrieved with
[`Params::get()`](../actix_web/dev/struct.Params.html#method.get).
Any matched parameter can be deserialized into a specific type if the type
implements the `FromParam` trait. For example most standard integer types
the trait, i.e.:
```rust
use actix_web::*;
fn index(req: HttpRequest) -> Result<String> {
let v1: u8 = req.match_info().query("v1")?;
let v2: u8 = req.match_info().query("v2")?;
Ok(format!("Values {} {}", v1, v2))
}
fn main() {
App::new()
.resource(r"/a/{v1}/{v2}/", |r| r.f(index))
.finish();
}
```
For this example for path '/a/1/2/', values v1 and v2 will resolve to "1" and "2".
It is possible to create a `PathBuf` from a tail path parameter. The returned `PathBuf` is
percent-decoded. If a segment is equal to "..", the previous segment (if
any) is skipped.
For security purposes, if a segment meets any of the following conditions,
an `Err` is returned indicating the condition met:
* Decoded segment starts with any of: `.` (except `..`), `*`
* Decoded segment ends with any of: `:`, `>`, `<`
* Decoded segment contains any of: `/`
* On Windows, decoded segment contains any of: '\'
* Percent-encoding results in invalid UTF8.
As a result of these conditions, a `PathBuf` parsed from request path parameter is
safe to interpolate within, or use as a suffix of, a path without additional checks.
```rust
use std::path::PathBuf;
use actix_web::{App, HttpRequest, Result, http::Method};
fn index(req: HttpRequest) -> Result<String> {
let path: PathBuf = req.match_info().query("tail")?;
Ok(format!("Path {:?}", path))
}
fn main() {
App::new()
.resource(r"/a/{tail:.*}", |r| r.method(Method::GET).f(index))
.finish();
}
```
List of `FromParam` implementations can be found in
[api docs](../../actix-web/actix_web/dev/trait.FromParam.html#foreign-impls)
## Path information extractor
Actix provides functionality for type safe path information extraction.
[*Path*](../../actix-web/actix_web/struct.Path.html) extracts information, destination type
could be defined in several different forms. Simplest approach is to use
`tuple` type. Each element in tuple must correpond to one element from
path pattern. i.e. you can match path pattern `/{id}/{username}/` against
`Pyth<(u32, String)>` type, but `Path<(String, String, String)>` type will
always fail.
```rust
use actix_web::{App, Path, Result, http::Method};
// extract path info using serde
fn index(info: Path<(String, u32)>) -> Result<String> {
Ok(format!("Welcome {}! id: {}", info.0, info.1))
}
fn main() {
let app = App::new()
.resource("/{username}/{id}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index));
}
```
It also possible to extract path pattern information to a struct. In this case,
this struct must implement *serde's *`Deserialize` trait.
```rust
#[macro_use] extern crate serde_derive;
use actix_web::{App, Path, Result, http::Method};
#[derive(Deserialize)]
struct Info {
username: String,
}
// extract path info using serde
fn index(info: Path<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
fn main() {
let app = App::new()
.resource("/{username}/index.html", // <- define path parameters
|r| r.method(Method::GET).with(index));
}
```
[*Query*](../../actix-web/actix_web/struct.Query.html) provides similar
functionality for request query parameters.
# Generating resource URLs
Use the [*HttpRequest.url_for()*](../../actix-web/actix_web/struct.HttpRequest.html#method.url_for)
method to generate URLs based on resource patterns. For example, if you've configured a
resource with the name "foo" and the pattern "{a}/{b}/{c}", you might do this:
```rust
fn index(req: HttpRequest) -> Result<HttpResponse> {
let url = req.url_for("foo", &["1", "2", "3"])?; // <- generate url for "foo" resource
Ok(HttpResponse::Found()
.header(header::LOCATION, url.as_str())
.finish())
}
fn main() {
let app = App::new()
.resource("/test/{a}/{b}/{c}", |r| {
r.name("foo"); // <- set resource name, then it could be used in `url_for`
r.method(Method::GET).f(|_| HttpResponse::Ok());
})
.route("/test/", Method::GET, index)
.finish();
}
```
This would return something like the string *http://example.com/test/1/2/3* (at least if
the current protocol and hostname implied http://example.com).
`url_for()` method returns [*Url object*](https://docs.rs/url/1.7.0/url/struct.Url.html) so you
can modify this url (add query parameters, anchor, etc).
`url_for()` could be called only for *named* resources otherwise error get returned.
# External resources
Resources that are valid URLs, can be registered as external resources. They are useful
for URL generation purposes only and are never considered for matching at request time.
```rust
# extern crate actix_web;
use actix_web::{App, HttpRequest, HttpResponse, Error};
fn index(mut req: HttpRequest) -> Result<HttpResponse, Error> {
let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
Ok(HttpResponse::Ok().into())
}
fn main() {
let app = App::new()
.resource("/index.html", |r| r.f(index))
.external_resource("youtube", "https://youtube.com/watch/{video_id}")
.finish();
}
```
# Path normalization and redirecting to slash-appended routes
By normalizing it means:
* Add a trailing slash to the path.
* Double slashes are replaced by one.
The handler returns as soon as it finds a path that resolves
correctly. The order if all enable is 1) merge, 2) both merge and append
and 3) append. If the path resolves with
at least one of those conditions, it will redirect to the new path.
If *append* is *true*, append slash when needed. If a resource is
defined with trailing slash and the request doesn't have one, it will
be appended automatically.
If *merge* is *true*, merge multiple consecutive slashes in the path into one.
This handler designed to be used as a handler for application's *default resource*.
```rust
use actix_web::http::NormalizePath;
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.h(NormalizePath::default()))
.finish();
}
```
In this example `/resource`, `//resource///` will be redirected to `/resource/`.
In this example, the path normalization handler is registered for all methods,
but you should not rely on this mechanism to redirect *POST* requests. The redirect of the
slash-appending *Not Found* will turn a *POST* request into a GET, losing any
*POST* data in the original request.
It is possible to register path normalization only for *GET* requests only:
```rust
use actix_web::{App, HttpRequest, http::Method, http::NormalizePath};
fn main() {
let app = App::new()
.resource("/resource/", |r| r.f(index))
.default_resource(|r| r.method(Method::GET).h(NormalizePath::default()))
.finish();
}
```
## Using an Application Prefix to Compose Applications
The `App::prefix()` method allows to set a specific application prefix.
This prefix represents a resource prefix that will be prepended to all resource patterns added
by the resource configuration. This can be used to help mount a set of routes at a different
location than the included callable's author intended while still maintaining the same
resource names.
For example:
```rust
fn show_users(req: HttpRequest) -> HttpResponse {
unimplemented!()
}
fn main() {
App::new()
.prefix("/users")
.resource("/show", |r| r.f(show_users))
.finish();
}
```
In the above example, the *show_users* route will have an effective route pattern of
*/users/show* instead of */show* because the application's prefix argument will be prepended
to the pattern. The route will then only match if the URL path is */users/show*,
and when the `HttpRequest.url_for()` function is called with the route name show_users,
it will generate a URL with that same path.
# Custom route predicates
You can think of a predicate as a simple function that accepts a *request* object reference
and returns *true* or *false*. Formally, a predicate is any object that implements the
[`Predicate`](../actix_web/pred/trait.Predicate.html) trait. Actix provides
several predicates, you can check
[functions section](../../actix-web/actix_web/pred/index.html#functions) of api docs.
Here is a simple predicate that check that a request contains a specific *header*:
```rust
use actix_web::{http, pred::Predicate, App, HttpMessage, HttpRequest};
struct ContentTypeHeader;
impl<S: 'static> Predicate<S> for ContentTypeHeader {
fn check(&self, req: &mut HttpRequest<S>) -> bool {
req.headers().contains_key(http::header::CONTENT_TYPE)
}
}
fn main() {
App::new()
.resource("/index.html", |r|
r.route()
.filter(ContentTypeHeader)
.f(|_| HttpResponse::Ok()));
}
```
In this example, *index* handler will be called only if request contains *CONTENT-TYPE* header.
Predicates have access to the application's state via `HttpRequest::state()`.
Also predicates can store extra information in
[request extensions](../../actix-web/actix_web/struct.HttpRequest.html#method.extensions).
## Modifying predicate values
You can invert the meaning of any predicate value by wrapping it in a `Not` predicate.
For example, if you want to return "METHOD NOT ALLOWED" response for all methods
except "GET":
```rust
use actix_web::{pred, App, HttpResponse};
fn main() {
App::new()
.resource("/index.html", |r|
r.route()
.filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed()))
.finish();
}
```
The `Any` predicate accepts a list of predicates and matches if any of the supplied
predicates match. i.e:
```rust
pred::Any(pred::Get()).or(pred::Post())
```
The `All` predicate accepts a list of predicates and matches if all of the supplied
predicates match. i.e:
```rust
pred::All(pred::Get()).and(pred::Header("content-type", "plain/text"))
```
# Changing the default Not Found response
If the path pattern can not be found in the routing table or a resource can not find matching
route, the default resource is used. The default response is *NOT FOUND*.
It is possible to override the *NOT FOUND* response with `App::default_resource()`.
This method accepts a *configuration function* same as normal resource configuration
with `App::resource()` method.
```rust
use actix_web::{App, HttpResponse, http::Method, pred};
fn main() {
App::new()
.default_resource(|r| {
r.method(Method::GET).f(|req| HttpResponse::NotFound());
r.route().filter(pred::Not(pred::Get()))
.f(|req| HttpResponse::MethodNotAllowed());
})
.finish();
}
```

View File

@ -0,0 +1,50 @@
---
title: Websockets
menu: docs_proto
weight: 240
---
Actix supports WebSockets out-of-the-box. It is possible to convert a request's `Payload`
to a stream of [*ws::Message*](../../actix-web/actix_web/ws/enum.Message.html) with
a [*ws::WsStream*](../../actix-web/actix_web/ws/struct.WsStream.html) and then use stream
combinators to handle actual messages, but it is simpler to handle websocket communications
with an http actor.
The following is an example of a simple websocket echo server:
```rust
use actix::*;
use actix_web::*;
/// Define http actor
struct Ws;
impl Actor for Ws {
type Context = ws::WebsocketContext<Self>;
}
/// Handler for ws::Message message
impl StreamHandler<ws::Message, ws::ProtocolError> for Ws {
fn handle(&mut self, msg: ws::Message, ctx: &mut Self::Context) {
match msg {
ws::Message::Ping(msg) => ctx.pong(&msg),
ws::Message::Text(text) => ctx.text(text),
ws::Message::Binary(bin) => ctx.binary(bin),
_ => (),
}
}
}
fn main() {
App::new()
.resource("/ws/", |r| r.f(|req| ws::start(req, Ws)))
.finish();
}
```
> A simple websocket echo server example is available in the
> [examples directory](https://github.com/actix/examples/tree/master/websocket/).
> An example chat server with the ability to chat over a websocket or tcp connection
> is available in [websocket-chat directory](https://github.com/actix/examples/tree/master/websocket-chat/)

27
content/docs/whatis.md Normal file
View File

@ -0,0 +1,27 @@
---
title: What is Actix
menu: docs_intro
weight: 100
---
# Actix is Multiple Things
Actix is a few things. The base of it is a powerful actor system for Rust on
top of which the `actix-web` system is built. This is what you are most likely
going to work with. What `actix-web` gives you is a fun and very fast web
development framework.
We call `actix-web` a small and pragmatic framework. For all intents and purposes
it's a microframework with a few twists. If you are already a Rust programmer
you will probably find yourself at home quickly, but even if you are coming from
another programming language you should find actix-web easy to pick up.
An application developed with `actix-web` will expose an HTTP server contained
within a native executable. You can put this behind another HTTP server like
nginx or serve it up as such. Even in the complete absence of another HTTP
server `actix-web` is powerful enough to provide HTTP 1 and HTTP 2 support as
well as SSL/TLS. This makes it useful for building small services ready for
distribution.
Most importantly: `actix-web` runs on Rust 1.24 or later and it works with
stable releases.

View File

@ -1,7 +0,0 @@
title: Docs::API Example
layout: docs.liquid
permalink: /docs/API/API-example.html
---
## API Example
The words is comming.

View File

@ -1,8 +0,0 @@
title: Docs::Advance Example
layout: docs.liquid
permalink: /docs/Advance/Advance-example.html
---
## Advance Example
The words is comming.

View File

@ -1,8 +0,0 @@
title: Docs::Basics Example
layout: docs.liquid
permalink: /docs/Basics/Basics-example.html
---
## Basics Example
The words is comming.

View File

@ -1,8 +0,0 @@
title: Docs::Reference Example
layout: docs.liquid
permalink: /docs/Reference/Reference-example.html
---
## Reference Example
The words is comming.

View File

@ -1,13 +0,0 @@
title: Docs
layout: docs.liquid
permalink: /docs/index.html
---
## Documentation
Welcome to the Actix-web docs!
If you feel like contributing further, you can fork that repo,
branch from `source`, and submit a pull request.
Contributions are always welcome!

View File

@ -1,37 +0,0 @@
title: Actix web framework
layout: default.liquid
permalink: /index.html
---
<section>
<div>
<div id="top">
<h1>Actix web framework</h1>
<p>Actix web is a simple, pragmatic and extremely fast web framework for Rust.</p>
</div>
<div id="Features">
<div id="feature" class="columns four">
<article>
<h3>Type Safe</h3>
<p>Forget about stringly typed objects,
from request to response, everything has types.</p>
</article>
<article>
<h3>Feature rich</h3>
<p>Actix provides a lot of features out of box.
WebSockets, Http/2, pipelining etc.</p>
</article>
<article>
<h3>Extensible</h3>
<p>Easily create your own libraries that any Actix application can use.</p>
</article>
<article>
<h3>Blazingly Fast</h3>
<p>Actix is blazingly fast.
<a href="https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=plaintext">Check yourself</a>.
</p>
</article>
</div>
</div>
</div>
</section>

23
layouts/code/baseof.html Normal file
View File

@ -0,0 +1,23 @@
{{ partial "header" . }}
{{ $currentURL := .URL }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-community">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="actix-content">
{{ block "main" . }}{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

3
layouts/code/code.html Normal file
View File

@ -0,0 +1,3 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

4
layouts/code/single.html Normal file
View File

@ -0,0 +1,4 @@
{{ define "main" }}
<p class="uplink"><a href="/code/">↑ Back to code</a>
{{ .Content }}
{{ end }}

View File

@ -0,0 +1,23 @@
{{ partial "header" . }}
{{ $currentURL := .URL }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-community">
<div class="row">
<div class="col-md-6 offset-md-3">
<div class="actix-content">
{{ block "main" . }}{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,3 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

View File

@ -0,0 +1,4 @@
{{ define "main" }}
<p class="uplink"><a href="/community/">↑ Back to community</a>
{{ .Content }}
{{ end }}

100
layouts/docs/baseof.html Normal file
View File

@ -0,0 +1,100 @@
{{ partial "header" . }}
{{ $currentURL := .URL }}
<div class="actix-pageheader">
<div class="container">
<h1 class="display-4">{{ .Title }}</h1>
{{ if .Description }}
<p class="lead">{{ .Description }}</p>
{{ end }}
</div>
</div>
<div class="container actix-docs">
<div class="row">
<div class="col-md-3">
<nav class="leftnav">
<div class="" id="">
<h5>Introduction</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_intro }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Basics</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_basics }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Advanced</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_advanced }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Protocols</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_proto }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>Patterns</h5>
<div>
<ul class="nav">
{{ range .Site.Menus.docs_patterns }}
<li{{ if eq $currentURL .URL }} class="active"{{ end }}>
<a href="{{ .URL }}">{{ .Name }}</a>
</li>
{{ end }}
</ul>
</div>
<h5>API Documentation</h5>
<div>
<ul class="nav">
<li><a href="https://docs.rs/actix">actix <span class="fa fa-external-link"></span></a></li>
<li><a href="https://docs.rs/actix-web">actix-web <span class="fa fa-external-link"></span></a></li>
</ul>
</div>
</div>
</nav>
</div>
<div class="col-md-9">
<div class="actix-content">
{{ block "main" . }}{{ end }}
<div class="github-edit">
<a class="fa fa-github" href="https://github.com/actix/actix-website/tree/master/content/{{ .File.Path }}"> Edit on GitHub</a>
</div>
<!-- Yes, we have to use *Prev* to get the next content, because the
weights are sorted inversely to how they are actually displayed... -->
{{ with .PrevInSection }}<div class="actix-next"><b>Next up</b>: <a href = {{ .URL }}>{{ end }}
{{ with .PrevInSection }} {{ .Title }}</a></div>{{ end }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

3
layouts/docs/docs.html Normal file
View File

@ -0,0 +1,3 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

3
layouts/docs/single.html Normal file
View File

@ -0,0 +1,3 @@
{{ define "main" }}
{{ .Content }}
{{ end }}

64
layouts/index.html Normal file
View File

@ -0,0 +1,64 @@
{{ partial "header" . }}
<div class="jumbotron">
<div class="actix-jumbotron">
<img src="/img/logo-large.png" class="align-middle actix-logo" alt="">
<p class="lead">rust's powerful actor system and most fun web framework</p>
</div>
</div>
<div class="container actix-home">
<div class="row">
<div class="col-md-4">
<div class="actix-features">
<h2>
<i class="fa fa-fw fa-shield" aria-hidden="true"></i>
Type Safe
</h2>
<p>Forget about stringly typed objects, from request to response, everything has types.</p>
<h2>
<i class="fa fa-fw fa-battery-full" aria-hidden="true"></i>
Feature Rich
</h2>
<p>Actix provides a lot of features out of box. WebSockets, HTTP/2, pipelining etc.</p>
<h2>
<i class="fa fa-fw fa-puzzle-piece" aria-hidden="true"></i>
Extensible
</h2>
<p>Easily create your own libraries that any Actix application can use.</p>
<h2>
<i class="fa fa-fw fa-dashboard" aria-hidden="true"></i>
Blazingly Fast
</h2>
<p>Actix is blazingly fast. <a href="https://www.techempower.com/benchmarks/#section=data-r15&hw=ph&test=fortune">Check yourself</a>.</p>
</div>
</div>
<div class="col-md-8">
<div class="actix-content">
{{ highlight `extern crate actix_web;
use actix_web::{server, App, HttpRequest, Responder};
fn greet(req: HttpRequest) -> impl Responder {
let to = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", to)
}
fn main() {
server::new(|| {
App::new()
.resource("/", |r| r.f(greet))
.resource("/{name}", |r| r.f(greet))
})
.bind("127.0.0.1:8000")
.expect("Can not bind to port 8000")
.run();
}` "rust" "" }}
</div>
</div>
</div>
</div>
{{ partial "footer" . }}

View File

@ -0,0 +1,18 @@
<footer class="actix-footer">
<div class="text-muted actix-footer-gray d-flex justify-content-between">
<div class="actix-footer-info">
Copyright © 2018 The Actix Team
</div>
<div class="actix-footer-social">
<a href="https://github.com/actix" class="text-muted"><i class="fa fa-github" aria-hidden="true"></i></a>
</div>
</div>
</footer>
<!-- jQuery first, then Tether, then Bootstrap JS. -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha384-3ceskX3iaEnIogmQchP8opvBy3Mi7Ce34nWjpBIwVTHfGYWQS9jwHDVRnpKKHJg7" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.3.7/js/tether.min.js" integrity="sha384-XTs3FgkjiBgo8qjEjBk0tGmf3wPrWtA6coPfQDfFEY8AnYJwjalXCiosYRBIBZX8" crossorigin="anonymous"></script>
<script src="/js/bootstrap.min.js"></script>
{{ template "_internal/google_analytics.html" . }}
</body>
</html>

View File

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<link rel="stylesheet" href="/css/bootstrap-reboot.css">
<link rel="stylesheet" href="/css/bootstrap.css">
<link rel="stylesheet" href="/css/font-awesome.min.css">
<link rel="stylesheet" href="/css/actix.css">
<link rel="stylesheet" href="/css/highlight.css">
<title>{{ .Title }}</title>
</head>
<body>
<header class="navbar navbar-light navbar-toggleable-md bd-navbar">
<nav class="actix-main-nav">
<div class="d-flex justify-content-between hidden-lg-up">
<a href="/" class="navbar-brand">
<img src="/img/logo-nav.png" class="align-middle" alt="">
</a>
<button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#actix-main-nav" aria-label="Toggle navigation" aria-controls="actix-main-nav" aria-expanded="false">
<span class="navbar-toggler-icon"></span>
</button>
</div>
<div class="navbar-collapse collapse" id="actix-main-nav">
<ul class="nav navbar-nav">
<li class="nav-item hd-lg-down">
<a class="navbar-brand" href="/"><img src="/img/logo-nav.png" class="align-middle" alt=""></a>
</li>
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/docs/">Documentation</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/community/">Community</a>
</li>
<li class="nav-item">
<a class="nav-link" href="/code/">Code</a>
</li>
</ul>
</div>
</nav>
</header>

View File

@ -0,0 +1,5 @@
{{- if eq (.Get 0) "actix" -}}
{{- .Page.Site.Params.actixVersion -}}
{{- else if eq (.Get 0) "actix-web" -}}
{{- .Page.Site.Params.actixWebVersion -}}
{{- end -}}

View File

@ -0,0 +1 @@
0.6

15
more.md
View File

@ -1,15 +0,0 @@
title: More
layout: default.liquid
permalink: /more.html
---
<section >
<div >
## Examples
The words is comming.
</div>
</section>

View File

@ -1,328 +0,0 @@
:root {
--grey: #f5f5f5;
--text-color: #413e3e;
--black: #000000;
--white: #ffffff;
--new: rgb(82, 29, 79);
--font-stack: helvetica, arial, sans-serif;
}
*, *:before, *:after {
box-sizing: border-box;
padding:0px; margin:0px;
}
body {
font-family: var(--font-stack);
line-height: 1.4rem;
color: var(--text-color);
}
a {
color: var(--text-color);
text-decoration: none;
}
a:hover { color: var(--new) ;text-decoration:none; }
ul, ol, li {
list-style: none;
}
/* ----------------------------------------nav----------------------------------------*/
#lnav {
position: fixed;
width: 100%;
line-height: 60px;
zoom:1;
display: flex;
background-color: var(--white);
justify-content: space-between;
border-bottom: 1px solid #dfdcdc;
box-shadow: 0 3px 3px rgba(200, 216, 206, 0.12), 0 3px 3px rgba(97, 107, 100, 0.24);
}
#line {
position: fixed;
zoom:1;
display: flex;
width: 100%;
height: 4px;
background-color: var(--black);
}
#lnav h1 #home {
font-size: 33px;
margin: auto 1vw;
font-weight: bold;
}
#lnav label {
float: right;
font-size: 26px;
font-weight: bold;
margin: auto 2vw;
}
#lnav h1 img {
width: 2.8rem;
height: 2.8rem;
margin-top: -0.25rem;
vertical-align:middle;
}
/* ----------------------------------------section----------------------------------------*/
section {
min-height: 100vh;
}
section.docs aside ul li a, section.blog aside ul li a {
display: inline-block;
margin-left: 0.8rem;
text-decoration: none;
transition: all 0.15s ease-in-out;
}
section.docs aside ul li a.active,
section.docs aside ul li a:hover,
section.docs aside ul li a:active,
section.docs aside ul li a:focus,
section.blog aside ul li a.active,
section.blog aside ul li a:hover,
section.blog aside ul li a:active,
section.blog aside ul li a:focus {
color: var(--new);
border-left: 3px solid var(--new);
padding: 0.25rem 0.5rem;
}
section.docs aside p, section.blog aside p {
font-size: 1.2rem;
font-weight: bold;
}
pre {
display: block;
padding: 9.5px;
margin: 0 0 10px;
font-size: 13px;
line-height: 1.42857143;
color: var(--new);
word-break: break-all;
word-wrap: break-word;
background-color: #f5f5f5;
border: 1px solid #ccc;
text-shadow: none;
}
code {
padding: 2px 4px;
font-size: 90%;
background-color: #f5f5f5;
border-radius: 4px;
border: 1px solid #ccc;
color: var(--new);
text-shadow: none;
}
pre code {
padding: 0;
font-size: inherit;
color: inherit;
white-space: pre-wrap;
background-color: transparent;
border-radius: 0;
border: 0;
}
/* ----------------------------------------footer----------------------------------------*/
footer{
text-align: center;
justify-content: center;
border-top: 2px solid var(--new);
}
footer a {
color: var(--new);
font-weight: bold;
}
footer p {
margin: 0;
font-weight: lighter;
padding: 1rem 0;
}
/* ----------------------------------------home page----------------------------------------*/
#top {
padding: 2rem;
text-align: center;
line-height: 3rem;
border: 2px solid var(--grey);
}
#top h1 {
font-size: 2.2rem;
}
#top p {
font-size: 1.1rem;
}
#top button {
margin-right: 18px;
font-size: 18px;
padding: 8px 22px;
border: none;
}
#Features {
margin: 2rem auto;
padding: 1rem 2rem;
border: 2px solid var(--grey);
}
#Features #feature {
padding-bottom: 1rem;
}
#Features article {
padding-top: 1rem;
}
#Features article h3 {
font-size: 1.3rem;
padding-bottom: 0.5rem;
}
/* ----------------------------------------mobile----------------------------------------*/
@media only screen and (max-width: 600px) {
#lnav h1 img {
margin-left: 1vw;
}
#rnav {
padding-top: 3.6rem;
height: 0;
display: block;
overflow: hidden;
}
#rnav li a {
display:block;
font-weight: bold;
border-bottom: 1px solid #d8d8d8;
padding: 0.5rem 1rem;
}
#top {
margin: 0 auto;
padding: 3.5rem 2rem 5rem 2rem;
}
section {
margin: 0 auto ;
padding-top: 1rem;
width: 94%;
}
#hr {
width: 100%;
height: 0.25rem;
margin: 0.6rem 0;
background-color: var(--black);
}
}
/* ----------------------------------------table----------------------------------------*/
@media only screen and (min-width: 600px) and (max-width: 1000px) {
#menu {
display: none;
}
#lnav h1 img {
margin-left: 5vw;
}
#rnav {
position: fixed;
right: 0;
line-height: 60px;
padding-right: 5vw;
}
#rnav li {
display: inline-block;
}
#rnav li a {
font-size: 20px;
font-weight: bold;
padding-left: 2vw;
}
section {
margin: 0 auto;
padding-top: 5rem;
width: 85%;
}
section.docs .inner, section.blog .inner {
display: flex;
}
section.docs aside, section.blog aside {
flex: 0 1 30%;
}
section.docs main, section.blog main {
flex: 0 1 70%;
}
#hr {
display: none;
}
.columns {
display: flex;
}
.columns.three > article {
flex: 0 1 33%;
margin-right: 1.5%;
}
.columns.three > article:last-child {
margin-right: 0;
}
}
/* ----------------------------------------desktop----------------------------------------*/
@media only screen and (min-width: 1000px) {
#menu {
display: none;
}
#lnav h1 img {
margin-left: 11vw;
}
#rnav {
position: fixed;
right: 0;
line-height: 60px;
padding-right: 10vw;
}
#rnav li {
display: inline-block;
}
#rnav li a {
font-size: 20px;
font-weight: bold;
padding-left: 2vw;
}
section {
margin: 0 auto;
padding-top: 5.5rem;
width: 75%;
}
section.docs .inner, section.blog .inner {
display: flex;
}
section.docs aside, section.blog aside {
flex: 0 1 30%;
}
section.docs main, section.blog main {
flex: 0 1 70%;
}
#hr {
display: none;
}
.columns {
display: flex;
}
.columns.four > article {
flex: 0 1 25%;
margin-right: 1.5%;
}
.columns.four > article:last-child {
margin-right: 0;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

View File

@ -1,14 +0,0 @@
var menu = document.getElementById('menu');
menu.addEventListener('click', function() {
var nav = document.getElementById('rnav');
if (nav.style.height == 'auto') {
nav.style.height = '0';
}else{
nav.style.height = 'auto';
}
// they are same
// manv.style.height = 'auto';
// manv.setAttribute('style', 'height: auto !important');
// manv.style.setProperty( 'height',' auto', 'important');
}, false);

496
static/css/actix.css Normal file
View File

@ -0,0 +1,496 @@
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600|Roboto+Mono');
body {
font-family: 'Source Sans Pro', sans-serif;
}
a {
color: #156060;
}
a:hover {
color: #003B3B;
}
table, td {
border: 1px solid black;
}
td {
padding: 4px;
}
img {
max-width: 100%;
}
/*
*
* ===== Content ====
*
*/
.actix-content {
line-height: 1.75rem;
}
.actix-content h1 {
margin-top: 2rem;
margin-bottom: 1.5rem;
font-size: 2.2rem;
}
.actix-content h1:first-child {
margin-top: 0;
}
.actix-content h2, .actix-content h3 {
margin-top: 2rem;
margin-bottom: 1rem;
}
.actix-content hr {
margin: 2rem 0;
}
.actix-content figure {
margin: 2.5rem auto;
max-width: 80%;
}
.actix-content figure figcaption {
font-size: 90%;
color: #818a91;
text-align: right;
}
.actix-content figure img {
max-width: 100%;
}
.actix-next {
text-align: right;
}
.github-edit .fa {
font-family: inherit;
}
.github-edit .fa:before {
font-family: "FontAwesome";
display: inline-block;
font-style: normal;
font-weight: normal;
line-height: 1;
text-decoration: inherit;
}
.github-edit {
text-align: right;
padding-bottom: 1rem;
}
/*
*
* ===== Navbar =====
*
*/
.navbar-nav {
display: flex;
}
.navbar-nav .nav-item {
margin-left: 1rem;
margin-right: 1rem;
}
.navbar-nav .nav-link {
padding-top: 0;
padding-bottom: 0;
}
.navbar-brand {
margin-right: 0;
padding-top: 0;
padding-bottom: 0;
width: 52px;
height: auto;
}
@keyframes spin-logo { 100% { transform: rotate(360deg); } }
.navbar-brand img {
animation: spin-logo 30s linear infinite;
}
.navbar-light {
background: #eee;
}
.navbar-toggle {
padding: .25rem .35rem;
font-size: 1.25rem;
line-height: 1;
height: 2.6rem;
width: 2.8rem;
background: 0 0;
border: 1px solid transparent;
border-radius: .25rem;
}
.navbar-light .navbar-toggle {
border-color: rgba(0, 0, 0, 0.1);
color: rgba(0, 0, 0, 0.9);
margin-top: 4px;
}
/* reset border and change color outline */
.navbar-light .navbar-toggle:focus {
outline: none;
border-color: #888;
}
.navbar-toggler-icon {
display: inline-block;
width: 1.5em;
height: 1.5em;
vertical-align: middle;
content: "";
background: no-repeat center center;
-webkit-background-size: 100% 100%;
background-size: 100% 100%;
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 32 32' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 8h24M4 16h24M4 24h24'/%3E%3C/svg%3E");
}
.navbar-nav li {
line-height: 60px;
height: 60px;
padding-top: 0;
}
.leftnav li {
margin: 1rem 0rem;
}
.leftnav li a {
color: #888;
}
.leftnav li.active {
margin: -0.5rem 0 -.5rem -1rem;
padding: 0.5rem 0 0.5rem 1rem;
background: #dceaea;
}
.leftnav li.active a {
color: #156060;
}
.leftnav h5 {
margin: 2rem 0 1rem 0;
}
.leftnav h5:first-child {
margin-top: 0;
}
.leftnav a {
font-weight: 600;
}
.in .navbar-nav .nav-item {
text-align: left;
}
.navbar-toggleable-md .navbar-nav {
margin-top: 0;
margin-bottom: 0;
margin-left: auto;
margin-right: auto;
}
.navbar-toggleable-md .navbar-brand {
margin-top: 0;
}
/*
*
* ===== Page header =====
*
*/
.actix-pageheader, .jumbotron {
background-image: url(/img/jumbotron.jpg);
background-size: cover;
background-color: #156060;
color: #fff;
}
.jumbotron {
border-radius: 0;
padding: 4rem 1rem;
}
.actix-jumbotron {
margin-left: auto;
margin-right: auto;
max-width: 700px;
text-align: center;
}
.jumbotron .actix-logo {
max-height: 10rem;
}
.jumbotron .lead {
font-size: 1.5rem;
margin: 2rem 0 0 0;
}
.actix-pageheader {
padding: 1rem 1rem;
margin-bottom: 2.5rem;
}
.actix-pageheader h1 {
font-weight: 300;
font-size: 3rem;
}
.actix-pageheader .lead {
color: #DADADA;
font-size: 1.5rem;
margin-bottom: 0;
}
@media (min-width: 576px) {
.actix-pageheader {
padding: 2rem 0rem;
}
}
/*
*
* ===== Home =====
*
*/
.actix-home {
margin: 0 auto;
width: auto;
max-width: 1000px;
}
.actix-features h2 {
font-size: 1.5rem;
margin-top: 2.25rem;
margin-bottom: 0.8rem;
}
.actix-features .fa {
margin-right: 0.4rem;
color: #156060;
}
.actix-features p {
font-size: 1rem;
}
.final-pitch {
text-align: center;
}
/*
*
* ===== Footer =====
*
*/
.actix-footer {
padding: 4rem 0;
margin-top: 4rem;
font-size: 85%;
line-height: 35px;
}
.actix-footer-gray {
padding-left: 1rem;
padding-right: 1rem;
margin: 0 auto;
}
.actix-footer-social a .fa {
font-size: 35px;
}
.actix-footer-social a .fa-github {
margin-left: 35px;
}
/*
*
* ===== Misc =====
*
*/
p.uplink {
margin: 0;
float: right;
}
/*
*
* ===== Highlight =====
*
*/
code {
color: #006a70;
}
pre, code {
font-family: 'Roboto Mono', monospace;
}
pre {
display: block;
overflow-x: auto;
margin: 2rem 0rem;
padding: 1rem;
background: #f7f7f7;
font-size: 90%;
line-height: 1.4rem;
}
/* Show section header on hover */
h2:hover a:after,
h3:hover a:after,
h4:hover a:after,
h5:hover a:after {
content: '\2002\00a7\2002';
}
h2:hover a,
h3:hover a,
h4:hover a,
h5:hover a {
text-decoration: none;
}
@media (min-width:520px) {
.jumbotron {
padding: 4rem;
}
}
/*
*
* ===== Utilities =====
*
*/
.d-flex {
display: flex;
}
.justify-content-between {
justify-content: space-between;
}
/*
*
* ===== Media queries =====
*
*/
@media (min-width: 768px) {
.bootstrap-vertical-nav .navbar .navbar-collapse {
padding: 0;
max-height: none;
}
.bootstrap-vertical-nav .navbar ul {
float: none;
}
.bootstrap-vertical-nav .navbar ul:not {
display: block;
}
.bootstrap-vertical-nav .navbar li {
float: none;
display: block;
}
.bootstrap-vertical-nav .navbar-nav .nav-item+.nav-item {
margin-left: 0;
}
}
@media screen and (min-width: 480px) {
.hidden-lg-up {
display: none !important;
}
.navbar-collapse {
display: flex !important;
}
}
@media screen and (max-width: 479px) {
.hd-lg-down {
display: none !important;
}
.navbar-toggleable-md > .container {
padding-right: 0;
padding-left: 0;
}
.navbar-brand {
margin-top: 0 !important;
margin-right: 1rem !important;
}
.navbar-nav {
display: flex;
flex-direction: column;
padding-left: 0;
margin-bottom: 0 !important;
margin-top: 0 !important;
list-style: none;
}
.navbar-nav .nav-link {
padding: 0;
}
.navbar-nav li {
line-height: 36px;
height: 36px;
padding-top: 0;
}
.navbar-nav .nav-item + .nav-item {
margin-left: 0;
}
.actix-footer-gray {
text-align: center;
flex-direction: column;
}
.actix-footer-social a .fa-github {
margin-right: 1rem;
}
}
@media (min-width: 992px) {
.actix-footer-gray {
width: 960px;
}
}
@media (min-width: 1200px) {
.actix-footer-gray {
width: 1140px;
}
}

2
static/css/bootstrap-reboot.css vendored Normal file
View File

@ -0,0 +1,2 @@
/*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]){display:none;height:0}progress{vertical-align:baseline}[hidden],template{display:none}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:inherit}b,strong{font-weight:bolder}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}svg:not(:root){overflow:hidden}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;box-sizing:content-box;height:0;overflow:visible}button,input,optgroup,select,textarea{font:inherit;margin:0}optgroup{font-weight:700}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{-webkit-box-sizing:border-box;box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{-webkit-box-sizing:border-box;box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}html{-webkit-box-sizing:border-box;box-sizing:border-box}*,::after,::before{-webkit-box-sizing:inherit;box-sizing:inherit}@-ms-viewport{width:device-width}html{font-size:16px;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;line-height:1.5;color:#373a3c;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #818a91}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}a{color:#0275d8;text-decoration:none}a:focus,a:hover{color:#014c8c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}pre{margin-top:0;margin-bottom:1rem;overflow:auto}figure{margin:0 0 1rem}img{vertical-align:middle}[role=button]{cursor:pointer}[role=button],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation;touch-action:manipulation}table{border-collapse:collapse;background-color:transparent}caption{padding-top:.75rem;padding-bottom:.75rem;color:#818a91;text-align:left;caption-side:bottom}th{text-align:left}label{display:inline-block;margin-bottom:.5rem}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,select,textarea{line-height:inherit}input[type=checkbox]:disabled,input[type=radio]:disabled{cursor:not-allowed}input[type=date],input[type=time],input[type=datetime-local],input[type=month]{-webkit-appearance:listbox}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit}input[type=search]{-webkit-appearance:none}output{display:inline-block}[hidden]{display:none!important}
/*# sourceMappingURL=bootstrap-reboot.min.css.map */

7
static/css/bootstrap.css vendored Normal file

File diff suppressed because one or more lines are too long

4
static/css/font-awesome.min.css vendored Normal file

File diff suppressed because one or more lines are too long

66
static/css/highlight.css Normal file
View File

@ -0,0 +1,66 @@
/* Error */ .chroma .err { }
/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; width: auto; overflow: auto; display: block; }
/* LineHighlight */ .chroma .hl { display: block; width: 100%;background-color: #ffffcc }
/* LineNumbersTable */ .chroma .lnt { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
/* LineNumbers */ .chroma .ln { margin-right: 0.4em; padding: 0 0.4em 0 0.4em; }
/* Keyword */ .chroma .k { color: #006a70; font-weight: bold }
/* KeywordConstant */ .chroma .kc { color: #006a70; font-weight: bold }
/* KeywordDeclaration */ .chroma .kd { color: #006a70; font-weight: bold }
/* KeywordNamespace */ .chroma .kn { color: #006a70; font-weight: bold }
/* KeywordPseudo */ .chroma .kp { color: #006a70 }
/* KeywordReserved */ .chroma .kr { color: #006a70; font-weight: bold }
/* KeywordType */ .chroma .kt { color: #902000 }
/* NameAttribute */ .chroma .na { color: #197a7a }
/* NameBuiltin */ .chroma .nb { color: #006a70 }
/* NameClass */ .chroma .nc { color: #137ea5; font-weight: bold }
/* NameConstant */ .chroma .no { color: #60add5 }
/* NameDecorator */ .chroma .nd { color: #555555; font-weight: bold }
/* NameEntity */ .chroma .ni { color: #d55537; font-weight: bold }
/* NameException */ .chroma .ne { color: #006a70 }
/* NameFunction */ .chroma .nf { color: #06287e }
/* NameLabel */ .chroma .nl { color: #002070; font-weight: bold }
/* NameNamespace */ .chroma .nn { color: #137ea5; font-weight: bold }
/* NameTag */ .chroma .nt { color: #062873; font-weight: bold }
/* NameVariable */ .chroma .nv { color: #bb60d5 }
/* LiteralString */ .chroma .s { color: #197a7a }
/* LiteralStringAffix */ .chroma .sa { color: #197a7a }
/* LiteralStringBacktick */ .chroma .sb { color: #197a7a }
/* LiteralStringChar */ .chroma .sc { color: #197a7a }
/* LiteralStringDelimiter */ .chroma .dl { color: #197a7a }
/* LiteralStringDoc */ .chroma .sd { color: #197a7a; font-style: italic }
/* LiteralStringDouble */ .chroma .s2 { color: #197a7a }
/* LiteralStringEscape */ .chroma .se { color: #197a7a; font-weight: bold }
/* LiteralStringHeredoc */ .chroma .sh { color: #197a7a }
/* LiteralStringInterpol */ .chroma .si { color: #70a0d0; font-style: italic }
/* LiteralStringOther */ .chroma .sx { color: #c65d09 }
/* LiteralStringRegex */ .chroma .sr { color: #235388 }
/* LiteralStringSingle */ .chroma .s1 { color: #197a7a }
/* LiteralStringSymbol */ .chroma .ss { color: #517918 }
/* LiteralNumber */ .chroma .m { color: #40a070 }
/* LiteralNumberBin */ .chroma .mb { color: #40a070 }
/* LiteralNumberFloat */ .chroma .mf { color: #40a070 }
/* LiteralNumberHex */ .chroma .mh { color: #40a070 }
/* LiteralNumberInteger */ .chroma .mi { color: #40a070 }
/* LiteralNumberIntegerLong */ .chroma .il { color: #40a070 }
/* LiteralNumberOct */ .chroma .mo { color: #40a070 }
/* Operator */ .chroma .o { color: #666666 }
/* OperatorWord */ .chroma .ow { color: #006a70; font-weight: bold }
/* Comment */ .chroma .c { color: #60a0b0; font-style: italic }
/* CommentHashbang */ .chroma .ch { color: #60a0b0; font-style: italic }
/* CommentMultiline */ .chroma .cm { color: #60a0b0; font-style: italic }
/* CommentSingle */ .chroma .c1 { color: #60a0b0; font-style: italic }
/* CommentSpecial */ .chroma .cs { color: #60a0b0; background-color: #fff0f0 }
/* CommentPreproc */ .chroma .cp { color: #006a70 }
/* CommentPreprocFile */ .chroma .cpf { color: #006a70 }
/* GenericDeleted */ .chroma .gd { color: #a00000 }
/* GenericEmph */ .chroma .ge { font-style: italic }
/* GenericError */ .chroma .gr { color: #ff0000 }
/* GenericHeading */ .chroma .gh { color: #000080; font-weight: bold }
/* GenericInserted */ .chroma .gi { color: #00a000 }
/* GenericOutput */ .chroma .go { color: #888888 }
/* GenericPrompt */ .chroma .gp { color: #c65d09; font-weight: bold }
/* GenericStrong */ .chroma .gs { font-weight: bold }
/* GenericSubheading */ .chroma .gu { color: #800080; font-weight: bold }
/* GenericTraceback */ .chroma .gt { color: #0044dd }
/* TextWhitespace */ .chroma .w { color: #bbbbbb }

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Cheatsheet for Futures</title>
</head>
<body><pre style="margin: 0"><code style="background: transparent"><span style="color: #8e908c">// Constructing leaf futures</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.empty.html">empty</a> () -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.ok.html">ok</a> (<var>T</var>) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.err.html">err</a> (<var>E</var>) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.result.html">result</a>(<span style="color: #4271ae">Result</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8e908c">// General future constructor</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.poll_fn.html">poll_fn</a>(<span style="color: #8959a8">FnMut</span>(<span style="color: #3e999f">thread_local!</span>(<span style="color: #4271ae">Task</span>)) -&gt; <span style="color: #4271ae">Poll</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8e908c">// Mapping futures</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.map">Future::map</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #8959a8">FnOnce</span>(<var>T</var>) -&gt; <var>U</var>) -&gt; <span style="color: #c82829">Future</span>&lt;<var>U</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.map_err">Future::map_err</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #8959a8">FnOnce</span>(<var>E</var>) -&gt; <var>F</var>) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>F</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.from_err">Future::from_err</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <span style="color: #4271ae">Into</span>&lt;<var>E</var>&gt;&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8e908c">// Chaining (sequencing) futures</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.then">Future::then</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #8959a8">FnOnce</span>(<span style="color: #4271ae">Result</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>F</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>U</var>, <var>F</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.and_then">Future::and_then</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #8959a8">FnOnce</span>(<var>T</var>) -&gt; <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>U</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.or_else">Future::or_else</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #8959a8">FnOnce</span>(<var>E</var>) -&gt; <span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>F</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>F</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.flatten">Future::flatten</a> (<span style="color: #c82829">Future</span>&lt;<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #4271ae">Into</span>&lt;<var>E</var>&gt;&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8e908c">// Joining (waiting) futures</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.join">Future::join</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <var>U</var>), <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.join3">Future::join3</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>V</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <var>U</var>, <var>V</var>), <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.join4">Future::join4</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>V</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>W</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <var>U</var>, <var>V</var>, <var>W</var>), <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.join5">Future::join5</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>V</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>W</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>X</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <var>U</var>, <var>V</var>, <var>W</var>, <var>X</var>), <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.join_all.html">join_all</a> (<span style="color: #4271ae">IntoIterator</span>&lt;<span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<span style="color: #4271ae">Vec</span>&lt;<var>T</var>&gt;, <var>E</var>&gt;
<span style="color: #8e908c">// Selecting (racing) futures</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.select">Future::select</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;), (<var>E</var>, <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;)&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.select2">Future::select2</a>(<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #c82829">IntoFuture</span>&lt;<var>U</var>, <var>F</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<span style="color: #4271ae">Either</span>&lt;(<var>T</var>, <span style="color: #c82829">Future</span>&lt;<var>U</var>, <var>F</var>&gt;), (<var>U</var>, <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;)&gt;, <span style="color: #4271ae">Either</span>&lt;(<var>E</var>, <span style="color: #c82829">Future</span>&lt;<var>U</var>, <var>F</var>&gt;), (<var>F</var>, <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;)&gt;&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.select_all.html">select_all</a> (<span style="color: #4271ae">IntoIterator</span>&lt;<span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <span style="color: #4271ae">usize</span>, <span style="color: #4271ae">Vec</span>&lt;<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;), (<var>E</var>, <span style="color: #4271ae">usize</span>, <span style="color: #4271ae">Vec</span>&lt;<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;)&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.select_ok.html">select_ok</a> (<span style="color: #4271ae">IntoIterator</span>&lt;<span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;(<var>T</var>, <span style="color: #4271ae">Vec</span>&lt;<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;&gt;), <var>E</var>&gt;
<span style="color: #8e908c">// Utility</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.lazy.html">lazy</a> (<span style="color: #8959a8">FnOnce</span>() -&gt; <span style="color: #c82829">IntoFuture</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/fn.loop_fn.html">loop_fn</a> (<var>S</var>, <span style="color: #8959a8">FnMut</span>(<var>S</var>) -&gt; <span style="color: #c82829">IntoFuture</span>&lt;<span style="color: #4271ae">Loop</span>&lt;<var>T</var>, <var>S</var>&gt;, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8e908c">// Miscellaneous</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.into_stream">Future::into_stream</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #4271ae">Stream</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.flatten_stream">Future::flatten_stream</a>(<span style="color: #c82829">Future</span>&lt;<span style="color: #4271ae">Stream</span>&lt;<var>T</var>, <var>E</var>&gt;, <var>E</var>&gt;) -&gt; <span style="color: #4271ae">Stream</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.fuse">Future::fuse</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.catch_unwind">Future::catch_unwind</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;+<span style="color: #4271ae">UnwindSafe</span>) -&gt; <span style="color: #c82829">Future</span>&lt;<span style="color: #4271ae">Result</span>&lt;<var>T</var>, <var>E</var>&gt;, <span style="color: #4271ae">Any</span>+<span style="color: #4271ae">Send</span>&gt;
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.shared">Future::shared</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #c82829">Future</span>&lt;<span style="color: #4271ae">SharedItem</span>&lt;<var>T</var>&gt;, <span style="color: #4271ae">SharedError</span>&lt;<var>E</var>&gt;&gt;+<span style="color: #4271ae">Clone</span>
<span style="color: #8959a8">fn</span> <a href="https://docs.rs/futures/0.1.13/futures/future/trait.Future.html#method.wait">Future::wait</a> (<span style="color: #c82829">Future</span>&lt;<var>T</var>, <var>E</var>&gt;) -&gt; <span style="color: #4271ae">Result</span>&lt;<var>T</var>, <var>E</var>&gt;
</code></pre></body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
static/img/jumbotron.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

BIN
static/img/logo-large.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
static/img/logo-nav.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
static/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

7
static/js/bootstrap.js vendored Normal file

File diff suppressed because one or more lines are too long

7
static/js/bootstrap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
static/js/highlight.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,16 +0,0 @@
title: Tutorials
layout: default.liquid
permalink: /tutorials.html
---
<section >
<div >
## Examples
The words is comming.
</div>
</section>