This commit is contained in:
nora 2023-03-07 14:00:23 +01:00
parent 25adea4103
commit 7af1274587
160 changed files with 38999 additions and 4 deletions

1
warp/.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
github: [seanmonstar]

View file

@ -0,0 +1,36 @@
---
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]

View file

@ -0,0 +1,6 @@
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/.'

View file

@ -0,0 +1,20 @@
---
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.

97
warp/.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,97 @@
name: CI
on:
pull_request:
push:
branches:
- master
jobs:
style:
name: Check Style
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
components: rustfmt
profile: minimal
override: true
- name: cargo fmt -- --check
uses: actions-rs/cargo@v1
with:
command: fmt
args: --all -- --check
test:
name: Test
needs: [style]
runs-on: ubuntu-latest
strategy:
matrix:
build: [stable, beta, nightly, tls, no-default-features, compression]
include:
- build: beta
rust: beta
- build: nightly
rust: nightly
benches: true
- build: tls
features: "--features tls"
- build: no-default-features
features: "--no-default-features"
- build: compression
features: "--features compression"
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install rust
uses: actions-rs/toolchain@v1
with:
toolchain: ${{ matrix.rust || 'stable' }}
profile: minimal
override: true
- name: Test
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ matrix.features }}
- name: Test all benches
if: matrix.benches
uses: actions-rs/cargo@v1
with:
command: test
args: --benches ${{ matrix.features }}
doc:
name: Build docs
needs: [style, test]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: nightly
override: true
- name: cargo doc
uses: actions-rs/cargo@v1
with:
command: rustdoc
args: -- -D broken_intra_doc_links

6
warp/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
/target
**/*.rs.bk
Cargo.lock
.idea/
warp.iml

324
warp/CHANGELOG.md Normal file
View file

@ -0,0 +1,324 @@
### 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.

98
warp/Cargo.toml Normal file
View file

@ -0,0 +1,98 @@
[package]
name = "warp"
version = "0.3.3" # don't forget to update html_root_url
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 = "2018"
[package.metadata.docs.rs]
all-features = true
[dependencies]
async-compression = { version = "0.3.7", 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"]}
headers = "0.3"
http = "0.2"
hyper = { version = "0.14", features = ["stream", "server", "http1", "http2", "tcp", "client"] }
log = "0.4"
mime = "0.3"
mime_guess = "2.0.0"
multipart = { version = "0.18", default-features = false, features = ["server"], optional = true }
scoped-tls = "1.0"
serde = "1.0"
serde_json = "1.0"
serde_urlencoded = "0.7"
tokio = { version = "1.0", features = ["fs", "sync", "time"] }
tokio-stream = "0.1.1"
tokio-util = { version = "0.7", features = ["io"] }
tracing = { version = "0.1.21", default-features = false, features = ["log", "std"] }
tower-service = "0.3"
tokio-tungstenite = { version = "0.17", optional = true }
percent-encoding = "2.1"
pin-project = "1.0"
tokio-rustls = { version = "0.23", optional = true }
rustls-pemfile = "0.2"
[dev-dependencies]
pretty_env_logger = "0.4"
tracing-subscriber = "0.2.7"
tracing-log = "0.1"
serde_derive = "1.0"
handlebars = "4.0"
tokio = { version = "1.0", features = ["macros", "rt-multi-thread"] }
tokio-stream = { version = "0.1.1", features = ["net"] }
listenfd = "0.3"
[features]
default = ["multipart", "websocket"]
websocket = ["tokio-tungstenite"]
tls = ["tokio-rustls"]
# 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
[[test]]
name = "multipart"
required-features = ["multipart"]
[[test]]
name = "ws"
required-features = ["websocket"]
[[example]]
name = "compression"
required-features = ["compression"]
[[example]]
name = "unix_socket"
[[example]]
name = "websockets"
required-features = ["websocket"]
[[example]]
name = "websockets_chat"
required-features = ["websocket"]
[[example]]
name = "query_string"

20
warp/LICENSE Normal file
View file

@ -0,0 +1,20 @@
Copyright (c) 2018-2020 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.

63
warp/README.md Normal file
View file

@ -0,0 +1,63 @@
# warp
[![crates.io](https://img.shields.io/crates/v/warp.svg)](https://crates.io/crates/warp)
[![Released API docs](https://docs.rs/warp/badge.svg)](https://docs.rs/warp)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
[![GHA Build Status](https://github.com/seanmonstar/warp/workflows/CI/badge.svg)](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 = "0.3"
```
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

50
warp/src/error.rs Normal file
View file

@ -0,0 +1,50 @@
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 {
loop {}
}
}
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for Error {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
loop {}
}
}
impl From<Infallible> for Error {
fn from(infallible: Infallible) -> Error {
loop {}
}
}
#[test]
fn error_size_of() {
loop {}
}
#[test]
fn error_source() {
loop {}
}
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 {}
};
}

72
warp/src/filter/and.rs Normal file
View file

@ -0,0 +1,72 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
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 {
loop {}
}
}
#[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> {
loop {}
}
}
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 {}
}
}

View file

@ -0,0 +1,81 @@
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, 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 {
loop {}
}
}
#[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> {
loop {}
}
}
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 {}
}
}

82
warp/src/filter/boxed.rs Normal file
View file

@ -0,0 +1,82 @@
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use super::{Filter, FilterBase, Internal, Tuple};
use crate::reject::Rejection;
/// A type representing a boxed `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>,
{
loop {}
}
}
impl<T: Tuple> Clone for BoxedFilter<T> {
fn clone(&self) -> BoxedFilter<T> {
loop {}
}
}
impl<T: Tuple> fmt::Debug for BoxedFilter<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
fn _assert_send() {
loop {}
}
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 {
loop {}
}
}
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 {
loop {}
}
}

42
warp/src/filter/map.rs Normal file
View file

@ -0,0 +1,42 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
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 {
loop {}
}
}
#[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> {
loop {}
}
}

View file

@ -0,0 +1,44 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
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 {
loop {}
}
}
#[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> {
loop {}
}
}

425
warp/src/filter/mod.rs Normal file
View file

@ -0,0 +1,425 @@
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::{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};
pub trait FilterBase {
type Extract: Tuple;
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,
{
loop {}
}
}
#[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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
}
impl<T: FilterBase> Filter for T {}
pub trait FilterClone: Filter + Clone {}
impl<T: Filter + Clone> FilterClone for T {}
fn _assert_object_safe() {
loop {}
}
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,
{
loop {}
}
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> {
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 {
loop {}
}
}

62
warp/src/filter/or.rs Normal file
View file

@ -0,0 +1,62 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use pin_project::pin_project;
use super::{Filter, FilterBase, Internal};
use crate::generic::Either;
use crate::reject::CombineRejection;
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 = Combined<U::Error, T::Error>;
type Future = EitherFuture<T, U>;
fn filter(&self, _: Internal) -> Self::Future {
loop {}
}
}
#[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) {
loop {}
}
}
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 {}
}
}

View file

@ -0,0 +1,69 @@
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, Func, Internal};
use crate::reject::IsReject;
#[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 {
loop {}
}
}
#[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) {
loop {}
}
}
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 {}
}
}

View file

@ -0,0 +1,76 @@
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, Func, Internal};
use crate::generic::Either;
use crate::reject::IsReject;
#[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 {
loop {}
}
}
#[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) {
loop {}
}
}
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 {}
}
}

111
warp/src/filter/service.rs Normal file
View file

@ -0,0 +1,111 @@
use std::convert::Infallible;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::future::TryFuture;
use hyper::service::Service;
use pin_project::pin_project;
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
use crate::route::{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);
///
/// // Typical hyper setup...
/// let make_svc = hyper::service::make_service_fn(move |_| async move {
/// Ok::<_, Infallible>(svc)
/// });
///
/// hyper::Server::bind(&([127, 0, 0, 1], 3030).into())
/// .serve(make_svc)
/// .await?;
/// # Ok(())
/// # }
/// ```
///
/// [Service]: https://docs.rs/hyper/0.13.*/hyper/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,
{
loop {}
}
#[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_with_addr(
&self,
req: Request,
remote_addr: Option<SocketAddr>,
) -> FilteredFuture<F::Future> {
loop {}
}
}
impl<F> Service<Request> for FilteredService<F>
where
F: Filter,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
{
type Response = Response;
type Error = Infallible;
type Future = FilteredFuture<F::Future>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
loop {}
}
#[inline]
fn call(&mut self, req: Request) -> Self::Future {
loop {}
}
}
#[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> {
loop {}
}
}

69
warp/src/filter/then.rs Normal file
View file

@ -0,0 +1,69 @@
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, 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 {
loop {}
}
}
#[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> {
loop {}
}
}
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 {}
}
}

39
warp/src/filter/unify.rs Normal file
View file

@ -0,0 +1,39 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_util::{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 {
loop {}
}
}
#[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> {
loop {}
}
}

View file

@ -0,0 +1,40 @@
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
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 {
loop {}
}
}
#[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> {
loop {}
}
}

57
warp/src/filter/wrap.rs Normal file
View file

@ -0,0 +1,57 @@
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 {
loop {}
}
}
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,
{
loop {}
}
#[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 {
loop {}
}
}

26
warp/src/filters/addr.rs Normal file
View file

@ -0,0 +1,26 @@
//! Socket Address filters.
use std::convert::Infallible;
use std::net::SocketAddr;
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| futures_util::future::ok(route.remote_addr()))
}

68
warp/src/filters/any.rs Normal file
View file

@ -0,0 +1,68 @@
//! 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`](crate::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(&param_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 {
loop {}
}
}
#[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> {
loop {}
}
}

320
warp/src/filters/body.rs Normal file
View file

@ -0,0 +1,320 @@
//! Body filters
//!
//! Filters that extract a body for a route.
use std::error::Error as StdError;
use std::fmt;
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::{Buf, Bytes};
use futures_util::{future, Stream, TryFutureExt};
use headers::ContentLength;
use http::header::CONTENT_TYPE;
use hyper::Body;
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>;
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`.
///
/// # 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: Body| BodyStream { 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(|body: hyper::Body| {
hyper::body::to_bytes(body)
.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(|body: ::hyper::Body| {
hyper::body::aggregate(body)
.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 })
})
})
}
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> {
loop {}
}
}
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> {
loop {}
}
}
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 {
tracing::trace!("no content-type header, assuming {}/{}", type_, subtype);
future::ok(())
} else {
tracing::debug!("no content-type found");
future::err(reject::unsupported_media_type())
}
})
}
struct BodyStream {
body: Body,
}
impl Stream for BodyStream {
type Item = Result<Bytes, crate::Error>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}
/// 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 {
loop {}
}
}
impl StdError for BodyDeserializeError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
loop {}
}
}
#[derive(Debug)]
pub(crate) struct BodyReadError(::hyper::Error);
impl fmt::Display for BodyReadError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for BodyReadError {}
unit_error! {
pub (crate) BodyConsumedMultipleTimes : "Request body consumed multiple times"
}

View file

@ -0,0 +1,200 @@
//! 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 http::header::HeaderValue;
use hyper::{
header::{CONTENT_ENCODING, CONTENT_LENGTH},
Body,
};
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 {
loop {}
}
}
/// Compression
#[derive(Clone, Copy, Debug)]
pub struct Compression<F> {
func: F,
}
/// 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> {
loop {}
}
/// 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> {
loop {}
}
/// 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> {
loop {}
}
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 {
loop {}
}
}
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 hyper::Body;
use pin_project::pin_project;
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>> {
loop {}
}
}
impl From<Body> for CompressableBody<Body, hyper::Error> {
fn from(body: Body) -> Self {
loop {}
}
}
/// Compression Props
#[derive(Debug)]
pub struct CompressionProps {
pub(super) body: CompressableBody<Body, hyper::Error>,
pub(super) head: http::response::Parts,
}
impl From<http::Response<Body>> for CompressionProps {
fn from(resp: http::Response<Body>) -> Self {
loop {}
}
}
#[allow(missing_debug_implementations)]
pub struct Compressed(pub(super) Response);
impl Reply for Compressed {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
#[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 {
loop {}
}
}
#[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> {
loop {}
}
}
}

View file

@ -0,0 +1,46 @@
//! 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,
}
})
}

350
warp/src/filters/cors.rs Normal file
View file

@ -0,0 +1,350 @@
//! 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,
};
use http::{self, header::{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 {
loop {}
}
/// A wrapping filter constructed via `warp::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 {
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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>,
{
loop {}
}
/// 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 {
loop {}
}
/// 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 {
loop {}
}
/// 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,
{
loop {}
}
/// 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 {
loop {}
}
/// 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 {
loop {}
}
}
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 {
loop {}
}
}
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 {
loop {}
}
}
/// 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 {
loop {}
}
}
impl fmt::Display for CorsForbidden {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
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 {}
mod internal {
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use futures_util::{future, TryFuture};
use headers::Origin;
use http::header;
use pin_project::pin_project;
use super::Configured;
use crate::filter::{Filter, FilterBase, Internal, One};
use crate::generic::Either;
use crate::reject::{CombineRejection, Rejection};
#[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 {
loop {}
}
}
#[derive(Debug)]
pub struct Preflight {
config: Arc<Configured>,
origin: header::HeaderValue,
}
impl crate::reply::Reply for Preflight {
fn into_response(self) -> crate::reply::Response {
loop {}
}
}
#[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 {
loop {}
}
}
#[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> {
loop {}
}
}
pub trait Seconds {
fn seconds(self) -> u64;
}
impl Seconds for u32 {
fn seconds(self) -> u64 {
loop {}
}
}
impl Seconds for ::std::time::Duration {
fn seconds(self) -> u64 {
loop {}
}
}
pub trait IntoOrigin {
fn into_origin(self) -> Origin;
}
impl<'a> IntoOrigin for &'a str {
fn into_origin(self) -> Origin {
loop {}
}
}
}

36
warp/src/filters/ext.rs Normal file
View file

@ -0,0 +1,36 @@
//! 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"
}

358
warp/src/filters/fs.rs Normal file
View file

@ -0,0 +1,358 @@
//! File System Filters
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 hyper::Body;
use mime_guess;
use tokio::fs::File as TkFile;
use tokio::io::AsyncSeekExt;
use tokio_util::io::poll_read_buf;
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](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> {
loop {}
}
#[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 {
loop {}
}
}
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 {
loop {}
}
}
#[derive(Clone, Debug)]
struct ArcPath(Arc<PathBuf>);
impl AsRef<Path> for ArcPath {
fn as_ref(&self) -> &Path {
loop {}
}
}
impl Reply for File {
fn into_response(self) -> Response {
loop {}
}
}
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> {
loop {}
}
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| {
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> {
loop {}
}
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) {
loop {}
}
const DEFAULT_READ_BUF_SIZE: usize = 8_192;
fn optimal_buf_size(metadata: &Metadata) -> usize {
loop {}
}
#[cfg(unix)]
fn get_block_size(metadata: &Metadata) -> usize {
loop {}
}
#[cfg(not(unix))]
fn get_block_size(_metadata: &Metadata) -> usize {
loop {}
}
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() {
loop {}
}
#[test]
fn test_reserve_at_least() {
loop {}
}
}

230
warp/src/filters/header.rs Normal file
View file

@ -0,0 +1,230 @@
//! 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()))
}

96
warp/src/filters/host.rs Normal file
View file

@ -0,0 +1,96 @@
//! 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 `Authority`, otherwise continues the request,
/// extracting `None`.
///
/// Rejects with `400 Bad Request` if the `Host` header is malformed or if there
/// is a mismatch between the `Host` header and the target URI.
///
/// # Example
///
/// ```
/// use warp::{Filter, host::Authority};
///
/// let host = warp::host::optional()
/// .map(|authority: Option<Authority>| {
/// if let Some(a) = authority {
/// format!("{} is currently not at home", a.host())
/// } else {
/// "please state who you're trying to reach".to_owned()
/// }
/// });
/// ```
pub fn optional() -> impl Filter<Extract = One<Option<Authority>>, Error = Rejection> + Copy {
filter_fn_one(move |route| {
// The authority can be sent by clients in various ways:
//
// 1) in the "target URI"
// a) serialized in the start line (HTTP/1.1 proxy requests)
// b) serialized in `:authority` pseudo-header (HTTP/2 generated - "SHOULD")
// 2) in the `Host` header (HTTP/1.1 origin requests, HTTP/2 converted)
//
// Hyper transparently handles 1a/1b, but not 2, so we must look at both.
let from_uri = route.uri().authority();
let name = "host";
let from_header = route.headers()
.get(name)
.map(|value|
// Header present, parse it
value.to_str().map_err(|_| reject::invalid_header(name))
.and_then(|value| Authority::from_str(value).map_err(|_| reject::invalid_header(name)))
);
future::ready(match (from_uri, from_header) {
// no authority in the request (HTTP/1.0 or non-conforming)
(None, None) => Ok(None),
// authority specified in either or both matching
(Some(a), None) => Ok(Some(a.clone())),
(None, Some(Ok(a))) => Ok(Some(a)),
(Some(a), Some(Ok(b))) if *a == b => Ok(Some(b)),
// mismatch
(Some(_), Some(Ok(_))) => Err(reject::invalid_header(name)),
// parse error
(_, Some(Err(r))) => Err(r),
})
})
}

197
warp/src/filters/log.rs Normal file
View file

@ -0,0 +1,197 @@
//! Logger Filters
use std::fmt;
use std::net::SocketAddr;
use std::time::{Duration, Instant};
use http::{self, StatusCode};
use crate::filter::{Filter, WrapSealed};
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::route::Route;
use self::internal::WithLog;
/// Create a wrapping filter with the specified `name` as the `target`.
///
/// This uses the default access logging format, and log records produced
/// will have their `target` set to `name`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // If using something like `pretty_env_logger`,
/// // view logs by setting `RUST_LOG=example::api`.
/// let log = warp::log("example::api");
/// let route = warp::any()
/// .map(warp::reply)
/// .with(log);
/// ```
pub fn log(name: &'static str) -> Log<impl Fn(Info<'_>) + Copy> {
let func = move |info: Info<'_>| {
log::info!(
target : name, "{} \"{} {} {:?}\" {} \"{}\" \"{}\" {:?}", OptFmt(info.route
.remote_addr()), info.method(), info.path(), info.route.version(), info
.status().as_u16(), OptFmt(info.referer()), OptFmt(info.user_agent()), info
.elapsed(),
);
};
Log { func }
}
/// Create a wrapping filter that receives `warp::log::Info`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let log = warp::log::custom(|info| {
/// // Use a log macro, or slog, or println, or whatever!
/// eprintln!(
/// "{} {} {}",
/// info.method(),
/// info.path(),
/// info.status(),
/// );
/// });
/// let route = warp::any()
/// .map(warp::reply)
/// .with(log);
/// ```
pub fn custom<F>(func: F) -> Log<F>
where
F: Fn(Info<'_>),
{
loop {}
}
/// Decorates a [`Filter`](crate::Filter) to log requests and responses.
#[derive(Clone, Copy, Debug)]
pub struct Log<F> {
func: F,
}
/// Information about the request/response that can be used to prepare log lines.
#[allow(missing_debug_implementations)]
pub struct Info<'a> {
route: &'a Route,
start: Instant,
status: StatusCode,
}
impl<FN, F> WrapSealed<F> for Log<FN>
where
FN: Fn(Info<'_>) + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Wrapped = WithLog<FN, F>;
fn wrap(&self, filter: F) -> Self::Wrapped {
loop {}
}
}
impl<'a> Info<'a> {
/// View the remote `SocketAddr` of the request.
pub fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
/// View the `http::Method` of the request.
pub fn method(&self) -> &http::Method {
loop {}
}
/// View the URI path of the request.
pub fn path(&self) -> &str {
loop {}
}
/// View the `http::Version` of the request.
pub fn version(&self) -> http::Version {
loop {}
}
/// View the `http::StatusCode` of the response.
pub fn status(&self) -> http::StatusCode {
loop {}
}
/// View the referer of the request.
pub fn referer(&self) -> Option<&str> {
loop {}
}
/// View the user agent of the request.
pub fn user_agent(&self) -> Option<&str> {
loop {}
}
/// View the `Duration` that elapsed for the request.
pub fn elapsed(&self) -> Duration {
loop {}
}
/// View the host of the request
pub fn host(&self) -> Option<&str> {
loop {}
}
/// Access the full headers of the request
pub fn request_headers(&self) -> &http::HeaderMap {
loop {}
}
}
struct OptFmt<T>(Option<T>);
impl<T: fmt::Display> fmt::Display for OptFmt<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
mod internal {
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Instant;
use futures_util::{TryFuture};
use pin_project::pin_project;
use super::{Info, Log};
use crate::filter::{Filter, FilterBase, Internal};
use crate::reject::IsReject;
use crate::reply::{Reply, Response};
#[allow(missing_debug_implementations)]
pub struct Logged(pub(super) Response);
impl Reply for Logged {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct WithLog<FN, F> {
pub(super) filter: F,
pub(super) log: Log<FN>,
}
impl<FN, F> FilterBase for WithLog<FN, F>
where
FN: Fn(Info<'_>) + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Extract = (Logged,);
type Error = F::Error;
type Future = WithLogFuture<FN, F::Future>;
fn filter(&self, _: Internal) -> Self::Future {
loop {}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
pub struct WithLogFuture<FN, F> {
log: Log<FN>,
#[pin]
future: F,
started: Instant,
}
impl<FN, F> Future for WithLogFuture<FN, F>
where
FN: Fn(Info<'_>),
F: TryFuture,
F::Ok: Reply,
F::Error: IsReject,
{
type Output = Result<(Logged,), F::Error>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
loop {}
}
}
}

135
warp/src/filters/method.rs Normal file
View file

@ -0,0 +1,135 @@
//! HTTP Method filters.
//!
//! The filters deal with the HTTP Method part of a request. Several here will
//! match the request `Method`, and if not matched, will reject the request
//! with a `405 Method Not Allowed`.
//!
//! There is also [`warp::method()`](method), which never rejects
//! a request, and just extracts the method to be used in your filter chains.
use futures_util::future;
use http::Method;
use crate::filter::{filter_fn, filter_fn_one, Filter, One};
use crate::reject::Rejection;
use std::convert::Infallible;
/// Create a `Filter` that requires the request method to be `GET`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let get_only = warp::get().map(warp::reply);
/// ```
pub fn get() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::GET)
}
/// Create a `Filter` that requires the request method to be `POST`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let post_only = warp::post().map(warp::reply);
/// ```
pub fn post() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::POST)
}
/// Create a `Filter` that requires the request method to be `PUT`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let put_only = warp::put().map(warp::reply);
/// ```
pub fn put() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::PUT)
}
/// Create a `Filter` that requires the request method to be `DELETE`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let delete_only = warp::delete().map(warp::reply);
/// ```
pub fn delete() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::DELETE)
}
/// Create a `Filter` that requires the request method to be `HEAD`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let head_only = warp::head().map(warp::reply);
/// ```
pub fn head() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::HEAD)
}
/// Create a `Filter` that requires the request method to be `OPTIONS`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let options_only = warp::options().map(warp::reply);
/// ```
pub fn options() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::OPTIONS)
}
/// Create a `Filter` that requires the request method to be `PATCH`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let patch_only = warp::patch().map(warp::reply);
/// ```
pub fn patch() -> impl Filter<Extract = (), Error = Rejection> + Copy {
method_is(|| &Method::PATCH)
}
/// Extract the `Method` from the request.
///
/// This never rejects a request.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::method()
/// .map(|method| {
/// format!("You sent a {} request!", method)
/// });
/// ```
pub fn method() -> impl Filter<Extract = One<Method>, Error = Infallible> + Copy {
filter_fn_one(|route| future::ok::<_, Infallible>(route.method().clone()))
}
fn method_is<F>(func: F) -> impl Filter<Extract = (), Error = Rejection> + Copy
where
F: Fn() -> &'static Method + Copy,
{
filter_fn(move |route| {
let method = func();
tracing::trace!("method::{:?}?: {:?}", method, route.method());
if route.method() == method {
future::ok(())
} else {
future::err(crate::reject::method_not_allowed())
}
})
}
#[cfg(test)]
mod tests {
#[test]
fn method_size_of() {
loop {}
}
}

29
warp/src/filters/mod.rs Normal file
View file

@ -0,0 +1,29 @@
//! Built-in Filters
//!
//! This module mostly serves as documentation to group together the list of
//! built-in filters. Most of these are available at more convenient paths.
pub mod addr;
pub mod any;
pub mod body;
#[cfg(any(feature = "compression-brotli", feature = "compression-gzip"))]
pub mod compression;
pub mod cookie;
pub mod cors;
pub mod ext;
pub mod fs;
pub mod header;
pub mod host;
pub mod log;
pub mod method;
#[cfg(feature = "multipart")]
pub mod multipart;
pub mod path;
pub mod query;
pub mod reply;
pub mod sse;
pub mod trace;
#[cfg(feature = "websocket")]
pub mod ws;
pub use crate::filter::BoxedFilter;

View file

@ -0,0 +1,116 @@
//! Multipart body filters
//!
//! Filters that extract a multipart body for a route.
use std::fmt;
use std::future::Future;
use std::io::{Cursor, Read};
use std::pin::Pin;
use std::task::{Context, Poll};
use bytes::{Buf, Bytes};
use futures_util::{future, Stream};
use headers::ContentType;
use mime::Mime;
use multipart::server::Multipart;
use crate::filter::{Filter, FilterBase, Internal};
use crate::reject::{self, Rejection};
const DEFAULT_FORM_DATA_MAX_LENGTH: u64 = 1024 * 1024 * 2;
/// A `Filter` to extract a `multipart/form-data` body from a request.
///
/// Create with the `warp::multipart::form()` function.
#[derive(Debug, Clone)]
pub struct FormOptions {
max_length: u64,
}
/// A `Stream` of multipart/form-data `Part`s.
///
/// Extracted with a `warp::multipart::form` filter.
pub struct FormData {
inner: Multipart<Cursor<::bytes::Bytes>>,
}
/// A single "part" of a multipart/form-data body.
///
/// Yielded from the `FormData` stream.
pub struct Part {
name: String,
filename: Option<String>,
content_type: Option<String>,
data: Option<Vec<u8>>,
}
/// Create a `Filter` to extract a `multipart/form-data` body from a request.
///
/// The extracted `FormData` type is a `Stream` of `Part`s, and each `Part`
/// in turn is a `Stream` of bytes.
pub fn form() -> FormOptions {
loop {}
}
impl FormOptions {
/// Set the maximum byte length allowed for this body.
///
/// Defaults to 2MB.
pub fn max_length(mut self, max: u64) -> Self {
loop {}
}
}
type FormFut = Pin<Box<dyn Future<Output = Result<(FormData,), Rejection>> + Send>>;
impl FilterBase for FormOptions {
type Extract = (FormData,);
type Error = Rejection;
type Future = FormFut;
fn filter(&self, _: Internal) -> Self::Future {
loop {}
}
}
impl fmt::Debug for FormData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl Stream for FormData {
type Item = Result<Part, crate::Error>;
fn poll_next(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}
impl Part {
/// Get the name of this part.
pub fn name(&self) -> &str {
loop {}
}
/// Get the filename of this part, if present.
pub fn filename(&self) -> Option<&str> {
loop {}
}
/// Get the content-type of this part, if present.
pub fn content_type(&self) -> Option<&str> {
loop {}
}
/// Asynchronously get some of the data for this `Part`.
pub async fn data(&mut self) -> Option<Result<impl Buf, crate::Error>> {
loop {}
}
/// Convert this `Part` into a `Stream` of `Buf`s.
pub fn stream(self) -> impl Stream<Item = Result<impl Buf, crate::Error>> {
loop {}
}
fn take_data(&mut self) -> Option<Result<Bytes, crate::Error>> {
loop {}
}
}
impl fmt::Debug for Part {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
struct PartStream(Part);
impl Stream for PartStream {
type Item = Result<Bytes, crate::Error>;
fn poll_next(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}

527
warp/src/filters/path.rs Normal file
View file

@ -0,0 +1,527 @@
//! Path Filters
//!
//! The filters here work on the "path" of requests.
//!
//! - [`path`](./fn.path.html) matches a specific segment, like `/foo`.
//! - [`param`](./fn.param.html) tries to parse a segment into a type, like `/:u16`.
//! - [`end`](./fn.end.html) matches when the path end is found.
//! - [`path!`](../../macro.path.html) eases combining multiple `path` and `param` filters.
//!
//! # Routing
//!
//! Routing in warp is simple yet powerful.
//!
//! First up, matching a single segment:
//!
//! ```
//! use warp::Filter;
//!
//! // GET /hi
//! let hi = warp::path("hi").map(|| {
//! "Hello, World!"
//! });
//! ```
//!
//! How about multiple segments? It's easiest with the `path!` macro:
//!
//! ```
//! # use warp::Filter;
//! // GET /hello/from/warp
//! let hello_from_warp = warp::path!("hello" / "from" / "warp").map(|| {
//! "Hello from warp!"
//! });
//! ```
//!
//! Neat! But how do I handle **parameters** in paths?
//!
//! ```
//! # use warp::Filter;
//! // GET /sum/:u32/:u32
//! let sum = warp::path!("sum" / u32 / u32).map(|a, b| {
//! format!("{} + {} = {}", a, b, a + b)
//! });
//! ```
//!
//! In fact, any type that implements `FromStr` can be used, in any order:
//!
//! ```
//! # use warp::Filter;
//! // 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!
//!
//! ```
//! # use warp::Filter;
//! # let sum = warp::any().map(warp::reply);
//! # let times = sum.clone();
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let math = warp::path("math");
//! let math_sum = math.and(sum);
//! let math_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.
//!
//! ```
//! # use warp::Filter;
//! // GET /bye/:string
//! let bye = warp::path("bye")
//! .and(warp::path::param())
//! .map(|name: String| {
//! format!("Good bye, {}!", name)
//! });
//! ```
//!
//! Ah, so, 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`.
//!
//!
//! ```
//! # use warp::Filter;
//! # let sum = warp::path("sum");
//! # let times = warp::path("times");
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let math = warp::path("math")
//! .and(sum.or(times));
//! ```
//!
//! It turns out, using `or` is how you combine everything together into a
//! single API.
//!
//! ```
//! # use warp::Filter;
//! # let hi = warp::path("hi");
//! # let hello_from_warp = hi.clone();
//! # let bye = hi.clone();
//! # let math = hi.clone();
//! // GET /hi
//! // GET /hello/from/warp
//! // GET /bye/:string
//! // GET /math/sum/:u32/:u32
//! // GET /math/:u16/times/:u16
//! let routes = hi
//! .or(hello_from_warp)
//! .or(bye)
//! .or(math);
//! ```
//!
//! Note that you will generally want path filters to come **before** other filters
//! like `body` or `headers`. If a different type of filter comes first, a request
//! with an invalid body for route `/right-path-wrong-body` may try matching against `/wrong-path`
//! and return the error from `/wrong-path` instead of the correct body-related error.
use std::convert::Infallible;
use std::fmt;
use std::str::FromStr;
use futures_util::future;
use http::uri::PathAndQuery;
use self::internal::Opaque;
use crate::filter::{filter_fn, one, Filter, FilterBase, Internal, One, Tuple};
use crate::reject::{self, Rejection};
use crate::route::Route;
/// Create an exact match path segment `Filter`.
///
/// This will try to match exactly to the current request path segment.
///
/// # Note
///
/// - [`end()`](./fn.end.html) should be used to match the end of a path to avoid having
/// filters for shorter paths like `/math` unintentionally match a longer
/// path such as `/math/sum`
/// - Path-related filters should generally come **before** other types of filters, such
/// as those checking headers or body types. Including those other filters before
/// the path checks may result in strange errors being returned because a given request
/// does not match the parameters for a completely separate route.
///
/// # Panics
///
/// Exact path filters cannot be empty, or contain slashes.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Matches '/hello'
/// let hello = warp::path("hello")
/// .map(|| "Hello, World!");
/// ```
pub fn path<P>(p: P) -> Exact<Opaque<P>>
where
P: AsRef<str>,
{
loop {}
}
/// A `Filter` matching an exact path segment.
///
/// Constructed from `path()` or `path!()`.
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct Exact<P>(P);
impl<P> FilterBase for Exact<P>
where
P: AsRef<str>,
{
type Extract = ();
type Error = Rejection;
type Future = future::Ready<Result<Self::Extract, Self::Error>>;
#[inline]
fn filter(&self, _: Internal) -> Self::Future {
loop {}
}
}
/// Matches the end of a route.
///
/// Note that _not_ including `end()` may result in shorter paths like
/// `/math` unintentionally matching `/math/sum`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Matches '/'
/// let hello = warp::path::end()
/// .map(|| "Hello, World!");
/// ```
pub fn end() -> impl Filter<Extract = (), Error = Rejection> + Copy {
filter_fn(move |route| {
if route.path().is_empty() {
future::ok(())
} else {
future::err(reject::not_found())
}
})
}
/// Extract a parameter from a path segment.
///
/// This will try to parse a value from the current request path
/// segment, and if successful, the value is returned as the `Filter`'s
/// "extracted" value.
///
/// If the value could not be parsed, rejects with a `404 Not Found`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path::param()
/// .map(|id: u32| {
/// format!("You asked for /{}", id)
/// });
/// ```
pub fn param<T: FromStr + Send + 'static>() -> impl Filter<
Extract = One<T>,
Error = Rejection,
> + Copy {
filter_segment(|seg| {
tracing::trace!("param?: {:?}", seg);
if seg.is_empty() {
return Err(reject::not_found());
}
T::from_str(seg).map(one).map_err(|_| reject::not_found())
})
}
/// Extract the unmatched tail of the path.
///
/// This will return a `Tail`, which allows access to the rest of the path
/// that previous filters have not already matched.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("foo")
/// .and(warp::path::tail())
/// .map(|tail| {
/// // GET /foo/bar/baz would return "bar/baz".
/// format!("The tail after foo is {:?}", tail)
/// });
/// ```
pub fn tail() -> impl Filter<Extract = One<Tail>, Error = Infallible> + Copy {
filter_fn(move |route| {
let path = path_and_query(route);
let idx = route.matched_path_index();
let end = path.path().len() - idx;
route.set_unmatched_path(end);
future::ok(one(Tail { path, start_index: idx }))
})
}
/// Represents the tail part of a request path, returned by the [`tail()`] filter.
pub struct Tail {
path: PathAndQuery,
start_index: usize,
}
impl Tail {
/// Get the `&str` representation of the remaining path.
pub fn as_str(&self) -> &str {
loop {}
}
}
impl fmt::Debug for Tail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
/// Peek at the unmatched tail of the path, without affecting the matched path.
///
/// This will return a `Peek`, which allows access to the rest of the path
/// that previous filters have not already matched. This differs from `tail`
/// in that `peek` will **not** set the entire path as matched.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("foo")
/// .and(warp::path::peek())
/// .map(|peek| {
/// // GET /foo/bar/baz would return "bar/baz".
/// format!("The path after foo is {:?}", peek)
/// });
/// ```
pub fn peek() -> impl Filter<Extract = One<Peek>, Error = Infallible> + Copy {
filter_fn(move |route| {
let path = path_and_query(route);
let idx = route.matched_path_index();
future::ok(one(Peek { path, start_index: idx }))
})
}
/// Represents the tail part of a request path, returned by the [`peek()`] filter.
pub struct Peek {
path: PathAndQuery,
start_index: usize,
}
impl Peek {
/// Get the `&str` representation of the remaining path.
pub fn as_str(&self) -> &str {
loop {}
}
/// Get an iterator over the segments of the peeked path.
pub fn segments(&self) -> impl Iterator<Item = &str> {
self.as_str().split('/').filter(|seg| !seg.is_empty())
}
}
impl fmt::Debug for Peek {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
/// Returns the full request path, irrespective of other filters.
///
/// This will return a `FullPath`, which can be stringified to return the
/// full path of the request.
///
/// This is more useful in generic pre/post-processing filters, and should
/// probably not be used for request matching/routing.
///
/// # Example
///
/// ```
/// use warp::{Filter, path::FullPath};
/// use std::{collections::HashMap, sync::{Arc, Mutex}};
///
/// let counts = Arc::new(Mutex::new(HashMap::new()));
/// let access_counter = warp::path::full()
/// .map(move |path: FullPath| {
/// let mut counts = counts.lock().unwrap();
///
/// *counts.entry(path.as_str().to_string())
/// .and_modify(|c| *c += 1)
/// .or_insert(0)
/// });
///
/// let route = warp::path("foo")
/// .and(warp::path("bar"))
/// .and(access_counter)
/// .map(|count| {
/// format!("This is the {}th visit to this URL!", count)
/// });
/// ```
pub fn full() -> impl Filter<Extract = One<FullPath>, Error = Infallible> + Copy {
filter_fn(move |route| future::ok(one(FullPath(path_and_query(route)))))
}
/// Represents the full request path, returned by the [`full()`] filter.
pub struct FullPath(PathAndQuery);
impl FullPath {
/// Get the `&str` representation of the request path.
pub fn as_str(&self) -> &str {
loop {}
}
}
impl fmt::Debug for FullPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
fn filter_segment<F, U>(func: F) -> impl Filter<Extract = U, Error = Rejection> + Copy
where
F: Fn(&str) -> Result<U, Rejection> + Copy,
U: Tuple + Send + 'static,
{
filter_fn(move |route| future::ready(with_segment(route, func)))
}
fn with_segment<F, U>(route: &mut Route, func: F) -> Result<U, Rejection>
where
F: Fn(&str) -> Result<U, Rejection>,
{
loop {}
}
fn path_and_query(route: &Route) -> PathAndQuery {
loop {}
}
/// Convenient way to chain multiple path filters together.
///
/// Any number of either type identifiers or string expressions can be passed,
/// each separated by a forward slash (`/`). Strings will be used to match
/// path segments exactly, and type identifiers are used just like
/// [`param`](crate::path::param) filters.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Match `/sum/:a/:b`
/// let route = warp::path!("sum" / u32 / u32)
/// .map(|a, b| {
/// format!("{} + {} = {}", a, b, a + b)
/// });
/// ```
///
/// The equivalent filter chain without using the `path!` macro looks this:
///
/// ```
/// use warp::Filter;
///
/// let route = warp::path("sum")
/// .and(warp::path::param::<u32>())
/// .and(warp::path::param::<u32>())
/// .and(warp::path::end())
/// .map(|a, b| {
/// format!("{} + {} = {}", a, b, a + b)
/// });
/// ```
///
/// # Path Prefixes
///
/// The `path!` macro automatically assumes the path should include an `end()`
/// filter. To build up a path filter *prefix*, such that the `end()` isn't
/// included, use the `/ ..` syntax.
///
///
/// ```
/// use warp::Filter;
///
/// let prefix = warp::path!("math" / "sum" / ..);
///
/// let sum = warp::path!(u32 / u32)
/// .map(|a, b| {
/// format!("{} + {} = {}", a, b, a + b)
/// });
///
/// let help = warp::path::end()
/// .map(|| "This API returns the sum of two u32's");
///
/// let api = prefix.and(sum.or(help));
/// ```
#[macro_export]
macro_rules! path {
($($pieces:tt)*) => {
{ $crate ::__internal_path!(@ start $($pieces)*) }
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __internal_path {
(@ start) => {
$crate ::path::end()
};
(@ start ..) => {
{ compile_error!("'..' cannot be the only segment") }
};
(@ start $first:tt $(/ $tail:tt)*) => {
{ $crate ::__internal_path!(@ munch $crate ::any(); [$first] [$(/ $tail)*]) }
};
(@ munch $sum:expr; [$cur:tt] [/ $next:tt $(/ $tail:tt)*]) => {
{ $crate ::__internal_path!(@ munch $crate ::Filter::and($sum, $crate
::__internal_path!(@ segment $cur)); [$next] [$(/ $tail)*]) }
};
(@ munch $sum:expr; [$cur:tt] []) => {
{ $crate ::__internal_path!(@ last $sum; $cur) }
};
(@ last $sum:expr; ..) => {
$sum
};
(@ last $sum:expr; $end:tt) => {
$crate ::Filter::and($crate ::Filter::and($sum, $crate ::__internal_path!(@
segment $end)), $crate ::path::end())
};
(@ segment ..) => {
compile_error!("'..' must be the last segment")
};
(@ segment $param:ty) => {
$crate ::path::param::<$param > ()
};
(@ segment $s:literal) => {
{ #[derive(Clone, Copy)] struct __StaticPath; impl ::std::convert::AsRef < str >
for __StaticPath { fn as_ref(& self) -> & str { static S : & str = $s; S } }
$crate ::path(__StaticPath) }
};
}
/// ```compile_fail
/// warp::path!("foo" / .. / "bar");
/// ```
///
/// ```compile_fail
/// warp::path!(.. / "bar");
/// ```
///
/// ```compile_fail
/// warp::path!("foo" ..);
/// ```
///
/// ```compile_fail
/// warp::path!("foo" / .. /);
/// ```
///
/// ```compile_fail
/// warp::path!(..);
/// ```
fn _path_macro_compile_fail() {}
mod internal {
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct Opaque<T>(pub(super) T);
impl<T: AsRef<str>> AsRef<str> for Opaque<T> {
#[inline]
fn as_ref(&self) -> &str {
loop {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_path_exact_size() {
loop {}
}
}

91
warp/src/filters/query.rs Normal file
View file

@ -0,0 +1,91 @@
//! Query Filters
use futures_util::future;
use serde::de::DeserializeOwned;
use serde_urlencoded;
use crate::filter::{filter_fn_one, Filter, One};
use crate::reject::{self, Rejection};
/// Creates a `Filter` that decodes query parameters to the type `T`.
///
/// If cannot decode into a `T`, the request is rejected with a `400 Bad Request`.
///
/// # Example
///
/// ```
/// use std::collections::HashMap;
/// use warp::{
/// http::Response,
/// Filter,
/// };
///
/// let route = warp::any()
/// .and(warp::query::<HashMap<String, String>>())
/// .map(|map: HashMap<String, String>| {
/// let mut response: Vec<String> = Vec::new();
/// for (key, value) in map.into_iter() {
/// response.push(format!("{}={}", key, value))
/// }
/// Response::builder().body(response.join(";"))
/// });
/// ```
///
/// You can define your custom query object and deserialize with [Serde][Serde]. Ensure to include
/// the crate in your dependencies before usage.
///
/// ```
/// use serde_derive::{Deserialize, Serialize};
/// use std::collections::HashMap;
/// use warp::{
/// http::Response,
/// Filter,
/// };
///
/// #[derive(Serialize, Deserialize)]
/// struct FooQuery {
/// foo: Option<String>,
/// bar: u8,
/// }
///
/// let route = warp::any()
/// .and(warp::query::<FooQuery>())
/// .map(|q: FooQuery| {
/// if let Some(foo) = q.foo {
/// Response::builder().body(format!("foo={}", foo))
/// } else {
/// Response::builder().body(format!("bar={}", q.bar))
/// }
/// });
/// ```
///
/// For more examples, please take a look at [examples/query_string.rs](https://github.com/seanmonstar/warp/blob/master/examples/query_string.rs).
///
/// [Serde]: https://docs.rs/serde
pub fn query<T: DeserializeOwned + Send + 'static>(
) -> impl Filter<Extract = One<T>, Error = Rejection> + Copy {
filter_fn_one(|route| {
let query_string = route.query().unwrap_or_else(|| {
tracing::debug!("route was called without a query string, defaulting to empty");
""
});
let query_encoded = serde_urlencoded::from_str(query_string).map_err(|e| {
tracing::debug!("failed to decode query string '{}': {:?}", query_string, e);
reject::invalid_query()
});
future::ready(query_encoded)
})
}
/// Creates a `Filter` that returns the raw query string as type String.
pub fn raw() -> impl Filter<Extract = One<String>, Error = Rejection> + Copy {
filter_fn_one(|route| {
let route = route
.query()
.map(|q| q.to_owned())
.map(Ok)
.unwrap_or_else(|| Err(reject::invalid_query()));
future::ready(route)
})
}

201
warp/src/filters/reply.rs Normal file
View file

@ -0,0 +1,201 @@
//! Reply Filters
//!
//! These "filters" behave a little differently than the rest. Instead of
//! being used directly on requests, these filters "wrap" other filters.
//!
//!
//! ## Wrapping a `Filter` (`with`)
//!
//! ```
//! use warp::Filter;
//!
//! let with_server = warp::reply::with::header("server", "warp");
//!
//! let route = warp::any()
//! .map(warp::reply)
//! .with(with_server);
//! ```
//!
//! Wrapping allows adding in conditional logic *before* the request enters
//! the inner filter (though the `with::header` wrapper does not).
use std::convert::TryFrom;
use std::sync::Arc;
use http::header::{HeaderMap, HeaderName, HeaderValue};
use self::sealed::{WithDefaultHeader_, WithHeader_, WithHeaders_};
use crate::filter::{Filter, Map, WrapSealed};
use crate::reply::Reply;
/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Always set `foo: bar` header.
/// let route = warp::any()
/// .map(warp::reply)
/// .with(warp::reply::with::header("foo", "bar"));
/// ```
pub fn header<K, V>(name: K, value: V) -> WithHeader
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
loop {}
}
/// Wrap a [`Filter`](crate::Filter) that adds multiple headers to the reply.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
///
/// ```
/// use warp::http::header::{HeaderMap, HeaderValue};
/// use warp::Filter;
///
/// let mut headers = HeaderMap::new();
/// headers.insert("server", HeaderValue::from_static("wee/0"));
/// headers.insert("foo", HeaderValue::from_static("bar"));
///
/// // Always set `server: wee/0` and `foo: bar` headers.
/// let route = warp::any()
/// .map(warp::reply)
/// .with(warp::reply::with::headers(headers));
/// ```
pub fn headers(headers: HeaderMap) -> WithHeaders {
loop {}
}
/// Wrap a [`Filter`](crate::Filter) that adds a header to the reply, if they
/// aren't already set.
///
/// # Note
///
/// This **only** adds a header if the underlying filter is successful, and
/// returns a [`Reply`](Reply). If the underlying filter was rejected, the
/// header is not added.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // Set `server: warp` if not already set.
/// let route = warp::any()
/// .map(warp::reply)
/// .with(warp::reply::with::default_header("server", "warp"));
/// ```
pub fn default_header<K, V>(name: K, value: V) -> WithDefaultHeader
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
loop {}
}
/// Wrap a `Filter` to always set a header.
#[derive(Clone, Debug)]
pub struct WithHeader {
name: HeaderName,
value: HeaderValue,
}
impl<F, R> WrapSealed<F> for WithHeader
where
F: Filter<Extract = (R,)>,
R: Reply,
{
type Wrapped = Map<F, WithHeader_>;
fn wrap(&self, filter: F) -> Self::Wrapped {
loop {}
}
}
/// Wrap a `Filter` to always set multiple headers.
#[derive(Clone, Debug)]
pub struct WithHeaders {
headers: Arc<HeaderMap>,
}
impl<F, R> WrapSealed<F> for WithHeaders
where
F: Filter<Extract = (R,)>,
R: Reply,
{
type Wrapped = Map<F, WithHeaders_>;
fn wrap(&self, filter: F) -> Self::Wrapped {
loop {}
}
}
/// Wrap a `Filter` to set a header if it is not already set.
#[derive(Clone, Debug)]
pub struct WithDefaultHeader {
name: HeaderName,
value: HeaderValue,
}
impl<F, R> WrapSealed<F> for WithDefaultHeader
where
F: Filter<Extract = (R,)>,
R: Reply,
{
type Wrapped = Map<F, WithDefaultHeader_>;
fn wrap(&self, filter: F) -> Self::Wrapped {
loop {}
}
}
fn assert_name_and_value<K, V>(name: K, value: V) -> (HeaderName, HeaderValue)
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
loop {}
}
mod sealed {
use super::{WithDefaultHeader, WithHeader, WithHeaders};
use crate::generic::{Func, One};
use crate::reply::{Reply, Reply_};
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct WithHeader_ {
pub(super) with: WithHeader,
}
impl<R: Reply> Func<One<R>> for WithHeader_ {
type Output = Reply_;
fn call(&self, args: One<R>) -> Self::Output {
loop {}
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct WithHeaders_ {
pub(super) with: WithHeaders,
}
impl<R: Reply> Func<One<R>> for WithHeaders_ {
type Output = Reply_;
fn call(&self, args: One<R>) -> Self::Output {
loop {}
}
}
#[derive(Clone)]
#[allow(missing_debug_implementations)]
pub struct WithDefaultHeader_ {
pub(super) with: WithDefaultHeader,
}
impl<R: Reply> Func<One<R>> for WithDefaultHeader_ {
type Output = Reply_;
fn call(&self, args: One<R>) -> Self::Output {
loop {}
}
}
}

380
warp/src/filters/sse.rs Normal file
View file

@ -0,0 +1,380 @@
//! Server-Sent Events (SSE)
//!
//! # Example
//!
//! ```
//!
//! use std::time::Duration;
//! use std::convert::Infallible;
//! use warp::{Filter, sse::Event};
//! use futures_util::{stream::iter, Stream};
//!
//! fn sse_events() -> impl Stream<Item = Result<Event, Infallible>> {
//! iter(vec![
//! Ok(Event::default().data("unnamed event")),
//! Ok(
//! Event::default().event("chat")
//! .data("chat message")
//! ),
//! Ok(
//! Event::default().id(13.to_string())
//! .event("chat")
//! .data("other chat message\nwith next line")
//! .retry(Duration::from_millis(5000))
//! )
//! ])
//! }
//!
//! let app = warp::path("push-notifications")
//! .and(warp::get())
//! .map(|| {
//! warp::sse::reply(warp::sse::keep_alive().stream(sse_events()))
//! });
//! ```
//!
//! Each field already is event which can be sent to client.
//! The events with multiple fields can be created by combining fields using tuples.
//!
//! See also the [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) API,
//! which specifies the expected behavior of Server Sent Events.
//!
use serde::Serialize;
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{self, Write};
use std::pin::Pin;
use std::str::FromStr;
use std::task::{Context, Poll};
use std::time::Duration;
use futures_util::{Stream, TryStream};
use pin_project::pin_project;
use serde_json::{self, Error};
use tokio::time::{self, Sleep};
use self::sealed::SseError;
use super::header;
use crate::filter::One;
use crate::reply::Response;
use crate::{Filter, Rejection, Reply};
#[derive(Debug)]
enum DataType {
Text(String),
Json(String),
}
/// Server-sent event
#[derive(Default, Debug)]
pub struct Event {
id: Option<String>,
data: Option<DataType>,
event: Option<String>,
comment: Option<String>,
retry: Option<Duration>,
}
impl Event {
/// Set Server-sent event data
/// data field(s) ("data:<content>")
pub fn data<T: Into<String>>(mut self, data: T) -> Event {
loop {}
}
/// Set Server-sent event data
/// data field(s) ("data:<content>")
pub fn json_data<T: Serialize>(mut self, data: T) -> Result<Event, Error> {
loop {}
}
/// Set Server-sent event comment
/// Comment field (":<comment-text>")
pub fn comment<T: Into<String>>(mut self, comment: T) -> Event {
loop {}
}
/// Set Server-sent event event
/// Event name field ("event:<event-name>")
pub fn event<T: Into<String>>(mut self, event: T) -> Event {
loop {}
}
/// Set Server-sent event retry
/// Retry timeout field ("retry:<timeout>")
pub fn retry(mut self, duration: Duration) -> Event {
loop {}
}
/// Set Server-sent event id
/// Identifier field ("id:<identifier>")
pub fn id<T: Into<String>>(mut self, id: T) -> Event {
loop {}
}
}
impl fmt::Display for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
/// Gets the optional last event id from request.
/// Typically this identifier represented as number or string.
///
/// ```
/// let app = warp::sse::last_event_id::<u32>();
///
/// // The identifier is present
/// async {
/// assert_eq!(
/// warp::test::request()
/// .header("Last-Event-ID", "12")
/// .filter(&app)
/// .await
/// .unwrap(),
/// Some(12)
/// );
///
/// // The identifier is missing
/// assert_eq!(
/// warp::test::request()
/// .filter(&app)
/// .await
/// .unwrap(),
/// None
/// );
///
/// // The identifier is not a valid
/// assert!(
/// warp::test::request()
/// .header("Last-Event-ID", "abc")
/// .filter(&app)
/// .await
/// .is_err(),
/// );
///};
/// ```
pub fn last_event_id<T>() -> impl Filter<
Extract = One<Option<T>>,
Error = Rejection,
> + Copy
where
T: FromStr + Send + Sync + 'static,
{
header::optional("last-event-id")
}
/// Server-sent events reply
///
/// This function converts stream of server events into a `Reply` with:
///
/// - Status of `200 OK`
/// - Header `content-type: text/event-stream`
/// - Header `cache-control: no-cache`.
///
/// # Example
///
/// ```
///
/// use std::time::Duration;
/// use futures_util::Stream;
/// use futures_util::stream::iter;
/// use std::convert::Infallible;
/// use warp::{Filter, sse::Event};
/// use serde_derive::Serialize;
///
/// #[derive(Serialize)]
/// struct Msg {
/// from: u32,
/// text: String,
/// }
///
/// fn event_stream() -> impl Stream<Item = Result<Event, Infallible>> {
/// iter(vec![
/// // Unnamed event with data only
/// Ok(Event::default().data("payload")),
/// // Named event with ID and retry timeout
/// Ok(
/// Event::default().data("other message\nwith next line")
/// .event("chat")
/// .id(1.to_string())
/// .retry(Duration::from_millis(15000))
/// ),
/// // Event with JSON data
/// Ok(
/// Event::default().id(2.to_string())
/// .json_data(Msg {
/// from: 2,
/// text: "hello".into(),
/// }).unwrap(),
/// )
/// ])
/// }
///
/// async {
/// let app = warp::path("sse").and(warp::get()).map(|| {
/// warp::sse::reply(event_stream())
/// });
///
/// let res = warp::test::request()
/// .method("GET")
/// .header("Connection", "Keep-Alive")
/// .path("/sse")
/// .reply(&app)
/// .await
/// .into_body();
///
/// assert_eq!(
/// res,
/// r#"data:payload
///
/// event:chat
/// data:other message
/// data:with next line
/// id:1
/// retry:15000
///
/// data:{"from":2,"text":"hello"}
/// id:2
///
/// "#
/// );
/// };
/// ```
pub fn reply<S>(event_stream: S) -> impl Reply
where
S: TryStream<Ok = Event> + Send + 'static,
S::Error: StdError + Send + Sync + 'static,
{
SseReply { event_stream }
}
#[allow(missing_debug_implementations)]
struct SseReply<S> {
event_stream: S,
}
impl<S> Reply for SseReply<S>
where
S: TryStream<Ok = Event> + Send + 'static,
S::Error: StdError + Send + Sync + 'static,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
/// Configure the interval between keep-alive messages, the content
/// of each message, and the associated stream.
#[derive(Debug)]
pub struct KeepAlive {
comment_text: Cow<'static, str>,
max_interval: Duration,
}
impl KeepAlive {
/// Customize the interval between keep-alive messages.
///
/// Default is 15 seconds.
pub fn interval(mut self, time: Duration) -> Self {
loop {}
}
/// Customize the text of the keep-alive message.
///
/// Default is an empty comment.
pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
loop {}
}
/// Wrap an event stream with keep-alive functionality.
///
/// See [`keep_alive`](keep_alive) for more.
pub fn stream<S>(
self,
event_stream: S,
) -> impl TryStream<
Ok = Event,
Error = impl StdError + Send + Sync + 'static,
> + Send + 'static
where
S: TryStream<Ok = Event> + Send + 'static,
S::Error: StdError + Send + Sync + 'static,
{
let alive_timer = time::sleep(self.max_interval);
SseKeepAlive {
event_stream,
comment_text: self.comment_text,
max_interval: self.max_interval,
alive_timer,
}
}
}
#[allow(missing_debug_implementations)]
#[pin_project]
struct SseKeepAlive<S> {
#[pin]
event_stream: S,
comment_text: Cow<'static, str>,
max_interval: Duration,
#[pin]
alive_timer: Sleep,
}
/// Keeps event source connection alive when no events sent over a some time.
///
/// Some proxy servers may drop HTTP connection after a some timeout of inactivity.
/// This function helps to prevent such behavior by sending comment events every
/// `keep_interval` of inactivity.
///
/// By default the comment is `:` (an empty comment) and the time interval between
/// events is 15 seconds. Both may be customized using the builder pattern
/// as shown below.
///
/// ```
/// use std::time::Duration;
/// use std::convert::Infallible;
/// use futures_util::StreamExt;
/// use tokio::time::interval;
/// use tokio_stream::wrappers::IntervalStream;
/// use warp::{Filter, Stream, sse::Event};
///
/// // create server-sent event
/// fn sse_counter(counter: u64) -> Result<Event, Infallible> {
/// Ok(Event::default().data(counter.to_string()))
/// }
///
/// fn main() {
/// let routes = warp::path("ticks")
/// .and(warp::get())
/// .map(|| {
/// let mut counter: u64 = 0;
/// let interval = interval(Duration::from_secs(15));
/// let stream = IntervalStream::new(interval);
/// let event_stream = stream.map(move |_| {
/// counter += 1;
/// sse_counter(counter)
/// });
/// // reply using server-sent events
/// let stream = warp::sse::keep_alive()
/// .interval(Duration::from_secs(5))
/// .text("thump".to_string())
/// .stream(event_stream);
/// warp::sse::reply(stream)
/// });
/// }
/// ```
///
/// See [notes](https://www.w3.org/TR/2009/WD-eventsource-20090421/#notes).
pub fn keep_alive() -> KeepAlive {
loop {}
}
impl<S> Stream for SseKeepAlive<S>
where
S: TryStream<Ok = Event> + Send + 'static,
S::Error: StdError + Send + Sync + 'static,
{
type Item = Result<Event, SseError>;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}
mod sealed {
use super::*;
/// SSE error type
#[derive(Debug)]
pub struct SseError;
impl fmt::Display for SseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for SseError {}
}

206
warp/src/filters/trace.rs Normal file
View file

@ -0,0 +1,206 @@
//! [`tracing`] filters.
//!
//! [`tracing`] is a framework for instrumenting Rust programs to
//! collect scoped, structured, and async-aware diagnostics. This module
//! provides a set of filters for instrumenting Warp applications with `tracing`
//! spans. [`Spans`] can be used to associate individual events with a request,
//! and track contexts through the application.
//!
//! [`tracing`]: https://crates.io/crates/tracing
//! [`Spans`]: https://docs.rs/tracing/latest/tracing/#spans
use tracing::Span;
use std::net::SocketAddr;
use http::{self};
use crate::filter::{Filter, WrapSealed};
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::route::Route;
use self::internal::WithTrace;
/// Create a wrapping filter that instruments every request with a `tracing`
/// [`Span`] at the [`INFO`] level, containing a summary of the request.
/// Additionally, if the [`DEBUG`] level is enabled, the span will contain an
/// event recording the request's headers.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(warp::reply)
/// .with(warp::trace::request());
/// ```
///
/// [`Span`]: https://docs.rs/tracing/latest/tracing/#spans
/// [`INFO`]: https://docs.rs/tracing/0.1.16/tracing/struct.Level.html#associatedconstant.INFO
/// [`DEBUG`]: https://docs.rs/tracing/0.1.16/tracing/struct.Level.html#associatedconstant.DEBUG
pub fn request() -> Trace<impl Fn(Info<'_>) -> Span + Clone> {
trace(|info: Info<'_>| {
loop {}
})
}
/// Create a wrapping filter that instruments every request with a custom
/// `tracing` [`Span`] provided by a function.
///
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(warp::reply)
/// .with(warp::trace(|info| {
/// // Create a span using tracing macros
/// tracing::info_span!(
/// "request",
/// method = %info.method(),
/// path = %info.path(),
/// )
/// }));
/// ```
///
/// [`Span`]: https://docs.rs/tracing/latest/tracing/#spans
pub fn trace<F>(func: F) -> Trace<F>
where
F: Fn(Info<'_>) -> Span + Clone,
{
loop {}
}
/// Create a wrapping filter that instruments every request with a `tracing`
/// [`Span`] at the [`DEBUG`] level representing a named context.
///
/// This can be used to instrument multiple routes with their own sub-spans in a
/// per-request trace.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let hello = warp::path("hello")
/// .map(warp::reply)
/// .with(warp::trace::named("hello"));
///
/// let goodbye = warp::path("goodbye")
/// .map(warp::reply)
/// .with(warp::trace::named("goodbye"));
///
/// let routes = hello.or(goodbye);
/// ```
///
/// [`Span`]: https://docs.rs/tracing/latest/tracing/#spans
/// [`DEBUG`]: https://docs.rs/tracing/0.1.16/tracing/struct.Level.html#associatedconstant.DEBUG
pub fn named(name: &'static str) -> Trace<impl Fn(Info<'_>) -> Span + Copy> {
trace(move |_| tracing::debug_span!("context", "{}", name,))
}
/// Decorates a [`Filter`](crate::Filter) to create a [`tracing`] [span] for
/// requests and responses.
///
/// [`tracing`]: https://crates.io/crates/tracing
/// [span]: https://docs.rs/tracing/latest/tracing/#spans
#[derive(Clone, Copy, Debug)]
pub struct Trace<F> {
func: F,
}
/// Information about the request/response that can be used to prepare log lines.
#[allow(missing_debug_implementations)]
pub struct Info<'a> {
route: &'a Route,
}
impl<FN, F> WrapSealed<F> for Trace<FN>
where
FN: Fn(Info<'_>) -> Span + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Wrapped = WithTrace<FN, F>;
fn wrap(&self, filter: F) -> Self::Wrapped {
loop {}
}
}
impl<'a> Info<'a> {
/// View the remote `SocketAddr` of the request.
pub fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
/// View the `http::Method` of the request.
pub fn method(&self) -> &http::Method {
loop {}
}
/// View the URI path of the request.
pub fn path(&self) -> &str {
loop {}
}
/// View the `http::Version` of the request.
pub fn version(&self) -> http::Version {
loop {}
}
/// View the referer of the request.
pub fn referer(&self) -> Option<&str> {
loop {}
}
/// View the user agent of the request.
pub fn user_agent(&self) -> Option<&str> {
loop {}
}
/// View the host of the request
pub fn host(&self) -> Option<&str> {
loop {}
}
/// View the request headers.
pub fn request_headers(&self) -> &http::HeaderMap {
loop {}
}
}
mod internal {
use futures_util::{future::Inspect, future::MapOk};
use super::{Info, Trace};
use crate::filter::{Filter, FilterBase, Internal};
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::reply::Response;
#[allow(missing_debug_implementations)]
pub struct Traced(pub(super) Response);
impl Reply for Traced {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct WithTrace<FN, F> {
pub(super) filter: F,
pub(super) trace: Trace<FN>,
}
use tracing::instrument::{Instrumented};
use tracing::Span;
fn finished_logger<E: IsReject>(reply: &Result<(Traced,), E>) {
loop {}
}
fn convert_reply<R: Reply>(reply: R) -> (Traced,) {
loop {}
}
impl<FN, F> FilterBase for WithTrace<FN, F>
where
FN: Fn(Info<'_>) -> Span + Clone + Send,
F: Filter + Clone + Send,
F::Extract: Reply,
F::Error: IsReject,
{
type Extract = (Traced,);
type Error = F::Error;
type Future = Instrumented<
Inspect<
MapOk<F::Future, fn(F::Extract) -> Self::Extract>,
fn(&Result<Self::Extract, F::Error>),
>,
>;
fn filter(&self, _: Internal) -> Self::Future {
loop {}
}
}
}

255
warp/src/filters/ws.rs Normal file
View file

@ -0,0 +1,255 @@
//! Websockets Filters
use std::borrow::Cow;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use super::header;
use crate::filter::{filter_fn_one, Filter, One};
use crate::reject::Rejection;
use crate::reply::{Reply, Response};
use futures_util::{future, ready, FutureExt, Sink, Stream, TryFutureExt};
use headers::{Connection, HeaderMapExt, SecWebsocketAccept, SecWebsocketKey, Upgrade};
use http;
use hyper::upgrade::OnUpgrade;
use tokio_tungstenite::{
tungstenite::protocol::{self, WebSocketConfig},
WebSocketStream,
};
/// Creates a Websocket Filter.
///
/// The yielded `Ws` is used to finish the websocket upgrade.
///
/// # Note
///
/// This filter combines multiple filters internally, so you don't need them:
///
/// - Method must be `GET`
/// - Header `connection` must be `upgrade`
/// - Header `upgrade` must be `websocket`
/// - Header `sec-websocket-version` must be `13`
/// - Header `sec-websocket-key` must be set.
///
/// If the filters are met, yields a `Ws`. Calling `Ws::on_upgrade` will
/// return a reply with:
///
/// - Status of `101 Switching Protocols`
/// - Header `connection: upgrade`
/// - Header `upgrade: websocket`
/// - Header `sec-websocket-accept` with the hash value of the received key.
pub fn ws() -> impl Filter<Extract = One<Ws>, Error = Rejection> + Copy {
loop {}
}
/// Extracted by the [`ws`](ws) filter, and used to finish an upgrade.
pub struct Ws {
config: Option<WebSocketConfig>,
key: SecWebsocketKey,
on_upgrade: Option<OnUpgrade>,
}
impl Ws {
/// Finish the upgrade, passing a function to handle the `WebSocket`.
///
/// The passed function must return a `Future`.
pub fn on_upgrade<F, U>(self, func: F) -> impl Reply
where
F: FnOnce(WebSocket) -> U + Send + 'static,
U: Future<Output = ()> + Send + 'static,
{
loop {}
}
/// Set the size of the internal message send queue.
pub fn max_send_queue(mut self, max: usize) -> Self {
loop {}
}
/// Set the maximum message size (defaults to 64 megabytes)
pub fn max_message_size(mut self, max: usize) -> Self {
loop {}
}
/// Set the maximum frame size (defaults to 16 megabytes)
pub fn max_frame_size(mut self, max: usize) -> Self {
loop {}
}
}
impl fmt::Debug for Ws {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
#[allow(missing_debug_implementations)]
struct WsReply<F> {
ws: Ws,
on_upgrade: F,
}
impl<F, U> Reply for WsReply<F>
where
F: FnOnce(WebSocket) -> U + Send + 'static,
U: Future<Output = ()> + Send + 'static,
{
fn into_response(self) -> Response {
loop {}
}
}
fn on_upgrade() -> impl Filter<
Extract = (Option<OnUpgrade>,),
Error = Rejection,
> + Copy {
loop {}
}
/// A websocket `Stream` and `Sink`, provided to `ws` filters.
///
/// Ping messages sent from the client will be handled internally by replying with a Pong message.
/// Close messages need to be handled explicitly: usually by closing the `Sink` end of the
/// `WebSocket`.
///
/// **Note!**
/// Due to rust futures nature, pings won't be handled until read part of `WebSocket` is polled
pub struct WebSocket {
inner: WebSocketStream<hyper::upgrade::Upgraded>,
}
impl WebSocket {
pub(crate) async fn from_raw_socket(
upgraded: hyper::upgrade::Upgraded,
role: protocol::Role,
config: Option<protocol::WebSocketConfig>,
) -> Self {
loop {}
}
/// Gracefully close this websocket.
pub async fn close(mut self) -> Result<(), crate::Error> {
loop {}
}
}
impl Stream for WebSocket {
type Item = Result<Message, crate::Error>;
fn poll_next(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}
impl Sink<Message> for WebSocket {
type Error = crate::Error;
fn poll_ready(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
fn start_send(mut self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
loop {}
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
fn poll_close(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
}
impl fmt::Debug for WebSocket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
/// A WebSocket message.
///
/// This will likely become a `non-exhaustive` enum in the future, once that
/// language feature has stabilized.
#[derive(Eq, PartialEq, Clone)]
pub struct Message {
inner: protocol::Message,
}
impl Message {
/// Construct a new Text `Message`.
pub fn text<S: Into<String>>(s: S) -> Message {
loop {}
}
/// Construct a new Binary `Message`.
pub fn binary<V: Into<Vec<u8>>>(v: V) -> Message {
loop {}
}
/// Construct a new Ping `Message`.
pub fn ping<V: Into<Vec<u8>>>(v: V) -> Message {
loop {}
}
/// Construct a new Pong `Message`.
///
/// Note that one rarely needs to manually construct a Pong message because the underlying tungstenite socket
/// automatically responds to the Ping messages it receives. Manual construction might still be useful in some cases
/// like in tests or to send unidirectional heartbeats.
pub fn pong<V: Into<Vec<u8>>>(v: V) -> Message {
loop {}
}
/// Construct the default Close `Message`.
pub fn close() -> Message {
loop {}
}
/// Construct a Close `Message` with a code and reason.
pub fn close_with(
code: impl Into<u16>,
reason: impl Into<Cow<'static, str>>,
) -> Message {
loop {}
}
/// Returns true if this message is a Text message.
pub fn is_text(&self) -> bool {
loop {}
}
/// Returns true if this message is a Binary message.
pub fn is_binary(&self) -> bool {
loop {}
}
/// Returns true if this message a is a Close message.
pub fn is_close(&self) -> bool {
loop {}
}
/// Returns true if this message is a Ping message.
pub fn is_ping(&self) -> bool {
loop {}
}
/// Returns true if this message is a Pong message.
pub fn is_pong(&self) -> bool {
loop {}
}
/// Try to get the close frame (close code and reason)
pub fn close_frame(&self) -> Option<(u16, &str)> {
loop {}
}
/// Try to get a reference to the string text, if this is a Text message.
pub fn to_str(&self) -> Result<&str, ()> {
loop {}
}
/// Return the bytes of this message, if the message can contain data.
pub fn as_bytes(&self) -> &[u8] {
loop {}
}
/// Destructure this message into binary data.
pub fn into_bytes(self) -> Vec<u8> {
loop {}
}
}
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl From<Message> for Vec<u8> {
fn from(m: Message) -> Self {
loop {}
}
}
/// Connection header did not include 'upgrade'
#[derive(Debug)]
pub struct MissingConnectionUpgrade;
impl fmt::Display for MissingConnectionUpgrade {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl ::std::error::Error for MissingConnectionUpgrade {}

144
warp/src/generic.rs Normal file
View file

@ -0,0 +1,144 @@
#[derive(Debug)]
pub struct Product<H, T: HList>(pub(crate) H, pub(crate) T);
pub type One<T> = (T,);
#[inline]
pub(crate) fn one<T>(val: T) -> One<T> {
loop {}
}
#[derive(Debug)]
pub enum Either<T, U> {
A(T),
B(U),
}
pub trait HList: Sized {
type Tuple: Tuple<HList = Self>;
fn flatten(self) -> Self::Tuple;
}
pub trait Tuple: Sized {
type HList: HList<Tuple = Self>;
fn hlist(self) -> Self::HList;
#[inline]
fn combine<T>(self, other: T) -> CombinedTuples<Self, T>
where
Self: Sized,
T: Tuple,
Self::HList: Combine<T::HList>,
{
loop {}
}
}
pub type CombinedTuples<T, U> = <<<T as Tuple>::HList as Combine<
<U as Tuple>::HList,
>>::Output as HList>::Tuple;
pub trait Combine<T: HList> {
type Output: HList;
fn combine(self, other: T) -> Self::Output;
}
pub trait Func<Args> {
type Output;
fn call(&self, args: Args) -> Self::Output;
}
impl<T: HList> Combine<T> for () {
type Output = T;
#[inline]
fn combine(self, other: T) -> Self::Output {
loop {}
}
}
impl<H, T: HList, U: HList> Combine<U> for Product<H, T>
where
T: Combine<U>,
Product<H, <T as Combine<U>>::Output>: HList,
{
type Output = Product<H, <T as Combine<U>>::Output>;
#[inline]
fn combine(self, other: U) -> Self::Output {
loop {}
}
}
impl HList for () {
type Tuple = ();
#[inline]
fn flatten(self) -> Self::Tuple {}
}
impl Tuple for () {
type HList = ();
#[inline]
fn hlist(self) -> Self::HList {}
}
impl<F, R> Func<()> for F
where
F: Fn() -> R,
{
type Output = R;
#[inline]
fn call(&self, _args: ()) -> Self::Output {
loop {}
}
}
impl<F, R> Func<crate::Rejection> for F
where
F: Fn(crate::Rejection) -> R,
{
type Output = R;
#[inline]
fn call(&self, arg: crate::Rejection) -> Self::Output {
loop {}
}
}
macro_rules! product {
($H:expr) => {
Product($H, ())
};
($H:expr, $($T:expr),*) => {
Product($H, product!($($T),*))
};
}
macro_rules! Product {
($H:ty) => {
Product <$H, () >
};
($H:ty, $($T:ty),*) => {
Product <$H, Product!($($T),*) >
};
}
macro_rules! product_pat {
($H:pat) => {
Product($H, ())
};
($H:pat, $($T:pat),*) => {
Product($H, product_pat!($($T),*))
};
}
macro_rules! generics {
($type:ident) => {
impl <$type > HList for Product!($type) { type Tuple = ($type,); #[inline] fn
flatten(self) -> Self::Tuple { (self.0,) } } impl <$type > Tuple for ($type,) {
type HList = Product!($type); #[inline] fn hlist(self) -> Self::HList {
product!(self.0) } } impl < F, R, $type > Func < Product!($type) > for F where F
: Fn($type) -> R, { type Output = R; #[inline] fn call(& self, args :
Product!($type)) -> Self::Output { (* self) (args.0) } } impl < F, R, $type >
Func < ($type,) > for F where F : Fn($type) -> R, { type Output = R; #[inline] fn
call(& self, args : ($type,)) -> Self::Output { (* self) (args.0) } }
};
($type1:ident, $($type:ident),*) => {
generics!($($type),*); impl <$type1, $($type),*> HList for Product!($type1,
$($type),*) { type Tuple = ($type1, $($type),*); #[inline] fn flatten(self) ->
Self::Tuple { #[allow(non_snake_case)] let product_pat!($type1, $($type),*) =
self; ($type1, $($type),*) } } impl <$type1, $($type),*> Tuple for ($type1,
$($type),*) { type HList = Product!($type1, $($type),*); #[inline] fn hlist(self)
-> Self::HList { #[allow(non_snake_case)] let ($type1, $($type),*) = self;
product!($type1, $($type),*) } } impl < F, R, $type1, $($type),*> Func <
Product!($type1, $($type),*) > for F where F : Fn($type1, $($type),*) -> R, {
type Output = R; #[inline] fn call(& self, args : Product!($type1, $($type),*))
-> Self::Output { #[allow(non_snake_case)] let product_pat!($type1, $($type),*) =
args; (* self) ($type1, $($type),*) } } impl < F, R, $type1, $($type),*> Func <
($type1, $($type),*) > for F where F : Fn($type1, $($type),*) -> R, { type Output
= R; #[inline] fn call(& self, args : ($type1, $($type),*)) -> Self::Output {
#[allow(non_snake_case)] let ($type1, $($type),*) = args; (* self) ($type1,
$($type),*) } }
};
}
generics! {
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
}

179
warp/src/lib.rs Normal file
View file

@ -0,0 +1,179 @@
#![doc(html_root_url = "https://docs.rs/warp/0.3.3")]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rust_2018_idioms)]
#![cfg_attr(test, deny(warnings))]
//! # warp
//!
//! warp is a super-easy, composable, web server framework for warp speeds.
//!
//! Thanks to its [`Filter`][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
//! - Etc
//!
//! 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**
//!
//! ## Filters
//!
//! The main concept in warp is the [`Filter`][Filter], which allows composition
//! to describe various endpoints in your web service. Besides this powerful
//! trait, warp comes with several built in [filters](filters/index.html), which
//! can be combined for your specific needs.
//!
//! As a small example, consider an endpoint that has path and header requirements:
//!
//! ```
//! use warp::Filter;
//!
//! let hi = warp::path("hello")
//! .and(warp::path::param())
//! .and(warp::header("user-agent"))
//! .map(|param: String, agent: String| {
//! format!("Hello {}, whose agent is {}", param, agent)
//! });
//! ```
//!
//! This example composes several [`Filter`s][Filter] together using `and`:
//!
//! - A path prefix of "hello"
//! - A path parameter of a `String`
//! - The `user-agent` header parsed as a `String`
//!
//! These specific filters will [`reject`][reject] requests that don't match
//! their requirements.
//!
//! This ends up matching requests like:
//!
//! ```notrust
//! GET /hello/sean HTTP/1.1
//! Host: hyper.rs
//! User-Agent: reqwest/v0.8.6
//!
//! ```
//! And it returns a response similar to this:
//!
//! ```notrust
//! HTTP/1.1 200 OK
//! Content-Length: 41
//! Date: ...
//!
//! Hello sean, whose agent is reqwest/v0.8.6
//! ```
//!
//! Take a look at the full list of [`filters`](filters/index.html) to see what
//! you can build.
//!
//! ## Testing
//!
//! Testing your web services easily is extremely important, and warp provides
//! a [`test`](self::test) module to help send mocked requests through your service.
//!
//! [Filter]: trait.Filter.html
//! [reject]: reject/index.html
#[macro_use]
mod error;
mod filter;
pub mod filters;
mod generic;
pub mod redirect;
pub mod reject;
pub mod reply;
mod route;
mod server;
mod service;
pub mod test;
#[cfg(feature = "tls")]
mod tls;
mod transport;
pub use self::error::Error;
pub use self::filter::Filter;
// This otherwise shows a big dump of re-exports in the doc homepage,
// with zero context, so just hide it from the docs. Doc examples
// on each can show that a convenient import exists.
#[cfg(feature = "compression")]
#[doc(hidden)]
pub use self::filters::compression;
#[cfg(feature = "multipart")]
#[doc(hidden)]
pub use self::filters::multipart;
#[cfg(feature = "websocket")]
#[doc(hidden)]
pub use self::filters::ws;
#[doc(hidden)]
pub use self::filters::{
addr,
// any() function
any::any,
body,
cookie,
// cookie() function
cookie::cookie,
cors,
// cors() function
cors::cors,
ext,
fs,
header,
// header() function
header::header,
host,
log,
// log() function
log::log,
method::{delete, get, head, method, options, patch, post, put},
path,
// path() function and macro
path::path,
query,
// query() function
query::query,
sse,
trace,
// trace() function
trace::trace,
};
// ws() function
pub use self::filter::wrap_fn;
#[cfg(feature = "websocket")]
#[doc(hidden)]
pub use self::filters::ws::ws;
#[doc(hidden)]
pub use self::redirect::redirect;
#[doc(hidden)]
#[allow(deprecated)]
pub use self::reject::{reject, Rejection};
#[doc(hidden)]
pub use self::reply::{reply, Reply};
#[cfg(feature = "tls")]
pub use self::server::TlsServer;
pub use self::server::{serve, Server};
pub use self::service::service;
#[doc(hidden)]
pub use http;
#[doc(hidden)]
pub use hyper;
#[doc(hidden)]
pub use bytes::Buf;
#[doc(hidden)]
pub use futures_util::{Future, Sink, Stream};
#[doc(hidden)]
pub(crate) type Request = http::Request<hyper::Body>;

120
warp/src/redirect.rs Normal file
View file

@ -0,0 +1,120 @@
//! Redirect requests to a new location.
//!
//! The types in this module are helpers that implement [`Reply`](Reply), and easy
//! to use in order to setup redirects.
use http::{header, StatusCode};
pub use self::sealed::AsLocation;
use crate::reply::{self, Reply};
/// A simple `301` permanent redirect to a different location.
///
/// # Example
///
/// ```
/// use warp::{http::Uri, Filter};
///
/// let route = warp::path("v1")
/// .map(|| {
/// warp::redirect(Uri::from_static("/v2"))
/// });
/// ```
pub fn redirect(uri: impl AsLocation) -> impl Reply {
reply::with_header(
StatusCode::MOVED_PERMANENTLY,
header::LOCATION,
uri.header_value(),
)
}
/// A simple `302` found redirect to a different location
///
/// # Example
///
/// ```
/// use warp::{http::Uri, Filter};
///
/// let route = warp::path("v1")
/// .map(|| {
/// warp::redirect::found(Uri::from_static("/v2"))
/// });
/// ```
pub fn found(uri: impl AsLocation) -> impl Reply {
reply::with_header(StatusCode::FOUND, header::LOCATION, uri.header_value())
}
/// A simple `303` redirect to a different location.
///
/// The HTTP method of the request to the new location will always be `GET`.
///
/// # Example
///
/// ```
/// use warp::{http::Uri, Filter};
///
/// let route = warp::path("v1")
/// .map(|| {
/// warp::redirect::see_other(Uri::from_static("/v2"))
/// });
/// ```
pub fn see_other(uri: impl AsLocation) -> impl Reply {
reply::with_header(StatusCode::SEE_OTHER, header::LOCATION, uri.header_value())
}
/// A simple `307` temporary redirect to a different location.
///
/// This is similar to [`see_other`](fn@see_other) but the HTTP method and the body of the request
/// to the new location will be the same as the method and body of the current request.
///
/// # Example
///
/// ```
/// use warp::{http::Uri, Filter};
///
/// let route = warp::path("v1")
/// .map(|| {
/// warp::redirect::temporary(Uri::from_static("/v2"))
/// });
/// ```
pub fn temporary(uri: impl AsLocation) -> impl Reply {
reply::with_header(
StatusCode::TEMPORARY_REDIRECT,
header::LOCATION,
uri.header_value(),
)
}
/// A simple `308` permanent redirect to a different location.
///
/// This is similar to [`redirect`](fn@redirect) but the HTTP method of the request to the new
/// location will be the same as the method of the current request.
///
/// # Example
///
/// ```
/// use warp::{http::Uri, Filter};
///
/// let route = warp::path("v1")
/// .map(|| {
/// warp::redirect::permanent(Uri::from_static("/v2"))
/// });
/// ```
pub fn permanent(uri: impl AsLocation) -> impl Reply {
reply::with_header(
StatusCode::PERMANENT_REDIRECT,
header::LOCATION,
uri.header_value(),
)
}
mod sealed {
use http::{header::HeaderValue, Uri};
/// Trait for redirect locations. Currently only a `Uri` can be used in
/// redirect.
/// This sealed trait exists to allow adding possibly new impls so other
/// arguments could be accepted, like maybe just `warp::redirect("/v2")`.
pub trait AsLocation: Sealed {}
pub trait Sealed {
fn header_value(self) -> HeaderValue;
}
impl AsLocation for Uri {}
impl Sealed for Uri {
fn header_value(self) -> HeaderValue {
loop {}
}
}
}

452
warp/src/reject.rs Normal file
View file

@ -0,0 +1,452 @@
//! Rejections
//!
//! Part of the power of the [`Filter`](../trait.Filter.html) system is being able to
//! reject a request from a filter chain. This allows for filters to be
//! combined with `or`, so that if one side of the chain finds that a request
//! doesn't fulfill its requirements, the other side can try to process
//! the request.
//!
//! Many of the built-in [`filters`](../filters) will automatically reject
//! the request with an appropriate rejection. However, you can also build
//! new custom [`Filter`](../trait.Filter.html)s and still want other routes to be
//! matchable in the case a predicate doesn't hold.
//!
//! As a request is processed by a Filter chain, the rejections are accumulated into
//! a list contained by the [`Rejection`](struct.Rejection.html) type. Rejections from
//! filters can be handled using [`Filter::recover`](../trait.Filter.html#method.recover).
//! This is a convenient way to map rejections into a [`Reply`](../reply/trait.Reply.html).
//!
//! For a more complete example see the
//! [Rejection Example](https://github.com/seanmonstar/warp/blob/master/examples/rejections.rs)
//! from the repository.
//!
//! # Example
//!
//! ```
//! use warp::{reply, Reply, Filter, reject, Rejection, http::StatusCode};
//!
//! #[derive(Debug)]
//! struct InvalidParameter;
//!
//! impl reject::Reject for InvalidParameter {}
//!
//! // Custom rejection handler that maps rejections into responses.
//! async fn handle_rejection(err: Rejection) -> Result<impl Reply, std::convert::Infallible> {
//! if err.is_not_found() {
//! Ok(reply::with_status("NOT_FOUND", StatusCode::NOT_FOUND))
//! } else if let Some(e) = err.find::<InvalidParameter>() {
//! Ok(reply::with_status("BAD_REQUEST", StatusCode::BAD_REQUEST))
//! } else {
//! eprintln!("unhandled rejection: {:?}", err);
//! Ok(reply::with_status("INTERNAL_SERVER_ERROR", StatusCode::INTERNAL_SERVER_ERROR))
//! }
//! }
//!
//!
//! // Filter on `/:id`, but reject with InvalidParameter if the `id` is `0`.
//! // Recover from this rejection using a custom rejection handler.
//! let route = warp::path::param()
//! .and_then(|id: u32| async move {
//! if id == 0 {
//! Err(warp::reject::custom(InvalidParameter))
//! } else {
//! Ok("id is valid")
//! }
//! })
//! .recover(handle_rejection);
//! ```
use std::any::Any;
use std::convert::Infallible;
use std::error::Error as StdError;
use std::fmt;
use http::{self, StatusCode};
pub(crate) use self::sealed::{CombineRejection, IsReject};
/// Rejects a request with `404 Not Found`.
#[inline]
pub fn reject() -> Rejection {
loop {}
}
/// Rejects a request with `404 Not Found`.
#[inline]
pub fn not_found() -> Rejection {
loop {}
}
#[inline]
pub(crate) fn invalid_query() -> Rejection {
loop {}
}
#[inline]
pub(crate) fn missing_header(name: &'static str) -> Rejection {
loop {}
}
#[inline]
pub(crate) fn invalid_header(name: &'static str) -> Rejection {
loop {}
}
#[inline]
pub(crate) fn missing_cookie(name: &'static str) -> Rejection {
loop {}
}
#[inline]
pub(crate) fn method_not_allowed() -> Rejection {
loop {}
}
#[inline]
pub(crate) fn length_required() -> Rejection {
loop {}
}
#[inline]
pub(crate) fn payload_too_large() -> Rejection {
loop {}
}
#[inline]
pub(crate) fn unsupported_media_type() -> Rejection {
loop {}
}
/// Rejects a request with a custom cause.
///
/// A [`recover`][] filter should convert this `Rejection` into a `Reply`,
/// or else this will be returned as a `500 Internal Server Error`.
///
/// [`recover`]: ../trait.Filter.html#method.recover
pub fn custom<T: Reject>(err: T) -> Rejection {
loop {}
}
/// Protect against re-rejecting a rejection.
///
/// ```compile_fail
/// fn with(r: warp::Rejection) {
/// let _wat = warp::reject::custom(r);
/// }
/// ```
fn __reject_custom_compilefail() {}
/// A marker trait to ensure proper types are used for custom rejections.
///
/// Can be converted into Rejection.
///
/// # Example
///
/// ```
/// use warp::{Filter, reject::Reject};
///
/// #[derive(Debug)]
/// struct RateLimited;
///
/// impl Reject for RateLimited {}
///
/// let route = warp::any().and_then(|| async {
/// Err::<(), _>(warp::reject::custom(RateLimited))
/// });
/// ```
pub trait Reject: fmt::Debug + Sized + Send + Sync + 'static {}
trait Cause: fmt::Debug + Send + Sync + 'static {
fn as_any(&self) -> &dyn Any;
}
impl<T> Cause for T
where
T: fmt::Debug + Send + Sync + 'static,
{
fn as_any(&self) -> &dyn Any {
loop {}
}
}
impl dyn Cause {}
pub(crate) fn known<T: Into<Known>>(err: T) -> Rejection {
loop {}
}
/// Rejection of a request by a [`Filter`](crate::Filter).
///
/// See the [`reject`](module@crate::reject) documentation for more.
pub struct Rejection {
reason: Reason,
}
enum Reason {
NotFound,
Other(Box<Rejections>),
}
enum Rejections {
Known(Known),
Custom(Box<dyn Cause>),
Combined(Box<Rejections>, Box<Rejections>),
}
macro_rules! enum_known {
($($(#[$attr:meta])* $var:ident ($ty:path),)+) => {
pub (crate) enum Known { $($(#[$attr])* $var ($ty),)+ } impl Known { fn
inner_as_any(& self) -> & dyn Any { match * self { $($(#[$attr])* Known::$var
(ref t) => t,)+ } } } impl fmt::Debug for Known { fn fmt(& self, f : & mut
fmt::Formatter <'_ >) -> fmt::Result { match * self { $($(#[$attr])* Known::$var
(ref t) => t.fmt(f),)+ } } } impl fmt::Display for Known { fn fmt(& self, f : &
mut fmt::Formatter <'_ >) -> fmt::Result { match * self { $($(#[$attr])*
Known::$var (ref t) => t.fmt(f),)+ } } } $(#[doc(hidden)] $(#[$attr])* impl From
<$ty > for Known { fn from(ty : $ty) -> Known { Known::$var (ty) } })+
};
}
enum_known! {
MethodNotAllowed(MethodNotAllowed), InvalidHeader(InvalidHeader),
MissingHeader(MissingHeader), MissingCookie(MissingCookie),
InvalidQuery(InvalidQuery), LengthRequired(LengthRequired),
PayloadTooLarge(PayloadTooLarge), UnsupportedMediaType(UnsupportedMediaType),
FileOpenError(crate ::fs::FileOpenError), FilePermissionError(crate
::fs::FilePermissionError), BodyReadError(crate ::body::BodyReadError),
BodyDeserializeError(crate ::body::BodyDeserializeError), CorsForbidden(crate
::cors::CorsForbidden), #[cfg(feature = "websocket")] MissingConnectionUpgrade(crate
::ws::MissingConnectionUpgrade), MissingExtension(crate ::ext::MissingExtension),
BodyConsumedMultipleTimes(crate ::body::BodyConsumedMultipleTimes),
}
impl Rejection {
/// Searches this `Rejection` for a specific cause.
///
/// A `Rejection` will accumulate causes over a `Filter` chain. This method
/// can search through them and return the first cause of this type.
///
/// # Example
///
/// ```
/// #[derive(Debug)]
/// struct Nope;
///
/// impl warp::reject::Reject for Nope {}
///
/// let reject = warp::reject::custom(Nope);
///
/// if let Some(nope) = reject.find::<Nope>() {
/// println!("found it: {:?}", nope);
/// }
/// ```
pub fn find<T: 'static>(&self) -> Option<&T> {
loop {}
}
/// Returns true if this Rejection was made via `warp::reject::not_found`.
///
/// # Example
///
/// ```
/// let rejection = warp::reject();
///
/// assert!(rejection.is_not_found());
/// ```
pub fn is_not_found(&self) -> bool {
loop {}
}
}
impl<T: Reject> From<T> for Rejection {
#[inline]
fn from(err: T) -> Rejection {
loop {}
}
}
impl From<Infallible> for Rejection {
#[inline]
fn from(infallible: Infallible) -> Rejection {
loop {}
}
}
impl IsReject for Infallible {
fn status(&self) -> StatusCode {
loop {}
}
fn into_response(&self) -> crate::reply::Response {
loop {}
}
}
impl IsReject for Rejection {
fn status(&self) -> StatusCode {
loop {}
}
fn into_response(&self) -> crate::reply::Response {
loop {}
}
}
impl fmt::Debug for Rejection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl fmt::Debug for Reason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl Rejections {}
unit_error! {
#[doc = " Invalid query"] pub InvalidQuery : "Invalid query string"
}
unit_error! {
#[doc = " HTTP method not allowed"] pub MethodNotAllowed : "HTTP method not allowed"
}
unit_error! {
#[doc = " A content-length header is required"] pub LengthRequired :
"A content-length header is required"
}
unit_error! {
#[doc = " The request payload is too large"] pub PayloadTooLarge :
"The request payload is too large"
}
unit_error! {
#[doc = " The request's content-type is not supported"] pub UnsupportedMediaType :
"The request's content-type is not supported"
}
/// Missing request header
#[derive(Debug)]
pub struct MissingHeader {
name: &'static str,
}
impl MissingHeader {
/// Retrieve the name of the header that was missing
pub fn name(&self) -> &str {
loop {}
}
}
impl fmt::Display for MissingHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for MissingHeader {}
/// Invalid request header
#[derive(Debug)]
pub struct InvalidHeader {
name: &'static str,
}
impl InvalidHeader {
/// Retrieve the name of the header that was invalid
pub fn name(&self) -> &str {
loop {}
}
}
impl fmt::Display for InvalidHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for InvalidHeader {}
/// Missing cookie
#[derive(Debug)]
pub struct MissingCookie {
name: &'static str,
}
impl MissingCookie {
/// Retrieve the name of the cookie that was missing
pub fn name(&self) -> &str {
loop {}
}
}
impl fmt::Display for MissingCookie {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for MissingCookie {}
mod sealed {
use super::Rejection;
use http::StatusCode;
use std::convert::Infallible;
use std::fmt;
pub trait IsReject: fmt::Debug + Send + Sync {
fn status(&self) -> StatusCode;
fn into_response(&self) -> crate::reply::Response;
}
fn _assert_object_safe() {
loop {}
}
pub trait CombineRejection<E>: Send + Sized {
/// The type that should be returned when only 1 of the two
/// "rejections" occurs.
///
/// # For example:
///
/// `warp::any().and(warp::path("foo"))` has the following steps:
///
/// 1. Since this is `and`, only **one** of the rejections will occur,
/// and as soon as it does, it will be returned.
/// 2. `warp::any()` rejects with `Never`. So, it will never return `Never`.
/// 3. `warp::path()` rejects with `Rejection`. It may return `Rejection`.
///
/// Thus, if the above filter rejects, it will definitely be `Rejection`.
type One: IsReject + From<Self> + From<E> + Into<Rejection>;
/// The type that should be returned when both rejections occur,
/// and need to be combined.
type Combined: IsReject;
fn combine(self, other: E) -> Self::Combined;
}
impl CombineRejection<Rejection> for Rejection {
type One = Rejection;
type Combined = Rejection;
fn combine(self, other: Rejection) -> Self::Combined {
loop {}
}
}
impl CombineRejection<Infallible> for Rejection {
type One = Rejection;
type Combined = Infallible;
fn combine(self, other: Infallible) -> Self::Combined {
loop {}
}
}
impl CombineRejection<Rejection> for Infallible {
type One = Rejection;
type Combined = Infallible;
fn combine(self, _: Rejection) -> Self::Combined {
loop {}
}
}
impl CombineRejection<Infallible> for Infallible {
type One = Infallible;
type Combined = Infallible;
fn combine(self, _: Infallible) -> Self::Combined {
loop {}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use http::StatusCode;
#[derive(Debug, PartialEq)]
struct Left;
#[derive(Debug, PartialEq)]
struct Right;
impl Reject for Left {}
impl Reject for Right {}
#[test]
fn rejection_status() {
loop {}
}
#[tokio::test]
async fn combine_rejection_causes_with_some_left_and_none_right() {
loop {}
}
#[tokio::test]
async fn combine_rejection_causes_with_none_left_and_some_right() {
loop {}
}
#[tokio::test]
async fn unhandled_customs() {
loop {}
}
async fn response_body_string(resp: crate::reply::Response) -> String {
loop {}
}
#[test]
fn find_cause() {
loop {}
}
#[test]
fn size_of_rejection() {
loop {}
}
#[derive(Debug)]
struct X(u32);
impl Reject for X {}
fn combine_n<F, R>(n: u32, new_reject: F) -> Rejection
where
F: Fn(u32) -> R,
R: Reject,
{
loop {}
}
#[test]
fn test_debug() {
loop {}
}
}

388
warp/src/reply.rs Normal file
View file

@ -0,0 +1,388 @@
//! Reply to requests.
//!
//! A [`Reply`](./trait.Reply.html) is a type that can be converted into an HTTP
//! response to be sent to the client. These are typically the successful
//! counterpart to a [rejection](../reject).
//!
//! The functions in this module are helpers for quickly creating a reply.
//! Besides them, you can return a type that implements [`Reply`](./trait.Reply.html). This
//! could be any of the following:
//!
//! - [`http::Response<impl Into<hyper::Body>>`](https://docs.rs/http)
//! - `String`
//! - `&'static str`
//! - `http::StatusCode`
//!
//! # Example
//!
//! ```
//! use warp::{Filter, http::Response};
//!
//! // Returns an empty `200 OK` response.
//! let empty_200 = warp::any().map(warp::reply);
//!
//! // Returns a `200 OK` response with custom header and body.
//! let custom = warp::any().map(|| {
//! Response::builder()
//! .header("my-custom-header", "some-value")
//! .body("and a custom body")
//! });
//!
//! // GET requests return the empty 200, POST return the custom.
//! let routes = warp::get().and(empty_200)
//! .or(warp::post().and(custom));
//! ```
use std::borrow::Cow;
use std::convert::TryFrom;
use std::error::Error as StdError;
use std::fmt;
use crate::generic::{Either, One};
use http::header::{HeaderName, HeaderValue};
use http::StatusCode;
use hyper::Body;
use serde::Serialize;
pub(crate) use self::sealed::Reply_;
use self::sealed::BoxedReply;
#[doc(hidden)]
pub use crate::filters::reply as with;
/// Response type into which types implementing the `Reply` trait are convertable.
pub type Response = ::http::Response<Body>;
/// Returns an empty `Reply` with status code `200 OK`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // GET /just-ok returns an empty `200 OK`.
/// let route = warp::path("just-ok")
/// .map(|| {
/// println!("got a /just-ok request!");
/// warp::reply()
/// });
/// ```
#[inline]
pub fn reply() -> impl Reply {
StatusCode::OK
}
/// Convert the value into a `Reply` with the value encoded as JSON.
///
/// The passed value must implement [`Serialize`][ser]. Many
/// collections do, and custom domain types can have `Serialize` derived.
///
/// [ser]: https://serde.rs
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// // GET /ids returns a `200 OK` with a JSON array of ids:
/// // `[1, 3, 7, 13]`
/// let route = warp::path("ids")
/// .map(|| {
/// let our_ids = vec![1, 3, 7, 13];
/// warp::reply::json(&our_ids)
/// });
/// ```
///
/// # Note
///
/// If a type fails to be serialized into JSON, the error is logged at the
/// `error` level, and the returned `impl Reply` will be an empty
/// `500 Internal Server Error` response.
pub fn json<T>(val: &T) -> Json
where
T: Serialize,
{
loop {}
}
/// A JSON formatted reply.
#[allow(missing_debug_implementations)]
pub struct Json {
inner: Result<Vec<u8>, ()>,
}
impl Reply for Json {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
#[derive(Debug)]
pub(crate) struct ReplyJsonError;
impl fmt::Display for ReplyJsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for ReplyJsonError {}
/// Reply with a body and `content-type` set to `text/html; charset=utf-8`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let body = r#"
/// <html>
/// <head>
/// <title>HTML with warp!</title>
/// </head>
/// <body>
/// <h1>warp + HTML = &hearts;</h1>
/// </body>
/// </html>
/// "#;
///
/// let route = warp::any()
/// .map(move || {
/// warp::reply::html(body)
/// });
/// ```
pub fn html<T>(body: T) -> Html<T>
where
Body: From<T>,
T: Send,
{
loop {}
}
/// An HTML reply.
#[allow(missing_debug_implementations)]
pub struct Html<T> {
body: T,
}
impl<T> Reply for Html<T>
where
Body: From<T>,
T: Send,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
/// Types that can be converted into a `Response`.
///
/// This trait is implemented for the following:
///
/// - `http::StatusCode`
/// - `http::Response<impl Into<hyper::Body>>`
/// - `String`
/// - `&'static str`
///
/// # Example
///
/// ```rust
/// use warp::{Filter, http::Response};
///
/// struct Message {
/// msg: String
/// }
///
/// impl warp::Reply for Message {
/// fn into_response(self) -> warp::reply::Response {
/// Response::new(format!("message: {}", self.msg).into())
/// }
/// }
///
/// fn handler() -> Message {
/// Message { msg: "Hello".to_string() }
/// }
///
/// let route = warp::any().map(handler);
/// ```
pub trait Reply: BoxedReply + Send {
/// Converts the given value into a [`Response`].
///
/// [`Response`]: type.Response.html
fn into_response(self) -> Response;
}
impl<T: Reply + ?Sized> Reply for Box<T> {
fn into_response(self) -> Response {
loop {}
}
}
fn _assert_object_safe() {
loop {}
}
/// Wrap an `impl Reply` to change its `StatusCode`.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(warp::reply)
/// .map(|reply| {
/// warp::reply::with_status(reply, warp::http::StatusCode::CREATED)
/// });
/// ```
pub fn with_status<T: Reply>(reply: T, status: StatusCode) -> WithStatus<T> {
loop {}
}
/// Wrap an `impl Reply` to change its `StatusCode`.
///
/// Returned by `warp::reply::with_status`.
#[derive(Debug)]
pub struct WithStatus<T> {
reply: T,
status: StatusCode,
}
impl<T: Reply> Reply for WithStatus<T> {
fn into_response(self) -> Response {
loop {}
}
}
/// Wrap an `impl Reply` to add a header when rendering.
///
/// # Example
///
/// ```
/// use warp::Filter;
///
/// let route = warp::any()
/// .map(warp::reply)
/// .map(|reply| {
/// warp::reply::with_header(reply, "server", "warp")
/// });
/// ```
pub fn with_header<T: Reply, K, V>(reply: T, name: K, value: V) -> WithHeader<T>
where
HeaderName: TryFrom<K>,
<HeaderName as TryFrom<K>>::Error: Into<http::Error>,
HeaderValue: TryFrom<V>,
<HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
{
loop {}
}
/// Wraps an `impl Reply` and adds a header when rendering.
///
/// Returned by `warp::reply::with_header`.
#[derive(Debug)]
pub struct WithHeader<T> {
header: Option<(HeaderName, HeaderValue)>,
reply: T,
}
impl<T: Reply> Reply for WithHeader<T> {
fn into_response(self) -> Response {
loop {}
}
}
impl<T: Send> Reply for ::http::Response<T>
where
Body: From<T>,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for ::http::StatusCode {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl<T> Reply for Result<T, ::http::Error>
where
T: Reply + Send,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for String {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for Vec<u8> {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for &'static str {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for Cow<'static, str> {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for &'static [u8] {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl<T, U> Reply for Either<T, U>
where
T: Reply,
U: Reply,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl<T> Reply for One<T>
where
T: Reply,
{
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
impl Reply for std::convert::Infallible {
#[inline(always)]
fn into_response(self) -> Response {
loop {}
}
}
mod sealed {
use super::{Reply, Response};
#[allow(missing_debug_implementations)]
pub struct Reply_(pub(crate) Response);
impl Reply for Reply_ {
#[inline]
fn into_response(self) -> Response {
loop {}
}
}
#[allow(missing_debug_implementations)]
pub struct Internal;
pub trait BoxedReply {
fn boxed_into_response(self: Box<Self>, internal: Internal) -> Response;
}
impl<T: Reply> BoxedReply for T {
fn boxed_into_response(self: Box<Self>, _: Internal) -> Response {
loop {}
}
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::*;
#[test]
fn json_serde_error() {
loop {}
}
#[test]
fn response_builder_error() {
loop {}
}
#[test]
fn boxed_reply() {
loop {}
}
}

82
warp/src/route.rs Normal file
View file

@ -0,0 +1,82 @@
use scoped_tls::scoped_thread_local;
use std::cell::RefCell;
use std::net::SocketAddr;
use hyper::Body;
use crate::Request;
scoped_thread_local!(static ROUTE : RefCell < Route >);
pub(crate) fn set<F, U>(r: &RefCell<Route>, func: F) -> U
where
F: FnOnce() -> U,
{
loop {}
}
pub(crate) fn is_set() -> bool {
loop {}
}
pub(crate) fn with<F, R>(func: F) -> R
where
F: FnOnce(&mut Route) -> R,
{
loop {}
}
#[derive(Debug)]
pub(crate) struct Route {
body: BodyState,
remote_addr: Option<SocketAddr>,
req: Request,
segments_index: usize,
}
#[derive(Debug)]
enum BodyState {
Ready,
Taken,
}
impl Route {
pub(crate) fn new(req: Request, remote_addr: Option<SocketAddr>) -> RefCell<Route> {
loop {}
}
pub(crate) fn method(&self) -> &http::Method {
loop {}
}
pub(crate) fn headers(&self) -> &http::HeaderMap {
loop {}
}
pub(crate) fn version(&self) -> http::Version {
loop {}
}
pub(crate) fn extensions(&self) -> &http::Extensions {
loop {}
}
#[cfg(feature = "websocket")]
pub(crate) fn extensions_mut(&mut self) -> &mut http::Extensions {
loop {}
}
pub(crate) fn uri(&self) -> &http::Uri {
loop {}
}
pub(crate) fn path(&self) -> &str {
loop {}
}
pub(crate) fn full_path(&self) -> &str {
loop {}
}
pub(crate) fn set_unmatched_path(&mut self, index: usize) {
loop {}
}
pub(crate) fn query(&self) -> Option<&str> {
loop {}
}
pub(crate) fn matched_path_index(&self) -> usize {
loop {}
}
pub(crate) fn reset_matched_path_index(&mut self, index: usize) {
loop {}
}
pub(crate) fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
pub(crate) fn take_body(&mut self) -> Option<Body> {
loop {}
}
}

441
warp/src/server.rs Normal file
View file

@ -0,0 +1,441 @@
#[cfg(feature = "tls")]
use crate::tls::TlsConfigBuilder;
use std::convert::Infallible;
use std::error::Error as StdError;
use std::future::Future;
use std::net::SocketAddr;
#[cfg(feature = "tls")]
use std::path::Path;
use futures_util::{future, FutureExt, TryFuture, TryStream, TryStreamExt};
use hyper::server::conn::AddrIncoming;
use hyper::service::{make_service_fn, service_fn};
use hyper::Server as HyperServer;
use tokio::io::{AsyncRead, AsyncWrite};
use tracing::Instrument;
use crate::filter::Filter;
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::transport::Transport;
/// Create a `Server` with the provided `Filter`.
pub fn serve<F>(filter: F) -> Server<F>
where
F: Filter + Clone + Send + Sync + 'static,
F::Extract: Reply,
F::Error: IsReject,
{
loop {}
}
/// A Warp Server ready to filter requests.
#[derive(Debug)]
pub struct Server<F> {
pipeline: bool,
filter: F,
}
/// A Warp Server ready to filter requests over TLS.
///
/// *This type requires the `"tls"` feature.*
#[cfg(feature = "tls")]
pub struct TlsServer<F> {
server: Server<F>,
tls: TlsConfigBuilder,
}
macro_rules! into_service {
($into:expr) => {
{ let inner = crate ::service($into); make_service_fn(move | transport | { let
inner = inner.clone(); let remote_addr = Transport::remote_addr(transport);
future::ok::< _, Infallible > (service_fn(move | req | { inner
.call_with_addr(req, remote_addr) })) }) }
};
}
macro_rules! addr_incoming {
($addr:expr) => {
{ let mut incoming = AddrIncoming::bind($addr) ?; incoming.set_nodelay(true); let
addr = incoming.local_addr(); (addr, incoming) }
};
}
macro_rules! bind_inner {
($this:ident, $addr:expr) => {
{ let service = into_service!($this .filter); let (addr, incoming) =
addr_incoming!($addr); let srv = HyperServer::builder(incoming)
.http1_pipeline_flush($this .pipeline).serve(service); Ok::< _, hyper::Error >
((addr, srv)) }
};
(tls : $this:ident, $addr:expr) => {
{ let service = into_service!($this .server.filter); let (addr, incoming) =
addr_incoming!($addr); let tls = $this .tls.build() ?; let srv =
HyperServer::builder(crate ::tls::TlsAcceptor::new(tls, incoming))
.http1_pipeline_flush($this .server.pipeline).serve(service); Ok::< _, Box < dyn
std::error::Error + Send + Sync >> ((addr, srv)) }
};
}
macro_rules! bind {
($this:ident, $addr:expr) => {
{ let addr = $addr .into(); (| addr | bind_inner!($this, addr)) (& addr)
.unwrap_or_else(| e | { panic!("error binding to {}: {}", addr, e); }) }
};
(tls : $this:ident, $addr:expr) => {
{ let addr = $addr .into(); (| addr | bind_inner!(tls : $this, addr)) (& addr)
.unwrap_or_else(| e | { panic!("error binding to {}: {}", addr, e); }) }
};
}
macro_rules! try_bind {
($this:ident, $addr:expr) => {
{ (| addr | bind_inner!($this, addr)) ($addr) }
};
(tls : $this:ident, $addr:expr) => {
{ (| addr | bind_inner!(tls : $this, addr)) ($addr) }
};
}
impl<F> Server<F>
where
F: Filter + Clone + Send + Sync + 'static,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
{
/// Run this `Server` forever on the current thread.
pub async fn run(self, addr: impl Into<SocketAddr>) {
loop {}
}
/// Run this `Server` forever on the current thread with a specific stream
/// of incoming connections.
///
/// This can be used for Unix Domain Sockets, or TLS, etc.
pub async fn run_incoming<I>(self, incoming: I)
where
I: TryStream + Send,
I::Ok: AsyncRead + AsyncWrite + Send + 'static + Unpin,
I::Error: Into<Box<dyn StdError + Send + Sync>>,
{
loop {}
}
/// Bind to a socket address, returning a `Future` that can be
/// executed on the current runtime.
///
/// # Panics
///
/// Panics if we are unable to bind to the provided address.
pub fn bind(
self,
addr: impl Into<SocketAddr> + 'static,
) -> impl Future<Output = ()> + 'static {
let (_, fut) = self.bind_ephemeral(addr);
fut
}
/// Bind to a socket address, returning a `Future` that can be
/// executed on any runtime.
///
/// In case we are unable to bind to the specified address, resolves to an
/// error and logs the reason.
pub async fn try_bind(self, addr: impl Into<SocketAddr>) {
loop {}
}
/// Bind to a possibly ephemeral socket address.
///
/// Returns the bound address and a `Future` that can be executed on
/// the current runtime.
///
/// # Panics
///
/// Panics if we are unable to bind to the provided address.
pub fn bind_ephemeral(
self,
addr: impl Into<SocketAddr>,
) -> (SocketAddr, impl Future<Output = ()> + 'static) {
let (addr, srv) = bind!(self, addr);
let srv = srv
.map(|result| {
if let Err(err) = result {
tracing::error!("server error: {}", err)
}
});
(addr, srv)
}
/// Tried to bind a possibly ephemeral socket address.
///
/// Returns a `Result` which fails in case we are unable to bind with the
/// underlying error.
///
/// Returns the bound address and a `Future` that can be executed on
/// the current runtime.
pub fn try_bind_ephemeral(
self,
addr: impl Into<SocketAddr>,
) -> Result<(SocketAddr, impl Future<Output = ()> + 'static), crate::Error> {
let addr = addr.into();
let (addr, srv) = try_bind!(self, & addr).map_err(crate::Error::new)?;
let srv = srv
.map(|result| {
if let Err(err) = result {
tracing::error!("server error: {}", err)
}
});
Ok((addr, srv))
}
/// Create a server with graceful shutdown signal.
///
/// When the signal completes, the server will start the graceful shutdown
/// process.
///
/// Returns the bound address and a `Future` that can be executed on
/// the current runtime.
///
/// # Example
///
/// ```no_run
/// use warp::Filter;
/// use futures_util::future::TryFutureExt;
/// use tokio::sync::oneshot;
///
/// # fn main() {
/// let routes = warp::any()
/// .map(|| "Hello, World!");
///
/// let (tx, rx) = oneshot::channel();
///
/// let (addr, server) = warp::serve(routes)
/// .bind_with_graceful_shutdown(([127, 0, 0, 1], 3030), async {
/// rx.await.ok();
/// });
///
/// // Spawn the server into a runtime
/// tokio::task::spawn(server);
///
/// // Later, start the shutdown...
/// let _ = tx.send(());
/// # }
/// ```
pub fn bind_with_graceful_shutdown(
self,
addr: impl Into<SocketAddr> + 'static,
signal: impl Future<Output = ()> + Send + 'static,
) -> (SocketAddr, impl Future<Output = ()> + 'static) {
let (addr, srv) = bind!(self, addr);
let fut = srv
.with_graceful_shutdown(signal)
.map(|result| {
if let Err(err) = result {
tracing::error!("server error: {}", err)
}
});
(addr, fut)
}
/// Create a server with graceful shutdown signal.
///
/// When the signal completes, the server will start the graceful shutdown
/// process.
pub fn try_bind_with_graceful_shutdown(
self,
addr: impl Into<SocketAddr> + 'static,
signal: impl Future<Output = ()> + Send + 'static,
) -> Result<(SocketAddr, impl Future<Output = ()> + 'static), crate::Error> {
let addr = addr.into();
let (addr, srv) = try_bind!(self, & addr).map_err(crate::Error::new)?;
let srv = srv
.with_graceful_shutdown(signal)
.map(|result| {
if let Err(err) = result {
tracing::error!("server error: {}", err)
}
});
Ok((addr, srv))
}
/// Setup this `Server` with a specific stream of incoming connections.
///
/// This can be used for Unix Domain Sockets, or TLS, etc.
///
/// Returns a `Future` that can be executed on the current runtime.
pub fn serve_incoming<I>(self, incoming: I) -> impl Future<Output = ()>
where
I: TryStream + Send,
I::Ok: AsyncRead + AsyncWrite + Send + 'static + Unpin,
I::Error: Into<Box<dyn StdError + Send + Sync>>,
{
let incoming = incoming.map_ok(crate::transport::LiftIo);
self.serve_incoming2(incoming)
.instrument(tracing::info_span!("Server::serve_incoming"))
}
/// Setup this `Server` with a specific stream of incoming connections and a
/// signal to initiate graceful shutdown.
///
/// This can be used for Unix Domain Sockets, or TLS, etc.
///
/// When the signal completes, the server will start the graceful shutdown
/// process.
///
/// Returns a `Future` that can be executed on the current runtime.
pub fn serve_incoming_with_graceful_shutdown<I>(
self,
incoming: I,
signal: impl Future<Output = ()> + Send + 'static,
) -> impl Future<Output = ()>
where
I: TryStream + Send,
I::Ok: AsyncRead + AsyncWrite + Send + 'static + Unpin,
I::Error: Into<Box<dyn StdError + Send + Sync>>,
{
let incoming = incoming.map_ok(crate::transport::LiftIo);
let service = into_service!(self.filter);
let pipeline = self.pipeline;
async move {
let srv = HyperServer::builder(
hyper::server::accept::from_stream(incoming.into_stream()),
)
.http1_pipeline_flush(pipeline)
.serve(service)
.with_graceful_shutdown(signal)
.await;
if let Err(err) = srv {
tracing::error!("server error: {}", err);
}
}
.instrument(
tracing::info_span!("Server::serve_incoming_with_graceful_shutdown"),
)
}
async fn serve_incoming2<I>(self, incoming: I)
where
I: TryStream + Send,
I::Ok: Transport + Send + 'static + Unpin,
I::Error: Into<Box<dyn StdError + Send + Sync>>,
{
loop {}
}
#[doc(hidden)]
pub fn unstable_pipeline(mut self) -> Self {
loop {}
}
/// Configure a server to use TLS.
///
/// *This function requires the `"tls"` feature.*
#[cfg(feature = "tls")]
pub fn tls(self) -> TlsServer<F> {
loop {}
}
}
#[cfg(feature = "tls")]
impl<F> TlsServer<F>
where
F: Filter + Clone + Send + Sync + 'static,
<F::Future as TryFuture>::Ok: Reply,
<F::Future as TryFuture>::Error: IsReject,
{
/// Specify the file path to read the private key.
///
/// *This function requires the `"tls"` feature.*
pub fn key_path(self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Specify the file path to read the certificate.
///
/// *This function requires the `"tls"` feature.*
pub fn cert_path(self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Specify the file path to read the trust anchor for optional client authentication.
///
/// Anonymous and authenticated clients will be accepted. If no trust anchor is provided by any
/// of the `client_auth_` methods, then client authentication is disabled by default.
///
/// *This function requires the `"tls"` feature.*
pub fn client_auth_optional_path(self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Specify the file path to read the trust anchor for required client authentication.
///
/// Only authenticated clients will be accepted. If no trust anchor is provided by any of the
/// `client_auth_` methods, then client authentication is disabled by default.
///
/// *This function requires the `"tls"` feature.*
pub fn client_auth_required_path(self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Specify the in-memory contents of the private key.
///
/// *This function requires the `"tls"` feature.*
pub fn key(self, key: impl AsRef<[u8]>) -> Self {
loop {}
}
/// Specify the in-memory contents of the certificate.
///
/// *This function requires the `"tls"` feature.*
pub fn cert(self, cert: impl AsRef<[u8]>) -> Self {
loop {}
}
/// Specify the in-memory contents of the trust anchor for optional client authentication.
///
/// Anonymous and authenticated clients will be accepted. If no trust anchor is provided by any
/// of the `client_auth_` methods, then client authentication is disabled by default.
///
/// *This function requires the `"tls"` feature.*
pub fn client_auth_optional(self, trust_anchor: impl AsRef<[u8]>) -> Self {
loop {}
}
/// Specify the in-memory contents of the trust anchor for required client authentication.
///
/// Only authenticated clients will be accepted. If no trust anchor is provided by any of the
/// `client_auth_` methods, then client authentication is disabled by default.
///
/// *This function requires the `"tls"` feature.*
pub fn client_auth_required(self, trust_anchor: impl AsRef<[u8]>) -> Self {
loop {}
}
/// Specify the DER-encoded OCSP response.
///
/// *This function requires the `"tls"` feature.*
pub fn ocsp_resp(self, resp: impl AsRef<[u8]>) -> Self {
loop {}
}
fn with_tls<Func>(self, func: Func) -> Self
where
Func: FnOnce(TlsConfigBuilder) -> TlsConfigBuilder,
{
loop {}
}
/// Run this `TlsServer` forever on the current thread.
///
/// *This function requires the `"tls"` feature.*
pub async fn run(self, addr: impl Into<SocketAddr>) {
loop {}
}
/// Bind to a socket address, returning a `Future` that can be
/// executed on a runtime.
///
/// *This function requires the `"tls"` feature.*
pub async fn bind(self, addr: impl Into<SocketAddr>) {
loop {}
}
/// Bind to a possibly ephemeral socket address.
///
/// Returns the bound address and a `Future` that can be executed on
/// the current runtime.
///
/// *This function requires the `"tls"` feature.*
pub fn bind_ephemeral(
self,
addr: impl Into<SocketAddr>,
) -> (SocketAddr, impl Future<Output = ()> + 'static) {
loop {}
}
/// Create a server with graceful shutdown signal.
///
/// When the signal completes, the server will start the graceful shutdown
/// process.
///
/// *This function requires the `"tls"` feature.*
pub fn bind_with_graceful_shutdown(
self,
addr: impl Into<SocketAddr> + 'static,
signal: impl Future<Output = ()> + Send + 'static,
) -> (SocketAddr, impl Future<Output = ()> + 'static) {
loop {}
}
}
#[cfg(feature = "tls")]
impl<F> ::std::fmt::Debug for TlsServer<F>
where
F: ::std::fmt::Debug,
{
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
loop {}
}
}

3
warp/src/service.rs Normal file
View file

@ -0,0 +1,3 @@
//! Convert `Filter`s into `Service`s
pub use crate::filter::service::service;

537
warp/src/test.rs Normal file
View file

@ -0,0 +1,537 @@
//! Test utilities to test your filters.
//!
//! [`Filter`](../trait.Filter.html)s can be easily tested without starting up an HTTP
//! server, by making use of the [`RequestBuilder`](./struct.RequestBuilder.html) in this
//! module.
//!
//! # Testing Filters
//!
//! It's easy to test filters, especially if smaller filters are used to build
//! up your full set. Consider these example filters:
//!
//! ```
//! use warp::Filter;
//!
//! fn sum() -> impl Filter<Extract = (u32,), Error = warp::Rejection> + Copy {
//! warp::path::param()
//! .and(warp::path::param())
//! .map(|x: u32, y: u32| {
//! x + y
//! })
//! }
//!
//! fn math() -> impl Filter<Extract = (String,), Error = warp::Rejection> + Copy {
//! warp::post()
//! .and(sum())
//! .map(|z: u32| {
//! format!("Sum = {}", z)
//! })
//! }
//! ```
//!
//! We can test some requests against the `sum` filter like this:
//!
//! ```
//! # use warp::Filter;
//! #[tokio::test]
//! async fn test_sum() {
//! # let sum = || warp::any().map(|| 3);
//! let filter = sum();
//!
//! // Execute `sum` and get the `Extract` back.
//! let value = warp::test::request()
//! .path("/1/2")
//! .filter(&filter)
//! .await
//! .unwrap();
//! assert_eq!(value, 3);
//!
//! // Or simply test if a request matches (doesn't reject).
//! assert!(
//! warp::test::request()
//! .path("/1/-5")
//! .matches(&filter)
//! .await
//! );
//! }
//! ```
//!
//! If the filter returns something that implements `Reply`, and thus can be
//! turned into a response sent back to the client, we can test what exact
//! response is returned. The `math` filter uses the `sum` filter, but returns
//! a `String` that can be turned into a response.
//!
//! ```
//! # use warp::Filter;
//! #[test]
//! fn test_math() {
//! # let math = || warp::any().map(warp::reply);
//! let filter = math();
//!
//! let res = warp::test::request()
//! .path("/1/2")
//! .reply(&filter);
//! assert_eq!(res.status(), 405, "GET is not allowed");
//!
//! let res = warp::test::request()
//! .method("POST")
//! .path("/1/2")
//! .reply(&filter);
//! assert_eq!(res.status(), 200);
//! assert_eq!(res.body(), "Sum is 3");
//! }
//! ```
use std::convert::TryFrom;
use std::error::Error as StdError;
use std::fmt;
use std::net::SocketAddr;
#[cfg(feature = "websocket")]
use std::pin::Pin;
#[cfg(feature = "websocket")]
use std::task::Context;
#[cfg(feature = "websocket")]
use std::task::{self, Poll};
use bytes::Bytes;
#[cfg(feature = "websocket")]
use futures_channel::mpsc;
#[cfg(feature = "websocket")]
use futures_util::StreamExt;
use http::{
header::{HeaderName, HeaderValue},
Response,
};
use serde::Serialize;
#[cfg(feature = "websocket")]
use tokio::sync::oneshot;
use crate::filter::Filter;
#[cfg(feature = "websocket")]
use crate::filters::ws::Message;
use crate::reject::IsReject;
use crate::reply::Reply;
use crate::Request;
#[cfg(feature = "websocket")]
use crate::{Sink, Stream};
use self::inner::OneOrTuple;
/// Starts a new test `RequestBuilder`.
pub fn request() -> RequestBuilder {
loop {}
}
/// Starts a new test `WsBuilder`.
#[cfg(feature = "websocket")]
pub fn ws() -> WsBuilder {
loop {}
}
/// A request builder for testing filters.
///
/// See [module documentation](crate::test) for an overview.
#[must_use = "RequestBuilder does nothing on its own"]
#[derive(Debug)]
pub struct RequestBuilder {
remote_addr: Option<SocketAddr>,
req: Request,
}
/// A Websocket builder for testing filters.
///
/// See [module documentation](crate::test) for an overview.
#[cfg(feature = "websocket")]
#[must_use = "WsBuilder does nothing on its own"]
#[derive(Debug)]
pub struct WsBuilder {
req: RequestBuilder,
}
/// A test client for Websocket filters.
#[cfg(feature = "websocket")]
pub struct WsClient {
tx: mpsc::UnboundedSender<crate::ws::Message>,
rx: mpsc::UnboundedReceiver<Result<crate::ws::Message, crate::error::Error>>,
}
/// An error from Websocket filter tests.
#[derive(Debug)]
pub struct WsError {
cause: Box<dyn StdError + Send + Sync>,
}
impl RequestBuilder {
/// Sets the method of this builder.
///
/// The default if not set is `GET`.
///
/// # Example
///
/// ```
/// let req = warp::test::request()
/// .method("POST");
/// ```
///
/// # Panic
///
/// This panics if the passed string is not able to be parsed as a valid
/// `Method`.
pub fn method(mut self, method: &str) -> Self {
loop {}
}
/// Sets the request path of this builder.
///
/// The default is not set is `/`.
///
/// # Example
///
/// ```
/// let req = warp::test::request()
/// .path("/todos/33");
/// ```
///
/// # Panic
///
/// This panics if the passed string is not able to be parsed as a valid
/// `Uri`.
pub fn path(mut self, p: &str) -> Self {
loop {}
}
/// Set a header for this request.
///
/// # Example
///
/// ```
/// let req = warp::test::request()
/// .header("accept", "application/json");
/// ```
///
/// # Panic
///
/// This panics if the passed strings are not able to be parsed as a valid
/// `HeaderName` and `HeaderValue`.
pub fn header<K, V>(mut self, key: K, value: V) -> Self
where
HeaderName: TryFrom<K>,
HeaderValue: TryFrom<V>,
{
loop {}
}
/// Set the remote address of this request
///
/// Default is no remote address.
///
/// # Example
/// ```
/// use std::net::{IpAddr, Ipv4Addr, SocketAddr};
///
/// let req = warp::test::request()
/// .remote_addr(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080));
/// ```
pub fn remote_addr(mut self, addr: SocketAddr) -> Self {
loop {}
}
/// Add a type to the request's `http::Extensions`.
pub fn extension<T>(mut self, ext: T) -> Self
where
T: Send + Sync + 'static,
{
loop {}
}
/// Set the bytes of this request body.
///
/// Default is an empty body.
///
/// # Example
///
/// ```
/// let req = warp::test::request()
/// .body("foo=bar&baz=quux");
/// ```
pub fn body(mut self, body: impl AsRef<[u8]>) -> Self {
loop {}
}
/// Set the bytes of this request body by serializing a value into JSON.
///
/// # Example
///
/// ```
/// let req = warp::test::request()
/// .json(&true);
/// ```
pub fn json(mut self, val: &impl Serialize) -> Self {
loop {}
}
/// Tries to apply the `Filter` on this request.
///
/// # Example
///
/// ```no_run
/// async {
/// let param = warp::path::param::<u32>();
///
/// let ex = warp::test::request()
/// .path("/41")
/// .filter(&param)
/// .await
/// .unwrap();
///
/// assert_eq!(ex, 41);
///
/// assert!(
/// warp::test::request()
/// .path("/foo")
/// .filter(&param)
/// .await
/// .is_err()
/// );
///};
/// ```
pub async fn filter<F>(
self,
f: &F,
) -> Result<<F::Extract as OneOrTuple>::Output, F::Error>
where
F: Filter,
F::Future: Send + 'static,
F::Extract: OneOrTuple + Send + 'static,
F::Error: Send + 'static,
{
loop {}
}
/// Returns whether the `Filter` matches this request, or rejects it.
///
/// # Example
///
/// ```no_run
/// async {
/// let get = warp::get();
/// let post = warp::post();
///
/// assert!(
/// warp::test::request()
/// .method("GET")
/// .matches(&get)
/// .await
/// );
///
/// assert!(
/// !warp::test::request()
/// .method("GET")
/// .matches(&post)
/// .await
/// );
///};
/// ```
pub async fn matches<F>(self, f: &F) -> bool
where
F: Filter,
F::Future: Send + 'static,
F::Extract: Send + 'static,
F::Error: Send + 'static,
{
loop {}
}
/// Returns `Response` provided by applying the `Filter`.
///
/// This requires that the supplied `Filter` return a [`Reply`](Reply).
pub async fn reply<F>(self, f: &F) -> Response<Bytes>
where
F: Filter + 'static,
F::Extract: Reply + Send,
F::Error: IsReject + Send,
{
loop {}
}
}
#[cfg(feature = "websocket")]
impl WsBuilder {
/// Sets the request path of this builder.
///
/// The default is not set is `/`.
///
/// # Example
///
/// ```
/// let req = warp::test::ws()
/// .path("/chat");
/// ```
///
/// # Panic
///
/// This panics if the passed string is not able to be parsed as a valid
/// `Uri`.
pub fn path(self, p: &str) -> Self {
loop {}
}
/// Set a header for this request.
///
/// # Example
///
/// ```
/// let req = warp::test::ws()
/// .header("foo", "bar");
/// ```
///
/// # Panic
///
/// This panics if the passed strings are not able to be parsed as a valid
/// `HeaderName` and `HeaderValue`.
pub fn header<K, V>(self, key: K, value: V) -> Self
where
HeaderName: TryFrom<K>,
HeaderValue: TryFrom<V>,
{
loop {}
}
/// Execute this Websocket request against the provided filter.
///
/// If the handshake succeeds, returns a `WsClient`.
///
/// # Example
///
/// ```no_run
/// use futures_util::future;
/// use warp::Filter;
/// #[tokio::main]
/// # async fn main() {
///
/// // Some route that accepts websockets (but drops them immediately).
/// let route = warp::ws()
/// .map(|ws: warp::ws::Ws| {
/// ws.on_upgrade(|_| future::ready(()))
/// });
///
/// let client = warp::test::ws()
/// .handshake(route)
/// .await
/// .expect("handshake");
/// # }
/// ```
pub async fn handshake<F>(self, f: F) -> Result<WsClient, WsError>
where
F: Filter + Clone + Send + Sync + 'static,
F::Extract: Reply + Send,
F::Error: IsReject + Send,
{
loop {}
}
}
#[cfg(feature = "websocket")]
impl WsClient {
/// Send a "text" websocket message to the server.
pub async fn send_text(&mut self, text: impl Into<String>) {
loop {}
}
/// Send a websocket message to the server.
pub async fn send(&mut self, msg: crate::ws::Message) {
loop {}
}
/// Receive a websocket message from the server.
pub async fn recv(&mut self) -> Result<crate::filters::ws::Message, WsError> {
loop {}
}
/// Assert the server has closed the connection.
pub async fn recv_closed(&mut self) -> Result<(), WsError> {
loop {}
}
fn pinned_tx(
self: Pin<&mut Self>,
) -> Pin<&mut mpsc::UnboundedSender<crate::ws::Message>> {
loop {}
}
}
#[cfg(feature = "websocket")]
impl fmt::Debug for WsClient {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
#[cfg(feature = "websocket")]
impl Sink<crate::ws::Message> for WsClient {
type Error = WsError;
fn poll_ready(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
fn start_send(self: Pin<&mut Self>, message: Message) -> Result<(), Self::Error> {
loop {}
}
fn poll_flush(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
fn poll_close(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
}
#[cfg(feature = "websocket")]
impl Stream for WsClient {
type Item = Result<crate::ws::Message, WsError>;
fn poll_next(
self: Pin<&mut Self>,
context: &mut Context<'_>,
) -> Poll<Option<Self::Item>> {
loop {}
}
}
#[cfg(feature = "websocket")]
impl WsError {
fn new<E: Into<Box<dyn StdError + Send + Sync>>>(cause: E) -> Self {
loop {}
}
}
impl fmt::Display for WsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl StdError for WsError {
fn description(&self) -> &str {
loop {}
}
}
#[cfg(feature = "websocket")]
#[derive(Clone)]
struct AddrConnect(SocketAddr);
#[cfg(feature = "websocket")]
impl tower_service::Service<::http::Uri> for AddrConnect {
type Response = ::tokio::net::TcpStream;
type Error = ::std::io::Error;
type Future = Pin<
Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>,
>;
fn poll_ready(
&mut self,
_cx: &mut task::Context<'_>,
) -> Poll<Result<(), Self::Error>> {
loop {}
}
fn call(&mut self, _: ::http::Uri) -> Self::Future {
loop {}
}
}
mod inner {
pub trait OneOrTuple {
type Output;
fn one_or_tuple(self) -> Self::Output;
}
impl OneOrTuple for () {
type Output = ();
fn one_or_tuple(self) -> Self::Output {}
}
macro_rules! one_or_tuple {
($type1:ident) => {
impl <$type1 > OneOrTuple for ($type1,) { type Output = $type1; fn
one_or_tuple(self) -> Self::Output { self.0 } }
};
($type1:ident, $($type:ident),*) => {
one_or_tuple!($($type),*); impl <$type1, $($type),*> OneOrTuple for ($type1,
$($type),*) { type Output = Self; fn one_or_tuple(self) -> Self::Output {
self } }
};
}
one_or_tuple! {
T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16
}
}

212
warp/src/tls.rs Normal file
View file

@ -0,0 +1,212 @@
use std::fmt;
use std::fs::File;
use std::future::Future;
use std::io::{self, BufReader, Cursor, Read};
use std::net::SocketAddr;
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
use futures_util::ready;
use hyper::server::accept::Accept;
use hyper::server::conn::{AddrIncoming, AddrStream};
use crate::transport::Transport;
use tokio_rustls::rustls::{
server::{
AllowAnyAnonymousOrAuthenticatedClient, AllowAnyAuthenticatedClient, NoClientAuth,
},
Certificate, Error as TlsError, PrivateKey, RootCertStore, ServerConfig,
};
/// Represents errors that can occur building the TlsConfig
#[derive(Debug)]
pub(crate) enum TlsConfigError {
Io(io::Error),
/// An Error parsing the Certificate
CertParseError,
/// An Error parsing a Pkcs8 key
Pkcs8ParseError,
/// An Error parsing a Rsa key
RsaParseError,
/// An error from an empty key
EmptyKey,
/// An error from an invalid key
InvalidKey(TlsError),
}
impl fmt::Display for TlsConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl std::error::Error for TlsConfigError {}
/// Tls client authentication configuration.
pub(crate) enum TlsClientAuth {
/// No client auth.
Off,
/// Allow any anonymous or authenticated client.
Optional(Box<dyn Read + Send + Sync>),
/// Allow any authenticated client.
Required(Box<dyn Read + Send + Sync>),
}
/// Builder to set the configuration for the Tls server.
pub(crate) struct TlsConfigBuilder {
cert: Box<dyn Read + Send + Sync>,
key: Box<dyn Read + Send + Sync>,
client_auth: TlsClientAuth,
ocsp_resp: Vec<u8>,
}
impl fmt::Debug for TlsConfigBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
loop {}
}
}
impl TlsConfigBuilder {
/// Create a new TlsConfigBuilder
pub(crate) fn new() -> TlsConfigBuilder {
loop {}
}
/// sets the Tls key via File Path, returns `TlsConfigError::IoError` if the file cannot be open
pub(crate) fn key_path(mut self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// sets the Tls key via bytes slice
pub(crate) fn key(mut self, key: &[u8]) -> Self {
loop {}
}
/// Specify the file path for the TLS certificate to use.
pub(crate) fn cert_path(mut self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// sets the Tls certificate via bytes slice
pub(crate) fn cert(mut self, cert: &[u8]) -> Self {
loop {}
}
/// Sets the trust anchor for optional Tls client authentication via file path.
///
/// Anonymous and authenticated clients will be accepted. If no trust anchor is provided by any
/// of the `client_auth_` methods, then client authentication is disabled by default.
pub(crate) fn client_auth_optional_path(mut self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Sets the trust anchor for optional Tls client authentication via bytes slice.
///
/// Anonymous and authenticated clients will be accepted. If no trust anchor is provided by any
/// of the `client_auth_` methods, then client authentication is disabled by default.
pub(crate) fn client_auth_optional(mut self, trust_anchor: &[u8]) -> Self {
loop {}
}
/// Sets the trust anchor for required Tls client authentication via file path.
///
/// Only authenticated clients will be accepted. If no trust anchor is provided by any of the
/// `client_auth_` methods, then client authentication is disabled by default.
pub(crate) fn client_auth_required_path(mut self, path: impl AsRef<Path>) -> Self {
loop {}
}
/// Sets the trust anchor for required Tls client authentication via bytes slice.
///
/// Only authenticated clients will be accepted. If no trust anchor is provided by any of the
/// `client_auth_` methods, then client authentication is disabled by default.
pub(crate) fn client_auth_required(mut self, trust_anchor: &[u8]) -> Self {
loop {}
}
/// sets the DER-encoded OCSP response
pub(crate) fn ocsp_resp(mut self, ocsp_resp: &[u8]) -> Self {
loop {}
}
pub(crate) fn build(mut self) -> Result<ServerConfig, TlsConfigError> {
loop {}
}
}
struct LazyFile {
path: PathBuf,
file: Option<File>,
}
impl LazyFile {
fn lazy_read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {}
}
}
impl Read for LazyFile {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
loop {}
}
}
impl Transport for TlsStream {
fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
}
enum State {
Handshaking(tokio_rustls::Accept<AddrStream>),
Streaming(tokio_rustls::server::TlsStream<AddrStream>),
}
pub(crate) struct TlsStream {
state: State,
remote_addr: SocketAddr,
}
impl TlsStream {
fn new(stream: AddrStream, config: Arc<ServerConfig>) -> TlsStream {
loop {}
}
}
impl AsyncRead for TlsStream {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
loop {}
}
}
impl AsyncWrite for TlsStream {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
loop {}
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
loop {}
}
fn poll_shutdown(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<io::Result<()>> {
loop {}
}
}
pub(crate) struct TlsAcceptor {
config: Arc<ServerConfig>,
incoming: AddrIncoming,
}
impl TlsAcceptor {
pub(crate) fn new(config: ServerConfig, incoming: AddrIncoming) -> TlsAcceptor {
loop {}
}
}
impl Accept for TlsAcceptor {
type Conn = TlsStream;
type Error = io::Error;
fn poll_accept(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
loop {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn file_cert_key() {
loop {}
}
#[test]
fn bytes_cert_key() {
loop {}
}
}

50
warp/src/transport.rs Normal file
View file

@ -0,0 +1,50 @@
use std::io;
use std::net::SocketAddr;
use std::pin::Pin;
use std::task::{Context, Poll};
use hyper::server::conn::AddrStream;
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
pub trait Transport: AsyncRead + AsyncWrite {
fn remote_addr(&self) -> Option<SocketAddr>;
}
impl Transport for AddrStream {
fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
}
pub(crate) struct LiftIo<T>(pub(crate) T);
impl<T: AsyncRead + Unpin> AsyncRead for LiftIo<T> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
loop {}
}
}
impl<T: AsyncWrite + Unpin> AsyncWrite for LiftIo<T> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
loop {}
}
fn poll_flush(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
loop {}
}
fn poll_shutdown(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Result<(), io::Error>> {
loop {}
}
}
impl<T: AsyncRead + AsyncWrite + Unpin> Transport for LiftIo<T> {
fn remote_addr(&self) -> Option<SocketAddr> {
loop {}
}
}