Showing preview only (466K chars total). Download the full file or copy to clipboard to get everything.
Repository: seanmonstar/warp
Branch: master
Commit: de1ccd83e2b6
Files: 101
Total size: 440.0 KB
Directory structure:
gitextract_pan7l99a/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── examples/
│ ├── README.md
│ ├── body.rs
│ ├── compression.rs
│ ├── dir/
│ │ ├── another.html
│ │ └── index.html
│ ├── dir.rs
│ ├── file.rs
│ ├── futures.rs
│ ├── handlebars_template.rs
│ ├── headers.rs
│ ├── hello.rs
│ ├── multipart.rs
│ ├── query_string.rs
│ ├── rejections.rs
│ ├── returning.rs
│ ├── routing.rs
│ ├── sse.rs
│ ├── sse_chat.rs
│ ├── tls/
│ │ ├── cert.ecc.pem
│ │ ├── cert.pem
│ │ ├── key.ecc
│ │ └── key.rsa
│ ├── tls.rs
│ ├── todos.rs
│ ├── tracing.rs
│ ├── unix_socket.rs
│ ├── websockets.rs
│ └── websockets_chat.rs
├── src/
│ ├── bodyt.rs
│ ├── error.rs
│ ├── filter/
│ │ ├── and.rs
│ │ ├── and_then.rs
│ │ ├── boxed.rs
│ │ ├── map.rs
│ │ ├── map_err.rs
│ │ ├── mod.rs
│ │ ├── or.rs
│ │ ├── or_else.rs
│ │ ├── recover.rs
│ │ ├── service.rs
│ │ ├── then.rs
│ │ ├── unify.rs
│ │ ├── untuple_one.rs
│ │ └── wrap.rs
│ ├── filters/
│ │ ├── addr.rs
│ │ ├── any.rs
│ │ ├── body.rs
│ │ ├── compression.rs
│ │ ├── cookie.rs
│ │ ├── cors.rs
│ │ ├── ext.rs
│ │ ├── fs.rs
│ │ ├── header.rs
│ │ ├── host.rs
│ │ ├── log.rs
│ │ ├── method.rs
│ │ ├── mod.rs
│ │ ├── multipart.rs
│ │ ├── path.rs
│ │ ├── query.rs
│ │ ├── reply.rs
│ │ ├── sse.rs
│ │ ├── trace.rs
│ │ └── ws.rs
│ ├── generic.rs
│ ├── lib.rs
│ ├── redirect.rs
│ ├── reject.rs
│ ├── reply.rs
│ ├── route.rs
│ ├── server.rs
│ ├── service.rs
│ ├── test.rs
│ └── tls.rs
└── tests/
├── addr.rs
├── body.rs
├── cookie.rs
├── cors.rs
├── ext.rs
├── filter.rs
├── fs.rs
├── header.rs
├── host.rs
├── method.rs
├── multipart.rs
├── path.rs
├── query.rs
├── redirect.rs
├── reply_with.rs
├── tracing.rs
└── ws.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: [seanmonstar]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Version**
List the versions of all `warp` crates you are using. The easiest way to get
this information is using `cargo-tree`.
`cargo install cargo-tree`
(see install here: https://github.com/sfackler/cargo-tree)
Then:
`cargo tree | grep warp`
**Platform**
The output of `uname -a` (UNIX), or version and 32 or 64-bit (Windows)
**Description**
Enter your issue details here.
One way to structure the description:
[short summary of the bug]
I tried this code:
[code sample that causes the bug]
I expected to see this happen: [explanation]
Instead, this happened: [explanation]
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: false
contact_links:
- name: Question
url: https://discord.gg/RFsPjyt
about: 'Please post your question on the #warp discord channel. You may
also be able to find help at https://users.rust-lang.org/.'
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
pull_request:
push:
branches:
- master
env:
RUST_BACKTRACE: 1
jobs:
ci-pass:
name: CI is green
runs-on: ubuntu-latest
needs:
- style
- test
- doc
steps:
- run: exit 0
style:
name: Check Style
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all --check
test:
name: Test
needs: [style]
runs-on: ubuntu-latest
strategy:
matrix:
build: [stable, beta, nightly, without-test, multipart, websocket, compression]
include:
- build: stable
features: "--features test"
- build: beta
rust: beta
features: "--features server"
- build: nightly
rust: nightly
benches: true
features: "--features test"
- build: compression
features: "--features compression,test"
- build: multipart
features: "--features multipart,test"
- build: websocket
features: "--features websocket,test"
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust || 'stable' }}
- name: Test
run: cargo test ${{ matrix.features }}
- name: Test all benches
if: matrix.benches
run: cargo test --benches ${{ matrix.features }}
doc:
name: Build docs
needs: [style, test]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Install Rust
uses: dtolnay/rust-toolchain@nightly
- name: cargo doc
run: cargo doc --all-features --no-deps
env:
RUSTDOCFLAGS: "--cfg docsrs -D rustdoc::broken_intra_doc_links"
================================================
FILE: .gitignore
================================================
/target
**/*.rs.bk
Cargo.lock
.idea/
warp.iml
================================================
FILE: CHANGELOG.md
================================================
# CHANGELOG
### v0.4.2 (August 19, 2025)
- **Features**:
- Add support for passing `UnixListener` to `incoming(listener)`.
- **Fixes**:
- Reduce some dependencies when `server` is not enabled.
### v0.4.1 (August 6, 2025)
- **Fixes**:
- Fix `Server::graceful()` bounds incorrect requiring the filter to be a future.
- Enable `tokio/net` when the `server` feature is enabled.
- Render `cfg`s in the docs.
## v0.4.0 (August 5, 2025)
- **Changes**:
- Upgrade to `hyper`, `http`, and `http-body` to v1.
- Remove `multipart` and `websocket` features from being enabled by default.
- Put `warp::serve()` behind a `server` feature, not enabled by default.
- Put `warp::test` behind a `test` feature, not enabled by default.
- Remove `tls` feature and types.
- Remove `warp::addr` filters.
### v0.3.7 (April 5, 2024)
- **Features**:
- Add ecc private key support to `tls()` config.
- **Fixes**:
- Several dependency upgrades.
### v0.3.6 (September 27, 2023)
- **Features**:
- Add ability to pass `None` to `multipart::form().max_length()`.
- Implement `Reply` for `Result<impl Reply, impl Reply>`.
- Make `multipart::Part::content_type()` return the full mime string.
- Add `TlsServer::try_bind_with_graceful_shutdown()`.
- **Fixes**:
- Updated tungstenite and rustls dependencies for security fixes.
### v0.3.5 (April 28, 2023)
- **Fixes**:
- `multipart` filters now use `multer` dependency, fixing some streaming bugs.
- `Rejection::into_response()` is significantly faster.
### v0.3.4 (March 31, 2023)
- **Fixes**:
- `multipart::Part` data is now streamed instead of buffered.
- Update dependency used for `multipart` filters.
### v0.3.3 (September 27, 2022)
- **Fixes**:
- Fix `fs` filters path sanitization to reject colons on Windows.
### v0.3.2 (November 9, 2021)
- **Features**:
- Add `Filter::then()`, which is like `Filter::map()` in that it's infallible, but is async like `Filter::and_then()`.
- Add `redirect::found()` reply helper that returns `302 Found`.
- Add `compression-brotli` and `compression-gzip` cargo features to enable only the compression you need.
- Allow `HEAD` requests to be served to `fs::dir()` filters.
- Allow `path!()` with no arguments.
- **Fixes**:
- Update private dependencies Tungstenite and Multipart.
- Replaces uses of `futures` with `futures-util`, which is a smaller dependency.
### v0.3.1 (March 24, 2021)
- **Features**:
- Add `pong` constructor to websocket messages.
- Add `redirect::see_other` and `redirect::permanent` helpers.
- **Fixes**:
- Fix `fs` filters sometimes having an off-by-one error with range requests.
- Fix CORS to allow spaces when checking `Access-Control-Request-Headers`.
## v0.3.0 (January 19, 2021)
- **Features**:
- Add TLS client authentication support.
- Add TLS OCSP stapling support.
- Add `From<Reject>` for `Rejection`.
- Add `close_frame` accessor to `ws::Message`.
- **Changes**:
- Update to Tokio v1.
- Update to Bytes v1.
- Update to hyper v0.14.
- Rework `sse` filter to be more like `ws`, with a single `Event` type and builder.
- Change `cookie` filter to extract a generic `FromStr` value.
### v0.2.5 (August 31, 2020)
- **Features**:
- Add `wrap_fn`, which can be used to create a `Wrap` from a closure. These in turn are used with `Filter::with()`.
- Add `warp::host` filters to deal with `Host`/`:authority` headers.
- Relax some lifetime bounds on `Server`.
- **Fixes**:
- Fix panic when URI doesn't have a slash (for example, `CONNECT foo.bar`).
### v0.2.4 (July 20, 2020)
- **Features**:
- Add `tracing` internals in place of `log` (log is still emitted for backwards compatibility).
- Add `warp::trace` module set of filters to customize `tracing` dianostics.
- Add `path` method to `warp::fs::File` reply.
- Add `source` implementation for `BodyDeserializeError`.
- Make `warp::ws::MissingConnectionUpgrade` rejection public.
### v0.2.3 (May 19, 2020)
- **Features**:
- Add `warp::compression` filters, which will compress response bodies.
- Add `warp::header::value()` filter to get a request `HeaderValue`.
- Add `request_headers` method to `warp::log::Info`.
- Add `max_frame_size` to `warp::ws::Ws` builder.
- Add `remote_addr` to `warp::test::RequestBuilder`.
- Add `try_bind_with_graceful_shutdown` to `warp::Server` builder.
- Add `serve_incoming_with_graceful_shutdown` to `warp::Server` builder.
- **Fixes**:
- Fix `warp::addr::remote` when used with `Server::tls`.
- Fix panic in `warp::path::{peek, tail, full}` filters when the request URI is in authority-form or asterisk-form.
### v0.2.2 (March 3, 2020)
- **Features**:
- Implement `Reply` for all `Box<T>` where `T: Reply`.
- Add `name` methods to `MissingHeader`, `InvalidHeader`, and `MissingCookie` rejections.
- Add `warp::ext::optional()` filter that optionally retrieves an extension from the request.
- **Fixes**:
- Fix the sending of pings when a user sends a `ws::Message::ping()`.
### v0.2.1 (January 23, 2020)
- **Features**:
- Add `close` and `close_with` constructors to `warp::ws::Message`.
- **Fixes**:
- Fix `warp::fs` filters using a very small read buffer.
## v0.2.0 (January 16, 2020)
- **Features**:
- Update to `std::future`, adding `async`/`await` support!
- Add `warp::service()` to convert a `Filter` into a `tower::Service`.
- Implement `Reply` for `Box<dyn Reply>`.
- **Changes**:
- Refactored Rejection system (#311).
- Change `path!` macro to assume a `path::end()` by default, with explicit `/ ..` to allow building a prefix (#359).
- Change `warp::path(str)` to accept any `AsRef<str>` argument.
- Rename "2"-suffixed filters and types (`get2` to `get`, `ws2` to `ws`, etc).
- `Filter::{or, or_else, recover}` now require `Self::Error=Rejection`. This helps catch filters that didn't make sense (like `warp::any().or(warp::get())`).
- Change several `warp::body` filters (#345).
- Change `warp::cors()` to return a `warp::cors::Builder` which still implements `Wrap`, but can also `build` a cheaper-to-clone wrapper.
- Change `warp::multipart` stream API to allow for errors when streaming.
- Change `warp::sse` to no longer return a `Filter`, adds `warp::sse::reply` to do what `Sse::reply` did.
- Change `Server::tls()` to return a TLS server builder (#340).
- Change internal `warp::never::Never` usage with `std::convert::Infallible`.
- Remove `warp::ext::set()` function (#222).
- Remove deprecated `warp::cookie::optional_value()`.
### v0.1.20 (September 17, 2019)
- **Features**:
- Implement `Clone` for the `warp::cors` filter.
- Add `into_bytes` method for `warp::ws::Message`.
### v0.1.19 (August 16, 2019)
- **Features**:
- Make `warp::multipart` and `wrap::ws` support optional, though enabled by default.
- **Fixes**:
- Fix `warp::fs::dir` filter to reject paths containing backslashes.
### v0.1.18 (July 25, 2019)
- **Features**:
- Add `warp::multipart` support.
### v0.1.17 (July 8, 2019)
- **Features**:
- Export all built-in Rejection causes in the `warp::reject` module.
- Add `Server::try_bind` as fallible bind methods.
### v0.1.16 (June 11, 2019)
- **Features**:
- Unseal the `Reply` trait: custom types can now implement `Reply`.
- Add `warp::sse::keep_alive()` replacement for `warp::sse::keep()` which allows customizing keep-alive behavior.
- Add `warp::log::Info::host()` accessor.
- **Fixes**:
- Fix `warp::fs` filters from sending some headers for `304` responses.
### v0.1.15 (April 2, 2019)
- **Features**:
- Add more accessors to `warp::log::Info` type for building custom log formats.
- Implement `Reply` for `Cow<'static, str>`.
### v0.1.14 (March 19, 2019)
- **Features**:
- Add `warp::header::optional` filter.
### v0.1.13 (February 13, 2019)
- **Features**:
- Implement `Reply` for `Vec<u8>` and `&'static [u8]`.
- Set `content-type` header automatically for string and bytes replies.
- Add `expose_headers` to `warp::cors` filter.
### v0.1.12 (January 29, 2019)
- **Features**:
- Implement `PartialEq`, `Eq`, and `Clone` for `warp::ws::Message`.
- **Fixes**:
- Fix panic when incoming request URI may not have a path (such as `CONNECT` requests).
### v0.1.11 (January 14, 2019)
- **Features**:
- Add `warp::sse` filters for handling Server-Sent-Events.
- Add `allow_headers` to `warp::cors` filter.
- **Fixes**:
- Fix TLS handshake to close the connection if handshake fails.
### v0.1.10 (December 17, 2018)
- **Features**:
- Add optional TLS support. Enable the `tls` feature, and then use `Server::tls`.
- Add `warp::cors` filter for CORS support.
- Add `warp::addr::remote` to access the remote address of a request.
- Add `warp::log::custom` to support customizing of access logging.
- Add `warp::test::ws` to improve testing Websocket filters.
### v0.1.9 (October 30, 2018)
- **Features**:
- Add `warp::ext::get` and `warp::ext::set` to set request extensions.
- Add `Filter::untuple_one` to unroll nested tuple layers from extractions.
- Add `Ws2::max_send_queue` configuration method.
- Add `ws::Message::is_ping` method, and yield pings to user code.
- **Fixes**:
- Fix panic in debug mode when receiving a websocket ping.
### v0.1.8 (October 25, 2018)
- **Features**:
- Improved flexibility of `Rejection` system.
The `Rejection` type can now nest and combine arbitrary rejections,
so it is no longer bound to a small set of meanings. The ranking of
status codes is still used to determine which rejection gets priority.
A different priority can be implemented by handling rejections with
a `Filter::recover`, and searching for causes in order via
`Rejection::find_cause`.
- Adds `warp::reject::custom()` to create a `Rejection` with
any `Into<Box<std::error::Error>>`. These rejections should be
handled with an eventual `Filter::recover`. Any unhandled
custom rejections are considered a server error.
- Deprecates `Rejection::with`. Use custom rejections instead.
- Deprecates `Rejection::into_cause`, as it can no longer work. Always
returns `Err(Rejection)`.
- Deprecates `Rejection::json`, since the format needed is too generic.
The `errors.rs` example shows how to send custom JSON when recovering
from rejections.
- Deprecates `warp::reject()`, since it current signals a `400 Bad
Request`, but in newer versions, it will signal `404 Not Found`.
It's deprecated simply to warn that the semantics are changing,
but the function won't actually go away.
- Deprecates `reject::bad_request()`, `reject::forbidden()`, and
`reject::server_error()`. Uses custom rejections instead.
- Renamed `warp::path::index` to `warp::path::end`.
### v0.1.7 (October 15, 2018)
- **Features**:
- Export the types returned from the `warp::body::stream()` filter, `BodyStream` and `StreamBuf`.
- Deprecated `Rejection::into_cause`, since an upcoming Rejection refactor will make it impossible to support.
- **Fixes**:
- Fix websocket filters to do a case-insensitive match of the `Connection` header.
### v0.1.6 (October 5, 2018)
- **Features**:
- Add Conditional and Range request support for `warp::fs` filters.
- Relaxed bounds on `Rejection::with` to no longer need to be `Sized`.
- Add `warp::path::peek()` which gets the unmatched tail without adjusting the currently matched path.
### v0.1.5 (October 3, 2018)
- **Features**:
- Serve `index.html` automatically with `warp::fs::dir` filter.
- Include `last-modified` header with `warp::fs` filters.
- Add `warp::redirect` to easily reply with redirections.
- Add `warp::reply::{with_status, with_header}` to wrap `impl Reply`s directly with a new status code or header.
- Add support for running a warp `Server` with a custom source of incoming connections.
- `Server::run_incoming` to have the runtime started automatically.
- `Server::serve_incoming` to get a future to run on existing runtime.
- These can be used to support Unix Domain Sockets, TLS, and other transports.
- Add `Rejection::into_cause()` to retrieve the original error of a rejection back.
- Add `Rejection::json()` to convert a rejection into a JSON response.
- **Fixes**
- Internal errors in warp that result in rendering a `500 Internal Server Error` are now also logged at the `error` level.
### v0.1.4 (September 25, 2018)
- **Features**:
- Add `warp::reply::with::headers(HeaderMap)` filter wrapper.
- Add `warp::cookie::optional()` to get an optional cookie value.
- Add `warp::path::full()` to be able to extract the full request path without affecting route matching.
- Add graceful shutdown support to the `Server`.
- Allow empty query strings to be treated as for `warp::query()`.
### v0.1.3 (August 28, 2018)
- **Features**:
- Add `warp::reject::forbidden()` to represent `403 Forbidden` responses.
- Add `Rejection::with(cause)` to customize rejection messages.
- **Fixes**:
- Fix `warp::body::form` to allow charsets in the `content-type` header.
### v0.1.2 (August 14, 2018)
- **Features**:
- Implemented `Reply` for `Response<impl Into<hyper::Body>`, allowing streaming response bodies.
- Add `warp::body::stream()` filter to access the request body as an `impl Stream`.
- Add `warp::ws2()` as a more flexible websocket filter.
- This allows passing other extracted values to the upgrade callback, such as a value from a header or path.
- Deprecates `warp::ws()`, and `ws2()` will become `ws()` in 0.2.
- Add `warp::get2()`, `warp::post2()`, `warp::put2()`, and `warp::delete2()` as more standard method filters that are used via chaining instead of nesting.
- `get()`, `post()`, `put()`, and `delete()` are deprecated, and the new versions will become them in 0.2.
- Add `Filter::unify()` for when a filter returns `Either<T, T>`, converting the `Either` into the inner `T`, regardless of which variant it was.
- This requires that both sides of the `Either` be the same type.
- This can be useful when extracting a value that might be present in different places of the request.
```rust
// Allow `MyId` to be a path parameter or a header...
let id = warp::path::param::<MyId>()
.or(warp::header::<MyId>())
.unify();
// A way of providing default values...
let dnt = warp::header::<bool>("dnt")
.or(warp::any().map(|| true))
.unify();
```
- Add `content-type` header automatically to replies from `file` and `dir` filters based on file extension.
- Add `warp::head()`, `warp::options()`, and `warp::patch()` as new Method filters.
- Try to use OS blocksize in `warp::fs` filters.
- **Fixes**:
- Chaining filters that try to consume the request body will log that the body is already consumed, and return a `500 Internal Server Error` rejection.
### v0.1.1 (August 7, 2018)
- **Features**:
- Add `warp::query::raw()` filter to get query as a `String`.
- Add `Filter::recover()` to ease customizing of rejected responses.
- Add `warp::header::headers_clone()` filter to get a clone of request's `HeaderMap`.
- Add `warp::path::tail()` filter to get remaining "tail" of the request path.
- **Fixes**:
- URL decode path segments in `warp::fs` filters.
## v0.1.0 (August 1, 2018)
- Initial release.
================================================
FILE: Cargo.toml
================================================
[package]
name = "warp"
version = "0.4.2"
description = "serve the web at warp speeds"
authors = ["Sean McArthur <sean@seanmonstar.com>"]
license = "MIT"
readme = "README.md"
documentation = "https://docs.rs/warp"
repository = "https://github.com/seanmonstar/warp"
categories = ["web-programming::http-server"]
keywords = ["warp", "server", "http", "hyper"]
autotests = true
autoexamples = true
edition = "2021"
include = [
"Cargo.toml",
"LICENSE",
"src/**/*",
]
[package.metadata.docs.rs]
all-features = true
[dependencies]
async-compression = { version = "0.4.5", features = ["tokio"], optional = true }
bytes = "1.0"
futures-util = { version = "0.3", default-features = false, features = ["sink"] }
futures-channel = { version = "0.3.17", features = ["sink"], optional = true }
headers = "0.4"
http = "1"
http-body = "1"
http-body-util = "0.1.2"
hyper = { version = "1", optional = true }
hyper-util = { version = "0.1.12", features = ["server", "server-graceful", "server-auto", "http1", "http2", "service", "tokio"], optional = true }
log = "0.4"
mime = "0.3"
mime_guess = "2.0.0"
multer = { version = "3", optional = true }
scoped-tls = "1.0"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7.1"
tokio = { version = "1.0", features = ["io-util", "fs", "sync", "time"] }
tokio-util = { version = "0.7.1", features = ["io"] }
tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] }
tower-service = "0.3"
tokio-tungstenite = { version = "0.28", optional = true }
percent-encoding = "2.1"
pin-project = "1.0"
#tokio-rustls = { version = "0.26", default-features = false, features = ["logging", "tls12", "ring"], optional = true }
#rustls-pemfile = { version = "2.0", optional = true }
[dev-dependencies]
pretty_env_logger = "0.5"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-log = "0.2"
serde_derive = "1.0"
handlebars = "6.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "signal"] }
tokio-stream = "0.1.1"
[features]
default = []
multipart = ["dep:multer"]
websocket = ["dep:hyper", "dep:tokio-tungstenite", "hyper-util/tokio"]
server = ["dep:hyper", "dep:hyper-util", "tokio/net"]
test = ["server", "hyper/client", "hyper/http1", "dep:futures-channel"]
# tls might come back, uncertain
#tls = ["tokio-rustls", "rustls-pemfile"]
# Enable compression-related filters
compression = ["compression-brotli", "compression-gzip"]
compression-brotli = ["async-compression/brotli"]
compression-gzip = ["async-compression/deflate", "async-compression/gzip"]
[profile.release]
codegen-units = 1
incremental = false
[profile.bench]
codegen-units = 1
incremental = false
[[example]]
name = "body"
required-features = ["server"]
[[example]]
name = "compression"
required-features = ["compression", "server"]
[[example]]
name = "dir"
required-features = ["server"]
[[example]]
name = "file"
required-features = ["server"]
[[example]]
name = "futures"
required-features = ["server"]
[[example]]
name = "handlebars_template"
required-features = ["server"]
[[example]]
name = "headers"
required-features = ["server"]
[[example]]
name = "hello"
required-features = ["server"]
[[example]]
name = "multipart"
required-features = ["multipart", "server"]
[[example]]
name = "query_string"
required-features = ["server"]
[[example]]
name = "rejections"
required-features = ["server"]
[[example]]
name = "returning"
required-features = ["server"]
[[example]]
name = "routing"
required-features = ["server"]
[[example]]
name = "sse"
required-features = ["server"]
[[example]]
name = "sse_chat"
required-features = ["server"]
[[example]]
name = "todos"
required-features = ["server"]
[[example]]
name = "tracing"
required-features = ["server"]
[[example]]
name = "unix_socket"
required-features = ["server"]
[[example]]
name = "websockets"
required-features = ["websocket", "server"]
[[example]]
name = "websockets_chat"
required-features = ["websocket", "server"]
[[test]]
name = "addr"
required-features = ["test"]
[[test]]
name = "body"
required-features = ["test"]
[[test]]
name = "cookie"
required-features = ["test"]
[[test]]
name = "cors"
required-features = ["test"]
[[test]]
name = "ext"
required-features = ["test"]
[[test]]
name = "filter"
required-features = ["test"]
[[test]]
name = "fs"
required-features = ["test"]
[[test]]
name = "header"
required-features = ["test"]
[[test]]
name = "host"
required-features = ["test"]
[[test]]
name = "method"
required-features = ["test"]
[[test]]
name = "multipart"
required-features = ["multipart", "test"]
[[test]]
name = "path"
required-features = ["test"]
[[test]]
name = "query"
required-features = ["test"]
[[test]]
name = "redirect"
required-features = ["test"]
[[test]]
name = "reply_with"
required-features = ["test"]
[[test]]
name = "tracing"
required-features = ["test"]
[[test]]
name = "ws"
required-features = ["websocket", "test"]
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(feature, values("tls"))'] }
================================================
FILE: LICENSE
================================================
Copyright (c) 2018-2025 Sean McArthur
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.
================================================
FILE: README.md
================================================
# warp
[](https://crates.io/crates/warp)
[](https://docs.rs/warp)
[](./LICENSE)
[](https://github.com/seanmonstar/warp/actions?query=workflow%3ACI)
[![Discord chat][discord-badge]][discord-url]
A super-easy, composable, web server framework for warp speeds.
The fundamental building block of `warp` is the `Filter`: they can be combined
and composed to express rich requirements on requests.
Thanks to its `Filter` system, warp provides these out of the box:
* Path routing and parameter extraction
* Header requirements and extraction
* Query string deserialization
* JSON and Form bodies
* Multipart form data
* Static Files and Directories
* Websockets
* Access logging
* Gzip, Deflate, and Brotli compression
Since it builds on top of [hyper](https://hyper.rs), you automatically get:
- HTTP/1
- HTTP/2
- Asynchronous
- One of the fastest HTTP implementations
- Tested and **correct**
## Example
Add warp and Tokio to your dependencies:
```toml
tokio = { version = "1", features = ["full"] }
warp = { version = "0.4", features = ["server"] }
```
And then get started in your `main.rs`:
```rust
use warp::Filter;
#[tokio::main]
async fn main() {
// GET /hello/warp => 200 OK with body "Hello, warp!"
let hello = warp::path!("hello" / String)
.map(|name| format!("Hello, {}!", name));
warp::serve(hello)
.run(([127, 0, 0, 1], 3030))
.await;
}
```
For more information you can check the [docs](https://docs.rs/warp) or the [examples](https://github.com/seanmonstar/warp/tree/master/examples).
[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord
[discord-url]: https://discord.gg/RFsPjyt
================================================
FILE: examples/README.md
================================================
# Examples
Welcome to the examples! These show off `warp`'s functionality and explain how to use it.
## Getting Started
To get started, run `examples/hello.rs` with:
```bash
> cargo run --example hello
```
This will start a simple "hello world" service running on your localhost port 3030.
Open another terminal and run:
```bash
> curl http://localhost:3030/hi
Hello, World!%
```
Congratulations, you have just run your first warp service!
You can run other examples with `cargo run --example [example name]`:
- [`hello.rs`](./hello.rs) - Just a basic "Hello World" API
- [`routing.rs`](./routing.rs) - Builds up a more complex set of routes and shows how to combine filters
- [`body.rs`](./body.rs) - What's a good API without parsing data from the request body?
- [`headers.rs`](./headers.rs) - Parsing data from the request headers
- [`rejections.rs`](./rejections.rs) - Your APIs are obviously perfect, but for silly others who call them incorrectly you'll want to define errors for them
- [`futures.rs`](./futures.rs) - Wait, wait! ... Or how to integrate futures into filters
- [`todos.rs`](./todos.rs) - Putting this all together with a proper app
## Further Use Cases
### Serving HTML and Other Files
- [`file.rs`](./file.rs) - Serving static files
- [`dir.rs`](./dir.rs) - Or a whole directory of files
- [`handlebars_template.rs`](./handlebars_template.rs) - Using Handlebars to fill in an HTML template
### Websockets
Hooray! `warp` also includes built-in support for WebSockets
- [`websockets.rs`](./websockets.rs) - Basic handling of a WebSocket upgrade
- [`websockets_chat.rs`](./websockets_chat.rs) - Full WebSocket app
### Server-Side Events
- [`sse.rs`](./sse.rs) - Basic Server-Side Event
- [`sse_chat.rs`](./sse_chat.rs) - Full SSE app
### TLS
- [`tls.rs`](./tls.rs) - can i haz security?
### Debugging
- [`tracing.rs`](./tracing.rs) - Warp has built-in support for rich diagnostics with [`tracing`](https://docs.rs/tracing)!
## Custom HTTP Methods
- [`custom_methods.rs`](./custom_methods.rs) - It is also possible to use Warp with custom HTTP methods.
================================================
FILE: examples/body.rs
================================================
#![deny(warnings)]
use serde_derive::{Deserialize, Serialize};
use warp::Filter;
#[derive(Deserialize, Serialize)]
struct Employee {
name: String,
rate: u32,
}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// POST /employees/:rate {"name":"Sean","rate":2}
let promote = warp::post()
.and(warp::path("employees"))
.and(warp::path::param::<u32>())
// Only accept bodies smaller than 16kb...
.and(warp::body::content_length_limit(1024 * 16))
.and(warp::body::json())
.map(|rate, mut employee: Employee| {
employee.rate = rate;
warp::reply::json(&employee)
});
warp::serve(promote).run(([127, 0, 0, 1], 3030)).await
}
================================================
FILE: examples/compression.rs
================================================
#![deny(warnings)]
use warp::Filter;
#[tokio::main]
async fn main() {
let file = warp::path("todos").and(warp::fs::file("./examples/todos.rs"));
// NOTE: You could double compress something by adding a compression
// filter here, a la
// ```
// let file = warp::path("todos")
// .and(warp::fs::file("./examples/todos.rs"))
// .with(warp::compression::brotli());
// ```
// This would result in a browser error, or downloading a file whose contents
// are compressed
let dir = warp::path("ws_chat").and(warp::fs::file("./examples/websockets_chat.rs"));
let file_and_dir = warp::get()
.and(file.or(dir))
.with(warp::compression::gzip());
let examples = warp::path("ex")
.and(warp::fs::dir("./examples/"))
.with(warp::compression::deflate());
// GET /todos => gzip -> toods.rs
// GET /ws_chat => gzip -> ws_chat.rs
// GET /ex/... => deflate -> ./examples/...
let routes = file_and_dir.or(examples);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/dir/another.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>dir/another.html</title>
</head>
<body>
<h1>Welcome to Another Page</h1>
<a href="/">back</a>
</body>
</html>
================================================
FILE: examples/dir/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>dir/index.html</title>
</head>
<body>
<h1>Welcome to Dir</h1>
<a href="/another.html">another page</a>
</body>
</html>
================================================
FILE: examples/dir.rs
================================================
#![deny(warnings)]
#[tokio::main]
async fn main() {
pretty_env_logger::init();
warp::serve(warp::fs::dir("examples/dir"))
.run(([127, 0, 0, 1], 3030))
.await;
}
================================================
FILE: examples/file.rs
================================================
#![deny(warnings)]
use warp::Filter;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let readme = warp::get()
.and(warp::path::end())
.and(warp::fs::file("./README.md"));
// dir already requires GET...
let examples = warp::path("ex").and(warp::fs::dir("./examples/"));
// GET / => README.md
// GET /ex/... => ./examples/..
let routes = readme.or(examples);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/futures.rs
================================================
#![deny(warnings)]
use std::convert::Infallible;
use std::str::FromStr;
use std::time::Duration;
use warp::Filter;
#[tokio::main]
async fn main() {
// Match `/:Seconds`...
let routes = warp::path::param()
// and_then create a `Future` that will simply wait N seconds...
.and_then(sleepy);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
async fn sleepy(Seconds(seconds): Seconds) -> Result<impl warp::Reply, Infallible> {
tokio::time::sleep(Duration::from_secs(seconds)).await;
Ok(format!("I waited {} seconds!", seconds))
}
/// A newtype to enforce our maximum allowed seconds.
struct Seconds(u64);
impl FromStr for Seconds {
type Err = ();
fn from_str(src: &str) -> Result<Self, Self::Err> {
src.parse::<u64>().map_err(|_| ()).and_then(|num| {
if num <= 5 {
Ok(Seconds(num))
} else {
Err(())
}
})
}
}
================================================
FILE: examples/handlebars_template.rs
================================================
#![deny(warnings)]
use std::sync::Arc;
use handlebars::Handlebars;
use serde::Serialize;
use serde_json::json;
use warp::Filter;
struct WithTemplate<T: Serialize> {
name: &'static str,
value: T,
}
fn render<T>(template: WithTemplate<T>, hbs: Arc<Handlebars<'_>>) -> impl warp::Reply
where
T: Serialize,
{
let render = hbs
.render(template.name, &template.value)
.unwrap_or_else(|err| err.to_string());
warp::reply::html(render)
}
#[tokio::main]
async fn main() {
let template = "<!DOCTYPE html>
<html>
<head>
<title>Warp Handlebars template example</title>
</head>
<body>
<h1>Hello {{user}}!</h1>
</body>
</html>";
let mut hb = Handlebars::new();
// register the template
hb.register_template_string("template.html", template)
.unwrap();
// Turn Handlebars instance into a Filter so we can combine it
// easily with others...
let hb = Arc::new(hb);
// Create a reusable closure to render template
let handlebars = move |with_template| render(with_template, hb.clone());
//GET /
let route = warp::get()
.and(warp::path::end())
.map(|| WithTemplate {
name: "template.html",
value: json!({"user" : "Warp"}),
})
.map(handlebars);
warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/headers.rs
================================================
#![deny(warnings)]
use std::net::SocketAddr;
use warp::Filter;
/// Create a server that requires header conditions:
///
/// - `Host` is a `SocketAddr`
/// - `Accept` is exactly `*/*`
///
/// If these conditions don't match, a 404 is returned.
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// For this example, we assume no DNS was used,
// so the Host header should be an address.
let host = warp::header::<SocketAddr>("host");
// Match when we get `accept: */*` exactly.
let accept_stars = warp::header::exact("accept", "*/*");
let routes = host
.and(accept_stars)
.map(|addr| format!("accepting stars on {}", addr));
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/hello.rs
================================================
#![deny(warnings)]
use warp::Filter;
#[tokio::main]
async fn main() {
// Match any request and return hello world!
let routes = warp::any().map(|| "Hello, World!");
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/multipart.rs
================================================
use bytes::BufMut;
use futures_util::TryStreamExt;
use warp::multipart::FormData;
use warp::Filter;
#[tokio::main]
async fn main() {
// Running curl -F file=@.gitignore 'localhost:3030/' should print [("file", ".gitignore", "\n/target\n**/*.rs.bk\nCargo.lock\n.idea/\nwarp.iml\n")]
let route = warp::multipart::form().and_then(|form: FormData| async move {
let field_names: Vec<_> = form
.and_then(|mut field| async move {
let mut bytes: Vec<u8> = Vec::new();
// field.data() only returns a piece of the content, you should call over it until it replies None
while let Some(content) = field.data().await {
let content = content.unwrap();
bytes.put(content);
}
Ok((
field.name().to_string(),
field.filename().unwrap().to_string(),
String::from_utf8_lossy(&*bytes).to_string(),
))
})
.try_collect()
.await
.unwrap();
Ok::<_, warp::Rejection>(format!("{:?}", field_names))
});
warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/query_string.rs
================================================
use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap;
use warp::{
http::{Response, StatusCode},
Filter,
};
#[derive(Deserialize, Serialize)]
struct MyObject {
key1: String,
key2: u32,
}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// get /example1?key=value
// demonstrates an optional parameter.
let example1 = warp::get()
.and(warp::path("example1"))
.and(warp::query::<HashMap<String, String>>())
.map(|p: HashMap<String, String>| match p.get("key") {
Some(key) => Response::builder().body(format!("key = {}", key)),
None => Response::builder().body(String::from("No \"key\" param in query.")),
});
// get /example2?key1=value&key2=42
// uses the query string to populate a custom object
let example2 = warp::get()
.and(warp::path("example2"))
.and(warp::query::<MyObject>())
.map(|p: MyObject| {
Response::builder().body(format!("key1 = {}, key2 = {}", p.key1, p.key2))
});
let opt_query = warp::query::<MyObject>()
.map(Some)
.or_else(|_| async { Ok::<(Option<MyObject>,), std::convert::Infallible>((None,)) });
// get /example3?key1=value&key2=42
// builds on example2 but adds custom error handling
let example3 =
warp::get()
.and(warp::path("example3"))
.and(opt_query)
.map(|p: Option<MyObject>| match p {
Some(obj) => {
Response::builder().body(format!("key1 = {}, key2 = {}", obj.key1, obj.key2))
}
None => Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(String::from("Failed to decode query param.")),
});
warp::serve(example1.or(example2).or(example3))
.run(([127, 0, 0, 1], 3030))
.await
}
================================================
FILE: examples/rejections.rs
================================================
#![deny(warnings)]
use std::convert::Infallible;
use std::error::Error;
use std::num::NonZeroU16;
use serde_derive::{Deserialize, Serialize};
use warp::http::StatusCode;
use warp::{reject, Filter, Rejection, Reply};
/// Rejections represent cases where a filter should not continue processing
/// the request, but a different filter *could* process it.
#[tokio::main]
async fn main() {
let math = warp::path!("math" / u16);
let div_with_header = math
.and(warp::get())
.and(div_by())
.map(|num: u16, denom: NonZeroU16| {
warp::reply::json(&Math {
op: format!("{} / {}", num, denom),
output: num / denom.get(),
})
});
let div_with_body =
math.and(warp::post())
.and(warp::body::json())
.map(|num: u16, body: DenomRequest| {
warp::reply::json(&Math {
op: format!("{} / {}", num, body.denom),
output: num / body.denom.get(),
})
});
let routes = div_with_header.or(div_with_body).recover(handle_rejection);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
/// Extract a denominator from a "div-by" header, or reject with DivideByZero.
fn div_by() -> impl Filter<Extract = (NonZeroU16,), Error = Rejection> + Copy {
warp::header::<u16>("div-by").and_then(|n: u16| async move {
if let Some(denom) = NonZeroU16::new(n) {
Ok(denom)
} else {
Err(reject::custom(DivideByZero))
}
})
}
#[derive(Deserialize)]
struct DenomRequest {
pub denom: NonZeroU16,
}
#[derive(Debug)]
struct DivideByZero;
impl reject::Reject for DivideByZero {}
// JSON replies
/// A successful math operation.
#[derive(Serialize)]
struct Math {
op: String,
output: u16,
}
/// An API error serializable to JSON.
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallible> {
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(DivideByZero) = err.find() {
code = StatusCode::BAD_REQUEST;
message = "DIVIDE_BY_ZERO";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
// This error happens if the body could not be deserialized correctly
// We can use the cause to analyze the error and customize the error message
message = match e.source() {
Some(cause) => {
if cause.to_string().contains("denom") {
"FIELD_ERROR: denom"
} else {
"BAD_REQUEST"
}
}
None => "BAD_REQUEST",
};
code = StatusCode::BAD_REQUEST;
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
// We should have expected this... Just log and say its a 500
eprintln!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
let json = warp::reply::json(&ErrorMessage {
code: code.as_u16(),
message: message.into(),
});
Ok(warp::reply::with_status(json, code))
}
================================================
FILE: examples/returning.rs
================================================
use warp::{filters::BoxedFilter, Filter, Rejection, Reply};
// Option 1: BoxedFilter
// Note that this may be useful for shortening compile times when you are composing many filters.
// Boxing the filters will use dynamic dispatch and speed up compilation while
// making it slightly slower at runtime.
pub fn assets_filter() -> BoxedFilter<(impl Reply,)> {
warp::path("assets").and(warp::fs::dir("./assets")).boxed()
}
// Option 2: impl Filter + Clone
pub fn index_filter() -> impl Filter<Extract = (&'static str,), Error = Rejection> + Clone {
warp::path::end().map(|| "Index page")
}
#[tokio::main]
async fn main() {
let routes = index_filter().or(assets_filter());
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/routing.rs
================================================
#![deny(warnings)]
use warp::Filter;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// We'll start simple, and gradually show how you combine these powers
// into super powers!
// GET /
let hello_world = warp::path::end().map(|| "Hello, World at root!");
// GET /hi
let hi = warp::path("hi").map(|| "Hello, World!");
// How about multiple segments? First, we could use the `path!` macro:
//
// GET /hello/from/warp
let hello_from_warp = warp::path!("hello" / "from" / "warp").map(|| "Hello from warp!");
// Fine, but how do I handle parameters in paths?
//
// GET /sum/:u32/:u32
let sum = warp::path!("sum" / u32 / u32).map(|a, b| format!("{} + {} = {}", a, b, a + b));
// Any type that implements FromStr can be used, and in any order:
//
// GET /:u16/times/:u16
let times =
warp::path!(u16 / "times" / u16).map(|a, b| format!("{} times {} = {}", a, b, a * b));
// Oh shoot, those math routes should be mounted at a different path,
// is that possible? Yep.
//
// GET /math/sum/:u32/:u32
// GET /math/:u16/times/:u16
let math = warp::path("math");
let _sum = math.and(sum);
let _times = math.and(times);
// What! And? What's that do?
//
// It combines the filters in a sort of "this and then that" order. In
// fact, it's exactly what the `path!` macro has been doing internally.
//
// GET /bye/:string
let bye = warp::path("bye")
.and(warp::path::param())
.map(|name: String| format!("Good bye, {}!", name));
// Ah, can filters do things besides `and`?
//
// Why, yes they can! They can also `or`! As you might expect, `or` creates
// a "this or else that" chain of filters. If the first doesn't succeed,
// then it tries the other.
//
// So, those `math` routes could have been mounted all as one, with `or`.
//
// GET /math/sum/:u32/:u32
// GET /math/:u16/times/:u16
let math = warp::path("math").and(sum.or(times));
// We can use the end() filter to match a shorter path
let help = warp::path("math")
// Careful! Omitting the following line would make this filter match
// requests to /math/sum/:u32/:u32 and /math/:u16/times/:u16
.and(warp::path::end())
.map(|| "This is the Math API. Try calling /math/sum/:u32/:u32 or /math/:u16/times/:u16");
let math = help.or(math);
// Let's let people know that the `sum` and `times` routes are under `math`.
let sum = sum.map(|output| format!("(This route has moved to /math/sum/:u16/:u16) {}", output));
let times =
times.map(|output| format!("(This route has moved to /math/:u16/times/:u16) {}", output));
// It turns out, using `or` is how you combine everything together into
// a single API. (We also actually haven't been enforcing that the
// method is GET, so we'll do that too!)
//
// GET /
// GET /hi
// GET /hello/from/warp
// GET /bye/:string
// GET /math/sum/:u32/:u32
// GET /math/:u16/times/:u16
let routes = warp::get().and(
hello_world
.or(hi)
.or(hello_from_warp)
.or(bye)
.or(math)
.or(sum)
.or(times),
);
// Note that composing filters for many routes may increase compile times (because it uses a lot of generics).
// If you wish to use dynamic dispatch instead and speed up compile times while
// making it slightly slower at runtime, you can use Filter::boxed().
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/sse.rs
================================================
use futures_util::StreamExt;
use std::convert::Infallible;
use std::time::Duration;
use tokio::time::interval;
use tokio_stream::wrappers::IntervalStream;
use warp::{sse::Event, Filter};
// create server-sent event
fn sse_counter(counter: u64) -> Result<Event, Infallible> {
Ok(warp::sse::Event::default().data(counter.to_string()))
}
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let routes = warp::path("ticks").and(warp::get()).map(|| {
let mut counter: u64 = 0;
// create server event source
let interval = interval(Duration::from_secs(1));
let stream = IntervalStream::new(interval);
let event_stream = stream.map(move |_| {
counter += 1;
sse_counter(counter)
});
// reply using server-sent events
warp::sse::reply(event_stream)
});
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/sse_chat.rs
================================================
use futures_util::{Stream, StreamExt};
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc, Mutex,
};
use tokio::sync::mpsc;
use tokio_stream::wrappers::UnboundedReceiverStream;
use warp::{sse::Event, Filter};
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// Keep track of all connected users, key is usize, value
// is an event stream sender.
let users = Arc::new(Mutex::new(HashMap::new()));
// Turn our "state" into a new Filter...
let users = warp::any().map(move || users.clone());
// POST /chat -> send message
let chat_send = warp::path("chat")
.and(warp::post())
.and(warp::path::param::<usize>())
.and(warp::body::content_length_limit(500))
.and(
warp::body::bytes().and_then(|body: bytes::Bytes| async move {
std::str::from_utf8(&body)
.map(String::from)
.map_err(|_e| warp::reject::custom(NotUtf8))
}),
)
.and(users.clone())
.map(|my_id, msg, users| {
user_message(my_id, msg, &users);
warp::reply()
});
// GET /chat -> messages stream
let chat_recv = warp::path("chat").and(warp::get()).and(users).map(|users| {
// reply using server-sent events
let stream = user_connected(users);
warp::sse::reply(warp::sse::keep_alive().stream(stream))
});
// GET / -> index html
let index = warp::path::end().map(|| {
warp::http::Response::builder()
.header("content-type", "text/html; charset=utf-8")
.body(INDEX_HTML)
});
let routes = index.or(chat_recv).or(chat_send);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
/// Our global unique user id counter.
static NEXT_USER_ID: AtomicUsize = AtomicUsize::new(1);
/// Message variants.
#[derive(Debug)]
enum Message {
UserId(usize),
Reply(String),
}
#[derive(Debug)]
struct NotUtf8;
impl warp::reject::Reject for NotUtf8 {}
/// Our state of currently connected users.
///
/// - Key is their id
/// - Value is a sender of `Message`
type Users = Arc<Mutex<HashMap<usize, mpsc::UnboundedSender<Message>>>>;
fn user_connected(users: Users) -> impl Stream<Item = Result<Event, warp::Error>> + Send + 'static {
// Use a counter to assign a new unique ID for this user.
let my_id = NEXT_USER_ID.fetch_add(1, Ordering::Relaxed);
eprintln!("new chat user: {}", my_id);
// Use an unbounded channel to handle buffering and flushing of messages
// to the event source...
let (tx, rx) = mpsc::unbounded_channel();
let rx = UnboundedReceiverStream::new(rx);
tx.send(Message::UserId(my_id))
// rx is right above, so this cannot fail
.unwrap();
// Save the sender in our list of connected users.
users.lock().unwrap().insert(my_id, tx);
// Convert messages into Server-Sent Events and return resulting stream.
rx.map(|msg| match msg {
Message::UserId(my_id) => Ok(Event::default().event("user").data(my_id.to_string())),
Message::Reply(reply) => Ok(Event::default().data(reply)),
})
}
fn user_message(my_id: usize, msg: String, users: &Users) {
let new_msg = format!("<User#{}>: {}", my_id, msg);
// New message from this user, send it to everyone else (except same uid)...
//
// We use `retain` instead of a for loop so that we can reap any user that
// appears to have disconnected.
users.lock().unwrap().retain(|uid, tx| {
if my_id == *uid {
// don't send to same user, but do retain
true
} else {
// If not `is_ok`, the SSE stream is gone, and so don't retain
tx.send(Message::Reply(new_msg.clone())).is_ok()
}
});
}
static INDEX_HTML: &str = r#"
<!DOCTYPE html>
<html>
<head>
<title>Warp Chat</title>
</head>
<body>
<h1>warp chat</h1>
<div id="chat">
<p><em>Connecting...</em></p>
</div>
<input type="text" id="text" />
<button type="button" id="send">Send</button>
<script type="text/javascript">
var uri = 'http://' + location.host + '/chat';
var sse = new EventSource(uri);
function message(data) {
var line = document.createElement('p');
line.innerText = data;
chat.appendChild(line);
}
sse.onopen = function() {
chat.innerHTML = "<p><em>Connected!</em></p>";
}
var user_id;
sse.addEventListener("user", function(msg) {
user_id = msg.data;
});
sse.onmessage = function(msg) {
message(msg.data);
};
send.onclick = function() {
var msg = text.value;
var xhr = new XMLHttpRequest();
xhr.open("POST", uri + '/' + user_id, true);
xhr.send(msg);
text.value = '';
message('<You>: ' + msg);
};
</script>
</body>
</html>
"#;
================================================
FILE: examples/tls/cert.ecc.pem
================================================
-----BEGIN CERTIFICATE-----
MIICGjCCAb+gAwIBAgIUEbF3/5NuJeGvIywbwta91AkJxTgwCgYIKoZIzj0EAwIw
YjELMAkGA1UEBhMCREUxEDAOBgNVBAgMB0dlcm1hbnkxEDAOBgNVBAcMB0xlaXB6
aWcxEjAQBgNVBAMMCWxvY2FsLmRldjEbMBkGCSqGSIb3DQEJARYMaGlAbG9jYWwu
ZGV2MB4XDTI0MDcyMzA3MDMzOFoXDTI3MDcyMzA3MDMzOFowYjELMAkGA1UEBhMC
REUxEDAOBgNVBAgMB0dlcm1hbnkxEDAOBgNVBAcMB0xlaXB6aWcxEjAQBgNVBAMM
CWxvY2FsLmRldjEbMBkGCSqGSIb3DQEJARYMaGlAbG9jYWwuZGV2MFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAE2UeBetF/oh43g+pMkmX15YzXJA29tkGEO+k7OBhW
FpHQ7LVOsnocchEfjGVrJlJ0xPxst5p6UpjM6EgX6CkZh6NTMFEwHQYDVR0OBBYE
FFERjkqZLloI4V6XcrsutHi0oiwtMB8GA1UdIwQYMBaAFFERjkqZLloI4V6Xcrsu
tHi0oiwtMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAM3fWlv0
5OW660apTKBgNxtQQ/JW4mdeuqBuI1+Jea6DAiEAhb1Kpcksg1iyMLJ4uMQvUePY
smRQQ67YgoLE+W5JAuw=
-----END CERTIFICATE-----
================================================
FILE: examples/tls/cert.pem
================================================
-----BEGIN CERTIFICATE-----
MIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u
eXRvd24gUlNBIGxldmVsIDIgaW50ZXJtZWRpYXRlMB4XDTE2MDgxMzE2MDcwNFoX
DTIyMDIwMzE2MDcwNFowGTEXMBUGA1UEAwwOdGVzdHNlcnZlci5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpVhh1/FNP2qvWenbZSghari/UThwe
dynfnHG7gc3JmygkEdErWBO/CHzHgsx7biVE5b8sZYNEDKFojyoPHGWK2bQM/FTy
niJCgNCLdn6hUqqxLAml3cxGW77hAWu94THDGB1qFe+eFiAUnDmob8gNZtAzT6Ky
b/JGJdrEU0wj+Rd7wUb4kpLInNH/Jc+oz2ii2AjNbGOZXnRz7h7Kv3sO9vABByYe
LcCj3qnhejHMqVhbAT1MD6zQ2+YKBjE52MsQKU/xhUpu9KkUyLh0cxkh3zrFiKh4
Vuvtc+n7aeOv2jJmOl1dr0XLlSHBlmoKqH6dCTSbddQLmlK7dms8vE01AgMBAAGj
gb4wgbswDAYDVR0TAQH/BAIwADALBgNVHQ8EBAMCBsAwHQYDVR0OBBYEFMeUzGYV
bXwJNQVbY1+A8YXYZY8pMEIGA1UdIwQ7MDmAFJvEsUi7+D8vp8xcWvnEdVBGkpoW
oR6kHDAaMRgwFgYDVQQDDA9wb255dG93biBSU0EgQ0GCAXswOwYDVR0RBDQwMoIO
dGVzdHNlcnZlci5jb22CFXNlY29uZC50ZXN0c2VydmVyLmNvbYIJbG9jYWxob3N0
MA0GCSqGSIb3DQEBCwUAA4IBgQBsk5ivAaRAcNgjc7LEiWXFkMg703AqDDNx7kB1
RDgLalLvrjOfOp2jsDfST7N1tKLBSQ9bMw9X4Jve+j7XXRUthcwuoYTeeo+Cy0/T
1Q78ctoX74E2nB958zwmtRykGrgE/6JAJDwGcgpY9kBPycGxTlCN926uGxHsDwVs
98cL6ZXptMLTR6T2XP36dAJZuOICSqmCSbFR8knc/gjUO36rXTxhwci8iDbmEVaf
BHpgBXGU5+SQ+QM++v6bHGf4LNQC5NZ4e4xvGax8ioYu/BRsB/T3Lx+RlItz4zdU
XuxCNcm3nhQV2ZHquRdbSdoyIxV5kJXel4wCmOhWIq7A2OBKdu5fQzIAzzLi65EN
RPAKsKB4h7hGgvciZQ7dsMrlGw0DLdJ6UrFyiR5Io7dXYT/+JP91lP5xsl6Lhg9O
FgALt7GSYRm2cZdgi9pO9rRr83Br1VjQT1vHz6yoZMXSqc4A2zcN2a2ZVq//rHvc
FZygs8miAhWPzqnpmgTj1cPiU1M=
-----END CERTIFICATE-----
================================================
FILE: examples/tls/key.ecc
================================================
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIPwp3LAnLEyWe2lLz66Y3QCCJ/BEMJheTM0shZnnSw6toAoGCCqGSM49
AwEHoUQDQgAE2UeBetF/oh43g+pMkmX15YzXJA29tkGEO+k7OBhWFpHQ7LVOsnoc
chEfjGVrJlJ0xPxst5p6UpjM6EgX6CkZhw==
-----END EC PRIVATE KEY-----
================================================
FILE: examples/tls/key.rsa
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAqVYYdfxTT9qr1np22UoIWq4v1E4cHncp35xxu4HNyZsoJBHR
K1gTvwh8x4LMe24lROW/LGWDRAyhaI8qDxxlitm0DPxU8p4iQoDQi3Z+oVKqsSwJ
pd3MRlu+4QFrveExwxgdahXvnhYgFJw5qG/IDWbQM0+ism/yRiXaxFNMI/kXe8FG
+JKSyJzR/yXPqM9ootgIzWxjmV50c+4eyr97DvbwAQcmHi3Ao96p4XoxzKlYWwE9
TA+s0NvmCgYxOdjLEClP8YVKbvSpFMi4dHMZId86xYioeFbr7XPp+2njr9oyZjpd
Xa9Fy5UhwZZqCqh+nQk0m3XUC5pSu3ZrPLxNNQIDAQABAoIBAFKtZJgGsK6md4vq
kyiYSufrcBLaaEQ/rkQtYCJKyC0NAlZKFLRy9oEpJbNLm4cQSkYPXn3Qunx5Jj2k
2MYz+SgIDy7f7KHgr52Ew020dzNQ52JFvBgt6NTZaqL1TKOS1fcJSSNIvouTBerK
NCSXHzfb4P+MfEVe/w1c4ilE+kH9SzdEo2jK/sRbzHIY8TX0JbmQ4SCLLayr22YG
usIxtIYcWt3MMP/G2luRnYzzBCje5MXdpAhlHLi4TB6x4h5PmBKYc57uOVNngKLd
YyrQKcszW4Nx5v0a4HG3A5EtUXNCco1+5asXOg2lYphQYVh2R+1wgu5WiDjDVu+6
EYgjFSkCgYEA0NBk6FDoxE/4L/4iJ4zIhu9BptN8Je/uS5c6wRejNC/VqQyw7SHb
hRFNrXPvq5Y+2bI/DxtdzZLKAMXOMjDjj0XEgfOIn2aveOo3uE7zf1i+njxwQhPu
uSYA9AlBZiKGr2PCYSDPnViHOspVJjxRuAgyWM1Qf+CTC0D95aj0oz8CgYEAz5n4
Cb3/WfUHxMJLljJ7PlVmlQpF5Hk3AOR9+vtqTtdxRjuxW6DH2uAHBDdC3OgppUN4
CFj55kzc2HUuiHtmPtx8mK6G+otT7Lww+nLSFL4PvZ6CYxqcio5MPnoYd+pCxrXY
JFo2W7e4FkBOxb5PF5So5plg+d0z/QiA7aFP1osCgYEAtgi1rwC5qkm8prn4tFm6
hkcVCIXc+IWNS0Bu693bXKdGr7RsmIynff1zpf4ntYGpEMaeymClCY0ppDrMYlzU
RBYiFNdlBvDRj6s/H+FTzHRk2DT/99rAhY9nzVY0OQFoQIXK8jlURGrkmI/CYy66
XqBmo5t4zcHM7kaeEBOWEKkCgYAYnO6VaRtPNQfYwhhoFFAcUc+5t+AVeHGW/4AY
M5qlAlIBu64JaQSI5KqwS0T4H+ZgG6Gti68FKPO+DhaYQ9kZdtam23pRVhd7J8y+
xMI3h1kiaBqZWVxZ6QkNFzizbui/2mtn0/JB6YQ/zxwHwcpqx0tHG8Qtm5ZAV7PB
eLCYhQKBgQDALJxU/6hMTdytEU5CLOBSMby45YD/RrfQrl2gl/vA0etPrto4RkVq
UrkDO/9W4mZORClN3knxEFSTlYi8YOboxdlynpFfhcs82wFChs+Ydp1eEsVHAqtu
T+uzn0sroycBiBfVB949LExnzGDFUkhG0i2c2InarQYLTsIyHCIDEA==
-----END RSA PRIVATE KEY-----
================================================
FILE: examples/tls.rs
================================================
#![deny(warnings)]
// Don't copy this `cfg`, it's only needed because this file is within
// the warp repository.
// Instead, specify the "tls" feature in your warp dependency declaration.
#[cfg(feature = "tls")]
#[tokio::main]
async fn main() {
use warp::Filter;
// Match any request and return hello world!
let routes = warp::any().map(|| "Hello, World!");
warp::serve(routes)
.tls()
// RSA
.cert_path("examples/tls/cert.pem")
.key_path("examples/tls/key.rsa")
// ECC
// .cert_path("examples/tls/cert.ecc.pem")
// .key_path("examples/tls/key.ecc")
.run(([127, 0, 0, 1], 3030))
.await;
}
#[cfg(not(feature = "tls"))]
fn main() {
eprintln!("Requires the `tls` feature.");
}
================================================
FILE: examples/todos.rs
================================================
#![deny(warnings)]
use std::env;
use warp::Filter;
/// Provides a RESTful web server managing some Todos.
///
/// API will be:
///
/// - `GET /todos`: return a JSON list of Todos.
/// - `POST /todos`: create a new Todo.
/// - `PUT /todos/:id`: update a specific Todo.
/// - `DELETE /todos/:id`: delete a specific Todo.
#[tokio::main]
async fn main() {
if env::var_os("RUST_LOG").is_none() {
// Set `RUST_LOG=todos=debug` to see debug logs,
// this only shows access logs.
env::set_var("RUST_LOG", "todos=info");
}
pretty_env_logger::init();
let db = models::blank_db();
let api = filters::todos(db);
// View access logs by setting `RUST_LOG=todos`.
let routes = api.with(warp::log("todos"));
// Start up the server...
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
mod filters {
use super::handlers;
use super::models::{Db, ListOptions, Todo};
use warp::Filter;
/// The 4 TODOs filters combined.
pub fn todos(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
todos_list(db.clone())
.or(todos_create(db.clone()))
.or(todos_update(db.clone()))
.or(todos_delete(db))
}
/// GET /todos?offset=3&limit=5
pub fn todos_list(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
warp::path!("todos")
.and(warp::get())
.and(warp::query::<ListOptions>())
.and(with_db(db))
.and_then(handlers::list_todos)
}
/// POST /todos with JSON body
pub fn todos_create(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
warp::path!("todos")
.and(warp::post())
.and(json_body())
.and(with_db(db))
.and_then(handlers::create_todo)
}
/// PUT /todos/:id with JSON body
pub fn todos_update(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
warp::path!("todos" / u64)
.and(warp::put())
.and(json_body())
.and(with_db(db))
.and_then(handlers::update_todo)
}
/// DELETE /todos/:id
pub fn todos_delete(
db: Db,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
// We'll make one of our endpoints admin-only to show how authentication filters are used
let admin_only = warp::header::exact("authorization", "Bearer admin");
warp::path!("todos" / u64)
// It is important to put the auth check _after_ the path filters.
// If we put the auth check before, the request `PUT /todos/invalid-string`
// would try this filter and reject because the authorization header doesn't match,
// rather because the param is wrong for that other path.
.and(admin_only)
.and(warp::delete())
.and(with_db(db))
.and_then(handlers::delete_todo)
}
fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || db.clone())
}
fn json_body() -> impl Filter<Extract = (Todo,), Error = warp::Rejection> + Clone {
// When accepting a body, we want a JSON body
// (and to reject huge payloads)...
warp::body::content_length_limit(1024 * 16).and(warp::body::json())
}
}
/// These are our API handlers, the ends of each filter chain.
/// Notice how thanks to using `Filter::and`, we can define a function
/// with the exact arguments we'd expect from each filter in the chain.
/// No tuples are needed, it's auto flattened for the functions.
mod handlers {
use super::models::{Db, ListOptions, Todo};
use std::convert::Infallible;
use warp::http::StatusCode;
pub async fn list_todos(opts: ListOptions, db: Db) -> Result<impl warp::Reply, Infallible> {
// Just return a JSON array of todos, applying the limit and offset.
let todos = db.lock().await;
let todos: Vec<Todo> = todos
.clone()
.into_iter()
.skip(opts.offset.unwrap_or(0))
.take(opts.limit.unwrap_or(std::usize::MAX))
.collect();
Ok(warp::reply::json(&todos))
}
pub async fn create_todo(create: Todo, db: Db) -> Result<impl warp::Reply, Infallible> {
log::debug!("create_todo: {:?}", create);
let mut vec = db.lock().await;
for todo in vec.iter() {
if todo.id == create.id {
log::debug!(" -> id already exists: {}", create.id);
// Todo with id already exists, return `400 BadRequest`.
return Ok(StatusCode::BAD_REQUEST);
}
}
// No existing Todo with id, so insert and return `201 Created`.
vec.push(create);
Ok(StatusCode::CREATED)
}
pub async fn update_todo(
id: u64,
update: Todo,
db: Db,
) -> Result<impl warp::Reply, Infallible> {
log::debug!("update_todo: id={}, todo={:?}", id, update);
let mut vec = db.lock().await;
// Look for the specified Todo...
for todo in vec.iter_mut() {
if todo.id == id {
*todo = update;
return Ok(StatusCode::OK);
}
}
log::debug!(" -> todo id not found!");
// If the for loop didn't return OK, then the ID doesn't exist...
Ok(StatusCode::NOT_FOUND)
}
pub async fn delete_todo(id: u64, db: Db) -> Result<impl warp::Reply, Infallible> {
log::debug!("delete_todo: id={}", id);
let mut vec = db.lock().await;
let len = vec.len();
vec.retain(|todo| {
// Retain all Todos that aren't this id...
// In other words, remove all that *are* this id...
todo.id != id
});
// If the vec is smaller, we found and deleted a Todo!
let deleted = vec.len() != len;
if deleted {
// respond with a `204 No Content`, which means successful,
// yet no body expected...
Ok(StatusCode::NO_CONTENT)
} else {
log::debug!(" -> todo id not found!");
Ok(StatusCode::NOT_FOUND)
}
}
}
mod models {
use serde_derive::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::Mutex;
/// So we don't have to tackle how different database work, we'll just use
/// a simple in-memory DB, a vector synchronized by a mutex.
pub type Db = Arc<Mutex<Vec<Todo>>>;
pub fn blank_db() -> Db {
Arc::new(Mutex::new(Vec::new()))
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub struct Todo {
pub id: u64,
pub text: String,
pub completed: bool,
}
// The query parameters for list_todos.
#[derive(Debug, Deserialize)]
pub struct ListOptions {
pub offset: Option<usize>,
pub limit: Option<usize>,
}
}
#[cfg(test)]
mod tests {
use warp::http::StatusCode;
use warp::test::request;
use super::{
filters,
models::{self, Todo},
};
#[tokio::test]
async fn test_post() {
let db = models::blank_db();
let api = filters::todos(db);
let resp = request()
.method("POST")
.path("/todos")
.json(&todo1())
.reply(&api)
.await;
assert_eq!(resp.status(), StatusCode::CREATED);
}
#[tokio::test]
async fn test_post_conflict() {
let db = models::blank_db();
db.lock().await.push(todo1());
let api = filters::todos(db);
let resp = request()
.method("POST")
.path("/todos")
.json(&todo1())
.reply(&api)
.await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
#[tokio::test]
async fn test_put_unknown() {
let _ = pretty_env_logger::try_init();
let db = models::blank_db();
let api = filters::todos(db);
let resp = request()
.method("PUT")
.path("/todos/1")
.header("authorization", "Bearer admin")
.json(&todo1())
.reply(&api)
.await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
}
fn todo1() -> Todo {
Todo {
id: 1,
text: "test 1".into(),
completed: false,
}
}
}
================================================
FILE: examples/tracing.rs
================================================
//! [`tracing`] is a framework for instrumenting Rust programs to
//! collect scoped, structured, and async-aware diagnostics. This example
//! demonstrates how the `warp::trace` module can be used to instrument `warp`
//! applications with `tracing`.
//!
//! [`tracing`]: https://crates.io/crates/tracing
#![deny(warnings)]
use tracing_subscriber::fmt::format::FmtSpan;
use warp::Filter;
#[tokio::main]
async fn main() {
// Filter traces based on the RUST_LOG env var, or, if it's not set,
// default to show the output of the example.
let filter = std::env::var("RUST_LOG").unwrap_or_else(|_| "tracing=info,warp=debug".to_owned());
// Configure the default `tracing` subscriber.
// The `fmt` subscriber from the `tracing-subscriber` crate logs `tracing`
// events to stdout. Other subscribers are available for integrating with
// distributed tracing systems such as OpenTelemetry.
tracing_subscriber::fmt()
// Use the filter we built above to determine which traces to record.
.with_env_filter(filter)
// Record an event when each span closes. This can be used to time our
// routes' durations!
.with_span_events(FmtSpan::CLOSE)
.init();
let hello = warp::path("hello")
.and(warp::get())
// When the `hello` route is called, emit a `tracing` event.
.map(|| {
tracing::info!("saying hello...");
"Hello, World!"
})
// Wrap the route in a `tracing` span to add the route's name as context
// to any events that occur inside it.
.with(warp::trace::named("hello"));
let goodbye = warp::path("goodbye")
.and(warp::get())
.map(|| {
tracing::info!("saying goodbye...");
"So long and thanks for all the fish!"
})
// We can also provide our own custom `tracing` spans to wrap a route.
.with(warp::trace(|info| {
// Construct our own custom span for this route.
tracing::info_span!("goodbye", req.path = ?info.path())
}));
let routes = hello
.or(goodbye)
// Wrap all the routes with a filter that creates a `tracing` span for
// each request we receive, including data about the request.
.with(warp::trace::request());
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/unix_socket.rs
================================================
#![deny(warnings)]
#[cfg(unix)]
#[tokio::main]
async fn main() {
use tokio::net::UnixListener;
pretty_env_logger::init();
let socket = "/tmp/warp.sock";
let listener = UnixListener::bind(socket).unwrap();
warp::serve(warp::fs::dir("examples/dir"))
.incoming(listener)
.graceful(async { tokio::signal::ctrl_c().await.unwrap() })
.run()
.await;
std::fs::remove_file(socket).unwrap();
}
#[cfg(not(unix))]
#[tokio::main]
async fn main() {
panic!("Must run under Unix-like platform!");
}
================================================
FILE: examples/websockets.rs
================================================
#![deny(warnings)]
use futures_util::{FutureExt, StreamExt};
use warp::Filter;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
let routes = warp::path("echo")
// The `ws()` filter will prepare the Websocket handshake.
.and(warp::ws())
.map(|ws: warp::ws::Ws| {
// And then our closure will be called when it completes...
ws.on_upgrade(|websocket| {
// Just echo all messages back...
let (tx, rx) = websocket.split();
rx.forward(tx).map(|result| {
if let Err(e) = result {
eprintln!("websocket error: {:?}", e);
}
})
})
});
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
================================================
FILE: examples/websockets_chat.rs
================================================
// #![deny(warnings)]
use std::collections::HashMap;
use std::sync::{
atomic::{AtomicUsize, Ordering},
Arc,
};
use futures_util::{SinkExt, StreamExt, TryFutureExt};
use tokio::sync::{mpsc, RwLock};
use tokio_stream::wrappers::UnboundedReceiverStream;
use warp::ws::{Message, WebSocket};
use warp::Filter;
/// Our global unique user id counter.
static NEXT_USER_ID: AtomicUsize = AtomicUsize::new(1);
/// Our state of currently connected users.
///
/// - Key is their id
/// - Value is a sender of `warp::ws::Message`
type Users = Arc<RwLock<HashMap<usize, mpsc::UnboundedSender<Message>>>>;
#[tokio::main]
async fn main() {
pretty_env_logger::init();
// Keep track of all connected users, key is usize, value
// is a websocket sender.
let users = Users::default();
// Turn our "state" into a new Filter...
let users = warp::any().map(move || users.clone());
// GET /chat -> websocket upgrade
let chat = warp::path("chat")
// The `ws()` filter will prepare Websocket handshake...
.and(warp::ws())
.and(users)
.map(|ws: warp::ws::Ws, users| {
// This will call our function if the handshake succeeds.
ws.on_upgrade(move |socket| user_connected(socket, users))
});
// GET / -> index html
let index = warp::path::end().map(|| warp::reply::html(INDEX_HTML));
let routes = index.or(chat);
warp::serve(routes).run(([127, 0, 0, 1], 3030)).await;
}
async fn user_connected(ws: WebSocket, users: Users) {
// Use a counter to assign a new unique ID for this user.
let my_id = NEXT_USER_ID.fetch_add(1, Ordering::Relaxed);
eprintln!("new chat user: {}", my_id);
// Split the socket into a sender and receive of messages.
let (mut user_ws_tx, mut user_ws_rx) = ws.split();
// Use an unbounded channel to handle buffering and flushing of messages
// to the websocket...
let (tx, rx) = mpsc::unbounded_channel();
let mut rx = UnboundedReceiverStream::new(rx);
tokio::task::spawn(async move {
while let Some(message) = rx.next().await {
user_ws_tx
.send(message)
.unwrap_or_else(|e| {
eprintln!("websocket send error: {}", e);
})
.await;
}
});
// Save the sender in our list of connected users.
users.write().await.insert(my_id, tx);
// Return a `Future` that is basically a state machine managing
// this specific user's connection.
// Every time the user sends a message, broadcast it to
// all other users...
while let Some(result) = user_ws_rx.next().await {
let msg = match result {
Ok(msg) => msg,
Err(e) => {
eprintln!("websocket error(uid={}): {}", my_id, e);
break;
}
};
user_message(my_id, msg, &users).await;
}
// user_ws_rx stream will keep processing as long as the user stays
// connected. Once they disconnect, then...
user_disconnected(my_id, &users).await;
}
async fn user_message(my_id: usize, msg: Message, users: &Users) {
// Skip any non-Text messages...
let msg = if let Ok(s) = msg.to_str() {
s
} else {
return;
};
let new_msg = format!("<User#{}>: {}", my_id, msg);
// New message from this user, send it to everyone else (except same uid)...
for (&uid, tx) in users.read().await.iter() {
if my_id != uid {
if let Err(_disconnected) = tx.send(Message::text(new_msg.clone())) {
// The tx is disconnected, our `user_disconnected` code
// should be happening in another task, nothing more to
// do here.
}
}
}
}
async fn user_disconnected(my_id: usize, users: &Users) {
eprintln!("good bye user: {}", my_id);
// Stream closed up, so remove from the user list
users.write().await.remove(&my_id);
}
static INDEX_HTML: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<title>Warp Chat</title>
</head>
<body>
<h1>Warp chat</h1>
<div id="chat">
<p><em>Connecting...</em></p>
</div>
<input type="text" id="text" />
<button type="button" id="send">Send</button>
<script type="text/javascript">
const chat = document.getElementById('chat');
const text = document.getElementById('text');
const uri = 'ws://' + location.host + '/chat';
const ws = new WebSocket(uri);
function message(data) {
const line = document.createElement('p');
line.innerText = data;
chat.appendChild(line);
}
ws.onopen = function() {
chat.innerHTML = '<p><em>Connected!</em></p>';
};
ws.onmessage = function(msg) {
message(msg.data);
};
ws.onclose = function() {
chat.getElementsByTagName('em')[0].innerText = 'Disconnected!';
};
send.onclick = function() {
const msg = text.value;
ws.send(msg);
text.value = '';
message('<You>: ' + msg);
};
</script>
</body>
</html>
"#;
================================================
FILE: src/bodyt.rs
================================================
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::Buf;
use bytes::Bytes;
use futures_util::StreamExt;
use http_body::Frame;
use http_body_util::{combinators::BoxBody, BodyExt};
#[derive(Debug)]
pub struct Body(BoxBody<Bytes, crate::Error>);
impl Default for Body {
fn default() -> Self {
Body::empty()
}
}
impl http_body::Body for Body {
type Data = Bytes;
type Error = crate::Error;
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
Pin::new(&mut self.0).poll_frame(cx)
}
fn is_end_stream(&self) -> bool {
self.0.is_end_stream()
}
fn size_hint(&self) -> http_body::SizeHint {
self.0.size_hint()
}
}
impl Body {
pub(crate) fn empty() -> Self {
Body(
http_body_util::Empty::<Bytes>::new()
.map_err(crate::Error::new)
.boxed(),
)
}
pub(crate) fn wrap<B>(body: B) -> Self
where
B: http_body::Body + Send + Sync + 'static,
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let body = body
.map_frame(|f| f.map_data(|mut buf| buf.copy_to_bytes(buf.remaining())))
.map_err(crate::Error::new);
Body(http_body_util::BodyExt::boxed(body))
}
pub(crate) fn wrap_stream<S, B, E>(stream: S) -> Self
where
S: futures_util::Stream<Item = Result<B, E>> + Send + Sync + 'static,
B: Into<Bytes>,
E: Into<Box<dyn std::error::Error + Send + Sync>> + Send + 'static,
{
let body = http_body_util::StreamBody::new(stream.map(|item| {
item.map(|buf| Frame::data(buf.into()))
.map_err(crate::Error::new)
}));
Body(http_body_util::BodyExt::boxed(body))
}
}
impl From<Bytes> for Body {
fn from(b: Bytes) -> Self {
Body(
http_body_util::Full::new(b)
.map_err(crate::Error::new)
.boxed(),
)
}
}
impl From<&'static str> for Body {
fn from(s: &'static str) -> Self {
Bytes::from(s).into()
}
}
impl From<String> for Body {
fn from(s: String) -> Self {
Bytes::from(s).into()
}
}
impl From<&'static [u8]> for Body {
fn from(v: &'static [u8]) -> Self {
Bytes::from(v).into()
}
}
impl From<Vec<u8>> for Body {
fn from(v: Vec<u8>) -> Self {
Bytes::from(v).into()
}
}
impl From<Option<Bytes>> for Body {
fn from(opt: Option<Bytes>) -> Self {
match opt {
Some(b) => b.into(),
None => Body::empty(),
}
}
}
================================================
FILE: src/error.rs
================================================
use std::convert::Infallible;
use std::error::Error as StdError;
use std::fmt;
type BoxError = Box<dyn std::error::Error + Send + Sync>;
/// Errors that can happen inside warp.
pub struct Error {
inner: BoxError,
}
impl Error {
pub(crate) fn new<E: Into<BoxError>>(err: E) -> Error {
Error { inner: err.into() }
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Skip showing worthless `Error { .. }` wrapper.
fmt::Debug::fmt(&self.inner, f)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.inner, f)
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(self.inner.as_ref())
}
}
impl From<Infallible> for Error {
fn from(infallible: Infallible) -> Error {
match infallible {}
}
}
#[test]
fn error_size_of() {
assert_eq!(
::std::mem::size_of::<Error>(),
::std::mem::size_of::<usize>() * 2
);
}
#[test]
fn error_source() {
let e = Error::new(std::fmt::Error {});
assert!(e.source().unwrap().is::<std::fmt::Error>());
}
macro_rules! unit_error {
(
$(#[$docs:meta])*
$pub:vis $typ:ident: $display:literal
) => (
$(#[$docs])*
$pub struct $typ { _p: (), }
impl ::std::fmt::Debug for $typ {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.debug_struct(stringify!($typ)).finish()
}
}
impl ::std::fmt::Display for $typ {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
f.write_str($display)
}
}
impl ::std::error::Error for $typ {}
)
}
================================================
FILE: src/filter/and.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::ready;
use pin_project::pin_project;
use super::{Combine, Filter, FilterBase, Internal, Tuple};
use crate::generic::CombinedTuples;
use crate::reject::CombineRejection;
#[derive(Clone, Copy, Debug)]
pub struct And<T, U> {
pub(super) first: T,
pub(super) second: U,
}
impl<T, U> FilterBase for And<T, U>
where
T: Filter,
T::Extract: Send,
U: Filter + Clone + Send,
<T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList> + Send,
CombinedTuples<T::Extract, U::Extract>: Send,
U::Error: CombineRejection<T::Error>,
{
type Extract = CombinedTuples<T::Extract, U::Extract>;
type Error = <U::Error as CombineRejection<T::Error>>::One;
type Future = AndFuture<T, U>;
fn filter(&self, _: Internal) -> Self::Future {
AndFuture {
state: State::First(self.first.filter(Internal), self.second.clone()),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct AndFuture<T: Filter, U: Filter> {
#[pin]
state: State<T::Future, T::Extract, U>,
}
#[pin_project(project = StateProj)]
enum State<T, TE, U: Filter> {
First(#[pin] T, U),
Second(Option<TE>, #[pin] U::Future),
Done,
}
impl<T, U> Future for AndFuture<T, U>
where
T: Filter,
U: Filter,
<T::Extract as Tuple>::HList: Combine<<U::Extract as Tuple>::HList> + Send,
U::Error: CombineRejection<T::Error>,
{
type Output = Result<
CombinedTuples<T::Extract, U::Extract>,
<U::Error as CombineRejection<T::Error>>::One,
>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().state.poll(cx)
}
}
impl<T, TE, U, E> Future for State<T, TE, U>
where
T: Future<Output = Result<TE, E>>,
U: Filter,
TE: Tuple,
TE::HList: Combine<<U::Extract as Tuple>::HList> + Send,
U::Error: CombineRejection<E>,
{
type Output = Result<CombinedTuples<TE, U::Extract>, <U::Error as CombineRejection<E>>::One>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match self.as_mut().project() {
StateProj::First(first, second) => {
let ex1 = ready!(first.poll(cx))?;
let fut2 = second.filter(Internal);
self.set(State::Second(Some(ex1), fut2));
}
StateProj::Second(ex1, second) => {
let ex2 = ready!(second.poll(cx))?;
let ex3 = ex1.take().unwrap().combine(ex2);
self.set(State::Done);
return Poll::Ready(Ok(ex3));
}
StateProj::Done => panic!("polled after complete"),
}
}
}
}
================================================
FILE: src/filter/and_then.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Func, Internal};
use crate::reject::CombineRejection;
#[derive(Clone, Copy, Debug)]
pub struct AndThen<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F> FilterBase for AndThen<T, F>
where
T: Filter,
F: Func<T::Extract> + Clone + Send,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<T::Error>,
{
type Extract = (<F::Output as TryFuture>::Ok,);
type Error = <<F::Output as TryFuture>::Error as CombineRejection<T::Error>>::One;
type Future = AndThenFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
AndThenFuture {
state: State::First(self.filter.filter(Internal), self.callback.clone()),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct AndThenFuture<T, F>
where
T: Filter,
F: Func<T::Extract>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<T::Error>,
{
#[pin]
state: State<T::Future, F>,
}
#[pin_project(project = StateProj)]
enum State<T, F>
where
T: TryFuture,
F: Func<T::Ok>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<T::Error>,
{
First(#[pin] T, F),
Second(#[pin] F::Output),
Done,
}
impl<T, F> Future for AndThenFuture<T, F>
where
T: Filter,
F: Func<T::Extract>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<T::Error>,
{
type Output = Result<
(<F::Output as TryFuture>::Ok,),
<<F::Output as TryFuture>::Error as CombineRejection<T::Error>>::One,
>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().state.poll(cx)
}
}
impl<T, F> Future for State<T, F>
where
T: TryFuture,
F: Func<T::Ok>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<T::Error>,
{
type Output = Result<
(<F::Output as TryFuture>::Ok,),
<<F::Output as TryFuture>::Error as CombineRejection<T::Error>>::One,
>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match self.as_mut().project() {
StateProj::First(first, second) => {
let ex1 = ready!(first.try_poll(cx))?;
let fut2 = second.call(ex1);
self.set(State::Second(fut2));
}
StateProj::Second(second) => {
let ex2 = match ready!(second.try_poll(cx)) {
Ok(item) => Ok((item,)),
Err(err) => Err(From::from(err)),
};
self.set(State::Done);
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
}
}
}
}
================================================
FILE: src/filter/boxed.rs
================================================
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use futures_util::TryFutureExt;
use super::{Filter, FilterBase, Internal, Tuple};
use crate::reject::Rejection;
/// A type representing a boxed [`Filter`](crate::Filter) trait object.
///
/// The filter inside is a dynamic trait object. The purpose of this type is
/// to ease returning `Filter`s from other functions.
///
/// To create one, call `Filter::boxed` on any filter.
///
/// # Examples
///
/// ```
/// use warp::{Filter, filters::BoxedFilter, Reply};
///
/// pub fn assets_filter() -> BoxedFilter<(impl Reply,)> {
/// warp::path("assets")
/// .and(warp::fs::dir("./assets"))
/// .boxed()
/// }
/// ```
///
pub struct BoxedFilter<T: Tuple> {
filter: Arc<
dyn Filter<
Extract = T,
Error = Rejection,
Future = Pin<Box<dyn Future<Output = Result<T, Rejection>> + Send>>,
> + Send
+ Sync,
>,
}
impl<T: Tuple + Send> BoxedFilter<T> {
pub(super) fn new<F>(filter: F) -> BoxedFilter<T>
where
F: Filter<Extract = T> + Send + Sync + 'static,
F::Error: Into<Rejection>,
{
BoxedFilter {
filter: Arc::new(BoxingFilter {
filter: filter.map_err(super::Internal, Into::into),
}),
}
}
}
impl<T: Tuple> Clone for BoxedFilter<T> {
fn clone(&self) -> BoxedFilter<T> {
BoxedFilter {
filter: self.filter.clone(),
}
}
}
impl<T: Tuple> fmt::Debug for BoxedFilter<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxedFilter").finish()
}
}
fn _assert_send() {
fn _assert<T: Send>() {}
_assert::<BoxedFilter<()>>();
}
impl<T: Tuple + Send> FilterBase for BoxedFilter<T> {
type Extract = T;
type Error = Rejection;
type Future = Pin<Box<dyn Future<Output = Result<T, Rejection>> + Send>>;
fn filter(&self, _: Internal) -> Self::Future {
self.filter.filter(Internal)
}
}
struct BoxingFilter<F> {
filter: F,
}
impl<F> FilterBase for BoxingFilter<F>
where
F: Filter,
F::Future: Send + 'static,
{
type Extract = F::Extract;
type Error = F::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Extract, Self::Error>> + Send>>;
fn filter(&self, _: Internal) -> Self::Future {
Box::pin(self.filter.filter(Internal).into_future())
}
}
================================================
FILE: src/filter/map.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Func, Internal};
#[derive(Clone, Copy, Debug)]
pub struct Map<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F> FilterBase for Map<T, F>
where
T: Filter,
F: Func<T::Extract> + Clone + Send,
{
type Extract = (F::Output,);
type Error = T::Error;
type Future = MapFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
MapFuture {
extract: self.filter.filter(Internal),
callback: self.callback.clone(),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct MapFuture<T: Filter, F> {
#[pin]
extract: T::Future,
callback: F,
}
impl<T, F> Future for MapFuture<T, F>
where
T: Filter,
F: Func<T::Extract>,
{
type Output = Result<(F::Output,), T::Error>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pin = self.project();
match ready!(pin.extract.try_poll(cx)) {
Ok(ex) => {
let ex = (pin.callback.call(ex),);
Poll::Ready(Ok(ex))
}
Err(err) => Poll::Ready(Err(err)),
}
}
}
================================================
FILE: src/filter/map_err.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::TryFuture;
use pin_project::pin_project;
use super::{Filter, FilterBase, Internal};
use crate::reject::IsReject;
#[derive(Clone, Copy, Debug)]
pub struct MapErr<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F, E> FilterBase for MapErr<T, F>
where
T: Filter,
F: Fn(T::Error) -> E + Clone + Send,
E: IsReject,
{
type Extract = T::Extract;
type Error = E;
type Future = MapErrFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
MapErrFuture {
extract: self.filter.filter(Internal),
callback: self.callback.clone(),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct MapErrFuture<T: Filter, F> {
#[pin]
extract: T::Future,
callback: F,
}
impl<T, F, E> Future for MapErrFuture<T, F>
where
T: Filter,
F: Fn(T::Error) -> E,
{
type Output = Result<T::Extract, E>;
#[inline]
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.as_mut()
.project()
.extract
.try_poll(cx)
.map_err(|err| (self.callback)(err))
}
}
================================================
FILE: src/filter/mod.rs
================================================
mod and;
mod and_then;
mod boxed;
mod map;
mod map_err;
mod or;
mod or_else;
mod recover;
pub(crate) mod service;
mod then;
mod unify;
mod untuple_one;
mod wrap;
use std::future::Future;
use futures_util::{future, TryFuture, TryFutureExt};
pub(crate) use crate::generic::{one, Combine, Either, Func, One, Tuple};
use crate::reject::{CombineRejection, IsReject, Rejection};
use crate::route::{self, Route};
pub(crate) use self::and::And;
use self::and_then::AndThen;
pub use self::boxed::BoxedFilter;
pub(crate) use self::map::Map;
pub(crate) use self::map_err::MapErr;
pub(crate) use self::or::Or;
use self::or_else::OrElse;
use self::recover::Recover;
use self::then::Then;
use self::unify::Unify;
use self::untuple_one::UntupleOne;
pub use self::wrap::wrap_fn;
pub(crate) use self::wrap::{Wrap, WrapSealed};
// A crate-private base trait, allowing the actual `filter` method to change
// signatures without it being a breaking change.
pub trait FilterBase {
type Extract: Tuple; // + Send;
type Error: IsReject;
type Future: Future<Output = Result<Self::Extract, Self::Error>> + Send;
fn filter(&self, internal: Internal) -> Self::Future;
fn map_err<F, E>(self, _internal: Internal, fun: F) -> MapErr<Self, F>
where
Self: Sized,
F: Fn(Self::Error) -> E + Clone,
E: ::std::fmt::Debug + Send,
{
MapErr {
filter: self,
callback: fun,
}
}
}
// A crate-private argument to prevent users from calling methods on
// the `FilterBase` trait.
//
// For instance, this innocent user code could otherwise call `filter`:
//
// ```
// async fn with_filter<F: Filter>(f: F) -> Result<F::Extract, F::Error> {
// f.filter().await
// }
// ```
#[allow(missing_debug_implementations)]
pub struct Internal;
/// Composable request filters.
///
/// A `Filter` can optionally extract some data from a request, combine
/// it with others, mutate it, and return back some value as a reply. The
/// power of `Filter`s come from being able to isolate small subsets, and then
/// chain and reuse them in various parts of your app.
///
/// # Extracting Tuples
///
/// You may notice that several of these filters extract some tuple, often
/// times a tuple of just 1 item! Why?
///
/// If a filter extracts a `(String,)`, that simply means that it
/// extracts a `String`. If you were to `map` the filter, the argument type
/// would be exactly that, just a `String`.
///
/// What is it? It's just some type magic that allows for automatic combining
/// and flattening of tuples. Without it, combining two filters together with
/// `and`, where one extracted `()`, and another `String`, would mean the
/// `map` would be given a single argument of `((), String,)`, which is just
/// no fun.
pub trait Filter: FilterBase {
/// Composes a new `Filter` that requires both this and the other to filter a request.
///
/// Additionally, this will join together the extracted values of both
/// filters, so that `map` and `and_then` receive them as separate arguments.
///
/// If a `Filter` extracts nothing (so, `()`), combining with any other
/// filter will simply discard the `()`. If a `Filter` extracts one or
/// more items, combining will mean it extracts the values of itself
/// combined with the other.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Match `/hello/:name`...
/// warp::path("hello")
/// .and(warp::path::param::<String>());
/// ```
fn and<F>(self, other: F) -> And<Self, F>
where
Self: Sized,
<Self::Extract as Tuple>::HList: Combine<<F::Extract as Tuple>::HList>,
F: Filter + Clone,
F::Error: CombineRejection<Self::Error>,
{
And {
first: self,
second: other,
}
}
/// Composes a new `Filter` of either this or the other filter.
///
/// # Example
///
/// ```
/// use std::net::SocketAddr;
/// use warp::Filter;
///
/// // Match either `/:u32` or `/:socketaddr`
/// warp::path::param::<u32>()
/// .or(warp::path::param::<SocketAddr>());
/// ```
fn or<F>(self, other: F) -> Or<Self, F>
where
Self: Filter<Error = Rejection> + Sized,
F: Filter,
F::Error: CombineRejection<Self::Error>,
{
Or {
first: self,
second: other,
}
}
/// Composes this `Filter` with a function receiving the extracted value.
///
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Map `/:id`
/// warp::path::param().map(|id: u64| {
/// format!("Hello #{}", id)
/// });
/// ```
///
/// # `Func`
///
/// The generic `Func` trait is implemented for any function that receives
/// the same arguments as this `Filter` extracts. In practice, this
/// shouldn't ever bother you, and simply makes things feel more natural.
///
/// For example, if three `Filter`s were combined together, suppose one
/// extracts nothing (so `()`), and the other two extract two integers,
/// a function that accepts exactly two integer arguments is allowed.
/// Specifically, any `Fn(u32, u32)`.
///
/// Without `Product` and `Func`, this would be a lot messier. First of
/// all, the `()`s couldn't be discarded, and the tuples would be nested.
/// So, instead, you'd need to pass an `Fn(((), (u32, u32)))`. That's just
/// a single argument. Bleck!
///
/// Even worse, the tuples would shuffle the types around depending on
/// the exact invocation of `and`s. So, `unit.and(int).and(int)` would
/// result in a different extracted type from `unit.and(int.and(int))`,
/// or from `int.and(unit).and(int)`. If you changed around the order
/// of filters, while still having them be semantically equivalent, you'd
/// need to update all your `map`s as well.
///
/// `Product`, `HList`, and `Func` do all the heavy work so that none of
/// this is a bother to you. What's more, the types are enforced at
/// compile-time, and tuple flattening is optimized away to nothing by
/// LLVM.
fn map<F>(self, fun: F) -> Map<Self, F>
where
Self: Sized,
F: Func<Self::Extract> + Clone,
{
Map {
filter: self,
callback: fun,
}
}
/// Composes this `Filter` with an async function receiving
/// the extracted value.
///
/// The function should return some `Future` type.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Map `/:id`
/// warp::path::param().then(|id: u64| async move {
/// format!("Hello #{}", id)
/// });
/// ```
fn then<F>(self, fun: F) -> Then<Self, F>
where
Self: Sized,
F: Func<Self::Extract> + Clone,
F::Output: Future + Send,
{
Then {
filter: self,
callback: fun,
}
}
/// Composes this `Filter` with a fallible async function receiving
/// the extracted value.
///
/// The function should return some `TryFuture` type.
///
/// The `Error` type of the return `Future` needs be a `Rejection`, which
/// means most futures will need to have their error mapped into one.
///
/// Rejections are meant to say "this filter didn't accept the request,
/// maybe another can". So for application-level errors, consider using
/// [`Filter::then`] instead.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Validate after `/:id`
/// warp::path::param().and_then(|id: u64| async move {
/// if id != 0 {
/// Ok(format!("Hello #{}", id))
/// } else {
/// Err(warp::reject::not_found())
/// }
/// });
/// ```
fn and_then<F>(self, fun: F) -> AndThen<Self, F>
where
Self: Sized,
F: Func<Self::Extract> + Clone,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: CombineRejection<Self::Error>,
{
AndThen {
filter: self,
callback: fun,
}
}
/// Compose this `Filter` with a function receiving an error.
///
/// The function should return some `TryFuture` type yielding the
/// same item and error types.
fn or_else<F>(self, fun: F) -> OrElse<Self, F>
where
Self: Filter<Error = Rejection> + Sized,
F: Func<Rejection>,
F::Output: TryFuture<Ok = Self::Extract> + Send,
<F::Output as TryFuture>::Error: IsReject,
{
OrElse {
filter: self,
callback: fun,
}
}
/// Compose this `Filter` with a function receiving an error and
/// returning a *new* type, instead of the *same* type.
///
/// This is useful for "customizing" rejections into new response types.
/// See also the [rejections example][ex].
///
/// [ex]: https://github.com/seanmonstar/warp/blob/master/examples/rejections.rs
fn recover<F>(self, fun: F) -> Recover<Self, F>
where
Self: Filter<Error = Rejection> + Sized,
F: Func<Rejection>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: IsReject,
{
Recover {
filter: self,
callback: fun,
}
}
/// Unifies the extracted value of `Filter`s composed with `or`.
///
/// When a `Filter` extracts some `Either<T, T>`, where both sides
/// are the same type, this combinator can be used to grab the
/// inner value, regardless of which side of `Either` it was. This
/// is useful for values that could be extracted from multiple parts
/// of a request, and the exact place isn't important.
///
/// # Example
///
/// ```rust
/// use std::net::SocketAddr;
/// use warp::Filter;
///
/// let client_ip = warp::header("x-real-ip")
/// .or(warp::header("x-forwarded-for"))
/// .unify()
/// .map(|ip: SocketAddr| {
/// // Get the IP from either header,
/// // and unify into the inner type.
/// });
/// ```
fn unify<T>(self) -> Unify<Self>
where
Self: Filter<Extract = (Either<T, T>,)> + Sized,
T: Tuple,
{
Unify { filter: self }
}
/// Convenience method to remove one layer of tupling.
///
/// This is useful for when things like `map` don't return a new value,
/// but just `()`, since warp will wrap it up into a `((),)`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path::param()
/// .map(|num: u64| {
/// println!("just logging: {}", num);
/// // returning "nothing"
/// })
/// .untuple_one()
/// .map(|| {
/// println!("the ((),) was removed");
/// warp::reply()
/// });
/// ```
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(|| {
/// // wanting to return a tuple
/// (true, 33)
/// })
/// .untuple_one()
/// .map(|is_enabled: bool, count: i32| {
/// println!("untupled: ({}, {})", is_enabled, count);
/// });
/// ```
fn untuple_one<T>(self) -> UntupleOne<Self>
where
Self: Filter<Extract = (T,)> + Sized,
T: Tuple,
{
UntupleOne { filter: self }
}
/// Wraps the current filter with some wrapper.
///
/// The wrapper may do some preparation work before starting this filter,
/// and may do post-processing after the filter completes.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(warp::reply);
///
/// // Wrap the route with a log wrapper.
/// let route = route.with(warp::log("example"));
/// ```
fn with<W>(self, wrapper: W) -> W::Wrapped
where
Self: Sized,
W: Wrap<Self>,
{
wrapper.wrap(self)
}
/// Boxes this filter into a trait object, making it easier to name the type.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// fn impl_reply() -> warp::filters::BoxedFilter<(impl warp::Reply,)> {
/// warp::any()
/// .map(warp::reply)
/// .boxed()
/// }
///
/// fn named_i32() -> warp::filters::BoxedFilter<(i32,)> {
/// warp::path::param::<i32>()
/// .boxed()
/// }
///
/// fn named_and() -> warp::filters::BoxedFilter<(i32, String)> {
/// warp::path::param::<i32>()
/// .and(warp::header::<String>("host"))
/// .boxed()
/// }
/// ```
fn boxed(self) -> BoxedFilter<Self::Extract>
where
Self: Sized + Send + Sync + 'static,
Self::Extract: Send,
Self::Error: Into<Rejection>,
{
BoxedFilter::new(self)
}
}
impl<T: FilterBase> Filter for T {}
pub trait FilterClone: Filter + Clone {}
impl<T: Filter + Clone> FilterClone for T {}
fn _assert_object_safe() {
fn _assert(_f: &dyn Filter<Extract = (), Error = (), Future = future::Ready<()>>) {}
}
// ===== FilterFn =====
pub(crate) fn filter_fn<F, U>(func: F) -> FilterFn<F>
where
F: Fn(&mut Route) -> U,
U: TryFuture,
U::Ok: Tuple,
U::Error: IsReject,
{
FilterFn { func }
}
pub(crate) fn filter_fn_one<F, U>(
func: F,
) -> impl Filter<Extract = (U::Ok,), Error = U::Error> + Copy
where
F: Fn(&mut Route) -> U + Copy,
U: TryFuture + Send + 'static,
U::Ok: Send,
U::Error: IsReject,
{
filter_fn(move |route| func(route).map_ok(|item| (item,)))
}
#[derive(Copy, Clone)]
#[allow(missing_debug_implementations)]
pub(crate) struct FilterFn<F> {
// TODO: could include a `debug_str: &'static str` to be used in Debug impl
func: F,
}
impl<F, U> FilterBase for FilterFn<F>
where
F: Fn(&mut Route) -> U,
U: TryFuture + Send + 'static,
U::Ok: Tuple + Send,
U::Error: IsReject,
{
type Extract = U::Ok;
type Error = U::Error;
type Future = future::IntoFuture<U>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
route::with(|route| (self.func)(route)).into_future()
}
}
================================================
FILE: src/filter/or.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Internal};
use crate::generic::Either;
use crate::reject::CombineRejection;
use crate::route;
type Combined<E1, E2> = <E1 as CombineRejection<E2>>::Combined;
#[derive(Clone, Copy, Debug)]
pub struct Or<T, U> {
pub(super) first: T,
pub(super) second: U,
}
impl<T, U> FilterBase for Or<T, U>
where
T: Filter,
U: Filter + Clone + Send,
U::Error: CombineRejection<T::Error>,
{
type Extract = (Either<T::Extract, U::Extract>,);
//type Error = <U::Error as CombineRejection<T::Error>>::Combined;
type Error = Combined<U::Error, T::Error>;
type Future = EitherFuture<T, U>;
fn filter(&self, _: Internal) -> Self::Future {
let idx = route::with(|route| route.matched_path_index());
EitherFuture {
state: State::First(self.first.filter(Internal), self.second.clone()),
original_path_index: PathIndex(idx),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct EitherFuture<T: Filter, U: Filter> {
#[pin]
state: State<T, U>,
original_path_index: PathIndex,
}
#[pin_project(project = StateProj)]
enum State<T: Filter, U: Filter> {
First(#[pin] T::Future, U),
Second(Option<T::Error>, #[pin] U::Future),
Done,
}
#[derive(Copy, Clone)]
struct PathIndex(usize);
impl PathIndex {
fn reset_path(&self) {
route::with(|route| route.reset_matched_path_index(self.0));
}
}
impl<T, U> Future for EitherFuture<T, U>
where
T: Filter,
U: Filter,
U::Error: CombineRejection<T::Error>,
{
type Output = Result<(Either<T::Extract, U::Extract>,), Combined<U::Error, T::Error>>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let pin = self.as_mut().project();
let (err1, fut2) = match pin.state.project() {
StateProj::First(first, second) => match ready!(first.try_poll(cx)) {
Ok(ex1) => {
return Poll::Ready(Ok((Either::A(ex1),)));
}
Err(e) => {
pin.original_path_index.reset_path();
(e, second.filter(Internal))
}
},
StateProj::Second(err1, second) => {
let ex2 = match ready!(second.try_poll(cx)) {
Ok(ex2) => Ok((Either::B(ex2),)),
Err(e) => {
pin.original_path_index.reset_path();
let err1 = err1.take().expect("polled after complete");
Err(e.combine(err1))
}
};
self.set(EitherFuture {
state: State::Done,
..*self
});
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
};
self.set(EitherFuture {
state: State::Second(Some(err1), fut2),
..*self
});
}
}
}
================================================
FILE: src/filter/or_else.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Func, Internal};
use crate::reject::IsReject;
use crate::route;
#[derive(Clone, Copy, Debug)]
pub struct OrElse<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F> FilterBase for OrElse<T, F>
where
T: Filter,
F: Func<T::Error> + Clone + Send,
F::Output: TryFuture<Ok = T::Extract> + Send,
<F::Output as TryFuture>::Error: IsReject,
{
type Extract = <F::Output as TryFuture>::Ok;
type Error = <F::Output as TryFuture>::Error;
type Future = OrElseFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
let idx = route::with(|route| route.matched_path_index());
OrElseFuture {
state: State::First(self.filter.filter(Internal), self.callback.clone()),
original_path_index: PathIndex(idx),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct OrElseFuture<T: Filter, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture<Ok = T::Extract> + Send,
{
#[pin]
state: State<T, F>,
original_path_index: PathIndex,
}
#[pin_project(project = StateProj)]
enum State<T, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture<Ok = T::Extract> + Send,
{
First(#[pin] T::Future, F),
Second(#[pin] F::Output),
Done,
}
#[derive(Copy, Clone)]
struct PathIndex(usize);
impl PathIndex {
fn reset_path(&self) {
route::with(|route| route.reset_matched_path_index(self.0));
}
}
impl<T, F> Future for OrElseFuture<T, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture<Ok = T::Extract> + Send,
{
type Output = Result<<F::Output as TryFuture>::Ok, <F::Output as TryFuture>::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let pin = self.as_mut().project();
let (err, second) = match pin.state.project() {
StateProj::First(first, second) => match ready!(first.try_poll(cx)) {
Ok(ex) => return Poll::Ready(Ok(ex)),
Err(err) => (err, second),
},
StateProj::Second(second) => {
let ex2 = ready!(second.try_poll(cx));
self.set(OrElseFuture {
state: State::Done,
..*self
});
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
};
pin.original_path_index.reset_path();
let fut2 = second.call(err);
self.set(OrElseFuture {
state: State::Second(fut2),
..*self
});
}
}
}
================================================
FILE: src/filter/recover.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Func, Internal};
use crate::generic::Either;
use crate::reject::IsReject;
use crate::route;
#[derive(Clone, Copy, Debug)]
pub struct Recover<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F> FilterBase for Recover<T, F>
where
T: Filter,
F: Func<T::Error> + Clone + Send,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: IsReject,
{
type Extract = (Either<T::Extract, (<F::Output as TryFuture>::Ok,)>,);
type Error = <F::Output as TryFuture>::Error;
type Future = RecoverFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
let idx = route::with(|route| route.matched_path_index());
RecoverFuture {
state: State::First(self.filter.filter(Internal), self.callback.clone()),
original_path_index: PathIndex(idx),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct RecoverFuture<T: Filter, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: IsReject,
{
#[pin]
state: State<T, F>,
original_path_index: PathIndex,
}
#[pin_project(project = StateProj)]
enum State<T, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: IsReject,
{
First(#[pin] T::Future, F),
Second(#[pin] F::Output),
Done,
}
#[derive(Copy, Clone)]
struct PathIndex(usize);
impl PathIndex {
fn reset_path(&self) {
route::with(|route| route.reset_matched_path_index(self.0));
}
}
impl<T, F> Future for RecoverFuture<T, F>
where
T: Filter,
F: Func<T::Error>,
F::Output: TryFuture + Send,
<F::Output as TryFuture>::Error: IsReject,
{
type Output = Result<
(Either<T::Extract, (<F::Output as TryFuture>::Ok,)>,),
<F::Output as TryFuture>::Error,
>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
let pin = self.as_mut().project();
let (err, second) = match pin.state.project() {
StateProj::First(first, second) => match ready!(first.try_poll(cx)) {
Ok(ex) => return Poll::Ready(Ok((Either::A(ex),))),
Err(err) => (err, second),
},
StateProj::Second(second) => {
let ex2 = match ready!(second.try_poll(cx)) {
Ok(ex2) => Ok((Either::B((ex2,)),)),
Err(e) => Err(e),
};
self.set(RecoverFuture {
state: State::Done,
..*self
});
return Poll::Ready(ex2);
}
StateProj::Done => panic!("polled after complete"),
};
pin.original_path_index.reset_path();
let fut2 = second.call(err);
self.set(RecoverFuture {
state: State::Second(fut2),
..*self
});
}
}
}
================================================
FILE: src/filter/service.rs
================================================
use std::convert::Infallible;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::future::TryFuture;
use pin_project::pin_project;
use tower_service::Service;
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
use crate::route::{self, Route};
use crate::{Filter, Request};
/// Convert a `Filter` into a `Service`.
///
/// Filters are normally what APIs are built on in warp. However, it can be
/// useful to convert a `Filter` into a [`Service`][Service], such as if
/// further customizing a `hyper::Service`, or if wanting to make use of
/// the greater [Tower][tower] set of middleware.
///
/// # Example
///
/// Running a `warp::Filter` on a regular `hyper::Server`:
///
/// ```
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
/// use std::convert::Infallible;
/// use warp::Filter;
///
/// // Our Filter...
/// let route = warp::any().map(|| "Hello From Warp!");
///
/// // Convert it into a `Service`...
/// let svc = warp::service(route);
/// # drop(svc);
/// # Ok(())
/// # }
/// ```
///
/// [Service]: https://docs.rs/tower_service/latest/tower_service/trait.Service.html
/// [tower]: https://docs.rs/tower
pub fn service<F>(filter: F) -> FilteredService<F>
where
F: Filter,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
{
FilteredService { filter }
}
#[derive(Copy, Clone, Debug)]
pub struct FilteredService<F> {
filter: F,
}
impl<F> FilteredService<F>
where
F: Filter,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
{
#[inline]
pub(crate) fn call_route(&self, req: Request) -> FilteredFuture<F::Future> {
debug_assert!(!route::is_set(), "nested route::set calls");
let route = Route::new(req);
let fut = route::set(&route, || self.filter.filter(super::Internal));
FilteredFuture { future: fut, route }
}
}
impl<F, B> Service<http::Request<B>> for FilteredService<F>
where
F: Filter,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
B: http_body::Body + Send + Sync + 'static,
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
type Response = Response;
type Error = Infallible;
type Future = FilteredFuture<F::Future>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
fn call(&mut self, req: http::Request<B>) -> Self::Future {
let req = req.map(crate::bodyt::Body::wrap);
self.call_route(req)
}
}
#[pin_project]
#[derive(Debug)]
pub struct FilteredFuture<F> {
#[pin]
future: F,
route: ::std::cell::RefCell<Route>,
}
impl<F> Future for FilteredFuture<F>
where
F: TryFuture,
F::Ok: Reply,
F::Error: IsReject,
{
type Output = Result<Response, Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
debug_assert!(!route::is_set(), "nested route::set calls");
let pin = self.project();
let fut = pin.future;
match route::set(pin.route, || fut.try_poll(cx)) {
Poll::Ready(Ok(ok)) => Poll::Ready(Ok(ok.into_response())),
Poll::Pending => Poll::Pending,
Poll::Ready(Err(err)) => {
tracing::debug!("rejected: {:?}", err);
Poll::Ready(Ok(err.into_response()))
}
}
}
}
================================================
FILE: src/filter/then.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Func, Internal};
#[derive(Clone, Copy, Debug)]
pub struct Then<T, F> {
pub(super) filter: T,
pub(super) callback: F,
}
impl<T, F> FilterBase for Then<T, F>
where
T: Filter,
F: Func<T::Extract> + Clone + Send,
F::Output: Future + Send,
{
type Extract = (<F::Output as Future>::Output,);
type Error = T::Error;
type Future = ThenFuture<T, F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
ThenFuture {
state: State::First(self.filter.filter(Internal), self.callback.clone()),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct ThenFuture<T, F>
where
T: Filter,
F: Func<T::Extract>,
F::Output: Future + Send,
{
#[pin]
state: State<T::Future, F>,
}
#[pin_project(project = StateProj)]
enum State<T, F>
where
T: TryFuture,
F: Func<T::Ok>,
F::Output: Future + Send,
{
First(#[pin] T, F),
Second(#[pin] F::Output),
Done,
}
impl<T, F> Future for ThenFuture<T, F>
where
T: Filter,
F: Func<T::Extract>,
F::Output: Future + Send,
{
type Output = Result<(<F::Output as Future>::Output,), T::Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project().state.poll(cx)
}
}
impl<T, F> Future for State<T, F>
where
T: TryFuture,
F: Func<T::Ok>,
F::Output: Future + Send,
{
type Output = Result<(<F::Output as Future>::Output,), T::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {
match self.as_mut().project() {
StateProj::First(first, second) => {
let ex1 = ready!(first.try_poll(cx))?;
let fut2 = second.call(ex1);
self.set(State::Second(fut2));
}
StateProj::Second(second) => {
let ex2 = (ready!(second.poll(cx)),);
self.set(State::Done);
return Poll::Ready(Ok(ex2));
}
StateProj::Done => panic!("polled after complete"),
}
}
}
}
================================================
FILE: src/filter/unify.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Either, Filter, FilterBase, Internal, Tuple};
#[derive(Clone, Copy, Debug)]
pub struct Unify<F> {
pub(super) filter: F,
}
impl<F, T> FilterBase for Unify<F>
where
F: Filter<Extract = (Either<T, T>,)>,
T: Tuple,
{
type Extract = T;
type Error = F::Error;
type Future = UnifyFuture<F::Future>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
UnifyFuture {
inner: self.filter.filter(Internal),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct UnifyFuture<F> {
#[pin]
inner: F,
}
impl<F, T> Future for UnifyFuture<F>
where
F: TryFuture<Ok = (Either<T, T>,)>,
{
type Output = Result<T, F::Error>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(match ready!(self.project().inner.try_poll(cx))? {
(Either::A(x),) | (Either::B(x),) => Ok(x),
})
}
}
================================================
FILE: src/filter/untuple_one.rs
================================================
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{ready, TryFuture};
use pin_project::pin_project;
use super::{Filter, FilterBase, Internal, Tuple};
#[derive(Clone, Copy, Debug)]
pub struct UntupleOne<F> {
pub(super) filter: F,
}
impl<F, T> FilterBase for UntupleOne<F>
where
F: Filter<Extract = (T,)>,
T: Tuple,
{
type Extract = T;
type Error = F::Error;
type Future = UntupleOneFuture<F>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
UntupleOneFuture {
extract: self.filter.filter(Internal),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct UntupleOneFuture<F: Filter> {
#[pin]
extract: F::Future,
}
impl<F, T> Future for UntupleOneFuture<F>
where
F: Filter<Extract = (T,)>,
T: Tuple,
{
type Output = Result<T, F::Error>;
#[inline]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match ready!(self.project().extract.try_poll(cx)) {
Ok((t,)) => Poll::Ready(Ok(t)),
Err(err) => Poll::Ready(Err(err)),
}
}
}
================================================
FILE: src/filter/wrap.rs
================================================
use super::Filter;
pub trait WrapSealed<F: Filter> {
type Wrapped: Filter;
fn wrap(&self, filter: F) -> Self::Wrapped;
}
impl<'a, T, F> WrapSealed<F> for &'a T
where
T: WrapSealed<F>,
F: Filter,
{
type Wrapped = T::Wrapped;
fn wrap(&self, filter: F) -> Self::Wrapped {
(*self).wrap(filter)
}
}
pub trait Wrap<F: Filter>: WrapSealed<F> {}
impl<T, F> Wrap<F> for T
where
T: WrapSealed<F>,
F: Filter,
{
}
/// Combines received filter with pre and after filters
///
/// # Example
///
/// ```
/// use crate::warp::Filter;
///
/// let route = warp::any()
/// .map(|| "hello world")
/// .with(warp::wrap_fn(|filter| filter));
/// ```
///
/// You can find the full example in the [usage example](https://github.com/seanmonstar/warp/blob/master/examples/wrapping.rs).
pub fn wrap_fn<F, T, U>(func: F) -> WrapFn<F>
where
F: Fn(T) -> U,
T: Filter,
U: Filter,
{
WrapFn { func }
}
#[derive(Debug)]
pub struct WrapFn<F> {
func: F,
}
impl<F, T, U> WrapSealed<T> for WrapFn<F>
where
F: Fn(T) -> U,
T: Filter,
U: Filter,
{
type Wrapped = U;
fn wrap(&self, filter: T) -> Self::Wrapped {
(self.func)(filter)
}
}
================================================
FILE: src/filters/addr.rs
================================================
//! Socket Address filters.
use std::convert::Infallible;
use std::net::SocketAddr;
use futures_util::future;
use crate::filter::{filter_fn_one, Filter};
/// Creates a `Filter` to get the remote address of the connection.
///
/// If the underlying transport doesn't use socket addresses, this will yield
/// `None`.
///
/// # Example
///
/// ```
/// use std::net::SocketAddr;
/// use warp::Filter;
///
/// let route = warp::addr::remote()
/// .map(|addr: Option<SocketAddr>| {
/// println!("remote address = {:?}", addr);
/// });
/// ```
pub fn remote() -> impl Filter<Extract = (Option<SocketAddr>,), Error = Infallible> + Copy {
filter_fn_one(|route| {
future::ok(
route
.extensions()
.get::<RemoteAddr>()
.map(|RemoteAddr(addr)| *addr),
)
})
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct RemoteAddr(pub(crate) SocketAddr);
================================================
FILE: src/filters/any.rs
================================================
//! A filter that matches any route.
use std::convert::Infallible;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use crate::filter::{Filter, FilterBase, Internal};
/// A [`Filter`] that matches any route.
///
/// This can be a useful building block to build new filters from,
/// since [`Filter`] is otherwise a sealed trait.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(|| {
/// "I always return this string!"
/// });
/// ```
///
/// This could allow creating a single `impl Filter` returning a specific
/// reply, that can then be used as the end of several different filter
/// chains.
///
/// Another use case is turning some clone-able resource into a `Filter`,
/// thus allowing to easily `and` it together with others.
///
/// ```
/// use std::sync::Arc;
/// use warp::Filter;
///
/// let state = Arc::new(vec![33, 41]);
/// let with_state = warp::any().map(move || state.clone());
///
/// // Now we could `and` with any other filter:
///
/// let route = warp::path::param()
/// .and(with_state)
/// .map(|param_id: u32, db: Arc<Vec<u32>>| {
/// db.contains(¶m_id)
/// });
/// ```
pub fn any() -> impl Filter<Extract = (), Error = Infallible> + Copy {
Any
}
#[derive(Copy, Clone)]
#[allow(missing_debug_implementations)]
struct Any;
impl FilterBase for Any {
type Extract = ();
type Error = Infallible;
type Future = AnyFut;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
AnyFut
}
}
#[allow(missing_debug_implementations)]
struct AnyFut;
impl Future for AnyFut {
type Output = Result<(), Infallible>;
#[inline]
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
Poll::Ready(Ok(()))
}
}
================================================
FILE: src/filters/body.rs
================================================
//! Body filters
//!
//! Filters that extract a body for a route.
use std::error::Error as StdError;
use std::fmt;
use crate::bodyt::Body;
use bytes::{Buf, Bytes};
use futures_util::future;
use futures_util::Stream;
use headers::ContentLength;
use http::header::CONTENT_TYPE;
use http_body_util::BodyDataStream;
use http_body_util::BodyExt;
use mime;
use serde::de::DeserializeOwned;
use crate::filter::{filter_fn, filter_fn_one, Filter, FilterBase};
use crate::reject::{self, Rejection};
type BoxError = Box<dyn StdError + Send + Sync>;
// Extracts the `Body` Stream from the route.
//
// Does not consume any of it.
pub(crate) fn body() -> impl Filter<Extract = (Body,), Error = Rejection> + Copy {
filter_fn_one(|route| {
future::ready(route.take_body().ok_or_else(|| {
tracing::error!("request body already taken in previous filter");
reject::known(BodyConsumedMultipleTimes { _p: () })
}))
})
}
/// Require a `content-length` header to have a value no greater than some limit.
///
/// Rejects if `content-length` header is missing, is invalid, or has a number
/// larger than the limit provided.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Limit the upload to 4kb...
/// let upload = warp::body::content_length_limit(4096)
/// .and(warp::body::aggregate());
/// ```
pub fn content_length_limit(limit: u64) -> impl Filter<Extract = (), Error = Rejection> + Copy {
crate::filters::header::header2()
.map_err(crate::filter::Internal, |_| {
tracing::debug!("content-length missing");
reject::length_required()
})
.and_then(move |ContentLength(length)| {
if length <= limit {
future::ok(())
} else {
tracing::debug!("content-length: {} is over limit {}", length, limit);
future::err(reject::payload_too_large())
}
})
.untuple_one()
}
/// Create a `Filter` that extracts the request body as a `futures::Stream`.
///
/// If other filters have already extracted the body, this filter will reject
/// with a `500 Internal Server Error`.
///
/// For example usage, please take a look at [examples/stream.rs](https://github.com/seanmonstar/warp/blob/master/examples/stream.rs).
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
/// prevent a overly large request from using too much memory.
pub fn stream(
) -> impl Filter<Extract = (impl Stream<Item = Result<impl Buf, crate::Error>>,), Error = Rejection> + Copy
{
body().map(|body| BodyDataStream::new(body))
}
/// Returns a `Filter` that matches any request and extracts a `Future` of a
/// concatenated body.
///
/// The contents of the body will be flattened into a single contiguous
/// `Bytes`, which may require memory copies. If you don't require a
/// contiguous buffer, using `aggregate` can be give better performance.
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
/// prevent a overly large request from using too much memory.
///
/// # Example
///
/// ```
/// use warp::{Buf, Filter};
///
/// let route = warp::body::content_length_limit(1024 * 32)
/// .and(warp::body::bytes())
/// .map(|bytes: bytes::Bytes| {
/// println!("bytes = {:?}", bytes);
/// });
/// ```
pub fn bytes() -> impl Filter<Extract = (Bytes,), Error = Rejection> + Copy {
body().and_then(|mut body| async move {
BodyExt::collect(&mut body)
.await
.map(|b| b.to_bytes())
.map_err(|err| {
tracing::debug!("to_bytes error: {}", err);
reject::known(BodyReadError(err))
})
})
}
/// Returns a `Filter` that matches any request and extracts a `Future` of an
/// aggregated body.
///
/// The `Buf` may contain multiple, non-contiguous buffers. This can be more
/// performant (by reducing copies) when receiving large bodies.
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
/// prevent a overly large request from using too much memory.
///
/// # Example
///
/// ```
/// use warp::{Buf, Filter};
///
/// fn full_body(mut body: impl Buf) {
/// // It could have several non-contiguous slices of memory...
/// while body.has_remaining() {
/// println!("slice = {:?}", body.chunk());
/// let cnt = body.chunk().len();
/// body.advance(cnt);
/// }
/// }
///
/// let route = warp::body::content_length_limit(1024 * 32)
/// .and(warp::body::aggregate())
/// .map(full_body);
/// ```
pub fn aggregate() -> impl Filter<Extract = (impl Buf,), Error = Rejection> + Copy {
body().and_then(|mut body: crate::bodyt::Body| async move {
http_body_util::BodyExt::collect(&mut body)
.await
.map(|collected| collected.aggregate())
.map_err(|err| {
tracing::debug!("aggregate error: {}", err);
reject::known(BodyReadError(err))
})
})
}
/// Returns a `Filter` that matches any request and extracts a `Future` of a
/// JSON-decoded body.
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
/// prevent a overly large request from using too much memory.
///
/// # Example
///
/// ```
/// use std::collections::HashMap;
/// use warp::Filter;
///
/// let route = warp::body::content_length_limit(1024 * 32)
/// .and(warp::body::json())
/// .map(|simple_map: HashMap<String, String>| {
/// "Got a JSON body!"
/// });
/// ```
pub fn json<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,), Error = Rejection> + Copy {
is_content_type::<Json>()
.and(bytes())
.and_then(|buf| async move {
Json::decode(buf).map_err(|err| {
tracing::debug!("request json body error: {}", err);
reject::known(BodyDeserializeError { cause: err })
})
})
}
/// Returns a `Filter` that matches any request and extracts a
/// `Future` of a form encoded body.
///
/// # Note
///
/// This filter is for the simpler `application/x-www-form-urlencoded` format,
/// not `multipart/form-data`.
///
/// # Warning
///
/// This does not have a default size limit, it would be wise to use one to
/// prevent a overly large request from using too much memory.
///
///
/// ```
/// use std::collections::HashMap;
/// use warp::Filter;
///
/// let route = warp::body::content_length_limit(1024 * 32)
/// .and(warp::body::form())
/// .map(|simple_map: HashMap<String, String>| {
/// "Got a urlencoded body!"
/// });
/// ```
pub fn form<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,), Error = Rejection> + Copy {
is_content_type::<Form>()
.and(aggregate())
.and_then(|buf| async move {
Form::decode(buf).map_err(|err| {
tracing::debug!("request form body error: {}", err);
reject::known(BodyDeserializeError { cause: err })
})
})
}
// ===== Decoders =====
trait Decode {
const MIME: (mime::Name<'static>, mime::Name<'static>);
const WITH_NO_CONTENT_TYPE: bool;
fn decode<B: Buf, T: DeserializeOwned>(buf: B) -> Result<T, BoxError>;
}
struct Json;
impl Decode for Json {
const MIME: (mime::Name<'static>, mime::Name<'static>) = (mime::APPLICATION, mime::JSON);
const WITH_NO_CONTENT_TYPE: bool = true;
fn decode<B: Buf, T: DeserializeOwned>(mut buf: B) -> Result<T, BoxError> {
serde_json::from_slice(&buf.copy_to_bytes(buf.remaining())).map_err(Into::into)
}
}
struct Form;
impl Decode for Form {
const MIME: (mime::Name<'static>, mime::Name<'static>) =
(mime::APPLICATION, mime::WWW_FORM_URLENCODED);
const WITH_NO_CONTENT_TYPE: bool = true;
fn decode<B: Buf, T: DeserializeOwned>(buf: B) -> Result<T, BoxError> {
serde_urlencoded::from_reader(buf.reader()).map_err(Into::into)
}
}
// Require the `content-type` header to be this type (or, if there's no `content-type`
// header at all, optimistically hope it's the right type).
fn is_content_type<D: Decode>() -> impl Filter<Extract = (), Error = Rejection> + Copy {
filter_fn(move |route| {
let (type_, subtype) = D::MIME;
if let Some(value) = route.headers().get(CONTENT_TYPE) {
tracing::trace!("is_content_type {}/{}? {:?}", type_, subtype, value);
let ct = value
.to_str()
.ok()
.and_then(|s| s.parse::<mime::Mime>().ok());
if let Some(ct) = ct {
if ct.type_() == type_ && ct.subtype() == subtype {
future::ok(())
} else {
tracing::debug!(
"content-type {:?} doesn't match {}/{}",
value,
type_,
subtype
);
future::err(reject::unsupported_media_type())
}
} else {
tracing::debug!("content-type {:?} couldn't be parsed", value);
future::err(reject::unsupported_media_type())
}
} else if D::WITH_NO_CONTENT_TYPE {
// Optimistically assume its correct!
tracing::trace!("no content-type header, assuming {}/{}", type_, subtype);
future::ok(())
} else {
tracing::debug!("no content-type found");
future::err(reject::unsupported_media_type())
}
})
}
// ===== Rejections =====
/// An error used in rejections when deserializing a request body fails.
#[derive(Debug)]
pub struct BodyDeserializeError {
cause: BoxError,
}
impl fmt::Display for BodyDeserializeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Request body deserialize error: {}", self.cause)
}
}
impl StdError for BodyDeserializeError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
Some(self.cause.as_ref())
}
}
#[derive(Debug)]
pub(crate) struct BodyReadError(crate::Error);
impl fmt::Display for BodyReadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Request body read error: {}", self.0)
}
}
impl StdError for BodyReadError {}
unit_error! {
pub(crate) BodyConsumedMultipleTimes: "Request body consumed multiple times"
}
================================================
FILE: src/filters/compression.rs
================================================
//! Compression Filters
//!
//! Filters that compress the body of a response.
#[cfg(feature = "compression-brotli")]
use async_compression::tokio::bufread::BrotliEncoder;
#[cfg(feature = "compression-gzip")]
use async_compression::tokio::bufread::{DeflateEncoder, GzipEncoder};
use crate::bodyt::Body;
use http::header::{HeaderValue, CONTENT_ENCODING, CONTENT_LENGTH};
use tokio_util::io::{ReaderStream, StreamReader};
use crate::filter::{Filter, WrapSealed};
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
use self::internal::{CompressionProps, WithCompression};
enum CompressionAlgo {
#[cfg(feature = "compression-brotli")]
BR,
#[cfg(feature = "compression-gzip")]
DEFLATE,
#[cfg(feature = "compression-gzip")]
GZIP,
}
impl From<CompressionAlgo> for HeaderValue {
#[inline]
fn from(algo: CompressionAlgo) -> Self {
HeaderValue::from_static(match algo {
#[cfg(feature = "compression-brotli")]
CompressionAlgo::BR => "br",
#[cfg(feature = "compression-gzip")]
CompressionAlgo::DEFLATE => "deflate",
#[cfg(feature = "compression-gzip")]
CompressionAlgo::GZIP => "gzip",
})
}
}
/// Compression
#[derive(Clone, Copy, Debug)]
pub struct Compression<F> {
func: F,
}
// TODO: The implementation of `gzip()`, `deflate()`, and `brotli()` could be replaced with
// generics or a macro
/// Create a wrapping filter that compresses the Body of a [`Response`](crate::reply::Response)
/// using gzip, adding `content-encoding: gzip` to the Response's [`HeaderMap`](hyper::HeaderMap)
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::get()
/// .and(warp::path::end())
/// .and(warp::fs::file("./README.md"))
/// .with(warp::compression::gzip());
/// ```
#[cfg(feature = "compression-gzip")]
pub fn gzip() -> Compression<impl Fn(CompressionProps) -> Response + Copy> {
let func = move |mut props: CompressionProps| {
let body = Body::wrap_stream(ReaderStream::new(GzipEncoder::new(StreamReader::new(
props.body,
))));
props
.head
.headers
.append(CONTENT_ENCODING, CompressionAlgo::GZIP.into());
props.head.headers.remove(CONTENT_LENGTH);
Response::from_parts(props.head, body)
};
Compression { func }
}
/// Create a wrapping filter that compresses the Body of a [`Response`](crate::reply::Response)
/// using deflate, adding `content-encoding: deflate` to the Response's [`HeaderMap`](hyper::HeaderMap)
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::get()
/// .and(warp::path::end())
/// .and(warp::fs::file("./README.md"))
/// .with(warp::compression::deflate());
/// ```
#[cfg(feature = "compression-gzip")]
pub fn deflate() -> Compression<impl Fn(CompressionProps) -> Response + Copy> {
let func = move |mut props: CompressionProps| {
let body = Body::wrap_stream(ReaderStream::new(DeflateEncoder::new(StreamReader::new(
props.body,
))));
props
.head
.headers
.append(CONTENT_ENCODING, CompressionAlgo::DEFLATE.into());
props.head.headers.remove(CONTENT_LENGTH);
Response::from_parts(props.head, body)
};
Compression { func }
}
/// Create a wrapping filter that compresses the Body of a [`Response`](crate::reply::Response)
/// using brotli, adding `content-encoding: br` to the Response's [`HeaderMap`](hyper::HeaderMap)
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::get()
/// .and(warp::path::end())
/// .and(warp::fs::file("./README.md"))
/// .with(warp::compression::brotli());
/// ```
#[cfg(feature = "compression-brotli")]
pub fn brotli() -> Compression<impl Fn(CompressionProps) -> Response + Copy> {
let func = move |mut props: CompressionProps| {
let body = Body::wrap_stream(ReaderStream::new(BrotliEncoder::new(StreamReader::new(
props.body,
))));
props
.head
.headers
.append(CONTENT_ENCODING, CompressionAlgo::BR.into());
props.head.headers.remove(CONTENT_LENGTH);
Response::from_parts(props.head, body)
};
Compression { func }
}
impl<FN, F> WrapSealed<F> for Compression<FN>
where
FN: Fn(CompressionProps) -> Response + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Wrapped = WithCompression<FN, F>;
fn wrap(&self, filter: F) -> Self::Wrapped {
WithCompression {
filter,
compress: self.clone(),
}
}
}
mod internal {
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::Bytes;
use futures_util::{ready, Stream, TryFuture};
use http_body_util::BodyDataStream;
use pin_project::pin_project;
use crate::bodyt::Body;
use crate::filter::{Filter, FilterBase, Internal};
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
use super::Compression;
/// A wrapper around any type that implements [`Stream`](futures::Stream) to be
/// compatible with async_compression's Stream based encoders
#[pin_project]
#[derive(Debug)]
pub struct CompressableBody<S, E>
where
E: std::error::Error,
S: Stream<Item = Result<Bytes, E>>,
{
#[pin]
body: S,
}
impl<S, E> Stream for CompressableBody<S, E>
where
E: std::error::Error,
S: Stream<Item = Result<Bytes, E>>,
{
type Item = std::io::Result<Bytes>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
use std::io::{Error, ErrorKind};
let pin = self.project();
S::poll_next(pin.body, cx).map_err(|_| Error::from(ErrorKind::InvalidData))
}
}
impl From<Body> for CompressableBody<BodyDataStream<Body>, crate::Error> {
fn from(body: Body) -> Self {
CompressableBody {
body: BodyDataStream::new(body),
}
}
}
/// Compression Props
#[derive(Debug)]
pub struct CompressionProps {
pub(super) body: CompressableBody<BodyDataStream<Body>, crate::Error>,
pub(super) head: http::response::Parts,
}
impl From<http::Response<Body>> for CompressionProps {
fn from(resp: http::Response<Body>) -> Self {
let (head, body) = resp.into_parts();
CompressionProps {
body: body.into(),
head,
}
}
}
#[allow(missing_debug_implementations)]
pub struct Compressed(pub(super) Response);
impl Reply for Compressed {
#[inline]
fn into_response(self) -> Response {
self.0
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct WithCompression<FN, F> {
pub(super) compress: Compression<FN>,
pub(super) filter: F,
}
impl<FN, F> FilterBase for WithCompression<FN, F>
where
FN: Fn(CompressionProps) -> Response + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Extract = (Compressed,);
type Error = F::Error;
type Future = WithCompressionFuture<FN, F::Future>;
fn filter(&self, _: Internal) -> Self::Future {
WithCompressionFuture {
compress: self.compress.clone(),
future: self.filter.filter(Internal),
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct WithCompressionFuture<FN, F> {
compress: Compression<FN>,
#[pin]
future: F,
}
impl<FN, F> Future for WithCompressionFuture<FN, F>
where
FN: Fn(CompressionProps) -> Response,
F: TryFuture,
F::Ok: Reply,
F::Error: IsReject,
{
type Output = Result<(Compressed,), F::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pin = self.as_mut().project();
let result = ready!(pin.future.try_poll(cx));
match result {
Ok(reply) => {
let resp = (self.compress.func)(reply.into_response().into());
Poll::Ready(Ok((Compressed(resp),)))
}
Err(reject) => Poll::Ready(Err(reject)),
}
}
}
}
================================================
FILE: src/filters/cookie.rs
================================================
//! Cookie Filters
use futures_util::future;
use headers::Cookie;
use super::header;
use crate::filter::{Filter, One};
use crate::reject::Rejection;
use std::convert::Infallible;
use std::str::FromStr;
/// Creates a `Filter` that requires a cookie by name.
///
/// If found, extracts the value of the cookie, otherwise rejects.
pub fn cookie<T>(name: &'static str) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy
where
T: FromStr + Send + 'static,
{
header::header2().and_then(move |cookie: Cookie| {
let cookie = cookie
.get(name)
.ok_or_else(|| crate::reject::missing_cookie(name))
.and_then(|s| T::from_str(s).map_err(|_| crate::reject::missing_cookie(name)));
future::ready(cookie)
})
}
/// Creates a `Filter` that looks for an optional cookie by name.
///
/// If found, extracts the value of the cookie, otherwise continues
/// the request, extracting `None`.
pub fn optional<T>(
name: &'static str,
) -> impl Filter<Extract = One<Option<T>>, Error = Infallible> + Copy
where
T: FromStr + Send + 'static,
{
header::optional2().map(move |opt: Option<Cookie>| {
let cookie = opt.and_then(|cookie| cookie.get(name).map(|x| T::from_str(x)));
match cookie {
Some(Ok(t)) => Some(t),
Some(Err(_)) => None,
None => None,
}
})
}
================================================
FILE: src/filters/cors.rs
================================================
//! CORS Filters
use std::collections::HashSet;
use std::convert::TryFrom;
use std::error::Error as StdError;
use std::fmt;
use std::sync::Arc;
use headers::{
AccessControlAllowHeaders, AccessControlAllowMethods, AccessControlExposeHeaders, HeaderMapExt,
};
use http::header::{self, HeaderName, HeaderValue};
use crate::filter::{Filter, WrapSealed};
use crate::reject::{CombineRejection, Rejection};
use crate::reply::Reply;
use self::internal::{CorsFilter, IntoOrigin, Seconds};
/// Create a wrapping [`Filter`] that exposes [CORS][] behavior for a wrapped
/// filter.
///
/// [CORS]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let cors = warp::cors()
/// .allow_origin("https://hyper.rs")
/// .allow_methods(vec!["GET", "POST", "DELETE"]);
///
/// let route = warp::any()
/// .map(warp::reply)
/// .with(cors);
/// ```
/// If you want to allow any route:
/// ```
/// use warp::Filter;
/// let cors = warp::cors()
/// .allow_any_origin();
/// ```
/// You can find more usage examples [here](https://github.com/seanmonstar/warp/blob/7fa54eaecd0fe12687137372791ff22fc7995766/tests/cors.rs).
pub fn cors() -> Builder {
Builder {
credentials: false,
allowed_headers: HashSet::new(),
exposed_headers: HashSet::new(),
max_age: None,
methods: HashSet::new(),
origins: None,
}
}
/// A wrapping [`Filter`] constructed via [`cors()`].
#[derive(Clone, Debug)]
pub struct Cors {
config: Arc<Configured>,
}
/// A constructed via `warp::cors()`.
#[derive(Clone, Debug)]
pub struct Builder {
credentials: bool,
allowed_headers: HashSet<HeaderName>,
exposed_headers: HashSet<HeaderName>,
max_age: Option<u64>,
methods: HashSet<http::Method>,
origins: Option<HashSet<HeaderValue>>,
}
impl Builder {
/// Sets whether to add the `Access-Control-Allow-Credentials` header.
pub fn allow_credentials(mut self, allow: bool) -> Self {
self.credentials = allow;
self
}
/// Adds a method to the existing list of allowed request methods.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `http::Method`.
pub fn allow_method<M>(mut self, method: M) -> Self
where
http::Method: TryFrom<M>,
{
let method = match TryFrom::try_from(method) {
Ok(m) => m,
Err(_) => panic!("illegal Method"),
};
self.methods.insert(method);
self
}
/// Adds multiple methods to the existing list of allowed request methods.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `http::Method`.
pub fn allow_methods<I>(mut self, methods: I) -> Self
where
I: IntoIterator,
http::Method: TryFrom<I::Item>,
{
let iter = methods.into_iter().map(|m| match TryFrom::try_from(m) {
Ok(m) => m,
Err(_) => panic!("illegal Method"),
});
self.methods.extend(iter);
self
}
/// Adds a header to the list of allowed request headers.
///
/// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g. `content-type`.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `http::header::HeaderName`.
pub fn allow_header<H>(mut self, header: H) -> Self
where
HeaderName: TryFrom<H>,
{
let header = match TryFrom::try_from(header) {
Ok(m) => m,
Err(_) => panic!("illegal Header"),
};
self.allowed_headers.insert(header);
self
}
/// Adds multiple headers to the list of allowed request headers.
///
/// **Note**: These should match the values the browser sends via `Access-Control-Request-Headers`, e.g.`content-type`.
///
/// # Panics
///
/// Panics if any of the headers are not a valid `http::header::HeaderName`.
pub fn allow_headers<I>(mut self, headers: I) -> Self
where
I: IntoIterator,
HeaderName: TryFrom<I::Item>,
{
let iter = headers.into_iter().map(|h| match TryFrom::try_from(h) {
Ok(h) => h,
Err(_) => panic!("illegal Header"),
});
self.allowed_headers.extend(iter);
self
}
/// Adds a header to the list of exposed headers.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `http::header::HeaderName`.
pub fn expose_header<H>(mut self, header: H) -> Self
where
HeaderName: TryFrom<H>,
{
let header = match TryFrom::try_from(header) {
Ok(m) => m,
Err(_) => panic!("illegal Header"),
};
self.exposed_headers.insert(header);
self
}
/// Adds multiple headers to the list of exposed headers.
///
/// # Panics
///
/// Panics if any of the headers are not a valid `http::header::HeaderName`.
pub fn expose_headers<I>(mut self, headers: I) -> Self
where
I: IntoIterator,
HeaderName: TryFrom<I::Item>,
{
let iter = headers.into_iter().map(|h| match TryFrom::try_from(h) {
Ok(h) => h,
Err(_) => panic!("illegal Header"),
});
self.exposed_headers.extend(iter);
self
}
/// Sets that *any* `Origin` header is allowed.
///
/// # Warning
///
/// This can allow websites you didn't intend to access this resource,
/// it is usually better to set an explicit list.
pub fn allow_any_origin(mut self) -> Self {
self.origins = None;
self
}
/// Add an origin to the existing list of allowed `Origin`s.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `Origin`.
pub fn allow_origin(self, origin: impl IntoOrigin) -> Self {
self.allow_origins(Some(origin))
}
/// Add multiple origins to the existing list of allowed `Origin`s.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `Origin`.
pub fn allow_origins<I>(mut self, origins: I) -> Self
where
I: IntoIterator,
I::Item: IntoOrigin,
{
let iter = origins
.into_iter()
.map(IntoOrigin::into_origin)
.map(|origin| {
origin
.to_string()
.parse()
.expect("Origin is always a valid HeaderValue")
});
self.origins.get_or_insert_with(HashSet::new).extend(iter);
self
}
/// Sets the `Access-Control-Max-Age` header.
///
/// # Example
///
///
/// ```
/// use std::time::Duration;
/// use warp::Filter;
///
/// let cors = warp::cors()
/// .max_age(30) // 30u32 seconds
/// .max_age(Duration::from_secs(30)); // or a Duration
/// ```
pub fn max_age(mut self, seconds: impl Seconds) -> Self {
self.max_age = Some(seconds.seconds());
self
}
/// Builds the `Cors` wrapper from the configured settings.
///
/// This step isn't *required*, as the `Builder` itself can be passed
/// to `Filter::with`. This just allows constructing once, thus not needing
/// to pay the cost of "building" every time.
pub fn build(self) -> Cors {
let expose_headers_header = if self.exposed_headers.is_empty() {
None
} else {
Some(self.exposed_headers.iter().cloned().collect())
};
let allowed_headers_header = self.allowed_headers.iter().cloned().collect();
let methods_header = self.methods.iter().cloned().collect();
let config = Arc::new(Configured {
cors: self,
allowed_headers_header,
expose_headers_header,
methods_header,
});
Cors { config }
}
}
impl<F> WrapSealed<F> for Builder
where
F: Filter + Clone + Send + Sync + 'static,
F::Extract: Reply,
F::Error: CombineRejection<Rejection>,
<F::Error as CombineRejection<Rejection>>::One: CombineRejection<Rejection>,
{
type Wrapped = CorsFilter<F>;
fn wrap(&self, inner: F) -> Self::Wrapped {
let Cors { config } = self.clone().build();
CorsFilter { config, inner }
}
}
impl<F> WrapSealed<F> for Cors
where
F: Filter + Clone + Send + Sync + 'static,
F::Extract: Reply,
F::Error: CombineRejection<Rejection>,
<F::Error as CombineRejection<Rejection>>::One: CombineRejection<Rejection>,
{
type Wrapped = CorsFilter<F>;
fn wrap(&self, inner: F) -> Self::Wrapped {
let config = self.config.clone();
CorsFilter { config, inner }
}
}
/// An error used to reject requests that are forbidden by a `cors` filter.
pub struct CorsForbidden {
kind: Forbidden,
}
#[derive(Debug)]
enum Forbidden {
OriginNotAllowed,
MethodNotAllowed,
HeaderNotAllowed,
}
impl fmt::Debug for CorsForbidden {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("CorsForbidden").field(&self.kind).finish()
}
}
impl fmt::Display for CorsForbidden {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let detail = match self.kind {
Forbidden::OriginNotAllowed => "origin not allowed",
Forbidden::MethodNotAllowed => "request-method not allowed",
Forbidden::HeaderNotAllowed => "header not allowed",
};
write!(f, "CORS request forbidden: {}", detail)
}
}
impl StdError for CorsForbidden {}
#[derive(Clone, Debug)]
struct Configured {
cors: Builder,
allowed_headers_header: AccessControlAllowHeaders,
expose_headers_header: Option<AccessControlExposeHeaders>,
methods_header: AccessControlAllowMethods,
}
enum Validated {
Preflight(HeaderValue),
Simple(HeaderValue),
NotCors,
}
impl Configured {
fn check_request(
&self,
method: &http::Method,
headers: &http::HeaderMap,
) -> Result<Validated, Forbidden> {
match (headers.get(header::ORIGIN), method) {
(Some(origin), &http::Method::OPTIONS) => {
// OPTIONS requests are preflight CORS requests...
if !self.is_origin_allowed(origin) {
return Err(Forbidden::OriginNotAllowed);
}
if let Some(req_method) = headers.get(header::ACCESS_CONTROL_REQUEST_METHOD) {
if !self.is_method_allowed(req_method) {
return Err(Forbidden::MethodNotAllowed);
}
} else {
tracing::trace!(
"preflight request missing access-control-request-method header"
);
return Err(Forbidden::MethodNotAllowed);
}
if let Some(req_headers) = headers.get(header::ACCESS_CONTROL_REQUEST_HEADERS) {
let headers = req_headers
.to_str()
.map_err(|_| Forbidden::HeaderNotAllowed)?;
for header in headers.split(',') {
if !self.is_header_allowed(header.trim()) {
return Err(Forbidden::HeaderNotAllowed);
}
}
}
Ok(Validated::Preflight(origin.clone()))
}
(Some(origin), _) => {
// Any other method, simply check for a valid origin...
tracing::trace!("origin header: {:?}", origin);
if self.is_origin_allowed(origin) {
Ok(Validated::Simple(origin.clone()))
} else {
Err(Forbidden::OriginNotAllowed)
}
}
(None, _) => {
// No `ORIGIN` header means this isn't CORS!
Ok(Validated::NotCors)
}
}
}
fn is_method_allowed(&self, header: &HeaderValue) -> bool {
http::Method::from_bytes(header.as_bytes())
.map(|method| self.cors.methods.contains(&method))
.unwrap_or(false)
}
fn is_header_allowed(&self, header: &str) -> bool {
HeaderName::from_bytes(header.as_bytes())
.map(|header| self.cors.allowed_headers.contains(&header))
.unwrap_or(false)
}
fn is_origin_allowed(&self, origin: &HeaderValue) -> bool {
if let Some(ref allowed) = self.cors.origins {
allowed.contains(origin)
} else {
true
}
}
fn append_preflight_headers(&self, headers: &mut http::HeaderMap) {
self.append_common_headers(headers);
headers.typed_insert(self.allowed_headers_header.clone());
headers.typed_insert(self.methods_header.clone());
if let Some(max_age) = self.cors.max_age {
headers.insert(header::ACCESS_CONTROL_MAX_AGE, max_age.into());
}
}
fn append_common_headers(&self, headers: &mut http::HeaderMap) {
if self.cors.credentials {
headers.insert(
header::ACCESS_CONTROL_ALLOW_CREDENTIALS,
HeaderValue::from_static("true"),
);
}
if let Some(expose_headers_header) = &self.expose_headers_header {
headers.typed_insert(expose_headers_header.clone())
}
}
}
mod internal {
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use futures_util::{future, ready, TryFuture};
use headers::Origin;
use http::header;
use pin_project::pin_project;
use super::{Configured, CorsForbidden, Validated};
use crate::filter::{Filter, FilterBase, Internal, One};
use crate::generic::Either;
use crate::reject::{CombineRejection, Rejection};
use crate::route;
#[derive(Clone, Debug)]
pub struct CorsFilter<F> {
pub(super) config: Arc<Configured>,
pub(super) inner: F,
}
impl<F> FilterBase for CorsFilter<F>
where
F: Filter,
F::Extract: Send,
F::Future: Future,
F::Error: CombineRejection<Rejection>,
{
type Extract =
One<Either<One<Preflight>, One<Either<One<Wrapped<F::Extract>>, F::Extract>>>>;
type Error = <F::Error as CombineRejection<Rejection>>::One;
type Future = future::Either<
future::Ready<Result<Self::Extract, Self::Error>>,
WrappedFuture<F::Future>,
>;
fn filter(&self, _: Internal) -> Self::Future {
let validated =
route::with(|route| self.config.check_request(route.method(), route.headers()));
match validated {
Ok(Validated::Preflight(origin)) => {
let preflight = Preflight {
config: self.config.clone(),
origin,
};
future::Either::Left(future::ok((Either::A((preflight,)),)))
}
Ok(Validated::Simple(origin)) => future::Either::Right(WrappedFuture {
inner: self.inner.filter(Internal),
wrapped: Some((self.config.clone(), origin)),
}),
Ok(Validated::NotCors) => future::Either::Right(WrappedFuture {
inner: self.inner.filter(Internal),
wrapped: None,
}),
Err(err) => {
let rejection = crate::reject::known(CorsForbidden { kind: err });
future::Either::Left(future::err(rejection.into()))
}
}
}
}
#[derive(Debug)]
pub struct Preflight {
config: Arc<Configured>,
origin: header::HeaderValue,
}
impl crate::reply::Reply for Preflight {
fn into_response(self) -> crate::reply::Response {
let mut res = crate::reply::Response::default();
self.config.append_preflight_headers(res.headers_mut());
res.headers_mut()
.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, self.origin);
res
}
}
#[derive(Debug)]
pub struct Wrapped<R> {
config: Arc<Configured>,
inner: R,
origin: header::HeaderValue,
}
impl<R> crate::reply::Reply for Wrapped<R>
where
R: crate::reply::Reply,
{
fn into_response(self) -> crate::reply::Response {
let mut res = self.inner.into_response();
self.config.append_common_headers(res.headers_mut());
res.headers_mut()
.insert(header::ACCESS_CONTROL_ALLOW_ORIGIN, self.origin);
res
}
}
#[pin_project]
#[derive(Debug)]
pub struct WrappedFuture<F> {
#[pin]
inner: F,
wrapped: Option<(Arc<Configured>, header::HeaderValue)>,
}
impl<F> Future for WrappedFuture<F>
where
F: TryFuture,
F::Error: CombineRejection<Rejection>,
{
type Output = Result<
One<Either<One<Preflight>, One<Either<One<Wrapped<F::Ok>>, F::Ok>>>>,
<F::Error as CombineRejection<Rejection>>::One,
>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let pin = self.project();
match ready!(pin.inner.try_poll(cx)) {
Ok(inner) => {
let item = if let Some((config, origin)) = pin.wrapped.take() {
(Either::A((Wrapped {
config,
inner,
origin,
},)),)
} else {
(Either::B(inner),)
};
let item = (Either::B(item),);
Poll::Ready(Ok(item))
}
Err(err) => Poll::Ready(Err(err.into())),
}
}
}
pub trait Seconds {
fn seconds(self) -> u64;
}
impl Seconds for u32 {
fn seconds(self) -> u64 {
self.into()
}
}
impl Seconds for ::std::time::Duration {
fn seconds(self) -> u64 {
self.as_secs()
}
}
pub trait IntoOrigin {
fn into_origin(self) -> Origin;
}
impl<'a> IntoOrigin for &'a str {
fn into_origin(self) -> Origin {
let mut parts = self.splitn(2, "://");
let scheme = parts.next().expect("missing scheme");
let rest = parts.next().expect("missing scheme");
Origin::try_from_parts(scheme, rest, None).expect("invalid Origin")
}
}
}
================================================
FILE: src/filters/ext.rs
================================================
//! Request Extensions
use std::convert::Infallible;
use futures_util::future;
use crate::filter::{filter_fn_one, Filter};
use crate::reject::{self, Rejection};
/// Get a previously set extension of the current route.
///
/// If the extension doesn't exist, this rejects with a `MissingExtension`.
pub fn get<T: Clone + Send + Sync + 'static>(
) -> impl Filter<Extract = (T,), Error = Rejection> + Copy {
filter_fn_one(|route| {
let route = route
.extensions()
.get::<T>()
.cloned()
.ok_or_else(|| reject::known(MissingExtension { _p: () }));
future::ready(route)
})
}
/// Get a previously set extension of the current route.
///
/// If the extension doesn't exist, it yields `None`.
pub fn optional<T: Clone + Send + Sync + 'static>(
) -> impl Filter<Extract = (Option<T>,), Error = Infallible> + Copy {
filter_fn_one(|route| future::ok(route.extensions().get::<T>().cloned()))
}
unit_error! {
/// An error used to reject if `get` cannot find the extension.
pub MissingExtension: "Missing request extension"
}
================================================
FILE: src/filters/fs.rs
================================================
//! File System Filters
use std::cmp;
use std::convert::Infallible;
use std::fs::Metadata;
use std::future::Future;
use std::io;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
use std::task::Poll;
use bytes::{Bytes, BytesMut};
use futures_util::future::Either;
use futures_util::{future, ready, stream, FutureExt, Stream, StreamExt, TryFutureExt};
use headers::{
AcceptRanges, ContentLength, ContentRange, ContentType, HeaderMapExt, IfModifiedSince, IfRange,
IfUnmodifiedSince, LastModified, Range,
};
use http::StatusCode;
use mime_guess;
use percent_encoding::percent_decode_str;
use tokio::fs::File as TkFile;
use tokio::io::AsyncSeekExt;
use tokio_util::io::poll_read_buf;
use crate::bodyt::Body;
use crate::filter::{Filter, FilterClone, One};
use crate::reject::{self, Rejection};
use crate::reply::{Reply, Response};
/// Creates a `Filter` that serves a File at the `path`.
///
/// Does not filter out based on any information of the request. Always serves
/// the file at the exact `path` provided. Thus, this can be used to serve a
/// single file with `GET`s, but could also be used in combination with other
/// filters, such as after validating in `POST` request, wanting to return a
/// specific file as the body.
///
/// For serving a directory, see [dir].
///
/// # Example
///
/// ```
/// // Always serves this file from the file system.
/// let route = warp::fs::file("/www/static/app.js");
/// ```
pub fn file(path: impl Into<PathBuf>) -> impl FilterClone<Extract = One<File>, Error = Rejection> {
let path = Arc::new(path.into());
crate::any()
.map(move || {
tracing::trace!("file: {:?}", path);
ArcPath(path.clone())
})
.and(conditionals())
.and_then(file_reply)
}
/// Creates a `Filter` that serves a directory at the base `path` joined
/// by the request path.
///
/// This can be used to serve "static files" from a directory. By far the most
/// common pattern of serving static files is for `GET` requests, so this
/// filter automatically includes a `GET` check.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Matches requests that start with `/static`,
/// // and then uses the rest of that path to lookup
/// // and serve a file from `/www/static`.
/// let route = warp::path("static")
/// .and(warp::fs::dir("/www/static"));
///
/// // For example:
/// // - `GET /static/app.js` would serve the file `/www/static/app.js`
/// // - `GET /static/css/app.css` would serve the file `/www/static/css/app.css`
/// ```
pub fn dir(path: impl Into<PathBuf>) -> impl FilterClone<Extract = One<File>, Error = Rejection> {
let base = Arc::new(path.into());
crate::get()
.or(crate::head())
.unify()
.and(path_from_tail(base))
.and(conditionals())
.and_then(file_reply)
}
fn path_from_tail(
base: Arc<PathBuf>,
) -> impl FilterClone<Extract = One<ArcPath>, Error = Rejection> {
crate::path::tail().and_then(move |tail: crate::path::Tail| {
future::ready(sanitize_path(base.as_ref(), tail.as_str())).and_then(|mut buf| async {
let is_dir = tokio::fs::metadata(buf.clone())
.await
.map(|m| m.is_dir())
.unwrap_or(false);
if is_dir {
tracing::debug!("dir: appending index.html to directory path");
buf.push("index.html");
}
tracing::trace!("dir: {:?}", buf);
Ok(ArcPath(Arc::new(buf)))
})
})
}
fn sanitize_path(base: impl AsRef<Path>, tail: &str) -> Result<PathBuf, Rejection> {
let mut buf = PathBuf::from(base.as_ref());
let p = match percent_decode_str(tail).decode_utf8() {
Ok(p) => p,
Err(err) => {
tracing::debug!("dir: failed to decode route={:?}: {:?}", tail, err);
return Err(reject::not_found());
}
};
tracing::trace!("dir? base={:?}, route={:?}", base.as_ref(), p);
for seg in p.split('/') {
if seg.starts_with("..") {
tracing::warn!("dir: rejecting segment starting with '..'");
return Err(reject::not_found());
} else if seg.contains('\\') {
tracing::warn!("dir: rejecting segment containing backslash (\\)");
return Err(reject::not_found());
} else if cfg!(windows) && seg.contains(':') {
tracing::warn!("dir: rejecting segment containing colon (:)");
return Err(reject::not_found());
} else {
buf.push(seg);
}
}
Ok(buf)
}
#[derive(Debug)]
struct Conditionals {
if_modified_since: Option<IfModifiedSince>,
if_unmodified_since: Option<IfUnmodifiedSince>,
if_range: Option<IfRange>,
range: Option<Range>,
}
enum Cond {
NoBody(Response),
WithBody(Option<Range>),
}
impl Conditionals {
fn check(self, last_modified: Option<LastModified>) -> Cond {
if let Some(since) = self.if_unmodified_since {
let precondition = last_modified
.map(|time| since.precondition_passes(time.into()))
.unwrap_or(false);
tracing::trace!(
"if-unmodified-since? {:?} vs {:?} = {}",
since,
last_modified,
precondition
);
if !precondition {
let mut res = Response::new(Body::empty());
*res.status_mut() = StatusCode::PRECONDITION_FAILED;
return Cond::NoBody(res);
}
}
if let Some(since) = self.if_modified_since {
tracing::trace!(
"if-modified-since? header = {:?}, file = {:?}",
since,
last_modified
);
let unmodified = last_modified
.map(|time| !since.is_modified(time.into()))
// no last_modified means its always modified
.unwrap_or(false);
if unmodified {
let mut res = Response::new(Body::empty());
*res.status_mut() = StatusCode::NOT_MODIFIED;
return Cond::NoBody(res);
}
}
if let Some(if_range) = self.if_range {
tracing::trace!("if-range? {:?} vs {:?}", if_range, last_modified);
let can_range = !if_range.is_modified(None, last_modified.as_ref());
if !can_range {
return Cond::WithBody(None);
}
}
Cond::WithBody(self.range)
}
}
fn conditionals() -> impl Filter<Extract = One<Conditionals>, Error = Infallible> + Copy {
crate::header::optional2()
.and(crate::header::optional2())
.and(crate::header::optional2())
.and(crate::header::optional2())
.map(
|if_modified_since, if_unmodified_since, if_range, range| Conditionals {
if_modified_since,
if_unmodified_since,
if_range,
range,
},
)
}
/// A file response.
#[derive(Debug)]
pub struct File {
resp: Response,
path: ArcPath,
}
impl File {
/// Extract the `&Path` of the file this `Response` delivers.
///
/// # Example
///
/// The example below changes the Content-Type response header for every file called `video.mp4`.
///
/// ```
/// use warp::{Filter, reply::Reply};
///
/// let route = warp::path("static")
/// .and(warp::fs::dir("/www/static"))
/// .map(|reply: warp::filters::fs::File| {
/// if reply.path().ends_with("video.mp4") {
/// warp::reply::with_header(reply, "Content-Type", "video/mp4").into_response()
/// } else {
/// reply.into_response()
/// }
/// });
/// ```
pub fn path(&self) -> &Path {
self.path.as_ref()
}
}
// Silly wrapper since Arc<PathBuf> doesn't implement AsRef<Path> ;_;
#[derive(Clone, Debug)]
struct ArcPath(Arc<PathBuf>);
impl AsRef<Path> for ArcPath {
fn as_ref(&self) -> &Path {
(*self.0).as_ref()
}
}
impl Reply for File {
fn into_response(self) -> Response {
self.resp
}
}
fn file_reply(
path: ArcPath,
conditionals: Conditionals,
) -> impl Future<Output = Result<File, Rejection>> + Send {
TkFile::open(path.clone()).then(move |res| match res {
Ok(f) => Either::Left(file_conditional(f, path, conditionals)),
Err(err) => {
let rej = match err.kind() {
io::ErrorKind::NotFound => {
tracing::debug!("file not found: {:?}", path.as_ref().display());
reject::not_found()
}
io::ErrorKind::PermissionDenied => {
tracing::warn!("file permission denied: {:?}", path.as_ref().display());
reject::known(FilePermissionError { _p: () })
}
_ => {
tracing::error!(
"file open error (path={:?}): {} ",
path.as_ref().display(),
err
);
reject::known(FileOpenError { _p: () })
}
};
Either::Right(future::err(rej))
}
})
}
async fn file_metadata(f: TkFile) -> Result<(TkFile, Metadata), Rejection> {
match f.metadata().await {
Ok(meta) => Ok((f, meta)),
Err(err) => {
tracing::debug!("file metadata error: {}", err);
Err(reject::not_found())
}
}
}
fn file_conditional(
f: TkFile,
path: ArcPath,
conditionals: Conditionals,
) -> impl Future<Output = Result<File, Rejection>> + Send {
file_metadata(f).map_ok(move |(file, meta)| {
let mut len = meta.len();
let modified = meta.modified().ok().map(LastModified::from);
let resp = match conditionals.check(modified) {
Cond::NoBody(resp) => resp,
Cond::WithBody(range) => {
bytes_range(range, len)
.map(|(start, end)| {
let sub_len = end - start;
let buf_size = optimal_buf_size(&meta);
let stream = file_stream(file, buf_size, (start, end));
let body = Body::wrap_stream(stream);
let mut resp = Response::new(body);
if sub_len != len {
*resp.status_mut() = StatusCode::PARTIAL_CONTENT;
resp.headers_mut().typed_insert(
ContentRange::bytes(start..end, len).expect("valid ContentRange"),
);
len = sub_len;
}
let mime = mime_guess::from_path(path.as_ref()).first_or_octet_stream();
resp.headers_mut().typed_insert(ContentLength(len));
resp.headers_mut().typed_insert(ContentType::from(mime));
resp.headers_mut().typed_insert(AcceptRanges::bytes());
if let Some(last_modified) = modified {
resp.headers_mut().typed_insert(last_modified);
}
resp
})
.unwrap_or_else(|BadRange| {
// bad byte range
let mut resp = Response::new(Body::empty());
*resp.status_mut() = StatusCode::RANGE_NOT_SATISFIABLE;
resp.headers_mut()
.typed_insert(ContentRange::unsatisfied_bytes(len));
resp
})
}
};
File { resp, path }
})
}
struct BadRange;
fn bytes_range(range: Option<Range>, max_len: u64) -> Result<(u64, u64), BadRange> {
use std::ops::Bound;
let range = if let Some(range) = range {
range
} else {
return Ok((0, max_len));
};
let ret = range
.satisfiable_ranges(max_len)
.map(|(start, end)| {
let start = match start {
Bound::Unbounded => 0,
Bound::Included(s) => s,
Bound::Excluded(s) => s + 1,
};
let end = match end {
Bound::Unbounded => max_len,
Bound::Included(s) => {
// For the special case where s == the file size
if s == max_len {
s
} else {
s + 1
}
}
Bound::Excluded(s) => s,
};
if start < end && end <= max_len {
Ok((start, end))
} else {
tracing::trace!("unsatisfiable byte range: {}-{}/{}", start, end, max_len);
Err(BadRange)
}
})
.next()
.unwrap_or(Ok((0, max_len)));
ret
}
fn file_stream(
mut file: TkFile,
buf_size: usize,
(start, end): (u64, u64),
) -> impl Stream<Item = Result<Bytes, io::Error>> + Send {
use std::io::SeekFrom;
let seek = async move {
if start != 0 {
file.seek(SeekFrom::Start(start)).await?;
}
Ok(file)
};
seek.into_stream()
.map(move |result| {
let mut buf = BytesMut::new();
let mut len = end - start;
let mut f = match result {
Ok(f) => f,
Err(f) => return Either::Left(stream::once(future::err(f))),
};
Either::Right(stream::poll_fn(move |cx| {
if len == 0 {
return Poll::Ready(None);
}
reserve_at_least(&mut buf, buf_size);
let n = match ready!(poll_read_buf(Pin::new(&mut f), cx, &mut buf)) {
Ok(n) => n as u64,
Err(err) => {
tracing::debug!("file read error: {}", err);
return Poll::Ready(Some(Err(err)));
}
};
if n == 0 {
tracing::debug!("file read found EOF before expected length");
return Poll::Ready(None);
}
let mut chunk = buf.split().freeze();
if n > len {
chunk = chunk.split_to(len as usize);
len = 0;
} else {
len -= n;
}
Poll::Ready(Some(Ok(chunk)))
}))
})
.flatten()
}
fn reserve_at_least(buf: &mut BytesMut, cap: usize) {
if buf.capacity() - buf.len() < cap {
buf.reserve(cap);
}
}
const DEFAULT_READ_BUF_SIZE: usize = 8_192;
fn optimal_buf_size(metadata: &Metadata) -> usize {
let block_size = get_block_size(metadata);
// If file length is smaller than block size, don't waste space
// reserving a bigger-than-needed buffer.
cmp::min(block_size as u64, metadata.len()) as usize
}
#[cfg(unix)]
fn get_block_size(metadata: &Metadata) -> usize {
use std::os::unix::fs::MetadataExt;
//TODO: blksize() returns u64, should handle bad cast...
//(really, a block size bigger than 4gb?)
// Use device blocksize unless it's really small.
cmp::max(metadata.blksize() as usize, DEFAULT_READ_BUF_SIZE)
}
#[cfg(not(unix))]
fn get_block_size(_metadata: &Metadata) -> usize {
DEFAULT_READ_BUF_SIZE
}
// ===== Rejections =====
unit_error! {
pub(crate) FileOpenError: "file open error"
}
unit_error! {
pub(crate) FilePermissionError: "file perimission error"
}
#[cfg(test)]
mod tests {
use super::sanitize_path;
use bytes::BytesMut;
#[test]
fn test_sanitize_path() {
let base = "/var/www";
fn p(s: &str) -> &::std::path::Path {
s.as_ref()
}
assert_eq!(
sanitize_path(base, "/foo.html").unwrap(),
p("/var/www/foo.html")
);
// bad paths
sanitize_path(base, "/../foo.html").expect_err("dot dot");
sanitize_path(base, "/C:\\/foo.html").expect_err("C:\\");
}
#[test]
fn test_reserve_at_least() {
let mut buf = BytesMut::new();
let cap = 8_192;
assert_eq!(buf.len(), 0);
assert_eq!(buf.capacity(), 0);
super::reserve_at_least(&mut buf, cap);
assert_eq!(buf.len(), 0);
assert_eq!(buf.capacity(), cap);
}
}
================================================
FILE: src/filters/header.rs
================================================
//! Header Filters
//!
//! These filters are used to interact with the Request HTTP headers. Some
//! of them, like `exact` and `exact_ignore_case`, are just predicates,
//! they don't extract any values. The `header` filter allows parsing
//! a type from any header.
use std::convert::Infallible;
use std::str::FromStr;
use futures_util::future;
use headers::{Header, HeaderMapExt};
use http::header::HeaderValue;
use http::HeaderMap;
use crate::filter::{filter_fn, filter_fn_one, Filter, One};
use crate::reject::{self, Rejection};
/// Create a `Filter` that tries to parse the specified header.
///
/// This `Filter` will look for a header with supplied name, and try to
/// parse to a `T`, otherwise rejects the request.
///
/// # Example
///
/// ```
/// use std::net::SocketAddr;
///
/// // Parse `content-length: 100` as a `u64`
/// let content_length = warp::header::<u64>("content-length");
///
/// // Parse `host: 127.0.0.1:8080` as a `SocketAddr
/// let local_host = warp::header::<SocketAddr>("host");
///
/// // Parse `foo: bar` into a `String`
/// let foo = warp::header::<String>("foo");
/// ```
pub fn header<T: FromStr + Send + 'static>(
name: &'static str,
) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
filter_fn_one(move |route| {
tracing::trace!("header({:?})", name);
let route = route
.headers()
.get(name)
.ok_or_else(|| reject::missing_header(name))
.and_then(|value| value.to_str().map_err(|_| reject::invalid_header(name)))
.and_then(|s| T::from_str(s).map_err(|_| reject::invalid_header(name)));
future::ready(route)
})
}
pub(crate) fn header2<T: Header + Send + 'static>(
) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
filter_fn_one(move |route| {
tracing::trace!("header2({:?})", T::name());
let route = route
.headers()
.typed_get()
.ok_or_else(|| reject::invalid_header(T::name().as_str()));
future::ready(route)
})
}
/// Create a `Filter` that tries to parse the specified header, if it exists.
///
/// If the header does not exist, it yields `None`. Otherwise, it will try to
/// parse as a `T`, and if it fails, a invalid header rejection is return. If
/// successful, the filter yields `Some(T)`.
///
/// # Example
///
/// ```
/// // Grab the `authorization` header if it exists.
/// let opt_auth = warp::header::optional::<String>("authorization");
/// ```
pub fn optional<T>(
name: &'static str,
) -> impl Filter<Extract = One<Option<T>>, Error = Rejection> + Copy
where
T: FromStr + Send + 'static,
{
filter_fn_one(move |route| {
tracing::trace!("optional({:?})", name);
let result = route.headers().get(name).map(|value| {
value
.to_str()
.map_err(|_| reject::invalid_header(name))?
.parse::<T>()
.map_err(|_| reject::invalid_header(name))
});
match result {
Some(Ok(t)) => future::ok(Some(t)),
Some(Err(e)) => future::err(e),
None => future::ok(None),
}
})
}
pub(crate) fn optional2<T>() -> impl Filter<Extract = One<Option<T>>, Error = Infallible> + Copy
where
T: Header + Send + 'static,
{
filter_fn_one(move |route| future::ready(Ok(route.headers().typed_get())))
}
/* TODO
pub fn exact2<T>(header: T) -> impl FilterClone<Extract=(), Error=Rejection>
where
T: Header + PartialEq + Clone + Send,
{
filter_fn(move |route| {
tracing::trace!("exact2({:?})", T::NAME);
route.headers()
.typed_get::<T>()
.and_then(|val| if val == header {
Some(())
} else {
None
})
.ok_or_else(|| reject::bad_request())
})
}
*/
/// Create a `Filter` that requires a header to match the value exactly.
///
/// This `Filter` will look for a header with supplied name and the exact
/// value, otherwise rejects the request.
///
/// # Example
///
/// ```
/// // Require `dnt: 1` header to be set.
/// let must_dnt = warp::header::exact("dnt", "1");
/// ```
pub fn exact(
name: &'static str,
value: &'static str,
) -> impl Filter<Extract = (), Error = Rejection> + Copy {
filter_fn(move |route| {
tracing::trace!("exact?({:?}, {:?})", name, value);
let route = route
.headers()
.get(name)
.ok_or_else(|| reject::missing_header(name))
.and_then(|val| {
if val == value {
Ok(())
} else {
Err(reject::invalid_header(name))
}
});
future::ready(route)
})
}
/// Create a `Filter` that requires a header to match the value exactly.
///
/// This `Filter` will look for a header with supplied name and the exact
/// value, ignoring ASCII case, otherwise rejects the request.
///
/// # Example
///
/// ```
/// // Require `connection: keep-alive` header to be set.
/// let keep_alive = warp::header::exact_ignore_case("connection", "keep-alive");
/// ```
pub fn exact_ignore_case(
name: &'static str,
value: &'static str,
) -> impl Filter<Extract = (), Error = Rejection> + Copy {
filter_fn(move |route| {
tracing::trace!("exact_ignore_case({:?}, {:?})", name, value);
let route = route
.headers()
.get(name)
.ok_or_else(|| reject::missing_header(name))
.and_then(|val| {
if val.as_bytes().eq_ignore_ascii_case(value.as_bytes()) {
Ok(())
} else {
Err(reject::invalid_header(name))
}
});
future::ready(route)
})
}
/// Create a `Filter` that gets a `HeaderValue` for the name.
///
/// # Example
///
/// ```
/// use warp::{Filter, http::header::HeaderValue};
///
/// let filter = warp::header::value("x-token")
/// .map(|value: HeaderValue| {
/// format!("header value bytes: {:?}", value)
/// });
/// ```
pub fn value(
name: &'static str,
) -> impl Filter<Extract = One<HeaderValue>, Error = Rejection> + Copy {
filter_fn_one(move |route| {
tracing::trace!("value({:?})", name);
let route = route
.headers()
.get(name)
.cloned()
.ok_or_else(|| reject::missing_header(name));
future::ready(route)
})
}
/// Create a `Filter` that returns a clone of the request's `HeaderMap`.
///
/// # Example
///
/// ```
/// use warp::{Filter, http::HeaderMap};
///
/// let headers = warp::header::headers_cloned()
/// .map(|headers: HeaderMap| {
/// format!("header count: {}", headers.len())
/// });
/// ```
pub fn headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error = Infallible> + Copy {
filter_fn_one(|route| future::ok(route.headers().clone()))
}
================================================
FILE: src/filters/host.rs
================================================
//! Host ("authority") filter
//!
use crate::filter::{filter_fn_one, Filter, One};
use crate::reject::{self, Rejection};
use futures_util::future;
pub use http::uri::Authority;
use std::str::FromStr;
/// Creates a `Filter` that requires a specific authority (target server's
/// host and port) in the request.
///
/// Authority is specified either in the `Host` header or in the target URI.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let multihost =
/// warp::host::exact("foo.com").map(|| "you've reached foo.com")
/// .or(warp::host::exact("bar.com").map(|| "you've reached bar.com"));
/// ```
pub fn exact(expected: &str) -> impl Filter<Extract = (), Error = Rejection> + Clone {
let expected = Authority::from_str(expected).expect("invalid host/authority");
optional()
.and_then(move |option: Option<Authority>| match option {
Some(authority) if authority == expected => future::ok(()),
_ => future::err(reject::not_found()),
})
.untuple_one()
}
/// Creates a `Filter` that looks for an authority (target server's host
/// and port) in the request.
///
/// Authority is specified either in the `Host` header or in the target URI.
///
/// If found, extracts the
gitextract_pan7l99a/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ ├── config.yml
│ │ └── feature_request.md
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── examples/
│ ├── README.md
│ ├── body.rs
│ ├── compression.rs
│ ├── dir/
│ │ ├── another.html
│ │ └── index.html
│ ├── dir.rs
│ ├── file.rs
│ ├── futures.rs
│ ├── handlebars_template.rs
│ ├── headers.rs
│ ├── hello.rs
│ ├── multipart.rs
│ ├── query_string.rs
│ ├── rejections.rs
│ ├── returning.rs
│ ├── routing.rs
│ ├── sse.rs
│ ├── sse_chat.rs
│ ├── tls/
│ │ ├── cert.ecc.pem
│ │ ├── cert.pem
│ │ ├── key.ecc
│ │ └── key.rsa
│ ├── tls.rs
│ ├── todos.rs
│ ├── tracing.rs
│ ├── unix_socket.rs
│ ├── websockets.rs
│ └── websockets_chat.rs
├── src/
│ ├── bodyt.rs
│ ├── error.rs
│ ├── filter/
│ │ ├── and.rs
│ │ ├── and_then.rs
│ │ ├── boxed.rs
│ │ ├── map.rs
│ │ ├── map_err.rs
│ │ ├── mod.rs
│ │ ├── or.rs
│ │ ├── or_else.rs
│ │ ├── recover.rs
│ │ ├── service.rs
│ │ ├── then.rs
│ │ ├── unify.rs
│ │ ├── untuple_one.rs
│ │ └── wrap.rs
│ ├── filters/
│ │ ├── addr.rs
│ │ ├── any.rs
│ │ ├── body.rs
│ │ ├── compression.rs
│ │ ├── cookie.rs
│ │ ├── cors.rs
│ │ ├── ext.rs
│ │ ├── fs.rs
│ │ ├── header.rs
│ │ ├── host.rs
│ │ ├── log.rs
│ │ ├── method.rs
│ │ ├── mod.rs
│ │ ├── multipart.rs
│ │ ├── path.rs
│ │ ├── query.rs
│ │ ├── reply.rs
│ │ ├── sse.rs
│ │ ├── trace.rs
│ │ └── ws.rs
│ ├── generic.rs
│ ├── lib.rs
│ ├── redirect.rs
│ ├── reject.rs
│ ├── reply.rs
│ ├── route.rs
│ ├── server.rs
│ ├── service.rs
│ ├── test.rs
│ └── tls.rs
└── tests/
├── addr.rs
├── body.rs
├── cookie.rs
├── cors.rs
├── ext.rs
├── filter.rs
├── fs.rs
├── header.rs
├── host.rs
├── method.rs
├── multipart.rs
├── path.rs
├── query.rs
├── redirect.rs
├── reply_with.rs
├── tracing.rs
└── ws.rs
SYMBOL INDEX (1010 symbols across 82 files)
FILE: examples/body.rs
type Employee (line 8) | struct Employee {
function main (line 14) | async fn main() {
FILE: examples/compression.rs
function main (line 6) | async fn main() {
FILE: examples/dir.rs
function main (line 4) | async fn main() {
FILE: examples/file.rs
function main (line 6) | async fn main() {
FILE: examples/futures.rs
function main (line 9) | async fn main() {
function sleepy (line 18) | async fn sleepy(Seconds(seconds): Seconds) -> Result<impl warp::Reply, I...
type Seconds (line 24) | struct Seconds(u64);
type Err (line 27) | type Err = ();
method from_str (line 28) | fn from_str(src: &str) -> Result<Self, Self::Err> {
FILE: examples/handlebars_template.rs
type WithTemplate (line 9) | struct WithTemplate<T: Serialize> {
function render (line 14) | fn render<T>(template: WithTemplate<T>, hbs: Arc<Handlebars<'_>>) -> imp...
function main (line 25) | async fn main() {
FILE: examples/headers.rs
function main (line 12) | async fn main() {
FILE: examples/hello.rs
function main (line 5) | async fn main() {
FILE: examples/multipart.rs
function main (line 7) | async fn main() {
FILE: examples/query_string.rs
type MyObject (line 9) | struct MyObject {
function main (line 15) | async fn main() {
FILE: examples/rejections.rs
function main (line 14) | async fn main() {
function div_by (line 42) | fn div_by() -> impl Filter<Extract = (NonZeroU16,), Error = Rejection> +...
type DenomRequest (line 53) | struct DenomRequest {
type DivideByZero (line 58) | struct DivideByZero;
type Math (line 66) | struct Math {
type ErrorMessage (line 73) | struct ErrorMessage {
function handle_rejection (line 80) | async fn handle_rejection(err: Rejection) -> Result<impl Reply, Infallib...
FILE: examples/returning.rs
function assets_filter (line 7) | pub fn assets_filter() -> BoxedFilter<(impl Reply,)> {
function index_filter (line 12) | pub fn index_filter() -> impl Filter<Extract = (&'static str,), Error = ...
function main (line 17) | async fn main() {
FILE: examples/routing.rs
function main (line 6) | async fn main() {
FILE: examples/sse.rs
function sse_counter (line 9) | fn sse_counter(counter: u64) -> Result<Event, Infallible> {
function main (line 14) | async fn main() {
FILE: examples/sse_chat.rs
function main (line 12) | async fn main() {
type Message (line 63) | enum Message {
type NotUtf8 (line 69) | struct NotUtf8;
type Users (line 76) | type Users = Arc<Mutex<HashMap<usize, mpsc::UnboundedSender<Message>>>>;
function user_connected (line 78) | fn user_connected(users: Users) -> impl Stream<Item = Result<Event, warp...
function user_message (line 103) | fn user_message(my_id: usize, msg: String, users: &Users) {
FILE: examples/tls.rs
function main (line 8) | async fn main() {
function main (line 27) | fn main() {
FILE: examples/todos.rs
function main (line 15) | async fn main() {
function todos (line 39) | pub fn todos(
function todos_list (line 49) | pub fn todos_list(
function todos_create (line 60) | pub fn todos_create(
function todos_update (line 71) | pub fn todos_update(
function todos_delete (line 82) | pub fn todos_delete(
function with_db (line 99) | fn with_db(db: Db) -> impl Filter<Extract = (Db,), Error = std::convert:...
function json_body (line 103) | fn json_body() -> impl Filter<Extract = (Todo,), Error = warp::Rejection...
function list_todos (line 119) | pub async fn list_todos(opts: ListOptions, db: Db) -> Result<impl warp::...
function create_todo (line 131) | pub async fn create_todo(create: Todo, db: Db) -> Result<impl warp::Repl...
function update_todo (line 150) | pub async fn update_todo(
function delete_todo (line 172) | pub async fn delete_todo(id: u64, db: Db) -> Result<impl warp::Reply, In...
type Db (line 205) | pub type Db = Arc<Mutex<Vec<Todo>>>;
function blank_db (line 207) | pub fn blank_db() -> Db {
type Todo (line 212) | pub struct Todo {
type ListOptions (line 220) | pub struct ListOptions {
function test_post (line 237) | async fn test_post() {
function test_post_conflict (line 252) | async fn test_post_conflict() {
function test_put_unknown (line 268) | async fn test_put_unknown() {
function todo1 (line 284) | fn todo1() -> Todo {
FILE: examples/tracing.rs
function main (line 12) | async fn main() {
FILE: examples/unix_socket.rs
function main (line 5) | async fn main() {
function main (line 24) | async fn main() {
FILE: examples/websockets.rs
function main (line 7) | async fn main() {
FILE: examples/websockets_chat.rs
type Users (line 21) | type Users = Arc<RwLock<HashMap<usize, mpsc::UnboundedSender<Message>>>>;
function main (line 24) | async fn main() {
function user_connected (line 51) | async fn user_connected(ws: WebSocket, users: Users) {
function user_message (line 100) | async fn user_message(my_id: usize, msg: Message, users: &Users) {
function user_disconnected (line 122) | async fn user_disconnected(my_id: usize, users: &Users) {
FILE: src/bodyt.rs
type Body (line 11) | pub struct Body(BoxBody<Bytes, crate::Error>);
type Data (line 20) | type Data = Bytes;
type Error (line 21) | type Error = crate::Error;
method poll_frame (line 23) | fn poll_frame(
method is_end_stream (line 30) | fn is_end_stream(&self) -> bool {
method size_hint (line 34) | fn size_hint(&self) -> http_body::SizeHint {
method empty (line 40) | pub(crate) fn empty() -> Self {
method wrap (line 48) | pub(crate) fn wrap<B>(body: B) -> Self
method wrap_stream (line 59) | pub(crate) fn wrap_stream<S, B, E>(stream: S) -> Self
method from (line 74) | fn from(b: Bytes) -> Self {
method from (line 84) | fn from(s: &'static str) -> Self {
method from (line 90) | fn from(s: String) -> Self {
method from (line 96) | fn from(v: &'static [u8]) -> Self {
method from (line 102) | fn from(v: Vec<u8>) -> Self {
method from (line 108) | fn from(opt: Option<Bytes>) -> Self {
method default (line 14) | fn default() -> Self {
FILE: src/error.rs
type BoxError (line 5) | type BoxError = Box<dyn std::error::Error + Send + Sync>;
type Error (line 8) | pub struct Error {
method new (line 13) | pub(crate) fn new<E: Into<BoxError>>(err: E) -> Error {
method fmt (line 19) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method fmt (line 26) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method from (line 38) | fn from(infallible: Infallible) -> Error {
method source (line 32) | fn source(&self) -> Option<&(dyn StdError + 'static)> {
function error_size_of (line 44) | fn error_size_of() {
function error_source (line 52) | fn error_source() {
FILE: src/filter/and.rs
type And (line 13) | pub struct And<T, U> {
type Extract (line 27) | type Extract = CombinedTuples<T::Extract, U::Extract>;
type Error (line 28) | type Error = <U::Error as CombineRejection<T::Error>>::One;
type Future (line 29) | type Future = AndFuture<T, U>;
method filter (line 31) | fn filter(&self, _: Internal) -> Self::Future {
type AndFuture (line 40) | pub struct AndFuture<T: Filter, U: Filter> {
type State (line 46) | enum State<T, TE, U: Filter> {
type Output (line 59) | type Output = Result<
method poll (line 64) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type Output (line 77) | type Output = Result<CombinedTuples<TE, U::Extract>, <U::Error as Combin...
method poll (line 79) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/and_then.rs
type AndThen (line 12) | pub struct AndThen<T, F> {
type Extract (line 24) | type Extract = (<F::Output as TryFuture>::Ok,);
type Error (line 25) | type Error = <<F::Output as TryFuture>::Error as CombineRejection<T::Err...
type Future (line 26) | type Future = AndThenFuture<T, F>;
method filter (line 28) | fn filter(&self, _: Internal) -> Self::Future {
type AndThenFuture (line 37) | pub struct AndThenFuture<T, F>
type State (line 49) | enum State<T, F>
type Output (line 68) | type Output = Result<
method poll (line 73) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type Output (line 85) | type Output = Result<
method poll (line 90) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/boxed.rs
type BoxedFilter (line 30) | pub struct BoxedFilter<T: Tuple> {
function new (line 42) | pub(super) fn new<F>(filter: F) -> BoxedFilter<T>
method clone (line 56) | fn clone(&self) -> BoxedFilter<T> {
function fmt (line 64) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function _assert_send (line 69) | fn _assert_send() {
type Extract (line 75) | type Extract = T;
type Error (line 76) | type Error = Rejection;
type Future (line 77) | type Future = Pin<Box<dyn Future<Output = Result<T, Rejection>> + Send>>;
method filter (line 79) | fn filter(&self, _: Internal) -> Self::Future {
type BoxingFilter (line 84) | struct BoxingFilter<F> {
type Extract (line 93) | type Extract = F::Extract;
type Error (line 94) | type Error = F::Error;
type Future (line 95) | type Future = Pin<Box<dyn Future<Output = Result<Self::Extract, Self::Er...
method filter (line 97) | fn filter(&self, _: Internal) -> Self::Future {
FILE: src/filter/map.rs
type Map (line 11) | pub struct Map<T, F> {
type Extract (line 21) | type Extract = (F::Output,);
type Error (line 22) | type Error = T::Error;
type Future (line 23) | type Future = MapFuture<T, F>;
method filter (line 25) | fn filter(&self, _: Internal) -> Self::Future {
type MapFuture (line 35) | pub struct MapFuture<T: Filter, F> {
type Output (line 46) | type Output = Result<(F::Output,), T::Error>;
method poll (line 49) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
FILE: src/filter/map_err.rs
type MapErr (line 12) | pub struct MapErr<T, F> {
type Extract (line 23) | type Extract = T::Extract;
type Error (line 24) | type Error = E;
type Future (line 25) | type Future = MapErrFuture<T, F>;
method filter (line 27) | fn filter(&self, _: Internal) -> Self::Future {
type MapErrFuture (line 37) | pub struct MapErrFuture<T: Filter, F> {
type Output (line 48) | type Output = Result<T::Extract, E>;
method poll (line 51) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/mod.rs
type FilterBase (line 39) | pub trait FilterBase {
method filter (line 44) | fn filter(&self, internal: Internal) -> Self::Future;
method map_err (line 46) | fn map_err<F, E>(self, _internal: Internal, fun: F) -> MapErr<Self, F>
type Extract (line 484) | type Extract = U::Ok;
type Error (line 485) | type Error = U::Error;
type Future (line 486) | type Future = future::IntoFuture<U>;
method filter (line 489) | fn filter(&self, _: Internal) -> Self::Future {
type Internal (line 70) | pub struct Internal;
type Filter (line 93) | pub trait Filter: FilterBase {
method and (line 113) | fn and<F>(self, other: F) -> And<Self, F>
method or (line 138) | fn or<F>(self, other: F) -> Or<Self, F>
method map (line 191) | fn map<F>(self, fun: F) -> Map<Self, F>
method then (line 217) | fn then<F>(self, fun: F) -> Then<Self, F>
method and_then (line 255) | fn and_then<F>(self, fun: F) -> AndThen<Self, F>
method or_else (line 272) | fn or_else<F>(self, fun: F) -> OrElse<Self, F>
method recover (line 292) | fn recover<F>(self, fun: F) -> Recover<Self, F>
method unify (line 327) | fn unify<T>(self) -> Unify<Self>
method untuple_one (line 370) | fn untuple_one<T>(self) -> UntupleOne<Self>
method with (line 394) | fn with<W>(self, wrapper: W) -> W::Wrapped
method boxed (line 426) | fn boxed(self) -> BoxedFilter<Self::Extract>
type FilterClone (line 438) | pub trait FilterClone: Filter + Clone {}
function _assert_object_safe (line 442) | fn _assert_object_safe() {
function filter_fn (line 448) | pub(crate) fn filter_fn<F, U>(func: F) -> FilterFn<F>
function filter_fn_one (line 458) | pub(crate) fn filter_fn_one<F, U>(
type FilterFn (line 472) | pub(crate) struct FilterFn<F> {
FILE: src/filter/or.rs
type Combined (line 13) | type Combined<E1, E2> = <E1 as CombineRejection<E2>>::Combined;
type Or (line 16) | pub struct Or<T, U> {
type Extract (line 27) | type Extract = (Either<T::Extract, U::Extract>,);
type Error (line 29) | type Error = Combined<U::Error, T::Error>;
type Future (line 30) | type Future = EitherFuture<T, U>;
method filter (line 32) | fn filter(&self, _: Internal) -> Self::Future {
type EitherFuture (line 43) | pub struct EitherFuture<T: Filter, U: Filter> {
type State (line 50) | enum State<T: Filter, U: Filter> {
type PathIndex (line 57) | struct PathIndex(usize);
method reset_path (line 60) | fn reset_path(&self) {
type Output (line 71) | type Output = Result<(Either<T::Extract, U::Extract>,), Combined<U::Erro...
method poll (line 73) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/or_else.rs
type OrElse (line 13) | pub struct OrElse<T, F> {
type Extract (line 25) | type Extract = <F::Output as TryFuture>::Ok;
type Error (line 26) | type Error = <F::Output as TryFuture>::Error;
type Future (line 27) | type Future = OrElseFuture<T, F>;
method filter (line 29) | fn filter(&self, _: Internal) -> Self::Future {
type OrElseFuture (line 40) | pub struct OrElseFuture<T: Filter, F>
type State (line 52) | enum State<T, F>
type PathIndex (line 64) | struct PathIndex(usize);
method reset_path (line 67) | fn reset_path(&self) {
type Output (line 78) | type Output = Result<<F::Output as TryFuture>::Ok, <F::Output as TryFutu...
method poll (line 80) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/recover.rs
type Recover (line 14) | pub struct Recover<T, F> {
type Extract (line 26) | type Extract = (Either<T::Extract, (<F::Output as TryFuture>::Ok,)>,);
type Error (line 27) | type Error = <F::Output as TryFuture>::Error;
type Future (line 28) | type Future = RecoverFuture<T, F>;
method filter (line 30) | fn filter(&self, _: Internal) -> Self::Future {
type RecoverFuture (line 41) | pub struct RecoverFuture<T: Filter, F>
type State (line 54) | enum State<T, F>
type PathIndex (line 67) | struct PathIndex(usize);
method reset_path (line 70) | fn reset_path(&self) {
type Output (line 82) | type Output = Result<
method poll (line 87) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/service.rs
function service (line 43) | pub fn service<F>(filter: F) -> FilteredService<F>
type FilteredService (line 53) | pub struct FilteredService<F> {
function call_route (line 64) | pub(crate) fn call_route(&self, req: Request) -> FilteredFuture<F::Futur...
type Response (line 81) | type Response = Response;
type Error (line 82) | type Error = Infallible;
type Future (line 83) | type Future = FilteredFuture<F::Future>;
type Output (line 110) | type Output = Result<Response, Infallible>;
method poll (line 113) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Outp...
function poll_ready (line 85) | fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::E...
function call (line 90) | fn call(&mut self, req: http::Request<B>) -> Self::Future {
type FilteredFuture (line 98) | pub struct FilteredFuture<F> {
FILE: src/filter/then.rs
type Then (line 11) | pub struct Then<T, F> {
type Extract (line 22) | type Extract = (<F::Output as Future>::Output,);
type Error (line 23) | type Error = T::Error;
type Future (line 24) | type Future = ThenFuture<T, F>;
method filter (line 26) | fn filter(&self, _: Internal) -> Self::Future {
type ThenFuture (line 35) | pub struct ThenFuture<T, F>
type State (line 46) | enum State<T, F>
type Output (line 63) | type Output = Result<(<F::Output as Future>::Output,), T::Error>;
method poll (line 65) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type Output (line 76) | type Output = Result<(<F::Output as Future>::Output,), T::Error>;
method poll (line 78) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filter/unify.rs
type Unify (line 11) | pub struct Unify<F> {
type Extract (line 20) | type Extract = T;
type Error (line 21) | type Error = F::Error;
type Future (line 22) | type Future = UnifyFuture<F::Future>;
method filter (line 24) | fn filter(&self, _: Internal) -> Self::Future {
type UnifyFuture (line 33) | pub struct UnifyFuture<F> {
type Output (line 42) | type Output = Result<T, F::Error>;
method poll (line 45) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
FILE: src/filter/untuple_one.rs
type UntupleOne (line 11) | pub struct UntupleOne<F> {
type Extract (line 20) | type Extract = T;
type Error (line 21) | type Error = F::Error;
type Future (line 22) | type Future = UntupleOneFuture<F>;
method filter (line 24) | fn filter(&self, _: Internal) -> Self::Future {
type UntupleOneFuture (line 33) | pub struct UntupleOneFuture<F: Filter> {
type Output (line 43) | type Output = Result<T, F::Error>;
method poll (line 46) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
FILE: src/filter/wrap.rs
type WrapSealed (line 3) | pub trait WrapSealed<F: Filter> {
method wrap (line 6) | fn wrap(&self, filter: F) -> Self::Wrapped;
type Wrapped (line 14) | type Wrapped = T::Wrapped;
function wrap (line 15) | fn wrap(&self, filter: F) -> Self::Wrapped {
type Wrap (line 20) | pub trait Wrap<F: Filter>: WrapSealed<F> {}
function wrap_fn (line 42) | pub fn wrap_fn<F, T, U>(func: F) -> WrapFn<F>
type WrapFn (line 52) | pub struct WrapFn<F> {
type Wrapped (line 62) | type Wrapped = U;
function wrap (line 64) | fn wrap(&self, filter: T) -> Self::Wrapped {
FILE: src/filters/addr.rs
function remote (line 26) | pub fn remote() -> impl Filter<Extract = (Option<SocketAddr>,), Error = ...
type RemoteAddr (line 38) | pub(crate) struct RemoteAddr(pub(crate) SocketAddr);
FILE: src/filters/any.rs
function any (line 47) | pub fn any() -> impl Filter<Extract = (), Error = Infallible> + Copy {
type Any (line 53) | struct Any;
type Extract (line 56) | type Extract = ();
type Error (line 57) | type Error = Infallible;
type Future (line 58) | type Future = AnyFut;
method filter (line 61) | fn filter(&self, _: Internal) -> Self::Future {
type AnyFut (line 67) | struct AnyFut;
type Output (line 70) | type Output = Result<(), Infallible>;
method poll (line 73) | fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Outpu...
FILE: src/filters/body.rs
type BoxError (line 22) | type BoxError = Box<dyn StdError + Send + Sync>;
function body (line 27) | pub(crate) fn body() -> impl Filter<Extract = (Body,), Error = Rejection...
function content_length_limit (line 50) | pub fn content_length_limit(limit: u64) -> impl Filter<Extract = (), Err...
function stream (line 78) | pub fn stream(
function bytes (line 107) | pub fn bytes() -> impl Filter<Extract = (Bytes,), Error = Rejection> + C...
function aggregate (line 148) | pub fn aggregate() -> impl Filter<Extract = (impl Buf,), Error = Rejecti...
function json (line 180) | pub fn json<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,),...
function form (line 215) | pub fn form<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,),...
type Decode (line 228) | trait Decode {
constant MIME (line 229) | const MIME: (mime::Name<'static>, mime::Name<'static>);
constant WITH_NO_CONTENT_TYPE (line 230) | const WITH_NO_CONTENT_TYPE: bool;
method decode (line 232) | fn decode<B: Buf, T: DeserializeOwned>(buf: B) -> Result<T, BoxError>;
constant MIME (line 238) | const MIME: (mime::Name<'static>, mime::Name<'static>) = (mime::APPLIC...
constant WITH_NO_CONTENT_TYPE (line 239) | const WITH_NO_CONTENT_TYPE: bool = true;
method decode (line 241) | fn decode<B: Buf, T: DeserializeOwned>(mut buf: B) -> Result<T, BoxErr...
constant MIME (line 249) | const MIME: (mime::Name<'static>, mime::Name<'static>) =
constant WITH_NO_CONTENT_TYPE (line 251) | const WITH_NO_CONTENT_TYPE: bool = true;
method decode (line 253) | fn decode<B: Buf, T: DeserializeOwned>(buf: B) -> Result<T, BoxError> {
type Json (line 235) | struct Json;
type Form (line 246) | struct Form;
function is_content_type (line 260) | fn is_content_type<D: Decode>() -> impl Filter<Extract = (), Error = Rej...
type BodyDeserializeError (line 300) | pub struct BodyDeserializeError {
method fmt (line 305) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method source (line 311) | fn source(&self) -> Option<&(dyn StdError + 'static)> {
type BodyReadError (line 317) | pub(crate) struct BodyReadError(crate::Error);
method fmt (line 320) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
FILE: src/filters/compression.rs
type CompressionAlgo (line 21) | enum CompressionAlgo {
method from (line 32) | fn from(algo: CompressionAlgo) -> Self {
type Compression (line 46) | pub struct Compression<F> {
function gzip (line 67) | pub fn gzip() -> Compression<impl Fn(CompressionProps) -> Response + Cop...
function deflate (line 96) | pub fn deflate() -> Compression<impl Fn(CompressionProps) -> Response + ...
function brotli (line 125) | pub fn brotli() -> Compression<impl Fn(CompressionProps) -> Response + C...
type Wrapped (line 147) | type Wrapped = WithCompression<FN, F>;
function wrap (line 149) | fn wrap(&self, filter: F) -> Self::Wrapped {
type CompressableBody (line 178) | pub struct CompressableBody<S, E>
type Item (line 192) | type Item = std::io::Result<Bytes>;
method poll_next (line 194) | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<...
function from (line 203) | fn from(body: Body) -> Self {
type CompressionProps (line 212) | pub struct CompressionProps {
method from (line 218) | fn from(resp: http::Response<Body>) -> Self {
type Compressed (line 228) | pub struct Compressed(pub(super) Response);
method into_response (line 232) | fn into_response(self) -> Response {
type WithCompression (line 239) | pub struct WithCompression<FN, F> {
type Extract (line 251) | type Extract = (Compressed,);
type Error (line 252) | type Error = F::Error;
type Future (line 253) | type Future = WithCompressionFuture<FN, F::Future>;
method filter (line 255) | fn filter(&self, _: Internal) -> Self::Future {
type WithCompressionFuture (line 265) | pub struct WithCompressionFuture<FN, F> {
type Output (line 278) | type Output = Result<(Compressed,), F::Error>;
method poll (line 280) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filters/cookie.rs
function cookie (line 15) | pub fn cookie<T>(name: &'static str) -> impl Filter<Extract = One<T>, Er...
function optional (line 32) | pub fn optional<T>(
FILE: src/filters/cors.rs
function cors (line 45) | pub fn cors() -> Builder {
type Cors (line 58) | pub struct Cors {
type Wrapped (line 300) | type Wrapped = CorsFilter<F>;
method wrap (line 302) | fn wrap(&self, inner: F) -> Self::Wrapped {
type Builder (line 64) | pub struct Builder {
method allow_credentials (line 75) | pub fn allow_credentials(mut self, allow: bool) -> Self {
method allow_method (line 85) | pub fn allow_method<M>(mut self, method: M) -> Self
method allow_methods (line 102) | pub fn allow_methods<I>(mut self, methods: I) -> Self
method allow_header (line 122) | pub fn allow_header<H>(mut self, header: H) -> Self
method allow_headers (line 141) | pub fn allow_headers<I>(mut self, headers: I) -> Self
method expose_header (line 159) | pub fn expose_header<H>(mut self, header: H) -> Self
method expose_headers (line 176) | pub fn expose_headers<I>(mut self, headers: I) -> Self
method allow_any_origin (line 195) | pub fn allow_any_origin(mut self) -> Self {
method allow_origin (line 205) | pub fn allow_origin(self, origin: impl IntoOrigin) -> Self {
method allow_origins (line 214) | pub fn allow_origins<I>(mut self, origins: I) -> Self
method max_age (line 247) | pub fn max_age(mut self, seconds: impl Seconds) -> Self {
method build (line 257) | pub fn build(self) -> Cors {
type Wrapped (line 284) | type Wrapped = CorsFilter<F>;
method wrap (line 286) | fn wrap(&self, inner: F) -> Self::Wrapped {
type CorsForbidden (line 310) | pub struct CorsForbidden {
method fmt (line 322) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method fmt (line 328) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Forbidden (line 315) | enum Forbidden {
type Configured (line 341) | struct Configured {
method check_request (line 355) | fn check_request(
method is_method_allowed (line 409) | fn is_method_allowed(&self, header: &HeaderValue) -> bool {
method is_header_allowed (line 415) | fn is_header_allowed(&self, header: &str) -> bool {
method is_origin_allowed (line 421) | fn is_origin_allowed(&self, origin: &HeaderValue) -> bool {
method append_preflight_headers (line 429) | fn append_preflight_headers(&self, headers: &mut http::HeaderMap) {
method append_common_headers (line 440) | fn append_common_headers(&self, headers: &mut http::HeaderMap) {
type Validated (line 348) | enum Validated {
type CorsFilter (line 471) | pub struct CorsFilter<F> {
type Extract (line 483) | type Extract =
type Error (line 485) | type Error = <F::Error as CombineRejection<Rejection>>::One;
type Future (line 486) | type Future = future::Either<
method filter (line 491) | fn filter(&self, _: Internal) -> Self::Future {
type Preflight (line 520) | pub struct Preflight {
method into_response (line 526) | fn into_response(self) -> crate::reply::Response {
type Wrapped (line 536) | pub struct Wrapped<R> {
function into_response (line 546) | fn into_response(self) -> crate::reply::Response {
type WrappedFuture (line 557) | pub struct WrappedFuture<F> {
type Output (line 568) | type Output = Result<
method poll (line 573) | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
type Seconds (line 594) | pub trait Seconds {
method seconds (line 595) | fn seconds(self) -> u64;
method seconds (line 599) | fn seconds(self) -> u64 {
method seconds (line 605) | fn seconds(self) -> u64 {
type IntoOrigin (line 610) | pub trait IntoOrigin {
method into_origin (line 611) | fn into_origin(self) -> Origin;
method into_origin (line 615) | fn into_origin(self) -> Origin {
FILE: src/filters/ext.rs
function get (line 13) | pub fn get<T: Clone + Send + Sync + 'static>(
function optional (line 28) | pub fn optional<T: Clone + Send + Sync + 'static>(
FILE: src/filters/fs.rs
function file (line 48) | pub fn file(path: impl Into<PathBuf>) -> impl FilterClone<Extract = One<...
function dir (line 81) | pub fn dir(path: impl Into<PathBuf>) -> impl FilterClone<Extract = One<F...
function path_from_tail (line 91) | fn path_from_tail(
function sanitize_path (line 111) | fn sanitize_path(base: impl AsRef<Path>, tail: &str) -> Result<PathBuf, ...
type Conditionals (line 139) | struct Conditionals {
method check (line 152) | fn check(self, last_modified: Option<LastModified>) -> Cond {
type Cond (line 146) | enum Cond {
function conditionals (line 201) | fn conditionals() -> impl Filter<Extract = One<Conditionals>, Error = In...
type File (line 218) | pub struct File {
method path (line 243) | pub fn path(&self) -> &Path {
type ArcPath (line 250) | struct ArcPath(Arc<PathBuf>);
method as_ref (line 253) | fn as_ref(&self) -> &Path {
method into_response (line 259) | fn into_response(self) -> Response {
function file_reply (line 264) | fn file_reply(
function file_metadata (line 294) | async fn file_metadata(f: TkFile) -> Result<(TkFile, Metadata), Rejectio...
function file_conditional (line 304) | fn file_conditional(
type BadRange (line 361) | struct BadRange;
function bytes_range (line 363) | fn bytes_range(range: Option<Range>, max_len: u64) -> Result<(u64, u64),...
function file_stream (line 406) | fn file_stream(
function reserve_at_least (line 462) | fn reserve_at_least(buf: &mut BytesMut, cap: usize) {
constant DEFAULT_READ_BUF_SIZE (line 468) | const DEFAULT_READ_BUF_SIZE: usize = 8_192;
function optimal_buf_size (line 470) | fn optimal_buf_size(metadata: &Metadata) -> usize {
function get_block_size (line 479) | fn get_block_size(metadata: &Metadata) -> usize {
function get_block_size (line 489) | fn get_block_size(_metadata: &Metadata) -> usize {
function test_sanitize_path (line 509) | fn test_sanitize_path() {
function test_reserve_at_least (line 528) | fn test_reserve_at_least() {
FILE: src/filters/header.rs
function header (line 37) | pub fn header<T: FromStr + Send + 'static>(
function header2 (line 52) | pub(crate) fn header2<T: Header + Send + 'static>(
function optional (line 76) | pub fn optional<T>(
function optional2 (line 100) | pub(crate) fn optional2<T>() -> impl Filter<Extract = One<Option<T>>, Er...
function exact (line 137) | pub fn exact(
function exact_ignore_case (line 169) | pub fn exact_ignore_case(
function value (line 202) | pub fn value(
function headers_cloned (line 228) | pub fn headers_cloned() -> impl Filter<Extract = One<HeaderMap>, Error =...
FILE: src/filters/host.rs
function exact (line 23) | pub fn exact(expected: &str) -> impl Filter<Extract = (), Error = Reject...
function optional (line 58) | pub fn optional() -> impl Filter<Extract = One<Option<Authority>>, Error...
FILE: src/filters/log.rs
function log (line 33) | pub fn log(name: &'static str) -> Log<impl Fn(Info<'_>) + Copy> {
function custom (line 73) | pub fn custom<F>(func: F) -> Log<F>
type Log (line 82) | pub struct Log<F> {
type Info (line 88) | pub struct Info<'a> {
type Wrapped (line 101) | type Wrapped = WithLog<FN, F>;
function wrap (line 103) | fn wrap(&self, filter: F) -> Self::Wrapped {
function remote_addr (line 113) | pub fn remote_addr(&self) -> Option<SocketAddr> {
function method (line 118) | pub fn method(&self) -> &http::Method {
function path (line 123) | pub fn path(&self) -> &str {
function version (line 128) | pub fn version(&self) -> http::Version {
function status (line 133) | pub fn status(&self) -> http::StatusCode {
function referer (line 138) | pub fn referer(&self) -> Option<&str> {
function user_agent (line 146) | pub fn user_agent(&self) -> Option<&str> {
function elapsed (line 154) | pub fn elapsed(&self) -> Duration {
function host (line 159) | pub fn host(&self) -> Option<&str> {
function request_headers (line 167) | pub fn request_headers(&self) -> &http::HeaderMap {
type OptFmt (line 172) | struct OptFmt<T>(Option<T>);
function fmt (line 175) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Logged (line 200) | pub struct Logged(pub(super) Response);
method into_response (line 204) | fn into_response(self) -> Response {
type WithLog (line 211) | pub struct WithLog<FN, F> {
type Extract (line 223) | type Extract = (Logged,);
type Error (line 224) | type Error = F::Error;
type Future (line 225) | type Future = WithLogFuture<FN, F::Future>;
method filter (line 227) | fn filter(&self, _: Internal) -> Self::Future {
type WithLogFuture (line 239) | pub struct WithLogFuture<FN, F> {
type Output (line 253) | type Output = Result<(Logged,), F::Error>;
method poll (line 255) | fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Ou...
FILE: src/filters/method.rs
function get (line 25) | pub fn get() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function post (line 38) | pub fn post() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function put (line 51) | pub fn put() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function delete (line 64) | pub fn delete() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function head (line 77) | pub fn head() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function options (line 90) | pub fn options() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function patch (line 103) | pub fn patch() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function method (line 121) | pub fn method() -> impl Filter<Extract = One<Method>, Error = Infallible...
function method_is (line 128) | fn method_is<F>(func: F) -> impl Filter<Extract = (), Error = Rejection>...
function method_size_of (line 146) | fn method_size_of() {
FILE: src/filters/multipart.rs
constant DEFAULT_FORM_DATA_MAX_LENGTH (line 24) | const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;
type FormOptions (line 30) | pub struct FormOptions {
method max_length (line 65) | pub fn max_length(mut self, max: impl Into<Option<u64>>) -> Self {
type FormData (line 37) | pub struct FormData {
method fmt (line 112) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Part (line 44) | pub struct Part {
method name (line 140) | pub fn name(&self) -> &str {
method filename (line 147) | pub fn filename(&self) -> Option<&str> {
method content_type (line 152) | pub fn content_type(&self) -> Option<&str> {
method data (line 158) | pub async fn data(&mut self) -> Option<Result<impl Buf, crate::Error>> {
method stream (line 163) | pub fn stream(self) -> impl Stream<Item = Result<impl Buf, crate::Erro...
method poll_next (line 167) | fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<By...
method fmt (line 178) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function form (line 52) | pub fn form() -> FormOptions {
type FormFut (line 71) | type FormFut = Pin<Box<dyn Future<Output = Result<(FormData,), Rejection...
type Extract (line 74) | type Extract = (FormData,);
type Error (line 75) | type Error = Rejection;
type Future (line 76) | type Future = FormFut;
method filter (line 78) | fn filter(&self, _: Internal) -> Self::Future {
type Item (line 118) | type Item = Result<Part, crate::Error>;
method poll_next (line 120) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
type PartStream (line 194) | struct PartStream(Part);
type Item (line 197) | type Item = Result<Bytes, crate::Error>;
method poll_next (line 199) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
type BodyIoError (line 204) | struct BodyIoError(BodyDataStream<Body>);
type Item (line 207) | type Item = io::Result<Bytes>;
method poll_next (line 209) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
type MultipartFieldMissingName (line 223) | struct MultipartFieldMissingName;
method fmt (line 226) | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
FILE: src/filters/path.rs
function path (line 167) | pub fn path<P>(p: P) -> Exact<Opaque<P>>
type Exact (line 197) | pub struct Exact<P>(P);
type Extract (line 203) | type Extract = ();
type Error (line 204) | type Error = Rejection;
type Future (line 205) | type Future = future::Ready<Result<Self::Extract, Self::Error>>;
method filter (line 208) | fn filter(&self, _: Internal) -> Self::Future {
function end (line 238) | pub fn end() -> impl Filter<Extract = (), Error = Rejection> + Copy {
function param (line 266) | pub fn param<T: FromStr + Send + 'static>(
function tail (line 294) | pub fn tail() -> impl Filter<Extract = One<Tail>, Error = Infallible> + ...
type Tail (line 312) | pub struct Tail {
method as_str (line 319) | pub fn as_str(&self) -> &str {
method fmt (line 325) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function peek (line 348) | pub fn peek() -> impl Filter<Extract = One<Peek>, Error = Infallible> + ...
type Peek (line 361) | pub struct Peek {
method as_str (line 368) | pub fn as_str(&self) -> &str {
method segments (line 373) | pub fn segments(&self) -> impl Iterator<Item = &str> {
method fmt (line 379) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function full (line 415) | pub fn full() -> impl Filter<Extract = One<FullPath>, Error = Infallible...
type FullPath (line 420) | pub struct FullPath(PathAndQuery);
method as_str (line 424) | pub fn as_str(&self) -> &str {
method fmt (line 430) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function filter_segment (line 435) | fn filter_segment<F, U>(func: F) -> impl Filter<Extract = U, Error = Rej...
function with_segment (line 443) | fn with_segment<F, U>(route: &mut Route, func: F) -> Result<U, Rejection>
function segment (line 456) | fn segment(route: &Route) -> &str {
function path_and_query (line 464) | fn path_and_query(route: &Route) -> PathAndQuery {
function _path_macro_compile_fail (line 607) | fn _path_macro_compile_fail() {}
type Opaque (line 616) | pub struct Opaque<T>(pub(super) T);
function as_ref (line 620) | fn as_ref(&self) -> &str {
function test_path_exact_size (line 631) | fn test_path_exact_size() {
FILE: src/filters/query.rs
function query (line 64) | pub fn query<T: DeserializeOwned + Send + 'static>(
function raw (line 81) | pub fn raw() -> impl Filter<Extract = One<String>, Error = Rejection> + ...
FILE: src/filters/reply.rs
function header (line 49) | pub fn header<K, V>(name: K, value: V) -> WithHeader
function headers (line 83) | pub fn headers(headers: HeaderMap) -> WithHeaders {
function default_header (line 110) | pub fn default_header<K, V>(name: K, value: V) -> WithDefaultHeader
type WithHeader (line 123) | pub struct WithHeader {
type Wrapped (line 133) | type Wrapped = Map<F, WithHeader_>;
method wrap (line 135) | fn wrap(&self, filter: F) -> Self::Wrapped {
type WithHeaders (line 143) | pub struct WithHeaders {
type Wrapped (line 152) | type Wrapped = Map<F, WithHeaders_>;
method wrap (line 154) | fn wrap(&self, filter: F) -> Self::Wrapped {
type WithDefaultHeader (line 162) | pub struct WithDefaultHeader {
type Wrapped (line 172) | type Wrapped = Map<F, WithDefaultHeader_>;
method wrap (line 174) | fn wrap(&self, filter: F) -> Self::Wrapped {
function assert_name_and_value (line 180) | fn assert_name_and_value<K, V>(name: K, value: V) -> (HeaderName, Header...
type WithHeader_ (line 205) | pub struct WithHeader_ {
type Output (line 210) | type Output = Reply_;
method call (line 212) | fn call(&self, args: One<R>) -> Self::Output {
type WithHeaders_ (line 223) | pub struct WithHeaders_ {
type Output (line 228) | type Output = Reply_;
method call (line 230) | fn call(&self, args: One<R>) -> Self::Output {
type WithDefaultHeader_ (line 241) | pub struct WithDefaultHeader_ {
type Output (line 246) | type Output = Reply_;
method call (line 248) | fn call(&self, args: One<R>) -> Self::Output {
FILE: src/filters/sse.rs
type DataType (line 69) | enum DataType {
type Event (line 76) | pub struct Event {
method data (line 87) | pub fn data<T: Into<String>>(mut self, data: T) -> Event {
method json_data (line 94) | pub fn json_data<T: Serialize>(mut self, data: T) -> Result<Event, Err...
method comment (line 101) | pub fn comment<T: Into<String>>(mut self, comment: T) -> Event {
method event (line 108) | pub fn event<T: Into<String>>(mut self, event: T) -> Event {
method retry (line 115) | pub fn retry(mut self, duration: Duration) -> Event {
method id (line 122) | pub fn id<T: Into<String>>(mut self, id: T) -> Event {
method fmt (line 129) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
function last_event_id (line 230) | pub fn last_event_id<T>() -> impl Filter<Extract = One<Option<T>>, Error...
function reply (line 314) | pub fn reply<S>(event_stream: S) -> impl Reply
type SseReply (line 323) | struct SseReply<S> {
method into_response (line 333) | fn into_response(self) -> Response {
type KeepAlive (line 358) | pub struct KeepAlive {
method interval (line 367) | pub fn interval(mut self, time: Duration) -> Self {
method text (line 375) | pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
method stream (line 383) | pub fn stream<S>(
type SseKeepAlive (line 403) | struct SseKeepAlive<S> {
function keep_alive (line 457) | pub fn keep_alive() -> KeepAlive {
type Item (line 469) | type Item = Result<Event, SseError>;
method poll_next (line 471) | fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<...
type SseError (line 505) | pub struct SseError;
method fmt (line 508) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
FILE: src/filters/trace.rs
function request (line 40) | pub fn request() -> Trace<impl Fn(Info<'_>) -> Span + Clone> {
function trace (line 85) | pub fn trace<F>(func: F) -> Trace<F>
function named (line 116) | pub fn named(name: &'static str) -> Trace<impl Fn(Info<'_>) -> Span + Co...
type Trace (line 126) | pub struct Trace<F> {
type Info (line 132) | pub struct Info<'a> {
type Wrapped (line 143) | type Wrapped = WithTrace<FN, F>;
function wrap (line 145) | fn wrap(&self, filter: F) -> Self::Wrapped {
function method (line 155) | pub fn method(&self) -> &http::Method {
function path (line 160) | pub fn path(&self) -> &str {
function version (line 165) | pub fn version(&self) -> http::Version {
function referer (line 170) | pub fn referer(&self) -> Option<&str> {
function user_agent (line 178) | pub fn user_agent(&self) -> Option<&str> {
function host (line 186) | pub fn host(&self) -> Option<&str> {
function request_headers (line 194) | pub fn request_headers(&self) -> &http::HeaderMap {
type Traced (line 210) | pub struct Traced(pub(super) Response);
method into_response (line 214) | fn into_response(self) -> Response {
type WithTrace (line 221) | pub struct WithTrace<FN, F> {
function finished_logger (line 229) | fn finished_logger<E: IsReject>(reply: &Result<(Traced,), E>) {
function convert_reply (line 266) | fn convert_reply<R: Reply>(reply: R) -> (Traced,) {
type Extract (line 277) | type Extract = (Traced,);
type Error (line 278) | type Error = F::Error;
type Future (line 279) | type Future = Instrumented<
method filter (line 286) | fn filter(&self, _: Internal) -> Self::Future {
FILE: src/filters/ws.rs
function ws (line 44) | pub fn ws() -> impl Filter<Extract = One<Ws>, Error = Rejection> + Copy {
type Ws (line 73) | pub struct Ws {
method on_upgrade (line 83) | pub fn on_upgrade<F, U>(self, func: F) -> impl Reply
method max_send_queue (line 102) | pub fn max_send_queue(self, _max: usize) -> Self {
method max_write_buffer_size (line 107) | pub fn max_write_buffer_size(mut self, max: usize) -> Self {
method max_message_size (line 115) | pub fn max_message_size(mut self, max: usize) -> Self {
method max_frame_size (line 123) | pub fn max_frame_size(mut self, max: usize) -> Self {
method fmt (line 132) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type WsReply (line 138) | struct WsReply<F> {
method into_response (line 148) | fn into_response(self) -> Response {
function on_upgrade (line 182) | fn on_upgrade() -> impl Filter<Extract = (Option<OnUpgrade>,), Error = R...
type WebSocket (line 194) | pub struct WebSocket {
method from_raw_socket (line 199) | pub(crate) async fn from_raw_socket(
method close (line 211) | pub async fn close(mut self) -> Result<(), crate::Error> {
type Error (line 235) | type Error = crate::Error;
method poll_ready (line 237) | fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<...
method start_send (line 244) | fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), S...
method poll_flush (line 254) | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<...
method poll_close (line 261) | fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<...
method fmt (line 273) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Item (line 217) | type Item = Result<Message, crate::Error>;
method poll_next (line 219) | fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Opt...
type Message (line 283) | pub struct Message {
method text (line 289) | pub fn text<B: Into<String>>(bytes: B) -> Message {
method binary (line 296) | pub fn binary<B: Into<Bytes>>(bytes: B) -> Message {
method ping (line 303) | pub fn ping<B: Into<Bytes>>(bytes: B) -> Message {
method pong (line 314) | pub fn pong<B: Into<Bytes>>(bytes: B) -> Message {
method close (line 321) | pub fn close() -> Message {
method close_with (line 328) | pub fn close_with(code: impl Into<u16>, reason: impl Into<Cow<'static,...
method is_text (line 341) | pub fn is_text(&self) -> bool {
method is_binary (line 346) | pub fn is_binary(&self) -> bool {
method is_close (line 351) | pub fn is_close(&self) -> bool {
method is_ping (line 356) | pub fn is_ping(&self) -> bool {
method is_pong (line 361) | pub fn is_pong(&self) -> bool {
method close_frame (line 366) | pub fn close_frame(&self) -> Option<(u16, &str)> {
method to_str (line 375) | pub fn to_str(&self) -> Result<&str, ()> {
method as_bytes (line 383) | pub fn as_bytes(&self) -> &[u8] {
method into_bytes (line 395) | pub fn into_bytes(self) -> Bytes {
method fmt (line 401) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method from (line 407) | fn from(m: Message) -> Self {
type MissingConnectionUpgrade (line 416) | pub struct MissingConnectionUpgrade;
method fmt (line 419) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
FILE: src/generic.rs
type Product (line 2) | pub struct Product<H, T: HList>(pub(crate) H, pub(crate) T);
type One (line 4) | pub type One<T> = (T,);
function one (line 7) | pub(crate) fn one<T>(val: T) -> One<T> {
type Either (line 12) | pub enum Either<T, U> {
type HList (line 18) | pub trait HList: Sized {
method flatten (line 21) | fn flatten(self) -> Self::Tuple;
type Tuple (line 81) | type Tuple = ();
method flatten (line 83) | fn flatten(self) -> Self::Tuple {}
type Tuple (line 25) | pub trait Tuple: Sized {
method hlist (line 28) | fn hlist(self) -> Self::HList;
method combine (line 31) | fn combine<T>(self, other: T) -> CombinedTuples<Self, T>
type HList (line 87) | type HList = ();
method hlist (line 90) | fn hlist(self) -> Self::HList {}
type CombinedTuples (line 41) | pub type CombinedTuples<T, U> =
type Combine (line 45) | pub trait Combine<T: HList> {
method combine (line 48) | fn combine(self, other: T) -> Self::Output;
type Func (line 51) | pub trait Func<Args> {
method call (line 54) | fn call(&self, args: Args) -> Self::Output;
type Output (line 60) | type Output = T;
function combine (line 62) | fn combine(self, other: T) -> Self::Output {
type Output (line 72) | type Output = Product<H, <T as Combine<U>>::Output>;
function combine (line 75) | fn combine(self, other: U) -> Self::Output {
type Output (line 97) | type Output = R;
method call (line 100) | fn call(&self, _args: ()) -> Self::Output {
type Output (line 109) | type Output = R;
method call (line 112) | fn call(&self, arg: crate::Rejection) -> Self::Output {
FILE: src/lib.rs
type Request (line 184) | pub(crate) type Request = http::Request<crate::bodyt::Body>;
FILE: src/redirect.rs
function redirect (line 26) | pub fn redirect(uri: impl AsLocation) -> impl Reply {
function found (line 49) | pub fn found(uri: impl AsLocation) -> impl Reply {
function see_other (line 70) | pub fn see_other(uri: impl AsLocation) -> impl Reply {
function temporary (line 92) | pub fn temporary(uri: impl AsLocation) -> impl Reply {
function permanent (line 118) | pub fn permanent(uri: impl AsLocation) -> impl Reply {
type AsLocation (line 134) | pub trait AsLocation: Sealed {}
type Sealed (line 135) | pub trait Sealed {
method header_value (line 136) | fn header_value(self) -> HeaderValue;
method header_value (line 142) | fn header_value(self) -> HeaderValue {
FILE: src/reject.rs
function reject (line 74) | pub fn reject() -> Rejection {
function not_found (line 80) | pub fn not_found() -> Rejection {
function invalid_query (line 88) | pub(crate) fn invalid_query() -> Rejection {
function missing_header (line 94) | pub(crate) fn missing_header(name: &'static str) -> Rejection {
function invalid_header (line 100) | pub(crate) fn invalid_header(name: &'static str) -> Rejection {
function missing_cookie (line 106) | pub(crate) fn missing_cookie(name: &'static str) -> Rejection {
function method_not_allowed (line 112) | pub(crate) fn method_not_allowed() -> Rejection {
function length_required (line 118) | pub(crate) fn length_required() -> Rejection {
function payload_too_large (line 124) | pub(crate) fn payload_too_large() -> Rejection {
function unsupported_media_type (line 133) | pub(crate) fn unsupported_media_type() -> Rejection {
function custom (line 143) | pub fn custom<T: Reject>(err: T) -> Rejection {
function __reject_custom_compilefail (line 154) | fn __reject_custom_compilefail() {}
type Reject (line 176) | pub trait Reject: fmt::Debug + Sized + Send + Sync + 'static {}
type Cause (line 178) | trait Cause: fmt::Debug + Send + Sync + 'static {
method as_any (line 179) | fn as_any(&self) -> &dyn Any;
method as_any (line 186) | fn as_any(&self) -> &dyn Any {
function downcast_ref (line 192) | fn downcast_ref<T: Any>(&self) -> Option<&T> {
function known (line 197) | pub(crate) fn known<T: Into<Known>>(err: T) -> Rejection {
type Rejection (line 204) | pub struct Rejection {
method known (line 294) | fn known(known: Known) -> Self {
method custom (line 300) | fn custom(other: Box<dyn Cause>) -> Self {
method find (line 325) | pub fn find<T: 'static>(&self) -> Option<&T> {
method is_not_found (line 341) | pub fn is_not_found(&self) -> bool {
method from (line 348) | fn from(err: T) -> Rejection {
method from (line 355) | fn from(infallible: Infallible) -> Rejection {
method fmt (line 391) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type One (line 658) | type One = Rejection;
type Combined (line 659) | type Combined = Rejection;
method combine (line 661) | fn combine(self, other: Rejection) -> Self::Combined {
type One (line 679) | type One = Rejection;
type Combined (line 680) | type Combined = Infallible;
method combine (line 682) | fn combine(self, other: Infallible) -> Self::Combined {
type Reason (line 208) | enum Reason {
method fmt (line 397) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Rejections (line 213) | enum Rejections {
method status (line 417) | fn status(&self) -> StatusCode {
method into_response (line 442) | fn into_response(&self) -> crate::reply::Response {
method find (line 471) | fn find<T: 'static>(&self) -> Option<&T> {
method debug_list (line 479) | fn debug_list(&self, f: &mut fmt::DebugList<'_, '_>) {
method preferred (line 494) | fn preferred(&self) -> &Rejections {
type MissingHeader (line 547) | pub struct MissingHeader {
method name (line 553) | pub fn name(&self) -> &str {
method fmt (line 559) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type InvalidHeader (line 568) | pub struct InvalidHeader {
method name (line 574) | pub fn name(&self) -> &str {
method fmt (line 580) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type MissingCookie (line 589) | pub struct MissingCookie {
method name (line 595) | pub fn name(&self) -> &str {
method fmt (line 601) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type IsReject (line 616) | pub trait IsReject: fmt::Debug + Send + Sync {
method status (line 361) | fn status(&self) -> StatusCode {
method into_response (line 365) | fn into_response(&self) -> crate::reply::Response {
method status (line 371) | fn status(&self) -> StatusCode {
method into_response (line 378) | fn into_response(&self) -> crate::reply::Response {
method status (line 617) | fn status(&self) -> StatusCode;
method into_response (line 618) | fn into_response(&self) -> crate::reply::Response;
function _assert_object_safe (line 621) | fn _assert_object_safe() {
type CombineRejection (line 634) | pub trait CombineRejection<E>: Send + Sized {
method combine (line 654) | fn combine(self, other: E) -> Self::Combined;
type One (line 688) | type One = Rejection;
type Combined (line 689) | type Combined = Infallible;
method combine (line 691) | fn combine(self, _: Rejection) -> Self::Combined {
type One (line 697) | type One = Infallible;
type Combined (line 698) | type Combined = Infallible;
method combine (line 700) | fn combine(self, _: Infallible) -> Self::Combined {
type Left (line 711) | struct Left;
type Right (line 714) | struct Right;
function rejection_status (line 720) | fn rejection_status() {
function combine_rejection_causes_with_some_left_and_none_right (line 736) | async fn combine_rejection_causes_with_some_left_and_none_right() {
function combine_rejection_causes_with_none_left_and_some_right (line 750) | async fn combine_rejection_causes_with_none_left_and_some_right() {
function unhandled_customs (line 764) | async fn unhandled_customs() {
function response_body_string (line 800) | async fn response_body_string(resp: crate::reply::Response) -> String {
function find_cause (line 808) | fn find_cause() {
function size_of_rejection (line 820) | fn size_of_rejection() {
type X (line 828) | struct X(#[allow(unused)] u32);
function combine_n (line 831) | fn combine_n<F, R>(n: u32, new_reject: F) -> Rejection
function test_debug (line 846) | fn test_debug() {
function convert_big_rejections_into_response (line 854) | fn convert_big_rejections_into_response() {
FILE: src/reply.rs
type Response (line 51) | pub type Response = ::http::Response<crate::bodyt::Body>;
function reply (line 68) | pub fn reply() -> impl Reply {
function json (line 98) | pub fn json<T>(val: &T) -> Json
type Json (line 111) | pub struct Json {
function stream (line 144) | pub fn stream<S, B, E>(stream: S) -> impl Reply
function html (line 176) | pub fn html<T>(body: T) -> Html<T>
type Html (line 186) | pub struct Html<T> {
type Reply (line 236) | pub trait Reply: BoxedReply + Send {
method into_response (line 117) | fn into_response(self) -> Response {
method into_response (line 196) | fn into_response(self) -> Response {
method into_response (line 240) | fn into_response(self) -> Response;
method into_response (line 302) | fn into_response(self) -> Response {
method into_response (line 338) | fn into_response(self) -> Response {
method into_response (line 394) | fn into_response(self) -> Response {
method into_response (line 408) | fn into_response(self) -> Response {
method into_response (line 415) | fn into_response(self) -> Response {
method into_response (line 424) | fn into_response(self) -> Response {
method into_response (line 436) | fn into_response(self) -> Response {
method into_response (line 455) | fn into_response(self) -> Response {
method into_response (line 462) | fn into_response(self) -> Response {
method into_response (line 475) | fn into_response(self) -> Response {
method into_response (line 482) | fn into_response(self) -> Response {
method into_response (line 492) | fn into_response(self) -> Response {
method into_response (line 509) | fn into_response(self) -> Response {
method into_response (line 522) | fn into_response(self) -> Response {
method into_response (line 529) | fn into_response(self) -> Response {
method into_response (line 543) | fn into_response(self) -> Response {
function _assert_object_safe (line 307) | fn _assert_object_safe() {
function with_status (line 324) | pub fn with_status<T: Reply>(reply: T, status: StatusCode) -> WithStatus...
type WithStatus (line 332) | pub struct WithStatus<T> {
function with_header (line 358) | pub fn with_header<T: Reply, K, V>(reply: T, name: K, value: V) -> WithH...
type WithHeader (line 388) | pub struct WithHeader<T> {
function text_plain (line 444) | fn text_plain<T: Into<Body>>(body: T) -> Response {
type Reply_ (line 539) | pub struct Reply_(pub(crate) Response);
type Internal (line 549) | pub struct Internal;
type BoxedReply (line 555) | pub trait BoxedReply {
method boxed_into_response (line 556) | fn boxed_into_response(self: Box<Self>, internal: Internal) -> Response;
method boxed_into_response (line 560) | fn boxed_into_response(self: Box<Self>, _: Internal) -> Response {
function json_serde_error (line 573) | fn json_serde_error() {
function response_builder_error (line 583) | fn response_builder_error() {
function boxed_reply (line 593) | fn boxed_reply() {
FILE: src/route.rs
function set (line 11) | pub(crate) fn set<F, U>(r: &RefCell<Route>, func: F) -> U
function is_set (line 18) | pub(crate) fn is_set() -> bool {
function with (line 22) | pub(crate) fn with<F, R>(func: F) -> R
type Route (line 30) | pub(crate) struct Route {
method new (line 43) | pub(crate) fn new(req: http::Request<Body>) -> RefCell<Route> {
method method (line 58) | pub(crate) fn method(&self) -> &http::Method {
method headers (line 62) | pub(crate) fn headers(&self) -> &http::HeaderMap {
method version (line 66) | pub(crate) fn version(&self) -> http::Version {
method extensions (line 70) | pub(crate) fn extensions(&self) -> &http::Extensions {
method extensions_mut (line 75) | pub(crate) fn extensions_mut(&mut self) -> &mut http::Extensions {
method uri (line 79) | pub(crate) fn uri(&self) -> &http::Uri {
method path (line 83) | pub(crate) fn path(&self) -> &str {
method full_path (line 87) | pub(crate) fn full_path(&self) -> &str {
method set_unmatched_path (line 91) | pub(crate) fn set_unmatched_path(&mut self, index: usize) {
method query (line 105) | pub(crate) fn query(&self) -> Option<&str> {
method matched_path_index (line 109) | pub(crate) fn matched_path_index(&self) -> usize {
method reset_matched_path_index (line 113) | pub(crate) fn reset_matched_path_index(&mut self, index: usize) {
method remote_addr (line 123) | pub(crate) fn remote_addr(&self) -> Option<SocketAddr> {
method take_body (line 130) | pub(crate) fn take_body(&mut self) -> Option<Body> {
type BodyState (line 37) | enum BodyState {
FILE: src/server.rs
function serve (line 15) | pub fn serve<F>(filter: F) -> Server<F, accept::LazyTcp, run::Standard>
type Server (line 40) | pub struct Server<F, A, R> {
function run (line 63) | pub async fn run(self, addr: impl Into<SocketAddr>) {
function bind (line 74) | pub async fn bind(self, addr: impl Into<SocketAddr>) -> Server<F, tokio:...
function incoming (line 84) | pub fn incoming<A>(self, acceptor: A) -> Server<F, A, R> {
function tls (line 105) | pub fn tls(self) -> Server<F, accept::Tls<A>, R> {}
function graceful (line 123) | pub fn graceful<Fut>(self, shutdown_signal: Fut) -> Server<F, A, run::Gr...
function run (line 136) | pub async fn run(self) {
function key_path (line 157) | pub fn key_path(self, path: impl AsRef<Path>) -> Self {
function cert_path (line 164) | pub fn cert_path(self, path: impl AsRef<Path>) -> Self {
function client_auth_optional_path (line 174) | pub fn client_auth_optional_path(self, path: impl AsRef<Path>) -> Self {
function client_auth_required_path (line 184) | pub fn client_auth_required_path(self, path: impl AsRef<Path>) -> Self {
function key (line 191) | pub fn key(self, key: impl AsRef<[u8]>) -> Self {
function cert (line 198) | pub fn cert(self, cert: impl AsRef<[u8]>) -> Self {
function client_auth_optional (line 208) | pub fn client_auth_optional(self, trust_anchor: impl AsRef<[u8]>) -> Self {
function client_auth_required (line 218) | pub fn client_auth_required(self, trust_anchor: impl AsRef<[u8]>) -> Self {
function ocsp_resp (line 225) | pub fn ocsp_resp(self, resp: impl AsRef<[u8]>) -> Self {
function with_tls (line 229) | fn with_tls<Func>(self, func: Func) -> Self
type Accept (line 240) | pub trait Accept {
method accept (line 247) | async fn accept(&mut self) -> Result<Self::Accepting, std::io::Error>;
type IO (line 254) | type IO = hyper_util::rt::TokioIo<tokio::net::TcpStream>;
type AcceptError (line 255) | type AcceptError = std::convert::Infallible;
type Accepting (line 256) | type Accepting =
method accept (line 258) | async fn accept(&mut self) -> Result<Self::Accepting, std::io::Error> {
type IO (line 269) | type IO = hyper_util::rt::TokioIo<tokio::net::UnixStream>;
type AcceptError (line 270) | type AcceptError = std::convert::Infallible;
type Accepting (line 271) | type Accepting =
method accept (line 273) | async fn accept(&mut self) -> Result<Self::Accepting, std::io::Error> {
type IO (line 288) | type IO = hyper_util::rt::TokioIo<tokio::net::TcpStream>;
type AcceptError (line 289) | type AcceptError = std::convert::Infallible;
type Accepting (line 290) | type Accepting =
method accept (line 292) | async fn accept(&mut self) -> Result<Self::Accepting, std::io::Error> {
type LazyTcp (line 251) | pub struct LazyTcp;
type Tls (line 284) | pub struct Tls<A>(pub(super) A);
type RemoteAddrService (line 310) | pub(super) struct RemoteAddrService<S> {
function new (line 316) | pub(super) fn new(inner: S, remote_addr: Option<SocketAddr>) -> Self {
type Response (line 325) | type Response = S::Response;
type Error (line 326) | type Error = S::Error;
type Future (line 327) | type Future = S::Future;
function poll_ready (line 329) | fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::...
function call (line 333) | fn call(&mut self, mut req: http::Request<B>) -> Self::Future {
type Run (line 343) | pub trait Run {
method run (line 345) | async fn run<F, A>(server: super::Server<F, A, Self>)
method run (line 358) | async fn run<F, A>(mut server: super::Server<F, A, Self>)
method run (line 408) | async fn run<F, A>(mut server: super::Server<F, A, Self>)
type Standard (line 355) | pub struct Standard;
type Graceful (line 402) | pub struct Graceful<Fut>(pub(super) Fut);
function handle_accept_error (line 464) | async fn handle_accept_error(e: std::io::Error) {
function is_connection_error (line 481) | fn is_connection_error(e: &std::io::Error) -> bool {
FILE: src/test.rs
function request (line 125) | pub fn request() -> RequestBuilder {
function ws (line 133) | pub fn ws() -> WsBuilder {
type RequestBuilder (line 142) | pub struct RequestBuilder {
method method (line 185) | pub fn method(mut self, method: &str) -> Self {
method path (line 205) | pub fn path(mut self, p: &str) -> Self {
method header (line 224) | pub fn header<K, V>(mut self, key: K, value: V) -> Self
method remote_addr (line 250) | pub fn remote_addr(mut self, addr: SocketAddr) -> Self {
method extension (line 256) | pub fn extension<T>(mut self, ext: T) -> Self
method body (line 274) | pub fn body(mut self, body: impl AsRef<[u8]>) -> Self {
method json (line 289) | pub fn json(mut self, val: &impl Serialize) -> Self {
method filter (line 322) | pub async fn filter<F>(self, f: &F) -> Result<<F::Extract as OneOrTupl...
method matches (line 356) | pub async fn matches<F>(self, f: &F) -> bool
method reply (line 369) | pub async fn reply<F>(self, f: &F) -> Response<Bytes>
method apply_filter (line 401) | fn apply_filter<F>(self, f: &F) -> impl Future<Output = Result<F::Extr...
type WsBuilder (line 152) | pub struct WsBuilder {
method path (line 435) | pub fn path(self, p: &str) -> Self {
method header (line 454) | pub fn header<K, V>(self, key: K, value: V) -> Self
method handshake (line 488) | pub async fn handshake<F>(self, f: F) -> Result<WsClient, WsError>
type WsClient (line 158) | pub struct WsClient {
method send_text (line 588) | pub async fn send_text(&mut self, text: impl Into<String>) {
method send (line 593) | pub async fn send(&mut self, msg: crate::ws::Message) {
method recv (line 598) | pub async fn recv(&mut self) -> Result<crate::filters::ws::Message, Ws...
method recv_closed (line 610) | pub async fn recv_closed(&mut self) -> Result<(), WsError> {
method pinned_tx (line 624) | fn pinned_tx(self: Pin<&mut Self>) -> Pin<&mut mpsc::UnboundedSender<c...
method fmt (line 632) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Error (line 639) | type Error = WsError;
method poll_ready (line 641) | fn poll_ready(
method start_send (line 648) | fn start_send(self: Pin<&mut Self>, message: Message) -> Result<(), Se...
method poll_flush (line 652) | fn poll_flush(
method poll_close (line 659) | fn poll_close(
type WsError (line 165) | pub struct WsError {
method new (line 686) | fn new<E: Into<Box<dyn StdError + Send + Sync>>>(cause: E) -> Self {
method fmt (line 694) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type Item (line 669) | type Item = Result<crate::ws::Message, WsError>;
method poll_next (line 671) | fn poll_next(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Op...
method description (line 700) | fn description(&self) -> &str {
type OneOrTuple (line 706) | pub trait OneOrTuple {
method one_or_tuple (line 709) | fn one_or_tuple(self) -> Self::Output;
type Output (line 713) | type Output = ();
method one_or_tuple (line 714) | fn one_or_tuple(self) -> Self::Output {}
FILE: src/tls.rs
type TlsConfigError (line 22) | pub(crate) enum TlsConfigError {
method fmt (line 39) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type TlsClientAuth (line 58) | pub(crate) enum TlsClientAuth {
type TlsConfigBuilder (line 68) | pub(crate) struct TlsConfigBuilder {
method fmt (line 76) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
method new (line 83) | pub(crate) fn new() -> TlsConfigBuilder {
method key_path (line 93) | pub(crate) fn key_path(mut self, path: impl AsRef<Path>) -> Self {
method key (line 102) | pub(crate) fn key(mut self, key: &[u8]) -> Self {
method cert_path (line 108) | pub(crate) fn cert_path(mut self, path: impl AsRef<Path>) -> Self {
method cert (line 117) | pub(crate) fn cert(mut self, cert: &[u8]) -> Self {
method client_auth_optional_path (line 126) | pub(crate) fn client_auth_optional_path(mut self, path: impl AsRef<Pat...
method client_auth_optional (line 139) | pub(crate) fn client_auth_optional(mut self, trust_anchor: &[u8]) -> S...
method client_auth_required_path (line 149) | pub(crate) fn client_auth_required_path(mut self, path: impl AsRef<Pat...
method client_auth_required (line 162) | pub(crate) fn client_auth_required(mut self, trust_anchor: &[u8]) -> S...
method ocsp_resp (line 169) | pub(crate) fn ocsp_resp(mut self, ocsp_resp: &[u8]) -> Self {
method build (line 174) | pub(crate) fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
type LazyFile (line 256) | struct LazyFile {
method lazy_read (line 262) | fn lazy_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
method read (line 272) | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
method remote_addr (line 284) | fn remote_addr(&self) -> Option<SocketAddr> {
type State (line 289) | enum State {
type TlsStream (line 297) | pub(crate) struct TlsStream {
method new (line 303) | fn new(stream: AddrStream, config: Arc<ServerConfig>) -> TlsStream {
method poll_read (line 314) | fn poll_read(
method poll_write (line 335) | fn poll_write(
method poll_flush (line 354) | fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io...
method poll_shutdown (line 361) | fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll...
type TlsAcceptor (line 369) | pub(crate) struct TlsAcceptor {
method new (line 375) | pub(crate) fn new(config: ServerConfig, incoming: AddrIncoming) -> Tls...
type Conn (line 384) | type Conn = TlsStream;
type Error (line 385) | type Error = io::Error;
method poll_accept (line 387) | fn poll_accept(
function file_cert_key (line 405) | fn file_cert_key() {
function bytes_cert_key (line 414) | fn bytes_cert_key() {
function file_ecc_cert_key (line 426) | fn file_ecc_cert_key() {
function bytes_ecc_cert_key (line 435) | fn bytes_ecc_cert_key() {
FILE: tests/addr.rs
function remote_addr_missing (line 6) | async fn remote_addr_missing() {
function remote_addr_present (line 15) | async fn remote_addr_present() {
FILE: tests/body.rs
function matches (line 8) | async fn matches() {
function server_error_if_taking_body_multiple_times (line 26) | async fn server_error_if_taking_body_multiple_times() {
function content_length_limit (line 39) | async fn content_length_limit() {
function json (line 61) | async fn json() {
function json_rejects_bad_content_type (line 80) | async fn json_rejects_bad_content_type() {
function json_invalid (line 98) | async fn json_invalid() {
function json_size_of (line 110) | fn json_size_of() {
function form (line 116) | async fn form() {
function form_rejects_bad_content_type (line 132) | async fn form_rejects_bad_content_type() {
function form_allows_charset (line 156) | async fn form_allows_charset() {
function form_invalid (line 174) | async fn form_invalid() {
function stream (line 186) | async fn stream() {
FILE: tests/cookie.rs
function cookie (line 4) | async fn cookie() {
function optional (line 21) | async fn optional() {
function missing (line 38) | async fn missing() {
FILE: tests/cors.rs
function allow_methods (line 5) | async fn allow_methods() {
function origin_not_allowed (line 30) | async fn origin_not_allowed() {
function headers_not_exposed (line 56) | async fn headers_not_exposed() {
function headers_not_allowed (line 88) | async fn headers_not_allowed() {
function success (line 107) | async fn success() {
function with_log (line 166) | async fn with_log() {
FILE: tests/ext.rs
type Ext1 (line 5) | struct Ext1(i32);
function set_and_get (line 8) | async fn set_and_get() {
function get_missing (line 21) | async fn get_missing() {
FILE: tests/filter.rs
function flattens_tuples (line 6) | async fn flattens_tuples() {
function map (line 72) | async fn map() {
function or (line 83) | async fn or() {
function or_else (line 95) | async fn or_else() {
function recover (line 115) | async fn recover() {
function unify (line 137) | async fn unify() {
function nested (line 151) | async fn nested() {
FILE: tests/fs.rs
function file (line 5) | async fn file() {
function dir (line 30) | async fn dir() {
function dir_encoded (line 52) | async fn dir_encoded() {
function dir_not_found (line 69) | async fn dir_not_found() {
function dir_bad_path (line 81) | async fn dir_bad_path() {
function dir_bad_encoded_path (line 93) | async fn dir_bad_encoded_path() {
function dir_fallback_index_on_dir (line 105) | async fn dir_fallback_index_on_dir() {
function not_modified (line 121) | async fn not_modified() {
function precondition (line 152) | async fn precondition() {
function byte_ranges (line 178) | async fn byte_ranges() {
function byte_ranges_with_excluded_file_size (line 234) | async fn byte_ranges_with_excluded_file_size() {
FILE: tests/header.rs
function exact (line 5) | async fn exact() {
function exact_rejections (line 22) | async fn exact_rejections() {
function optional (line 45) | async fn optional() {
FILE: tests/host.rs
function exact (line 5) | async fn exact() {
function optional (line 76) | async fn optional() {
FILE: tests/method.rs
function method (line 5) | async fn method() {
function method_not_allowed_trumps_not_found (line 21) | async fn method_not_allowed_trumps_not_found() {
function bad_request_trumps_method_not_allowed (line 36) | async fn bad_request_trumps_method_not_allowed() {
FILE: tests/multipart.rs
function form_fields (line 7) | async fn form_fields() {
function max_length_is_enforced (line 57) | async fn max_length_is_enforced() {
function max_length_can_be_disabled (line 81) | async fn max_length_can_be_disabled() {
FILE: tests/path.rs
function path (line 9) | async fn path() {
function param (line 32) | async fn param() {
function end (line 63) | async fn end() {
function tail (line 116) | async fn tail() {
function or (line 164) | async fn or() {
function or_else (line 200) | async fn or_else() {
function path_macro (line 215) | async fn path_macro() {
function full_path (line 262) | async fn full_path() {
function peek (line 334) | async fn peek() {
function peek_segments (line 394) | async fn peek_segments() {
FILE: tests/query.rs
function query (line 8) | async fn query() {
function query_struct (line 19) | async fn query_struct() {
function empty_query_struct (line 35) | async fn empty_query_struct() {
function query_struct_no_values (line 51) | async fn query_struct_no_values() {
function missing_query_struct (line 67) | async fn missing_query_struct() {
type MyArgs (line 83) | struct MyArgs {
function required_query_struct (line 89) | async fn required_query_struct() {
function missing_required_query_struct_partial (line 105) | async fn missing_required_query_struct_partial() {
function missing_required_query_struct_no_query (line 115) | async fn missing_required_query_struct_no_query() {
type MyRequiredArgs (line 126) | struct MyRequiredArgs {
function raw_query (line 132) | async fn raw_query() {
FILE: tests/redirect.rs
function redirect_uri (line 5) | async fn redirect_uri() {
function redirect_found_uri (line 16) | async fn redirect_found_uri() {
function redirect_see_other_uri (line 27) | async fn redirect_see_other_uri() {
function redirect_temporary_uri (line 38) | async fn redirect_temporary_uri() {
function redirect_permanent_uri (line 49) | async fn redirect_permanent_uri() {
FILE: tests/reply_with.rs
function header (line 6) | async fn header() {
function headers (line 24) | async fn headers() {
function default_header (line 47) | async fn default_header() {
FILE: tests/tracing.rs
function uses_tracing (line 4) | async fn uses_tracing() {
FILE: tests/ws.rs
function upgrade (line 9) | async fn upgrade() {
function fail (line 43) | async fn fail() {
function text (line 55) | async fn text() {
function binary (line 70) | async fn binary() {
function wsclient_sink_and_stream (line 85) | async fn wsclient_sink_and_stream() {
function close_frame (line 100) | async fn close_frame() {
function send_ping (line 115) | async fn send_ping() {
function echo_pings (line 140) | async fn echo_pings() {
function pongs_only (line 168) | async fn pongs_only() {
function closed (line 190) | async fn closed() {
function limit_message_size (line 202) | async fn limit_message_size() {
function limit_frame_size (line 226) | async fn limit_frame_size() {
type MyQuery (line 250) | struct MyQuery {
function ws_with_query (line 255) | async fn ws_with_query() {
function ws_echo (line 278) | fn ws_echo() -> impl Filter<Extract = (impl warp::Reply,), Error = warp:...
Condensed preview — 101 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (474K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 22,
"preview": "github: [seanmonstar]\n"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 693,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: bug\nassignees: ''\n\n---\n\n**Version**\nLis"
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 245,
"preview": "blank_issues_enabled: false\ncontact_links:\n - name: Question\n url: https://discord.gg/RFsPjyt\n about: 'Please pos"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 600,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: feature\nassignees: ''\n\n---\n\n**Is you"
},
{
"path": ".github/workflows/ci.yml",
"chars": 2013,
"preview": "name: CI\n\non:\n pull_request:\n push:\n branches:\n - master\nenv:\n RUST_BACKTRACE: 1\n\njobs:\n ci-pass:\n name: "
},
{
"path": ".gitignore",
"chars": 47,
"preview": "\n/target\n**/*.rs.bk\nCargo.lock\n.idea/\nwarp.iml\n"
},
{
"path": "CHANGELOG.md",
"chars": 15383,
"preview": "# CHANGELOG\n\n### v0.4.2 (August 19, 2025)\n\n- **Features**:\n - Add support for passing `UnixListener` to `incoming(liste"
},
{
"path": "Cargo.toml",
"chars": 5018,
"preview": "[package]\nname = \"warp\"\nversion = \"0.4.2\"\ndescription = \"serve the web at warp speeds\"\nauthors = [\"Sean McArthur <sean@s"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "Copyright (c) 2018-2025 Sean McArthur\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "README.md",
"chars": 1923,
"preview": "# warp\n\n[](https://crates.io/crates/warp)\n[]\n\nuse serde_derive::{Deserialize, Serialize};\n\nuse warp::Filter;\n\n#[derive(Deserialize, Serialize)]\nst"
},
{
"path": "examples/compression.rs",
"chars": 1071,
"preview": "#![deny(warnings)]\n\nuse warp::Filter;\n\n#[tokio::main]\nasync fn main() {\n let file = warp::path(\"todos\").and(warp::fs:"
},
{
"path": "examples/dir/another.html",
"chars": 186,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>dir/another.html</title>\n </head>\n <body>\n <h1>Welcome to "
},
{
"path": "examples/dir/index.html",
"chars": 195,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>dir/index.html</title>\n </head>\n <body>\n <h1>Welcome to Di"
},
{
"path": "examples/dir.rs",
"chars": 187,
"preview": "#![deny(warnings)]\n\n#[tokio::main]\nasync fn main() {\n pretty_env_logger::init();\n\n warp::serve(warp::fs::dir(\"exam"
},
{
"path": "examples/file.rs",
"chars": 480,
"preview": "#![deny(warnings)]\n\nuse warp::Filter;\n\n#[tokio::main]\nasync fn main() {\n pretty_env_logger::init();\n\n let readme ="
},
{
"path": "examples/futures.rs",
"chars": 950,
"preview": "#![deny(warnings)]\n\nuse std::convert::Infallible;\nuse std::str::FromStr;\nuse std::time::Duration;\nuse warp::Filter;\n\n#[t"
},
{
"path": "examples/handlebars_template.rs",
"chars": 1515,
"preview": "#![deny(warnings)]\nuse std::sync::Arc;\n\nuse handlebars::Handlebars;\nuse serde::Serialize;\nuse serde_json::json;\nuse warp"
},
{
"path": "examples/headers.rs",
"chars": 743,
"preview": "#![deny(warnings)]\nuse std::net::SocketAddr;\nuse warp::Filter;\n\n/// Create a server that requires header conditions:\n///"
},
{
"path": "examples/hello.rs",
"chars": 236,
"preview": "#![deny(warnings)]\nuse warp::Filter;\n\n#[tokio::main]\nasync fn main() {\n // Match any request and return hello world!\n"
},
{
"path": "examples/multipart.rs",
"chars": 1221,
"preview": "use bytes::BufMut;\nuse futures_util::TryStreamExt;\nuse warp::multipart::FormData;\nuse warp::Filter;\n\n#[tokio::main]\nasyn"
},
{
"path": "examples/query_string.rs",
"chars": 1918,
"preview": "use serde_derive::{Deserialize, Serialize};\nuse std::collections::HashMap;\nuse warp::{\n http::{Response, StatusCode},"
},
{
"path": "examples/rejections.rs",
"chars": 3688,
"preview": "#![deny(warnings)]\n\nuse std::convert::Infallible;\nuse std::error::Error;\nuse std::num::NonZeroU16;\n\nuse serde_derive::{D"
},
{
"path": "examples/returning.rs",
"chars": 745,
"preview": "use warp::{filters::BoxedFilter, Filter, Rejection, Reply};\n\n// Option 1: BoxedFilter\n// Note that this may be useful fo"
},
{
"path": "examples/routing.rs",
"chars": 3631,
"preview": "#![deny(warnings)]\n\nuse warp::Filter;\n\n#[tokio::main]\nasync fn main() {\n pretty_env_logger::init();\n\n // We'll sta"
},
{
"path": "examples/sse.rs",
"chars": 921,
"preview": "use futures_util::StreamExt;\nuse std::convert::Infallible;\nuse std::time::Duration;\nuse tokio::time::interval;\nuse tokio"
},
{
"path": "examples/sse_chat.rs",
"chars": 5063,
"preview": "use futures_util::{Stream, StreamExt};\nuse std::collections::HashMap;\nuse std::sync::{\n atomic::{AtomicUsize, Orderin"
},
{
"path": "examples/tls/cert.ecc.pem",
"chars": 790,
"preview": "-----BEGIN CERTIFICATE-----\nMIICGjCCAb+gAwIBAgIUEbF3/5NuJeGvIywbwta91AkJxTgwCgYIKoZIzj0EAwIw\nYjELMAkGA1UEBhMCREUxEDAOBgN"
},
{
"path": "examples/tls/cert.pem",
"chars": 1448,
"preview": "-----BEGIN CERTIFICATE-----\nMIIEADCCAmigAwIBAgICAcgwDQYJKoZIhvcNAQELBQAwLDEqMCgGA1UEAwwhcG9u\neXRvd24gUlNBIGxldmVsIDIgaW5"
},
{
"path": "examples/tls/key.ecc",
"chars": 227,
"preview": "-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIPwp3LAnLEyWe2lLz66Y3QCCJ/BEMJheTM0shZnnSw6toAoGCCqGSM49\nAwEHoUQDQgAE2UeBetF/oh43"
},
{
"path": "examples/tls/key.rsa",
"chars": 1679,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAqVYYdfxTT9qr1np22UoIWq4v1E4cHncp35xxu4HNyZsoJBHR\nK1gTvwh8x4LMe24lROW/LGW"
},
{
"path": "examples/tls.rs",
"chars": 770,
"preview": "#![deny(warnings)]\n\n// Don't copy this `cfg`, it's only needed because this file is within\n// the warp repository.\n// In"
},
{
"path": "examples/todos.rs",
"chars": 8663,
"preview": "#![deny(warnings)]\n\nuse std::env;\nuse warp::Filter;\n\n/// Provides a RESTful web server managing some Todos.\n///\n/// API "
},
{
"path": "examples/tracing.rs",
"chars": 2378,
"preview": "//! [`tracing`] is a framework for instrumenting Rust programs to\n//! collect scoped, structured, and async-aware diagno"
},
{
"path": "examples/unix_socket.rs",
"chars": 549,
"preview": "#![deny(warnings)]\n\n#[cfg(unix)]\n#[tokio::main]\nasync fn main() {\n use tokio::net::UnixListener;\n\n pretty_env_logg"
},
{
"path": "examples/websockets.rs",
"chars": 804,
"preview": "#![deny(warnings)]\n\nuse futures_util::{FutureExt, StreamExt};\nuse warp::Filter;\n\n#[tokio::main]\nasync fn main() {\n pr"
},
{
"path": "examples/websockets_chat.rs",
"chars": 5256,
"preview": "// #![deny(warnings)]\nuse std::collections::HashMap;\nuse std::sync::{\n atomic::{AtomicUsize, Ordering},\n Arc,\n};\n\n"
},
{
"path": "src/bodyt.rs",
"chars": 2681,
"preview": "use std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse bytes::Buf;\nuse bytes::Bytes;\nuse futures_util::StreamExt;\nuse h"
},
{
"path": "src/error.rs",
"chars": 1829,
"preview": "use std::convert::Infallible;\nuse std::error::Error as StdError;\nuse std::fmt;\n\ntype BoxError = Box<dyn std::error::Erro"
},
{
"path": "src/filter/and.rs",
"chars": 2841,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::ready;\nuse pin_project::p"
},
{
"path": "src/filter/and_then.rs",
"chars": 3084,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/boxed.rs",
"chars": 2465,
"preview": "use std::fmt;\nuse std::future::Future;\nuse std::pin::Pin;\nuse std::sync::Arc;\n\nuse futures_util::TryFutureExt;\n\nuse supe"
},
{
"path": "src/filter/map.rs",
"chars": 1352,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/map_err.rs",
"chars": 1271,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::TryFuture;\nuse pin_projec"
},
{
"path": "src/filter/mod.rs",
"chars": 14415,
"preview": "mod and;\nmod and_then;\nmod boxed;\nmod map;\nmod map_err;\nmod or;\nmod or_else;\nmod recover;\npub(crate) mod service;\nmod th"
},
{
"path": "src/filter/or.rs",
"chars": 3306,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/or_else.rs",
"chars": 2927,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/recover.rs",
"chars": 3258,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/service.rs",
"chars": 3484,
"preview": "use std::convert::Infallible;\nuse std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_u"
},
{
"path": "src/filter/then.rs",
"chars": 2332,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/unify.rs",
"chars": 1108,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/untuple_one.rs",
"chars": 1159,
"preview": "use std::future::Future;\nuse std::pin::Pin;\nuse std::task::{Context, Poll};\n\nuse futures_util::{ready, TryFuture};\nuse p"
},
{
"path": "src/filter/wrap.rs",
"chars": 1206,
"preview": "use super::Filter;\n\npub trait WrapSealed<F: Filter> {\n type Wrapped: Filter;\n\n fn wrap(&self, filter: F) -> Self::"
},
{
"path": "src/filters/addr.rs",
"chars": 935,
"preview": "//! Socket Address filters.\n\nuse std::convert::Infallible;\nuse std::net::SocketAddr;\n\nuse futures_util::future;\n\nuse cra"
},
{
"path": "src/filters/any.rs",
"chars": 1816,
"preview": "//! A filter that matches any route.\nuse std::convert::Infallible;\nuse std::future::Future;\nuse std::pin::Pin;\nuse std::"
},
{
"path": "src/filters/body.rs",
"chars": 10491,
"preview": "//! Body filters\n//!\n//! Filters that extract a body for a route.\n\nuse std::error::Error as StdError;\nuse std::fmt;\n\nuse"
},
{
"path": "src/filters/compression.rs",
"chars": 8612,
"preview": "//! Compression Filters\n//!\n//! Filters that compress the body of a response.\n\n#[cfg(feature = \"compression-brotli\")]\nus"
},
{
"path": "src/filters/cookie.rs",
"chars": 1380,
"preview": "//! Cookie Filters\n\nuse futures_util::future;\nuse headers::Cookie;\n\nuse super::header;\nuse crate::filter::{Filter, One};"
},
{
"path": "src/filters/cors.rs",
"chars": 18843,
"preview": "//! CORS Filters\n\nuse std::collections::HashSet;\nuse std::convert::TryFrom;\nuse std::error::Error as StdError;\nuse std::"
},
{
"path": "src/filters/ext.rs",
"chars": 1101,
"preview": "//! Request Extensions\n\nuse std::convert::Infallible;\n\nuse futures_util::future;\n\nuse crate::filter::{filter_fn_one, Fil"
},
{
"path": "src/filters/fs.rs",
"chars": 16636,
"preview": "//! File System Filters\n\nuse std::cmp;\nuse std::convert::Infallible;\nuse std::fs::Metadata;\nuse std::future::Future;\nuse"
},
{
"path": "src/filters/header.rs",
"chars": 6950,
"preview": "//! Header Filters\n//!\n//! These filters are used to interact with the Request HTTP headers. Some\n//! of them, like `exa"
},
{
"path": "src/filters/host.rs",
"chars": 3412,
"preview": "//! Host (\"authority\") filter\n//!\nuse crate::filter::{filter_fn_one, Filter, One};\nuse crate::reject::{self, Rejection};"
},
{
"path": "src/filters/log.rs",
"chars": 7066,
"preview": "//! Logger Filters\n\nuse std::fmt;\nuse std::net::SocketAddr;\nuse std::time::{Duration, Instant};\n\nuse http::{header, Stat"
},
{
"path": "src/filters/method.rs",
"chars": 3846,
"preview": "//! HTTP Method filters.\n//!\n//! The filters deal with the HTTP Method part of a request. Several here will\n//! match th"
},
{
"path": "src/filters/mod.rs",
"chars": 629,
"preview": "//! Built-in Filters\n//!\n//! This module mostly serves as documentation to group together the list of\n//! built-in filte"
},
{
"path": "src/filters/multipart.rs",
"chars": 7010,
"preview": "//! Multipart body filters\n//!\n//! [`Filter`](crate::Filter)s that extract a multipart body for a route.\n\nuse std::error"
},
{
"path": "src/filters/path.rs",
"chars": 17300,
"preview": "//! Path Filters\n//!\n//! The [`Filter`]s here work on the \"path\" of requests.\n//!\n//! - [`path`](./fn.path.html) matches"
},
{
"path": "src/filters/query.rs",
"chars": 2761,
"preview": "//! Query Filters\n\nuse futures_util::future;\nuse serde::de::DeserializeOwned;\n\nuse crate::filter::{filter_fn_one, Filter"
},
{
"path": "src/filters/reply.rs",
"chars": 6839,
"preview": "//! Reply Filters\n//!\n//! These \"filters\" behave a little differently than the rest. Instead of\n//! being used directly "
},
{
"path": "src/filters/sse.rs",
"chars": 14385,
"preview": "//! Server-Sent Events (SSE)\n//!\n//! # Example\n//!\n//! ```\n//!\n//! use std::time::Duration;\n//! use std::convert::Infall"
},
{
"path": "src/filters/trace.rs",
"chars": 8651,
"preview": "//! [`tracing`] filters.\n//!\n//! [`tracing`] is a framework for instrumenting Rust programs to\n//! collect scoped, struc"
},
{
"path": "src/filters/ws.rs",
"chars": 13380,
"preview": "//! Websockets Filters\n\nuse std::borrow::Cow;\nuse std::fmt;\nuse std::future::Future;\nuse std::pin::Pin;\nuse std::task::{"
},
{
"path": "src/generic.rs",
"chars": 5434,
"preview": "#[derive(Debug)]\npub struct Product<H, T: HList>(pub(crate) H, pub(crate) T);\n\npub type One<T> = (T,);\n\n#[inline]\npub(cr"
},
{
"path": "src/lib.rs",
"chars": 4559,
"preview": "#![deny(missing_docs)]\n#![deny(missing_debug_implementations)]\n#![deny(rust_2018_idioms)]\n#![cfg_attr(test, deny(warning"
},
{
"path": "src/redirect.rs",
"chars": 5601,
"preview": "//! Redirect requests to a new location.\n//!\n//! The types in this module are helpers that implement [`Reply`], and easy"
},
{
"path": "src/reject.rs",
"chars": 25704,
"preview": "//! Rejections\n//!\n//! Part of the power of the [`Filter`](../trait.Filter.html) system is being able to\n//! reject a re"
},
{
"path": "src/reply.rs",
"chars": 14556,
"preview": "//! Reply to requests.\n//!\n//! A [`Reply`](./trait.Reply.html) is a type that can be converted into an HTTP\n//! response"
},
{
"path": "src/route.rs",
"chars": 3312,
"preview": "use scoped_tls::scoped_thread_local;\nuse std::cell::RefCell;\nuse std::mem;\nuse std::net::SocketAddr;\n\nuse crate::addr::R"
},
{
"path": "src/server.rs",
"chars": 17009,
"preview": "use std::future::Future;\nuse std::net::SocketAddr;\n#[cfg(feature = \"tls\")]\nuse std::path::Path;\n\nuse futures_util::TryFu"
},
{
"path": "src/service.rs",
"chars": 80,
"preview": "//! Convert `Filter`s into `Service`s\n\npub use crate::filter::service::service;\n"
},
{
"path": "src/test.rs",
"chars": 21202,
"preview": "//! Test utilities to test your filters.\n//!\n//! [`Filter`](../trait.Filter.html)s can be easily tested without starting"
},
{
"path": "src/tls.rs",
"chars": 14850,
"preview": "use std::fmt;\nuse std::fs::File;\nuse std::future::Future;\nuse std::io::{self, BufReader, Cursor, Read};\nuse std::net::So"
},
{
"path": "tests/addr.rs",
"chars": 659,
"preview": "#![deny(warnings)]\n\nuse std::net::{IpAddr, Ipv4Addr, SocketAddr};\n\n#[tokio::test]\nasync fn remote_addr_missing() {\n l"
},
{
"path": "tests/body.rs",
"chars": 5314,
"preview": "#![deny(warnings)]\n\nuse bytes::Buf;\nuse futures_util::TryStreamExt;\nuse warp::Filter;\n\n#[tokio::test]\nasync fn matches()"
},
{
"path": "tests/cookie.rs",
"chars": 1491,
"preview": "#![deny(warnings)]\n\n#[tokio::test]\nasync fn cookie() {\n let foo = warp::cookie::<String>(\"foo\");\n\n let req = warp:"
},
{
"path": "tests/cors.rs",
"chars": 5440,
"preview": "#![deny(warnings)]\nuse warp::{http::Method, Filter};\n\n#[tokio::test]\nasync fn allow_methods() {\n let cors = warp::cor"
},
{
"path": "tests/ext.rs",
"chars": 595,
"preview": "#![deny(warnings)]\nuse warp::Filter;\n\n#[derive(Clone, Debug, PartialEq)]\nstruct Ext1(i32);\n\n#[tokio::test]\nasync fn set_"
},
{
"path": "tests/filter.rs",
"chars": 4571,
"preview": "#![deny(warnings)]\nuse std::convert::Infallible;\nuse warp::Filter;\n\n#[tokio::test]\nasync fn flattens_tuples() {\n let "
},
{
"path": "tests/fs.rs",
"chars": 7956,
"preview": "#![deny(warnings)]\nuse std::fs;\n\n#[tokio::test]\nasync fn file() {\n let _ = pretty_env_logger::try_init();\n\n let fi"
},
{
"path": "tests/header.rs",
"chars": 1791,
"preview": "#![deny(warnings)]\nuse warp::Filter;\n\n#[tokio::test]\nasync fn exact() {\n let _ = pretty_env_logger::try_init();\n\n "
},
{
"path": "tests/host.rs",
"chars": 2561,
"preview": "#![deny(warnings)]\nuse warp::host::Authority;\n\n#[tokio::test]\nasync fn exact() {\n let filter = warp::host::exact(\"kno"
},
{
"path": "tests/method.rs",
"chars": 1560,
"preview": "#![deny(warnings)]\nuse warp::Filter;\n\n#[tokio::test]\nasync fn method() {\n let _ = pretty_env_logger::try_init();\n "
},
{
"path": "tests/multipart.rs",
"chars": 2977,
"preview": "#![deny(warnings)]\nuse bytes::BufMut;\nuse futures_util::{TryFutureExt, TryStreamExt};\nuse warp::{multipart, Filter};\n\n#["
},
{
"path": "tests/path.rs",
"chars": 10540,
"preview": "#![deny(warnings)]\n#[macro_use]\nextern crate warp;\n\nuse futures_util::future;\nuse warp::Filter;\n\n#[tokio::test]\nasync fn"
},
{
"path": "tests/query.rs",
"chars": 3186,
"preview": "#![deny(warnings)]\n\nuse serde_derive::Deserialize;\nuse std::collections::HashMap;\nuse warp::Filter;\n\n#[tokio::test]\nasyn"
},
{
"path": "tests/redirect.rs",
"chars": 1691,
"preview": "#![deny(warnings)]\nuse warp::{http::Uri, Filter};\n\n#[tokio::test]\nasync fn redirect_uri() {\n let over_there = warp::a"
},
{
"path": "tests/reply_with.rs",
"chars": 2082,
"preview": "#![deny(warnings)]\nuse warp::http::header::{HeaderMap, HeaderValue};\nuse warp::Filter;\n\n#[tokio::test]\nasync fn header()"
},
{
"path": "tests/tracing.rs",
"chars": 1565,
"preview": "use warp::Filter;\n\n#[tokio::test]\nasync fn uses_tracing() {\n // Setup a log subscriber (responsible to print to outpu"
},
{
"path": "tests/ws.rs",
"chars": 8398,
"preview": "#![deny(warnings)]\n\nuse futures_util::{FutureExt, SinkExt, StreamExt};\nuse serde_derive::Deserialize;\nuse warp::ws::Mess"
}
]
About this extraction
This page contains the full source code of the seanmonstar/warp GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 101 files (440.0 KB), approximately 117.1k tokens, and a symbol index with 1010 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.