From d67ec8f0857c5dac4fb779b68ef06b2d6d52c3d0 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 13 May 2019 21:03:16 +0530 Subject: [PATCH 01/54] isolate core revamp fix cargo fmt loosen up clippy to warnings on examples snipe dev deps from core eliminate some clippy warnings for examples add publish false, and remove publish requirements rename tide-examples to just examples --- Cargo.toml | 65 ++----------------- examples/Cargo.toml | 46 +++++++++++++ examples/{ => src}/body_types.rs | 4 +- examples/{ => src}/catch_all.rs | 4 +- examples/{ => src}/cookies.rs | 5 +- examples/{ => src}/default_headers.rs | 4 +- examples/{ => src}/graphql.rs | 5 +- examples/{ => src}/hello.rs | 4 +- examples/src/lib.rs | 13 ++++ examples/{ => src}/messages.rs | 4 +- .../main.rs => src/multipart_form/mod.rs} | 4 +- .../multipart_form}/test.txt | 0 examples/{ => src}/staticfile.rs | 6 +- tide/Cargo.toml | 52 +++++++++++++++ {src => tide/src}/app.rs | 0 {src => tide/src}/context.rs | 0 {src => tide/src}/cookies.rs | 0 {src => tide/src}/endpoint.rs | 0 {src => tide/src}/error.rs | 0 {src => tide/src}/forms.rs | 0 {src => tide/src}/lib.rs | 0 {src => tide/src}/middleware/cookies.rs | 0 .../src}/middleware/default_headers.rs | 0 {src => tide/src}/middleware/logger.rs | 0 {src => tide/src}/middleware/mod.rs | 0 {src => tide/src}/querystring.rs | 0 {src => tide/src}/response.rs | 0 {src => tide/src}/route.rs | 0 {src => tide/src}/router.rs | 0 {tests => tide/tests}/wildcard.rs | 0 30 files changed, 125 insertions(+), 91 deletions(-) create mode 100644 examples/Cargo.toml rename examples/{ => src}/body_types.rs (97%) rename examples/{ => src}/catch_all.rs (88%) rename examples/{ => src}/cookies.rs (95%) rename examples/{ => src}/default_headers.rs (89%) rename examples/{ => src}/graphql.rs (98%) rename examples/{ => src}/hello.rs (77%) create mode 100644 examples/src/lib.rs rename examples/{ => src}/messages.rs (98%) rename examples/{multipart-form/main.rs => src/multipart_form/mod.rs} (98%) rename examples/{multipart-form => src/multipart_form}/test.txt (100%) rename examples/{ => src}/staticfile.rs (98%) create mode 100644 tide/Cargo.toml rename {src => tide/src}/app.rs (100%) rename {src => tide/src}/context.rs (100%) rename {src => tide/src}/cookies.rs (100%) rename {src => tide/src}/endpoint.rs (100%) rename {src => tide/src}/error.rs (100%) rename {src => tide/src}/forms.rs (100%) rename {src => tide/src}/lib.rs (100%) rename {src => tide/src}/middleware/cookies.rs (100%) rename {src => tide/src}/middleware/default_headers.rs (100%) rename {src => tide/src}/middleware/logger.rs (100%) rename {src => tide/src}/middleware/mod.rs (100%) rename {src => tide/src}/querystring.rs (100%) rename {src => tide/src}/response.rs (100%) rename {src => tide/src}/route.rs (100%) rename {src => tide/src}/router.rs (100%) rename {tests => tide/tests}/wildcard.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index d90daaea7..78f2f7172 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,65 +1,8 @@ -[package] -authors = [ - "Aaron Turon ", - "Yoshua Wuyts ", +[workspace] +members = [ + "tide", + "examples", ] -description = "WIP modular web framework" -documentation = "https://docs.rs/tide" -keywords = ["tide", "http", "web", "framework", "async"] -categories = [ - "network-programming", - "asynchronous", - "web-programming::http-server" -] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "tide" -readme = "README.md" -repository = "https://github.com/rustasync/tide" -version = "0.2.0" - -[dependencies] -cookie = { version = "0.12", features = ["percent-encode"] } -futures-preview = "0.3.0-alpha.16" -fnv = "1.0.6" -http = "0.1" -http-service = "0.2.0" -pin-utils = "0.1.0-alpha.4" -route-recognizer = "0.1.12" -serde = "1.0.91" -serde_derive = "1.0.91" -serde_json = "1.0.39" -slog = "2.4.1" -slog-async = "2.3.0" -slog-term = "2.4.0" -typemap = "0.3.3" -serde_urlencoded = "0.5.5" - -[dependencies.http-service-hyper] -optional = true -version = "0.2.0" - -[dependencies.multipart] -default-features = false -features = ["server"] -version = "0.16.1" - -[features] -default = ["hyper"] -hyper = ["http-service-hyper"] - -[dev-dependencies] -basic-cookies = "0.1.3" -bytes = "0.4.12" -futures-fs = "0.0.5" -futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } -http-service-mock = "0.2.0" -juniper = "0.11.1" -mime = "0.3.13" -mime_guess = "2.0.0-alpha.6" -percent-encoding = "1.0.1" -serde = { version = "1.0.90", features = ["derive"] } -structopt = "0.2.15" [patch.crates-io] http-service = { git = "https://github.com/rustasync/http-service", branch = "master" } diff --git a/examples/Cargo.toml b/examples/Cargo.toml new file mode 100644 index 000000000..54a73b1ff --- /dev/null +++ b/examples/Cargo.toml @@ -0,0 +1,46 @@ +[package] +authors = [ + "Tide Developers", +] +description = "Tide web server examples" +documentation = "https://docs.rs/tide" +edition = "2018" +license = "MIT OR Apache-2.0" +name = "examples" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" +publish = false + +[dependencies] +tide = { path = "../tide" } +cookie = { version = "0.12", features = ["percent-encode"] } +futures-preview = "0.3.0-alpha.16" +fnv = "1.0.6" +http = "0.1" +http-service = "0.2.0" +pin-utils = "0.1.0-alpha.4" +route-recognizer = "0.1.12" +serde_json = "1.0.39" +slog = "2.4.1" +slog-async = "2.3.0" +slog-term = "2.4.0" +typemap = "0.3.3" +serde_urlencoded = "0.5.5" +basic-cookies = "0.1.3" +bytes = "0.4.12" +futures-fs = "0.0.5" +futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } +http-service-mock = "0.2.0" +juniper = "0.11.1" +mime = "0.3.13" +mime_guess = "2.0.0-alpha.6" +percent-encoding = "1.0.1" +serde = { version = "1.0.91", features = ["derive"] } +structopt = "0.2.15" + +[dependencies.multipart] +default-features = false +features = ["server"] +version = "0.16.1" + diff --git a/examples/body_types.rs b/examples/src/body_types.rs similarity index 97% rename from examples/body_types.rs rename to examples/src/body_types.rs index 01c9b393c..0cc0e66a1 100644 --- a/examples/body_types.rs +++ b/examples/src/body_types.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use serde::{Deserialize, Serialize}; use tide::{ error::ResultExt, @@ -41,7 +39,7 @@ async fn echo_form(mut cx: Context<()>) -> EndpointResult { Ok(forms::form(msg)) } -fn main() { +pub fn main() { let mut app = App::new(); app.at("/echo/string").post(echo_string); diff --git a/examples/catch_all.rs b/examples/src/catch_all.rs similarity index 88% rename from examples/catch_all.rs rename to examples/src/catch_all.rs index 354ddbb03..69947f1f7 100644 --- a/examples/catch_all.rs +++ b/examples/src/catch_all.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use tide::Context; async fn echo_path(cx: Context<()>) -> String { @@ -7,7 +5,7 @@ async fn echo_path(cx: Context<()>) -> String { format!("Your path is: {}", path) } -fn main() { +pub fn main() { let mut app = tide::App::new(); app.at("/echo_path/*path").get(echo_path); app.serve("127.0.0.1:8000").unwrap(); diff --git a/examples/cookies.rs b/examples/src/cookies.rs similarity index 95% rename from examples/cookies.rs rename to examples/src/cookies.rs index 094030f27..4ebff0738 100644 --- a/examples/cookies.rs +++ b/examples/src/cookies.rs @@ -1,10 +1,7 @@ -#![feature(async_await)] - use cookie::Cookie; use tide::{cookies::ContextExt, middleware::CookiesMiddleware, Context}; /// Tide will use the the `Cookies`'s `Extract` implementation to build this parameter. -/// async fn retrieve_cookie(mut cx: Context<()>) -> String { format!("hello cookies: {:?}", cx.get_cookie("hello").unwrap()) } @@ -19,7 +16,7 @@ async fn remove_cookie(mut cx: Context<()>) { cx.remove_cookie(Cookie::named("hello")).unwrap(); } -fn main() { +pub fn main() { let mut app = tide::App::new(); app.middleware(CookiesMiddleware::new()); diff --git a/examples/default_headers.rs b/examples/src/default_headers.rs similarity index 89% rename from examples/default_headers.rs rename to examples/src/default_headers.rs index 47f13a091..23f057e30 100644 --- a/examples/default_headers.rs +++ b/examples/src/default_headers.rs @@ -1,8 +1,6 @@ -#![feature(async_await)] - use tide::middleware::DefaultHeaders; -fn main() { +pub fn main() { let mut app = tide::App::new(); app.middleware( diff --git a/examples/graphql.rs b/examples/src/graphql.rs similarity index 98% rename from examples/graphql.rs rename to examples/src/graphql.rs index aeb9abfdf..7a263cbcf 100644 --- a/examples/graphql.rs +++ b/examples/src/graphql.rs @@ -2,9 +2,6 @@ // a look at [the Juniper book]. // // [the Juniper book]: https://graphql-rust.github.io/ - -#![feature(async_await)] - use http::status::StatusCode; use juniper::graphql_object; use std::sync::{atomic, Arc}; @@ -59,7 +56,7 @@ async fn handle_graphql(mut cx: Context) -> EndpointResult { Ok(resp) } -fn main() { +pub fn main() { let mut app = App::with_state(Data::default()); app.at("/graphql").post(handle_graphql); app.serve("127.0.0.1:8000").unwrap(); diff --git a/examples/hello.rs b/examples/src/hello.rs similarity index 77% rename from examples/hello.rs rename to examples/src/hello.rs index 030d394c1..83e246463 100644 --- a/examples/hello.rs +++ b/examples/src/hello.rs @@ -1,6 +1,4 @@ -#![feature(async_await)] - -fn main() { +pub fn main() { let mut app = tide::App::new(); app.at("/").get(async move |_| "Hello, world!"); app.serve("127.0.0.1:8000").unwrap(); diff --git a/examples/src/lib.rs b/examples/src/lib.rs new file mode 100644 index 000000000..31679fc28 --- /dev/null +++ b/examples/src/lib.rs @@ -0,0 +1,13 @@ +#![feature(async_await)] +#![warn(clippy::all)] +#![allow(dead_code)] + +mod body_types; +mod catch_all; +mod cookies; +mod default_headers; +mod graphql; +mod hello; +mod messages; +mod multipart_form; +mod staticfile; diff --git a/examples/messages.rs b/examples/src/messages.rs similarity index 98% rename from examples/messages.rs rename to examples/src/messages.rs index 7b8ef25dd..3ec8db93e 100644 --- a/examples/messages.rs +++ b/examples/src/messages.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use http::status::StatusCode; use serde::{Deserialize, Serialize}; use std::sync::Mutex; @@ -66,7 +64,7 @@ async fn get_message(cx: Context) -> EndpointResult { } } -fn main() { +pub fn main() { let mut app = App::with_state(Database::default()); app.at("/message").post(new_message); app.at("/message/:id").get(get_message).post(set_message); diff --git a/examples/multipart-form/main.rs b/examples/src/multipart_form/mod.rs similarity index 98% rename from examples/multipart-form/main.rs rename to examples/src/multipart_form/mod.rs index 14ca63639..fd3485b12 100644 --- a/examples/multipart-form/main.rs +++ b/examples/src/multipart_form/mod.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use serde::{Deserialize, Serialize}; use std::io::Read; use tide::{forms::ExtractForms, response, App, Context, EndpointResult}; @@ -57,7 +55,7 @@ async fn upload_file(mut cx: Context<()>) -> EndpointResult { Ok(response::json(message)) } -fn main() { +pub fn run() { let mut app = App::new(); app.at("/upload_file").post(upload_file); app.serve("127.0.0.1:8000").unwrap(); diff --git a/examples/multipart-form/test.txt b/examples/src/multipart_form/test.txt similarity index 100% rename from examples/multipart-form/test.txt rename to examples/src/multipart_form/test.txt diff --git a/examples/staticfile.rs b/examples/src/staticfile.rs similarity index 98% rename from examples/staticfile.rs rename to examples/src/staticfile.rs index 462a1f4bd..63524b36b 100644 --- a/examples/staticfile.rs +++ b/examples/src/staticfile.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use bytes::Bytes; use futures_fs::FsPool; use futures_util::compat::*; @@ -44,7 +42,7 @@ impl StaticFile { // Check if the path exists and handle if it's a directory containing `index.html` if meta.is_some() && meta.as_ref().map(|m| !m.is_file()).unwrap_or(false) { // Redirect if path is a dir and URL doesn't end with "/" - if !actual_path.ends_with("/") { + if !actual_path.ends_with('/') { return Ok(response .status(StatusCode::MOVED_PERMANENTLY) .header(header::LOCATION, String::from(actual_path) + "/") @@ -121,7 +119,7 @@ async fn handle_path(ctx: Context) -> EndpointResult { }) } -fn main() { +pub fn main() { let mut app = App::with_state(StaticFile::new("./")); app.at("/*").get(handle_path); app.serve("127.0.0.1:8000").unwrap(); diff --git a/tide/Cargo.toml b/tide/Cargo.toml new file mode 100644 index 000000000..9e70bf5ea --- /dev/null +++ b/tide/Cargo.toml @@ -0,0 +1,52 @@ +[package] +authors = [ + "Aaron Turon ", + "Yoshua Wuyts ", +] +description = "WIP modular web framework" +documentation = "https://docs.rs/tide" +keywords = ["tide", "http", "web", "framework", "async"] +categories = [ + "network-programming", + "asynchronous", + "web-programming::http-server" +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.2.0" + +[dependencies] +cookie = { version = "0.12", features = ["percent-encode"] } +futures-preview = "0.3.0-alpha.16" +fnv = "1.0.6" +http = "0.1" +http-service = "0.2.0" +pin-utils = "0.1.0-alpha.4" +route-recognizer = "0.1.12" +serde = "1.0.91" +serde_derive = "1.0.91" +serde_json = "1.0.39" +slog = "2.4.1" +slog-async = "2.3.0" +slog-term = "2.4.0" +typemap = "0.3.3" +serde_urlencoded = "0.5.5" + +[dependencies.http-service-hyper] +optional = true +version = "0.2.0" + +[dependencies.multipart] +default-features = false +features = ["server"] +version = "0.16.1" + +[features] +default = ["hyper"] +hyper = ["http-service-hyper"] + +[dev-dependencies] +http-service-mock = "0.2.0" diff --git a/src/app.rs b/tide/src/app.rs similarity index 100% rename from src/app.rs rename to tide/src/app.rs diff --git a/src/context.rs b/tide/src/context.rs similarity index 100% rename from src/context.rs rename to tide/src/context.rs diff --git a/src/cookies.rs b/tide/src/cookies.rs similarity index 100% rename from src/cookies.rs rename to tide/src/cookies.rs diff --git a/src/endpoint.rs b/tide/src/endpoint.rs similarity index 100% rename from src/endpoint.rs rename to tide/src/endpoint.rs diff --git a/src/error.rs b/tide/src/error.rs similarity index 100% rename from src/error.rs rename to tide/src/error.rs diff --git a/src/forms.rs b/tide/src/forms.rs similarity index 100% rename from src/forms.rs rename to tide/src/forms.rs diff --git a/src/lib.rs b/tide/src/lib.rs similarity index 100% rename from src/lib.rs rename to tide/src/lib.rs diff --git a/src/middleware/cookies.rs b/tide/src/middleware/cookies.rs similarity index 100% rename from src/middleware/cookies.rs rename to tide/src/middleware/cookies.rs diff --git a/src/middleware/default_headers.rs b/tide/src/middleware/default_headers.rs similarity index 100% rename from src/middleware/default_headers.rs rename to tide/src/middleware/default_headers.rs diff --git a/src/middleware/logger.rs b/tide/src/middleware/logger.rs similarity index 100% rename from src/middleware/logger.rs rename to tide/src/middleware/logger.rs diff --git a/src/middleware/mod.rs b/tide/src/middleware/mod.rs similarity index 100% rename from src/middleware/mod.rs rename to tide/src/middleware/mod.rs diff --git a/src/querystring.rs b/tide/src/querystring.rs similarity index 100% rename from src/querystring.rs rename to tide/src/querystring.rs diff --git a/src/response.rs b/tide/src/response.rs similarity index 100% rename from src/response.rs rename to tide/src/response.rs diff --git a/src/route.rs b/tide/src/route.rs similarity index 100% rename from src/route.rs rename to tide/src/route.rs diff --git a/src/router.rs b/tide/src/router.rs similarity index 100% rename from src/router.rs rename to tide/src/router.rs diff --git a/tests/wildcard.rs b/tide/tests/wildcard.rs similarity index 100% rename from tests/wildcard.rs rename to tide/tests/wildcard.rs From a376d602eebf30afacf4f4be4284c0e09c1dfe53 Mon Sep 17 00:00:00 2001 From: dalei Date: Wed, 15 May 2019 11:06:20 +0800 Subject: [PATCH 02/54] Fix example link in readme --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 548b64ba2..6ed6885f2 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,14 @@ fn main() -> Result<(), std::io::Error> { **More Examples** -- [Hello World](https://github.com/rustasync/tide/tree/master/examples/hello.rs) -- [Messages](https://github.com/rustasync/tide/blob/master/examples/messages.rs) -- [Body Types](https://github.com/rustasync/tide/blob/master/examples/body_types.rs) -- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/multipart-form/main.rs) -- [Catch All](https://github.com/rustasync/tide/tree/master/examples/catch_all.rs) -- [Cookies](https://github.com/rustasync/tide/tree/master/examples/cookies.rs) -- [Default Headers](https://github.com/rustasync/tide/tree/master/examples/default_headers.rs) -- [GraphQL](https://github.com/rustasync/tide/tree/master/examples/graphql.rs) +- [Hello World](https://github.com/rustasync/tide/tree/master/examples/src/hello.rs) +- [Messages](https://github.com/rustasync/tide/blob/master/examples/src/messages.rs) +- [Body Types](https://github.com/rustasync/tide/blob/master/examples/src/body_types.rs) +- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/src/multipart-form/main.rs) +- [Catch All](https://github.com/rustasync/tide/tree/master/examples/src/catch_all.rs) +- [Cookies](https://github.com/rustasync/tide/tree/master/examples/src/cookies.rs) +- [Default Headers](https://github.com/rustasync/tide/tree/master/src/examples/default_headers.rs) +- [GraphQL](https://github.com/rustasync/tide/tree/master/examples/src/graphql.rs) ## Resources From be577efc069a2c34332f475e6493f9ee67e2ddc4 Mon Sep 17 00:00:00 2001 From: "Prasanna V. Loganathar" Date: Wed, 15 May 2019 09:19:38 +0530 Subject: [PATCH 03/54] fix broken links in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6ed6885f2..043de727f 100644 --- a/README.md +++ b/README.md @@ -72,12 +72,12 @@ fn main() -> Result<(), std::io::Error> { **More Examples** - [Hello World](https://github.com/rustasync/tide/tree/master/examples/src/hello.rs) -- [Messages](https://github.com/rustasync/tide/blob/master/examples/src/messages.rs) -- [Body Types](https://github.com/rustasync/tide/blob/master/examples/src/body_types.rs) -- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/src/multipart-form/main.rs) +- [Messages](https://github.com/rustasync/tide/tree/master/examples/src/messages.rs) +- [Body Types](https://github.com/rustasync/tide/tree/master/examples/src/body_types.rs) +- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/src/multipart_form/mod.rs) - [Catch All](https://github.com/rustasync/tide/tree/master/examples/src/catch_all.rs) - [Cookies](https://github.com/rustasync/tide/tree/master/examples/src/cookies.rs) -- [Default Headers](https://github.com/rustasync/tide/tree/master/src/examples/default_headers.rs) +- [Default Headers](https://github.com/rustasync/tide/tree/master/examples/src/default_headers.rs) - [GraphQL](https://github.com/rustasync/tide/tree/master/examples/src/graphql.rs) ## Resources From f4473d6489f461c92dd387d3a42fbc6d92e16b68 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Wed, 15 May 2019 14:53:04 +0900 Subject: [PATCH 04/54] Rename `serve` to `run`, add asynchronous `serve` --- README.md | 2 +- examples/src/body_types.rs | 2 +- examples/src/catch_all.rs | 2 +- examples/src/cookies.rs | 2 +- examples/src/default_headers.rs | 2 +- examples/src/graphql.rs | 2 +- examples/src/hello.rs | 2 +- examples/src/messages.rs | 2 +- examples/src/multipart_form/mod.rs | 2 +- examples/src/staticfile.rs | 2 +- tide/src/app.rs | 23 ++++++++++++++++++----- tide/src/endpoint.rs | 4 ++-- 12 files changed, 30 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 043de727f..124409d03 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Ecosystem WG, and **not ready for production use yet**. fn main() -> Result<(), std::io::Error> { let mut app = tide::App::new(); app.at("/").get(async move |_| "Hello, world!"); - Ok(app.serve("127.0.0.1:8000")?) + Ok(app.run("127.0.0.1:8000")?) } ``` diff --git a/examples/src/body_types.rs b/examples/src/body_types.rs index 0cc0e66a1..9df1509a5 100644 --- a/examples/src/body_types.rs +++ b/examples/src/body_types.rs @@ -47,5 +47,5 @@ pub fn main() { app.at("/echo/json").post(echo_json); app.at("/echo/form").post(echo_form); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/catch_all.rs b/examples/src/catch_all.rs index 69947f1f7..ebdf736c3 100644 --- a/examples/src/catch_all.rs +++ b/examples/src/catch_all.rs @@ -8,5 +8,5 @@ async fn echo_path(cx: Context<()>) -> String { pub fn main() { let mut app = tide::App::new(); app.at("/echo_path/*path").get(echo_path); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/cookies.rs b/examples/src/cookies.rs index 4ebff0738..a43dc67c1 100644 --- a/examples/src/cookies.rs +++ b/examples/src/cookies.rs @@ -23,5 +23,5 @@ pub fn main() { app.at("/").get(retrieve_cookie); app.at("/set").get(set_cookie); app.at("/remove").get(remove_cookie); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/default_headers.rs b/examples/src/default_headers.rs index 23f057e30..b76bfddc7 100644 --- a/examples/src/default_headers.rs +++ b/examples/src/default_headers.rs @@ -11,5 +11,5 @@ pub fn main() { app.at("/").get(async move |_| "Hello, world!"); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/graphql.rs b/examples/src/graphql.rs index 7a263cbcf..a85af2e69 100644 --- a/examples/src/graphql.rs +++ b/examples/src/graphql.rs @@ -59,5 +59,5 @@ async fn handle_graphql(mut cx: Context) -> EndpointResult { pub fn main() { let mut app = App::with_state(Data::default()); app.at("/graphql").post(handle_graphql); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/hello.rs b/examples/src/hello.rs index 83e246463..1a11e05c9 100644 --- a/examples/src/hello.rs +++ b/examples/src/hello.rs @@ -1,5 +1,5 @@ pub fn main() { let mut app = tide::App::new(); app.at("/").get(async move |_| "Hello, world!"); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/messages.rs b/examples/src/messages.rs index 3ec8db93e..4cf905f79 100644 --- a/examples/src/messages.rs +++ b/examples/src/messages.rs @@ -68,5 +68,5 @@ pub fn main() { let mut app = App::with_state(Database::default()); app.at("/message").post(new_message); app.at("/message/:id").get(get_message).post(set_message); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/examples/src/multipart_form/mod.rs b/examples/src/multipart_form/mod.rs index fd3485b12..ce4d74ca3 100644 --- a/examples/src/multipart_form/mod.rs +++ b/examples/src/multipart_form/mod.rs @@ -58,7 +58,7 @@ async fn upload_file(mut cx: Context<()>) -> EndpointResult { pub fn run() { let mut app = App::new(); app.at("/upload_file").post(upload_file); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } // Test with: diff --git a/examples/src/staticfile.rs b/examples/src/staticfile.rs index 63524b36b..cf44ccd97 100644 --- a/examples/src/staticfile.rs +++ b/examples/src/staticfile.rs @@ -122,5 +122,5 @@ async fn handle_path(ctx: Context) -> EndpointResult { pub fn main() { let mut app = App::with_state(StaticFile::new("./")); app.at("/*").get(handle_path); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } diff --git a/tide/src/app.rs b/tide/src/app.rs index 7e4273036..83cd3f517 100644 --- a/tide/src/app.rs +++ b/tide/src/app.rs @@ -34,7 +34,7 @@ use crate::{ /// /// let mut app = tide::App::new(); /// app.at("/hello").get(async move |_| "Hello, world!"); -/// app.serve("127.0.0.1:8000"); +/// app.run("127.0.0.1:8000"); /// ``` /// /// # Routing and parameters @@ -67,7 +67,7 @@ use crate::{ /// "Use /hello/{your name} or /goodbye/{your name}" /// }); /// -/// app.serve("127.0.0.1:8000"); +/// app.run("127.0.0.1:8000"); /// ``` /// /// You can learn more about routing in the [`App::at`] documentation. @@ -123,7 +123,7 @@ use crate::{ /// let mut app = App::with_state(Database::default()); /// app.at("/message").post(new_message); /// app.at("/message/:id").get(get_message); -/// app.serve("127.0.0.1:8000").unwrap(); +/// app.run("127.0.0.1:8000").unwrap(); /// } /// ``` @@ -232,11 +232,11 @@ impl App { } } - /// Start serving the app at the given address. + /// Run the app at the given address. /// /// Blocks the calling thread indefinitely. #[cfg(feature = "hyper")] - pub fn serve(self, addr: impl std::net::ToSocketAddrs) -> std::io::Result<()> { + pub fn run(self, addr: impl std::net::ToSocketAddrs) -> std::io::Result<()> { let addr = addr .to_socket_addrs()? .next() @@ -246,6 +246,19 @@ impl App { http_service_hyper::run(self.into_http_service(), addr); Ok(()) } + + /// Asynchronously serve the app at the given address. + #[cfg(feature = "hyper")] + pub async fn serve(self, addr: impl std::net::ToSocketAddrs) -> std::io::Result<()> { + let addr = addr + .to_socket_addrs()? + .next() + .ok_or(std::io::ErrorKind::InvalidInput)?; + + // TODO: propagate the error from hyper + http_service_hyper::serve(self.into_http_service(), addr).await.ok(); + Ok(()) + } } /// An instantiated Tide server. diff --git a/tide/src/endpoint.rs b/tide/src/endpoint.rs index f4adbfcf8..8ec5e988b 100644 --- a/tide/src/endpoint.rs +++ b/tide/src/endpoint.rs @@ -28,7 +28,7 @@ use crate::{response::IntoResponse, Context, Response}; /// fn main() { /// let mut app = tide::App::new(); /// app.at("/hello").get(hello); -/// app.serve("127.0.0.1:8000").unwrap() +/// app.run("127.0.0.1:8000").unwrap() /// } /// ``` /// @@ -43,7 +43,7 @@ use crate::{response::IntoResponse, Context, Response}; /// fn main() { /// let mut app = tide::App::new(); /// app.at("/hello").get(hello); -/// app.serve("127.0.0.1:8000").unwrap() +/// app.run("127.0.0.1:8000").unwrap() /// } /// ``` /// From 6078fc40dcfb2e59a65573b94bc41851e15de71d Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Wed, 15 May 2019 15:06:25 +0900 Subject: [PATCH 05/54] Run rustfmt --- tide/src/app.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tide/src/app.rs b/tide/src/app.rs index 83cd3f517..91699dd38 100644 --- a/tide/src/app.rs +++ b/tide/src/app.rs @@ -256,7 +256,9 @@ impl App { .ok_or(std::io::ErrorKind::InvalidInput)?; // TODO: propagate the error from hyper - http_service_hyper::serve(self.into_http_service(), addr).await.ok(); + http_service_hyper::serve(self.into_http_service(), addr) + .await + .ok(); Ok(()) } } From 059a8f833c4e150a14950f16a9a3da079aa4111b Mon Sep 17 00:00:00 2001 From: grey Date: Tue, 14 May 2019 23:15:27 -0700 Subject: [PATCH 06/54] add tide-compression crate --- .travis.yml | 6 +- Cargo.toml | 1 + tide-compression/Cargo.toml | 30 ++ tide-compression/README.md | 16 ++ tide-compression/examples/simple.rs | 22 ++ tide-compression/src/lib.rs | 423 ++++++++++++++++++++++++++++ tide/src/context.rs | 5 + 7 files changed, 500 insertions(+), 3 deletions(-) create mode 100644 tide-compression/Cargo.toml create mode 100644 tide-compression/README.md create mode 100644 tide-compression/examples/simple.rs create mode 100644 tide-compression/src/lib.rs diff --git a/.travis.yml b/.travis.yml index 3e4d10906..7a662d4f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ before_script: | rustup component add rustfmt clippy script: | cargo fmt --all -- --check && - cargo clippy --all -- -D clippy::all && + cargo clippy --all --all-features -- -D clippy::all && cargo build --no-default-features --verbose && - cargo build --all --verbose && - cargo test --all --verbose + cargo build --all --all-features --verbose && + cargo test --all --all-features --verbose cache: cargo diff --git a/Cargo.toml b/Cargo.toml index 78f2f7172..6105ea9a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "tide", + "tide-compression", "examples", ] diff --git a/tide-compression/Cargo.toml b/tide-compression/Cargo.toml new file mode 100644 index 000000000..19bb2c92c --- /dev/null +++ b/tide-compression/Cargo.toml @@ -0,0 +1,30 @@ +[package] +authors = [ + "Tide Developers", +] +description = "Compression-related middleware for Tide" +documentation = "https://docs.rs/tide-compression" +keywords = ["tide", "web", "async", "middleware", "compression"] +categories = ["network-programming", "compression", "asynchronous"] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-compression" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + +[dependencies] +tide = { path = "../tide" } +accept-encoding = "0.2.0-alpha.2" +bytes = "0.4.12" +futures-preview = "0.3.0-alpha.16" +http = "0.1" +http-service = "0.2.0" + +[dependencies.async-compression] +default-features = false +features = ["stream", "gzip", "zlib", "brotli", "zstd"] +version = "0.1.0-alpha.1" + +[dev-dependencies] +http-service-mock = "0.2.0" diff --git a/tide-compression/README.md b/tide-compression/README.md new file mode 100644 index 000000000..7f13f1f9f --- /dev/null +++ b/tide-compression/README.md @@ -0,0 +1,16 @@ +# tide-compression + +This crate provides compression-related middleware for Tide. + +## Examples + +Examples are in the `/examples` folder of this crate. + +__Simple Example__ + +You can test the simple example by running `cargo run --example simple` while in this crate's directory, and then running either of the following commands: + +```console +$ curl http://127.0.0.1:8000/ -v +$ curl http://127.0.0.1:8000/echo -v -d "why hello there" +``` diff --git a/tide-compression/examples/simple.rs b/tide-compression/examples/simple.rs new file mode 100644 index 000000000..981236353 --- /dev/null +++ b/tide-compression/examples/simple.rs @@ -0,0 +1,22 @@ +#![feature(async_await)] +use tide::{App, Context}; +use tide_compression::{Compression, Decompression, Encoding}; + +// Returns a portion of the lorem ipsum text. +async fn lorem_ipsum(_cx: Context<()>) -> String { + String::from("Lorem ipsum dolor sit amet, consectetur adipiscing elit.") +} + +// Echoes the request body in bytes. +async fn echo_bytes(mut cx: Context<()>) -> Vec { + cx.body_bytes().await.unwrap() +} + +pub fn main() { + let mut app = App::new(); + app.at("/").get(lorem_ipsum); + app.at("/echo").post(echo_bytes); + app.middleware(Compression::with_default(Encoding::Brotli)); + app.middleware(Decompression::new()); + app.serve("127.0.0.1:8000").unwrap(); +} diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs new file mode 100644 index 000000000..e0f88be3a --- /dev/null +++ b/tide-compression/src/lib.rs @@ -0,0 +1,423 @@ +#![cfg_attr(feature = "nightly", deny(missing_docs))] +#![cfg_attr(feature = "nightly", feature(external_doc))] +#![cfg_attr(feature = "nightly", doc(include = "../README.md"))] +#![cfg_attr(test, deny(warnings))] +#![feature(async_await)] +#![deny( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +pub use accept_encoding::Encoding; +use async_compression::stream; +use futures::future::BoxFuture; +use http::{header::CONTENT_ENCODING, status::StatusCode, HeaderMap}; +use http_service::{Body, Request}; +use tide::{ + middleware::{Middleware, Next}, + response::IntoResponse, + Context, Error, Response, +}; + +macro_rules! box_async { + {$($t:tt)*} => { + ::futures::future::FutureExt::boxed(async move { $($t)* }) + }; +} + +/// Encode settings for the compression middleware. +/// +/// This can be modified in the case that you want more control over the speed or quality of compression. +/// +/// For more information on how to configure each of these settings, see the async-compression crate. +#[derive(Debug)] +pub struct EncodeSettings { + /// Settings for gzip compression. + pub gzip: async_compression::flate2::Compression, + /// Settings for deflate compression. + pub deflate: async_compression::flate2::Compression, + /// Settings for brotli compression. Ranges from 0-11. (default: `11`) + pub brotli: u32, + /// Settings for zstd compression. Ranges from 1-21. (default: `3`) + pub zstd: i32, +} + +impl Default for EncodeSettings { + fn default() -> Self { + Self { + gzip: Default::default(), + deflate: Default::default(), + brotli: 11, + zstd: 3, + } + } +} + +/// Middleware for automatically handling outgoing response compression. +/// +/// This middleware currently supports HTTP compression using `gzip`, `deflate`, `br`, and `zstd`. +#[derive(Debug)] +pub struct Compression { + default_encoding: Encoding, + settings: EncodeSettings, +} + +impl Default for Compression { + fn default() -> Self { + Self::new() + } +} + +impl Compression { + /// Creates a new Compression middleware. The default encoding is [`Encoding::Identity`] (no encoding). + pub fn new() -> Self { + Self { + default_encoding: Encoding::Identity, + settings: Default::default(), + } + } + + /// Creates a new Compression middleware with a provided default encoding. + /// + /// This encoding will be selected if the client has not set the `Accept-Encoding` header or `*` is set as the most preferred encoding. + pub fn with_default(default_encoding: Encoding) -> Self { + Self { + default_encoding, + settings: Default::default(), + } + } + + /// Accesses a mutable handle to this middleware's [`EncodeSettings`]. + /// + /// This will allow you to configure this middleware's settings. + pub fn settings_mut(&mut self) -> &mut EncodeSettings { + &mut self.settings + } + + fn preferred_encoding(&self, headers: &HeaderMap) -> Result { + let encoding = match accept_encoding::parse(headers) { + Ok(encoding) => encoding, + Err(_) => return Err(Error::from(StatusCode::BAD_REQUEST)), + }; + Ok(encoding.unwrap_or(self.default_encoding)) + } + + /// Consumes the response and returns an encoded version of it. + fn encode(&self, mut res: Response, encoding: Encoding) -> Response { + if res.headers().get(CONTENT_ENCODING).is_some() || encoding == Encoding::Identity { + return res; // avoid double-encoding a given response + } + let body = std::mem::replace(res.body_mut(), Body::empty()); + match encoding { + Encoding::Gzip => { + let stream = stream::GzipEncoder::new(body, self.settings.gzip); + *res.body_mut() = Body::from_stream(stream); + } + Encoding::Deflate => { + let stream = stream::ZlibEncoder::new(body, self.settings.deflate); + *res.body_mut() = Body::from_stream(stream); + } + Encoding::Brotli => { + let stream = stream::BrotliEncoder::new(body, self.settings.brotli); + *res.body_mut() = Body::from_stream(stream); + } + Encoding::Zstd => { + let stream = stream::ZstdEncoder::new(body, self.settings.zstd); + *res.body_mut() = Body::from_stream(stream); + } + Encoding::Identity => unreachable!(), + }; + res.headers_mut() + .append(CONTENT_ENCODING, encoding.to_header_value()); + res + } +} + +impl Middleware for Compression { + fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + box_async! { + let encoding = match self.preferred_encoding(cx.headers()) { + Ok(encoding) => encoding, + Err(e) => return e.into_response(), + }; + let res = next.run(cx).await; + self.encode(res, encoding) + } + } +} + +/// Middleware for handling incoming request decompression. +/// +/// This middleware currently supports HTTP decompression under the `gzip`, `deflate`, `br`, and `zstd` algorithms. +#[derive(Debug, Default)] +pub struct Decompression {} + +impl Decompression { + /// Creates a new Decompression middleware. + pub fn new() -> Self { + Self {} + } + + fn parse_encoding(s: &str) -> Result { + match s { + "gzip" => Ok(Encoding::Gzip), + "deflate" => Ok(Encoding::Deflate), + "br" => Ok(Encoding::Brotli), + "zstd" => Ok(Encoding::Zstd), + "identity" => Ok(Encoding::Identity), + _ => Err(Error::from(StatusCode::UNSUPPORTED_MEDIA_TYPE)), + } + } + + fn decode(&self, req: &mut Request) -> Result<(), Error> { + let encodings = if let Some(hval) = req.headers().get(CONTENT_ENCODING) { + let hval = match hval.to_str() { + Ok(hval) => hval, + Err(_) => return Err(Error::from(StatusCode::BAD_REQUEST)), + }; + hval.split(',') + .map(str::trim) + .rev() // apply decodings in reverse order + .map(Decompression::parse_encoding) + .collect::, Error>>()? + } else { + return Ok(()); + }; + + for encoding in encodings { + match encoding { + Encoding::Gzip => { + let body = std::mem::replace(req.body_mut(), Body::empty()); + let stream = stream::GzipDecoder::new(body); + *req.body_mut() = Body::from_stream(stream); + } + Encoding::Deflate => { + let body = std::mem::replace(req.body_mut(), Body::empty()); + let stream = stream::ZlibDecoder::new(body); + *req.body_mut() = Body::from_stream(stream); + } + Encoding::Brotli => { + let body = std::mem::replace(req.body_mut(), Body::empty()); + let stream = stream::BrotliDecoder::new(body); + *req.body_mut() = Body::from_stream(stream); + } + Encoding::Zstd => { + let body = std::mem::replace(req.body_mut(), Body::empty()); + let stream = stream::ZstdDecoder::new(body); + *req.body_mut() = Body::from_stream(stream); + } + Encoding::Identity => (), + } + } + + // strip the content-encoding header + req.headers_mut().remove(CONTENT_ENCODING).unwrap(); + + Ok(()) + } +} + +impl Middleware for Decompression { + fn handle<'a>( + &'a self, + mut cx: Context, + next: Next<'a, Data>, + ) -> BoxFuture<'a, Response> { + box_async! { + match self.decode(cx.request_mut()) { + Ok(_) => (), + Err(e) => return e.into_response(), + }; + next.run(cx).await + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use async_compression::flate2; + use bytes::Bytes; + use futures::{ + executor::{block_on, block_on_stream}, + stream::StreamExt, + }; + use http::header::ACCEPT_ENCODING; + use http_service::Body; + use http_service_mock::make_server; + + async fn lorem_ipsum(_cx: Context<()>) -> String { + String::from(r#" + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam rutrum et risus sed egestas. Maecenas dapibus enim a posuere + semper. Cras venenatis et turpis quis aliquam. Suspendisse eget risus in libero tristique consectetur. Ut ut risus cursus, scelerisque + enim ac, tempus tellus. Vestibulum ac porta felis. Aenean fringilla posuere felis, in blandit enim tristique ut. Sed elementum iaculis + enim eu commodo. + "#) + } + + fn lorem_ipsum_bytes() -> Vec { + String::from(r#" + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam rutrum et risus sed egestas. Maecenas dapibus enim a posuere + semper. Cras venenatis et turpis quis aliquam. Suspendisse eget risus in libero tristique consectetur. Ut ut risus cursus, scelerisque + enim ac, tempus tellus. Vestibulum ac porta felis. Aenean fringilla posuere felis, in blandit enim tristique ut. Sed elementum iaculis + enim eu commodo. + "#).into_bytes() + } + + // Echoes the request body in bytes. + async fn echo_bytes(mut cx: Context<()>) -> Vec { + cx.body_bytes().await.unwrap() + } + + // Generates the app. + fn app() -> tide::App<()> { + let mut app = tide::App::new(); + app.at("/").get(lorem_ipsum); + app.at("/echo").post(echo_bytes); + app.middleware(Compression::new()); + app.middleware(Decompression::new()); + app + } + + // Generates a response given a string that represents the Accept-Encoding header value. + fn get_encoded_response(hval: &str) -> Response { + let app = app(); + let mut server = make_server(app.into_http_service()).unwrap(); + let req = http::Request::get("/") + .header(ACCEPT_ENCODING, hval) + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + res + } + + // Generates a decoded response given a request body and the header value representing its encoding. + fn get_decoded_response(body: Body, hval: &str) -> Response { + let app = app(); + let mut server = make_server(app.into_http_service()).unwrap(); + let req = http::Request::post("/echo") + .header(CONTENT_ENCODING, hval) + .body(body) + .unwrap(); + let res = server.simulate(req).unwrap(); + res + } + + #[test] + fn compressed_gzip_response() { + let res = get_encoded_response("gzip"); + assert_eq!(res.status(), 200); + let body = res.into_body(); + let stream = stream::GzipDecoder::new(body); + let decompressed_body: Vec = block_on_stream(stream) + .map(Result::unwrap) + .flatten() + .collect(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(decompressed_body, lorem_ipsum); + } + + #[test] + fn compressed_deflate_response() { + let res = get_encoded_response("deflate"); + assert_eq!(res.status(), 200); + let body = res.into_body(); + let stream = stream::ZlibDecoder::new(body); + let decompressed_body: Vec = block_on_stream(stream) + .map(Result::unwrap) + .flatten() + .collect(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(decompressed_body, lorem_ipsum); + } + + #[test] + fn compressed_brotli_response() { + let res = get_encoded_response("br"); + assert_eq!(res.status(), 200); + let body = res.into_body(); + let stream = stream::BrotliDecoder::new(body); + let decompressed_body: Vec = block_on_stream(stream) + .map(Result::unwrap) + .flatten() + .collect(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(decompressed_body, lorem_ipsum); + } + + #[test] + fn compressed_zstd_response() { + let res = get_encoded_response("zstd"); + assert_eq!(res.status(), 200); + let body = res.into_body(); + let stream = stream::ZstdDecoder::new(body); + let decompressed_body: Vec = block_on_stream(stream) + .map(Result::unwrap) + .flatten() + .collect(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(decompressed_body, lorem_ipsum); + } + + #[test] + fn decompressed_gzip_response() { + let lorem_ipsum = lorem_ipsum_bytes(); + let req_body = Body::from_stream(stream::GzipEncoder::new( + futures::stream::iter(vec![lorem_ipsum]) + .map(Bytes::from) + .map(Ok), + flate2::Compression::default(), + )); + let res = get_decoded_response(req_body, "gzip"); + let body = block_on(res.into_body().into_vec()).unwrap(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(body, lorem_ipsum); + } + + #[test] + fn decompressed_deflate_response() { + let lorem_ipsum = lorem_ipsum_bytes(); + let req_body = Body::from_stream(stream::ZlibEncoder::new( + futures::stream::iter(vec![lorem_ipsum]) + .map(Bytes::from) + .map(Ok), + flate2::Compression::default(), + )); + let res = get_decoded_response(req_body, "deflate"); + let body = block_on(res.into_body().into_vec()).unwrap(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(body, lorem_ipsum); + } + + #[test] + fn decompressed_brotli_response() { + let lorem_ipsum = lorem_ipsum_bytes(); + let req_body = Body::from_stream(stream::BrotliEncoder::new( + futures::stream::iter(vec![lorem_ipsum]) + .map(Bytes::from) + .map(Ok), + 11, + )); + let res = get_decoded_response(req_body, "br"); + let body = block_on(res.into_body().into_vec()).unwrap(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(body, lorem_ipsum); + } + + #[test] + fn decompressed_zstd_response() { + let lorem_ipsum = lorem_ipsum_bytes(); + let req_body = Body::from_stream(stream::ZstdEncoder::new( + futures::stream::iter(vec![lorem_ipsum]) + .map(Bytes::from) + .map(Ok), + 3, + )); + let res = get_decoded_response(req_body, "zstd"); + let body = block_on(res.into_body().into_vec()).unwrap(); + let lorem_ipsum = lorem_ipsum_bytes(); + assert_eq!(body, lorem_ipsum); + } +} diff --git a/tide/src/context.rs b/tide/src/context.rs index 4c96cc896..543ac8a5d 100644 --- a/tide/src/context.rs +++ b/tide/src/context.rs @@ -55,6 +55,11 @@ impl Context { &self.request } + /// Access a mutable handle to the entire request. + pub fn request_mut(&mut self) -> &mut http_service::Request { + &mut self.request + } + /// Access app-global data. pub fn state(&self) -> &State { &self.state From 80f0af6ba0b6825a404ee33846c858fd7fda6a28 Mon Sep 17 00:00:00 2001 From: grey Date: Tue, 14 May 2019 23:44:12 -0700 Subject: [PATCH 07/54] use run instead of serve --- tide-compression/examples/simple.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tide-compression/examples/simple.rs b/tide-compression/examples/simple.rs index 981236353..2f5d0e174 100644 --- a/tide-compression/examples/simple.rs +++ b/tide-compression/examples/simple.rs @@ -18,5 +18,5 @@ pub fn main() { app.at("/echo").post(echo_bytes); app.middleware(Compression::with_default(Encoding::Brotli)); app.middleware(Decompression::new()); - app.serve("127.0.0.1:8000").unwrap(); + app.run("127.0.0.1:8000").unwrap(); } From 962fddc55ad2c98aa37715327a74d97726404bdb Mon Sep 17 00:00:00 2001 From: Allen Date: Tue, 14 May 2019 23:45:30 -0700 Subject: [PATCH 08/54] Update tide-compression/README.md Co-Authored-By: Wonwoo Choi --- tide-compression/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tide-compression/README.md b/tide-compression/README.md index 7f13f1f9f..c84f65b12 100644 --- a/tide-compression/README.md +++ b/tide-compression/README.md @@ -12,5 +12,5 @@ You can test the simple example by running `cargo run --example simple` while in ```console $ curl http://127.0.0.1:8000/ -v -$ curl http://127.0.0.1:8000/echo -v -d "why hello there" +$ echo 'why hello there' | gzip | curl -v --compressed -H 'Content-Encoding: gzip' 'http://127.0.0.1:8000/echo' --data-binary @- ``` From b19eb2e2a8827595290bae4600e9b7337ba4b2a8 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Wed, 15 May 2019 20:19:14 +0200 Subject: [PATCH 09/54] Check example in readme compiles during testing --- README.md | 2 +- tide/src/lib.rs | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 124409d03..e54a09afa 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Ecosystem WG, and **not ready for production use yet**. **Hello World** -```rust +```rust,no_run #![feature(async_await)] fn main() -> Result<(), std::io::Error> { diff --git a/tide/src/lib.rs b/tide/src/lib.rs index ac931431d..c3c7841ba 100755 --- a/tide/src/lib.rs +++ b/tide/src/lib.rs @@ -1,5 +1,5 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(feature = "nightly", feature(external_doc))] +#![cfg_attr(any(feature = "nightly", test), feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![cfg_attr(test, deny(warnings))] #![feature(async_await, existential_type)] @@ -21,6 +21,10 @@ //! //! +#[cfg(test)] +#[doc(include = "../../README.md")] +const _README: () = (); + macro_rules! box_async { {$($t:tt)*} => { ::futures::future::FutureExt::boxed(async move { $($t)* }) From 3af12d9b5f549021c27f697ccdfc08405d5206d8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Fri, 17 May 2019 00:26:05 +0900 Subject: [PATCH 10/54] Remove #[allow(unused_mut)] --- examples/src/body_types.rs | 4 ---- examples/src/cookies.rs | 2 -- examples/src/messages.rs | 2 -- examples/src/multipart_form/mod.rs | 1 - tide/src/middleware/cookies.rs | 4 ---- 5 files changed, 13 deletions(-) diff --git a/examples/src/body_types.rs b/examples/src/body_types.rs index 9df1509a5..c2d518903 100644 --- a/examples/src/body_types.rs +++ b/examples/src/body_types.rs @@ -11,28 +11,24 @@ struct Message { contents: String, } -#[allow(unused_mut)] // Workaround clippy bug async fn echo_string(mut cx: Context<()>) -> String { let msg = cx.body_string().await.unwrap(); println!("String: {}", msg); msg } -#[allow(unused_mut)] // Workaround clippy bug async fn echo_bytes(mut cx: Context<()>) -> Vec { let msg = cx.body_bytes().await.unwrap(); println!("Bytes: {:?}", msg); msg } -#[allow(unused_mut)] // Workaround clippy bug async fn echo_json(mut cx: Context<()>) -> EndpointResult { let msg = cx.body_json().await.client_err()?; println!("JSON: {:?}", msg); Ok(response::json(msg)) } -#[allow(unused_mut)] // Workaround clippy bug async fn echo_form(mut cx: Context<()>) -> EndpointResult { let msg = cx.body_form().await?; println!("Form: {:?}", msg); diff --git a/examples/src/cookies.rs b/examples/src/cookies.rs index a43dc67c1..670c6afd7 100644 --- a/examples/src/cookies.rs +++ b/examples/src/cookies.rs @@ -6,12 +6,10 @@ async fn retrieve_cookie(mut cx: Context<()>) -> String { format!("hello cookies: {:?}", cx.get_cookie("hello").unwrap()) } -#[allow(unused_mut)] // Workaround clippy bug async fn set_cookie(mut cx: Context<()>) { cx.set_cookie(Cookie::new("hello", "world")).unwrap(); } -#[allow(unused_mut)] // Workaround clippy bug async fn remove_cookie(mut cx: Context<()>) { cx.remove_cookie(Cookie::named("hello")).unwrap(); } diff --git a/examples/src/messages.rs b/examples/src/messages.rs index 4cf905f79..4abdadf61 100644 --- a/examples/src/messages.rs +++ b/examples/src/messages.rs @@ -37,13 +37,11 @@ impl Database { } } -#[allow(unused_mut)] // Workaround clippy bug async fn new_message(mut cx: Context) -> EndpointResult { let msg = cx.body_json().await.client_err()?; Ok(cx.state().insert(msg).to_string()) } -#[allow(unused_mut)] // Workaround clippy bug async fn set_message(mut cx: Context) -> EndpointResult<()> { let msg = cx.body_json().await.client_err()?; let id = cx.param("id").client_err()?; diff --git a/examples/src/multipart_form/mod.rs b/examples/src/multipart_form/mod.rs index ce4d74ca3..1e7e91b45 100644 --- a/examples/src/multipart_form/mod.rs +++ b/examples/src/multipart_form/mod.rs @@ -9,7 +9,6 @@ struct Message { file: Option, } -#[allow(unused_mut)] // Workaround clippy bug async fn upload_file(mut cx: Context<()>) -> EndpointResult { // https://stackoverflow.com/questions/43424982/how-to-parse-multipart-forms-using-abonander-multipart-with-rocket let mut message = Message { diff --git a/tide/src/middleware/cookies.rs b/tide/src/middleware/cookies.rs index 9c3dd6910..adb1152c3 100644 --- a/tide/src/middleware/cookies.rs +++ b/tide/src/middleware/cookies.rs @@ -72,23 +72,19 @@ mod tests { static COOKIE_NAME: &str = "testCookie"; /// Tide will use the the `Cookies`'s `Extract` implementation to build this parameter. - #[allow(unused_mut)] // Workaround clippy bug async fn retrieve_cookie(mut cx: Context<()>) -> String { format!("{}", cx.get_cookie(COOKIE_NAME).unwrap().unwrap().value()) } - #[allow(unused_mut)] // Workaround clippy bug async fn set_cookie(mut cx: Context<()>) { cx.set_cookie(Cookie::new(COOKIE_NAME, "NewCookieValue")) .unwrap(); } - #[allow(unused_mut)] // Workaround clippy bug async fn remove_cookie(mut cx: Context<()>) { cx.remove_cookie(Cookie::named(COOKIE_NAME)).unwrap(); } - #[allow(unused_mut)] // Workaround clippy bug async fn set_multiple_cookie(mut cx: Context<()>) { cx.set_cookie(Cookie::new("C1", "V1")).unwrap(); cx.set_cookie(Cookie::new("C2", "V2")).unwrap(); From 45319777f0ef0f5f334404329c29f4bf2cecfc6b Mon Sep 17 00:00:00 2001 From: Ivan Tham Date: Fri, 17 May 2019 15:49:53 +0000 Subject: [PATCH 11/54] Improve curl command consistency --- tide-compression/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tide-compression/README.md b/tide-compression/README.md index c84f65b12..4c8eb8b10 100644 --- a/tide-compression/README.md +++ b/tide-compression/README.md @@ -11,6 +11,6 @@ __Simple Example__ You can test the simple example by running `cargo run --example simple` while in this crate's directory, and then running either of the following commands: ```console -$ curl http://127.0.0.1:8000/ -v -$ echo 'why hello there' | gzip | curl -v --compressed -H 'Content-Encoding: gzip' 'http://127.0.0.1:8000/echo' --data-binary @- +$ curl -v http://127.0.0.1:8000/ +$ echo 'why hello there' | gzip | curl -v --compressed -H 'Content-Encoding: gzip' http://127.0.0.1:8000/echo --data-binary @- ``` From 77b3a1cd2970f3fca6baa2ee9749e3eea15ae811 Mon Sep 17 00:00:00 2001 From: Ravi Shankar Date: Sun, 19 May 2019 21:43:11 +0530 Subject: [PATCH 12/54] Split tide into smaller crates (#220) * Move core traits and types to tide-core * Move cookies middleware to tide-cookies * Remove unncessary dependencies and use relative paths in README --- Cargo.toml | 2 + README.md | 17 +-- examples/Cargo.toml | 16 +-- tide-cookies/Cargo.toml | 21 ++++ .../cookies.rs => tide-cookies/src/data.rs | 3 +- tide-cookies/src/lib.rs | 18 +++ .../src/middleware.rs | 12 +- tide-core/Cargo.toml | 32 ++++++ {tide => tide-core}/src/app.rs | 1 + {tide => tide-core}/src/context.rs | 0 {tide => tide-core}/src/endpoint.rs | 0 tide-core/src/error.rs | 104 ++++++++++++++++++ tide-core/src/lib.rs | 37 +++++++ tide-core/src/middleware.rs | 38 +++++++ {tide => tide-core}/src/response.rs | 0 {tide => tide-core}/src/route.rs | 0 {tide => tide-core}/src/router.rs | 0 tide/Cargo.toml | 17 +-- tide/src/error.rs | 101 +---------------- tide/src/lib.rs | 26 ++--- tide/src/middleware/mod.rs | 45 +------- 21 files changed, 288 insertions(+), 202 deletions(-) create mode 100644 tide-cookies/Cargo.toml rename tide/src/cookies.rs => tide-cookies/src/data.rs (98%) create mode 100644 tide-cookies/src/lib.rs rename tide/src/middleware/cookies.rs => tide-cookies/src/middleware.rs (96%) create mode 100644 tide-core/Cargo.toml rename {tide => tide-core}/src/app.rs (99%) rename {tide => tide-core}/src/context.rs (100%) rename {tide => tide-core}/src/endpoint.rs (100%) create mode 100644 tide-core/src/error.rs create mode 100644 tide-core/src/lib.rs create mode 100644 tide-core/src/middleware.rs rename {tide => tide-core}/src/response.rs (100%) rename {tide => tide-core}/src/route.rs (100%) rename {tide => tide-core}/src/router.rs (100%) diff --git a/Cargo.toml b/Cargo.toml index 6105ea9a2..3cf23390f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,8 @@ members = [ "tide", "tide-compression", + "tide-cookies", + "tide-core", "examples", ] diff --git a/README.md b/README.md index e54a09afa..4e60e6f65 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,15 @@ fn main() -> Result<(), std::io::Error> { **More Examples** -- [Hello World](https://github.com/rustasync/tide/tree/master/examples/src/hello.rs) -- [Messages](https://github.com/rustasync/tide/tree/master/examples/src/messages.rs) -- [Body Types](https://github.com/rustasync/tide/tree/master/examples/src/body_types.rs) -- [Multipart Form](https://github.com/rustasync/tide/tree/master/examples/src/multipart_form/mod.rs) -- [Catch All](https://github.com/rustasync/tide/tree/master/examples/src/catch_all.rs) -- [Cookies](https://github.com/rustasync/tide/tree/master/examples/src/cookies.rs) -- [Default Headers](https://github.com/rustasync/tide/tree/master/examples/src/default_headers.rs) -- [GraphQL](https://github.com/rustasync/tide/tree/master/examples/src/graphql.rs) +- [Hello World](https://github.com/rustasync/tide/blob/master/examples/src/hello.rs) +- [Messages](https://github.com/rustasync/tide/blob/master/examples/src/messages.rs) +- [Body Types](https://github.com/rustasync/tide/blob/master/examples/src/body_types.rs) +- [Multipart Form](https://github.com/rustasync/tide/blob/master/examples/src/multipart-form/main.rs) +- [Catch All](https://github.com/rustasync/tide/blob/master/examples/src/catch_all.rs) +- [Cookies](https://github.com/rustasync/tide/blob/master/examples/src/cookies.rs) +- [Default Headers](https://github.com/rustasync/tide/blob/master/examples/src/default_headers.rs) +- [GraphQL](https://github.com/rustasync/tide/blob/master/examples/src/graphql.rs) +- [Staticfile](https://github.com/rustasync/tide/blob/master/examples/src/staticfile.rs) ## Resources diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 54a73b1ff..af1206539 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -1,6 +1,6 @@ [package] authors = [ - "Tide Developers", + "Tide Developers", ] description = "Tide web server examples" documentation = "https://docs.rs/tide" @@ -15,32 +15,18 @@ publish = false [dependencies] tide = { path = "../tide" } cookie = { version = "0.12", features = ["percent-encode"] } -futures-preview = "0.3.0-alpha.16" -fnv = "1.0.6" http = "0.1" http-service = "0.2.0" -pin-utils = "0.1.0-alpha.4" -route-recognizer = "0.1.12" -serde_json = "1.0.39" -slog = "2.4.1" -slog-async = "2.3.0" -slog-term = "2.4.0" -typemap = "0.3.3" -serde_urlencoded = "0.5.5" -basic-cookies = "0.1.3" bytes = "0.4.12" futures-fs = "0.0.5" futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } -http-service-mock = "0.2.0" juniper = "0.11.1" mime = "0.3.13" mime_guess = "2.0.0-alpha.6" percent-encoding = "1.0.1" serde = { version = "1.0.91", features = ["derive"] } -structopt = "0.2.15" [dependencies.multipart] default-features = false features = ["server"] version = "0.16.1" - diff --git a/tide-cookies/Cargo.toml b/tide-cookies/Cargo.toml new file mode 100644 index 000000000..34b388977 --- /dev/null +++ b/tide-cookies/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "tide-cookies" +version = "0.2.0" +edition = "2018" +authors = [ + "Tide Developers", +] +description = "Cookie management for Tide web framework" +documentation = "https://docs.rs/tide-core" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustasync/tide" + +[dependencies] +cookie = { version = "0.12", features = ["percent-encode"] } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +http-service = "0.2.0" +tide-core = { path = "../tide-core" } + +[dev-dependencies] +http-service-mock = "0.2.0" diff --git a/tide/src/cookies.rs b/tide-cookies/src/data.rs similarity index 98% rename from tide/src/cookies.rs rename to tide-cookies/src/data.rs index 48fa1a2cb..7430b10e0 100644 --- a/tide/src/cookies.rs +++ b/tide-cookies/src/data.rs @@ -1,9 +1,8 @@ use cookie::{Cookie, CookieJar, ParseError}; -use crate::error::StringError; -use crate::Context; use http::HeaderMap; use std::sync::{Arc, RwLock}; +use tide_core::{error::StringError, Context}; const MIDDLEWARE_MISSING_MSG: &str = "CookiesMiddleware must be used to populate request and response cookies"; diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs new file mode 100644 index 000000000..da88a7054 --- /dev/null +++ b/tide-cookies/src/lib.rs @@ -0,0 +1,18 @@ +#![cfg_attr(feature = "nightly", deny(missing_docs))] +#![cfg_attr(test, deny(warnings))] +#![feature(async_await)] +#![deny( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +#[macro_use] +extern crate tide_core; + +mod data; +mod middleware; + +pub use self::data::ContextExt; +pub use self::middleware::CookiesMiddleware; diff --git a/tide/src/middleware/cookies.rs b/tide-cookies/src/middleware.rs similarity index 96% rename from tide/src/middleware/cookies.rs rename to tide-cookies/src/middleware.rs index adb1152c3..99142c82a 100644 --- a/tide/src/middleware/cookies.rs +++ b/tide-cookies/src/middleware.rs @@ -1,8 +1,8 @@ -use crate::cookies::CookieData; +use crate::data::CookieData; use futures::future::BoxFuture; use http::header::HeaderValue; -use crate::{ +use tide_core::{ middleware::{Middleware, Next}, Context, Response, }; @@ -63,11 +63,12 @@ impl Middleware for CookiesMiddleware { #[cfg(test)] mod tests { use super::*; - use crate::{cookies::ContextExt, Context}; + use crate::data::ContextExt; use cookie::Cookie; use futures::executor::block_on; use http_service::Body; use http_service_mock::make_server; + use tide_core::Context; static COOKIE_NAME: &str = "testCookie"; @@ -90,8 +91,8 @@ mod tests { cx.set_cookie(Cookie::new("C2", "V2")).unwrap(); } - fn app() -> crate::App<()> { - let mut app = crate::App::new(); + fn app() -> tide_core::App<()> { + let mut app = tide_core::App::new(); app.middleware(CookiesMiddleware::new()); app.at("/get").get(retrieve_cookie); @@ -168,5 +169,4 @@ mod tests { assert!(iter.next().is_none()); } - } diff --git a/tide-core/Cargo.toml b/tide-core/Cargo.toml new file mode 100644 index 000000000..60ef60fe8 --- /dev/null +++ b/tide-core/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "tide-core" +version = "0.2.0" +edition = "2018" +authors = [ + "Tide Developers", +] +description = "Core types and traits for Tide web framework" +documentation = "https://docs.rs/tide-core" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustasync/tide" + +[dependencies] +fnv = "1.0.6" +futures-preview = "0.3.0-alpha.16" +http = "0.1" +http-service = "0.2.0" +route-recognizer = "0.1.12" +serde = "1.0.91" +serde_json = "1.0.39" + +[dependencies.http-service-hyper] +optional = true +version = "0.2.0" + +[dev-dependencies] +tide = { path = "../tide" } +serde_derive = "1.0.91" + +[features] +default = ["hyper"] +hyper = ["http-service-hyper"] diff --git a/tide/src/app.rs b/tide-core/src/app.rs similarity index 99% rename from tide/src/app.rs rename to tide-core/src/app.rs index 91699dd38..ef65b35ad 100644 --- a/tide/src/app.rs +++ b/tide-core/src/app.rs @@ -76,6 +76,7 @@ use crate::{ /// /// ```rust, no_run /// #![feature(async_await)] +/// #[macro_use] extern crate serde_derive; /// /// use http::status::StatusCode; /// use serde::{Deserialize, Serialize}; diff --git a/tide/src/context.rs b/tide-core/src/context.rs similarity index 100% rename from tide/src/context.rs rename to tide-core/src/context.rs diff --git a/tide/src/endpoint.rs b/tide-core/src/endpoint.rs similarity index 100% rename from tide/src/endpoint.rs rename to tide-core/src/endpoint.rs diff --git a/tide-core/src/error.rs b/tide-core/src/error.rs new file mode 100644 index 000000000..eacfe93ac --- /dev/null +++ b/tide-core/src/error.rs @@ -0,0 +1,104 @@ +// use core::pin::Pin; +// use futures::future::Future; +use http::{HttpTryFrom, Response, StatusCode}; +use http_service::Body; + +use crate::response::IntoResponse; + +#[derive(Debug)] +pub struct StringError(pub String); +impl std::error::Error for StringError {} + +impl std::fmt::Display for StringError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + self.0.fmt(f) + } +} + +#[macro_export] +macro_rules! err_fmt { + {$($t:tt)*} => { + crate::error::StringError(format!($($t)*)) + } +} + +/// A convenient `Result` instantiation appropriate for most endpoints. +pub type EndpointResult> = Result; + +/// A generic endpoint error, which can be converted into a response. +#[derive(Debug)] +pub struct Error { + resp: Response, +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + self.resp + } +} + +struct Cause(Box); + +impl From> for Error { + fn from(resp: Response) -> Error { + Error { resp } + } +} + +impl From for Error { + fn from(status: StatusCode) -> Error { + let resp = Response::builder() + .status(status) + .body(Body::empty()) + .unwrap(); + Error { resp } + } +} + +/// Extends the `Result` type with convenient methods for constructing Tide errors. +pub trait ResultExt: Sized { + /// Convert to an `EndpointResult`, treating the `Err` case as a client + /// error (response code 400). + fn client_err(self) -> EndpointResult { + self.with_err_status(400) + } + + /// Convert to an `EndpointResult`, treating the `Err` case as a server + /// error (response code 500). + fn server_err(self) -> EndpointResult { + self.with_err_status(500) + } + + /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom + /// response status. + fn with_err_status(self, status: S) -> EndpointResult + where + StatusCode: HttpTryFrom; +} + +/// Extends the `Response` type with a method to extract error causes when applicable. +pub trait ResponseExt { + /// Extract the cause of the unsuccessful response, if any + fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)>; +} + +impl ResponseExt for Response { + fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { + self.extensions().get().map(|Cause(c)| &**c) + } +} + +impl ResultExt for std::result::Result { + fn with_err_status(self, status: S) -> EndpointResult + where + StatusCode: HttpTryFrom, + { + self.map_err(|e| Error { + resp: Response::builder() + .status(status) + .extension(Cause(Box::new(e))) + .body(Body::empty()) + .unwrap(), + }) + } +} diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs new file mode 100644 index 000000000..ca0df3282 --- /dev/null +++ b/tide-core/src/lib.rs @@ -0,0 +1,37 @@ +#![cfg_attr(feature = "nightly", deny(missing_docs))] +#![cfg_attr(test, deny(warnings))] +#![feature(async_await, existential_type)] +#![deny( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] +// TODO: Remove this after clippy bug due to async await is resolved. +// ISSUE: https://github.com/rust-lang/rust-clippy/issues/3988 +#![allow(clippy::needless_lifetimes)] + +#[macro_export] +macro_rules! box_async { + {$($t:tt)*} => { + ::futures::future::FutureExt::boxed(async move { $($t)* }) + }; +} + +mod app; +mod context; +mod endpoint; +pub mod error; +pub mod middleware; +pub mod response; +mod route; +mod router; + +pub use crate::{ + app::{App, Server}, + context::Context, + endpoint::Endpoint, + error::{EndpointResult, Error}, + response::Response, + route::Route, +}; diff --git a/tide-core/src/middleware.rs b/tide-core/src/middleware.rs new file mode 100644 index 000000000..47d1caa13 --- /dev/null +++ b/tide-core/src/middleware.rs @@ -0,0 +1,38 @@ +use crate::{endpoint::DynEndpoint, Context, Response}; +use futures::future::BoxFuture; + +use std::sync::Arc; + +/// Middleware that wraps around remaining middleware chain. +pub trait Middleware: 'static + Send + Sync { + /// Asynchronously handle the request, and return a response. + fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response>; +} + +impl Middleware for F +where + F: Send + Sync + 'static + for<'a> Fn(Context, Next<'a, Data>) -> BoxFuture<'a, Response>, +{ + fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + (self)(cx, next) + } +} + +/// The remainder of a middleware chain, including the endpoint. +#[allow(missing_debug_implementations)] +pub struct Next<'a, State> { + pub(crate) endpoint: &'a DynEndpoint, + pub(crate) next_middleware: &'a [Arc>], +} + +impl<'a, State: 'static> Next<'a, State> { + /// Asynchronously execute the remaining middleware chain. + pub fn run(mut self, cx: Context) -> BoxFuture<'a, Response> { + if let Some((current, next)) = self.next_middleware.split_first() { + self.next_middleware = next; + current.handle(cx, self) + } else { + (self.endpoint)(cx) + } + } +} diff --git a/tide/src/response.rs b/tide-core/src/response.rs similarity index 100% rename from tide/src/response.rs rename to tide-core/src/response.rs diff --git a/tide/src/route.rs b/tide-core/src/route.rs similarity index 100% rename from tide/src/route.rs rename to tide-core/src/route.rs diff --git a/tide/src/router.rs b/tide-core/src/router.rs similarity index 100% rename from tide/src/router.rs rename to tide-core/src/router.rs diff --git a/tide/Cargo.toml b/tide/Cargo.toml index 9e70bf5ea..ce2cb9f28 100644 --- a/tide/Cargo.toml +++ b/tide/Cargo.toml @@ -19,25 +19,17 @@ repository = "https://github.com/rustasync/tide" version = "0.2.0" [dependencies] -cookie = { version = "0.12", features = ["percent-encode"] } futures-preview = "0.3.0-alpha.16" -fnv = "1.0.6" http = "0.1" http-service = "0.2.0" -pin-utils = "0.1.0-alpha.4" -route-recognizer = "0.1.12" serde = "1.0.91" serde_derive = "1.0.91" -serde_json = "1.0.39" slog = "2.4.1" slog-async = "2.3.0" slog-term = "2.4.0" -typemap = "0.3.3" serde_urlencoded = "0.5.5" - -[dependencies.http-service-hyper] -optional = true -version = "0.2.0" +tide-cookies = { path = "../tide-cookies", optional = true } +tide-core = { path = "../tide-core" } [dependencies.multipart] default-features = false @@ -45,8 +37,9 @@ features = ["server"] version = "0.16.1" [features] -default = ["hyper"] -hyper = ["http-service-hyper"] +default = ["hyper", "cookies"] +cookies = ["tide-cookies"] +hyper = ["tide-core/http-service-hyper"] [dev-dependencies] http-service-mock = "0.2.0" diff --git a/tide/src/error.rs b/tide/src/error.rs index 174c6987a..25ae1cc4b 100644 --- a/tide/src/error.rs +++ b/tide/src/error.rs @@ -1,105 +1,6 @@ use core::pin::Pin; use futures::future::Future; -use http::{HttpTryFrom, Response, StatusCode}; -use http_service::Body; -use crate::response::IntoResponse; +pub use tide_core::error::{EndpointResult, Error, ResponseExt, ResultExt, StringError}; pub(crate) type BoxTryFuture = Pin> + Send + 'static>>; - -/// A convenient `Result` instantiation appropriate for most endpoints. -pub type EndpointResult> = Result; - -#[derive(Debug)] -pub struct StringError(pub String); -impl std::error::Error for StringError {} - -impl std::fmt::Display for StringError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - self.0.fmt(f) - } -} - -macro_rules! err_fmt { - {$($t:tt)*} => { - crate::error::StringError(format!($($t)*)) - } -} - -/// A generic endpoint error, which can be converted into a response. -#[derive(Debug)] -pub struct Error { - resp: Response, -} - -impl IntoResponse for Error { - fn into_response(self) -> Response { - self.resp - } -} - -struct Cause(Box); - -impl From> for Error { - fn from(resp: Response) -> Error { - Error { resp } - } -} - -impl From for Error { - fn from(status: StatusCode) -> Error { - let resp = Response::builder() - .status(status) - .body(Body::empty()) - .unwrap(); - Error { resp } - } -} - -/// Extends the `Result` type with convenient methods for constructing Tide errors. -pub trait ResultExt: Sized { - /// Convert to an `EndpointResult`, treating the `Err` case as a client - /// error (response code 400). - fn client_err(self) -> EndpointResult { - self.with_err_status(400) - } - - /// Convert to an `EndpointResult`, treating the `Err` case as a server - /// error (response code 500). - fn server_err(self) -> EndpointResult { - self.with_err_status(500) - } - - /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom - /// response status. - fn with_err_status(self, status: S) -> EndpointResult - where - StatusCode: HttpTryFrom; -} - -/// Extends the `Response` type with a method to extract error causes when applicable. -pub trait ResponseExt { - /// Extract the cause of the unsuccessful response, if any - fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)>; -} - -impl ResponseExt for Response { - fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { - self.extensions().get().map(|Cause(c)| &**c) - } -} - -impl ResultExt for std::result::Result { - fn with_err_status(self, status: S) -> EndpointResult - where - StatusCode: HttpTryFrom, - { - self.map_err(|e| Error { - resp: Response::builder() - .status(status) - .extension(Cause(Box::new(e))) - .body(Body::empty()) - .unwrap(), - }) - } -} diff --git a/tide/src/lib.rs b/tide/src/lib.rs index c3c7841ba..6ef962c0b 100755 --- a/tide/src/lib.rs +++ b/tide/src/lib.rs @@ -10,9 +10,6 @@ future_incompatible, missing_debug_implementations )] -// TODO: Remove this after clippy bug due to async await is resolved. -// ISSUE: https://github.com/rust-lang/rust-clippy/issues/3988 -#![allow(clippy::needless_lifetimes)] //! //! Welcome to Tide. @@ -32,27 +29,20 @@ macro_rules! box_async { } #[macro_use] -pub mod error; +extern crate tide_core; + +#[cfg(feature = "cookies")] +#[doc(inline)] +pub use tide_cookies as cookies; -mod app; -mod context; -pub mod cookies; -mod endpoint; +pub mod error; pub mod forms; pub mod middleware; pub mod querystring; -pub mod response; -mod route; -mod router; #[doc(inline)] -pub use crate::{ - app::{App, Server}, - context::Context, - endpoint::Endpoint, - error::{EndpointResult, Error}, - response::Response, - route::Route, +pub use tide_core::{ + response, App, Context, Endpoint, EndpointResult, Error, Response, Route, Server, }; pub use http; diff --git a/tide/src/middleware/mod.rs b/tide/src/middleware/mod.rs index 64c6c5692..aaa20d48b 100644 --- a/tide/src/middleware/mod.rs +++ b/tide/src/middleware/mod.rs @@ -1,44 +1,7 @@ -use futures::future::BoxFuture; -use std::sync::Arc; - -use crate::{endpoint::DynEndpoint, Context, Response}; - -mod cookies; mod default_headers; mod logger; -pub use self::{cookies::CookiesMiddleware, default_headers::DefaultHeaders, logger::RootLogger}; - -/// Middleware that wraps around remaining middleware chain. -pub trait Middleware: 'static + Send + Sync { - /// Asynchronously handle the request, and return a response. - fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response>; -} - -impl Middleware for F -where - F: Send + Sync + 'static + for<'a> Fn(Context, Next<'a, Data>) -> BoxFuture<'a, Response>, -{ - fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { - (self)(cx, next) - } -} - -/// The remainder of a middleware chain, including the endpoint. -#[allow(missing_debug_implementations)] -pub struct Next<'a, State> { - pub(crate) endpoint: &'a DynEndpoint, - pub(crate) next_middleware: &'a [Arc>], -} - -impl<'a, State: 'static> Next<'a, State> { - /// Asynchronously execute the remaining middleware chain. - pub fn run(mut self, cx: Context) -> BoxFuture<'a, Response> { - if let Some((current, next)) = self.next_middleware.split_first() { - self.next_middleware = next; - current.handle(cx, self) - } else { - (self.endpoint)(cx) - } - } -} +pub use self::{default_headers::DefaultHeaders, logger::RootLogger}; +#[cfg(feature = "cookies")] +pub use tide_cookies::CookiesMiddleware; +pub use tide_core::middleware::{Middleware, Next}; From 85b85e9f12f596bad07153beff039bed590c1712 Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Sun, 19 May 2019 18:49:58 +0200 Subject: [PATCH 13/54] Update travis configuration (#228) * Update travis config * Separate out individual build jobs for faster wall-clock testing * Fix clippy not actually denying warnings (excluded examples because these are currently failing and have non-trivial fixes) * Add build job that checks --no-default-features works * Add build job that checks for intra-doc-resolution failures (excluded tide because of bugs in re-exports with the intra-doc feature) * Fix warnings * Fix doc-link in tide-cookies --- .travis.yml | 39 ++++++++++++++++++++++++---------- tide-compression/src/lib.rs | 6 ++---- tide-cookies/src/middleware.rs | 11 ++++++---- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7a662d4f7..cc4c46432 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,30 @@ language: rust -rust: - - nightly-2019-05-09 - -before_script: | - rustup component add rustfmt clippy -script: | - cargo fmt --all -- --check && - cargo clippy --all --all-features -- -D clippy::all && - cargo build --no-default-features --verbose && - cargo build --all --all-features --verbose && - cargo test --all --all-features --verbose +rust: nightly-2019-05-09 cache: cargo + +matrix: + include: + - name: cargo doc + env: [CACHE_NAME=docs] + script: + - RUSTDOCFLAGS=-Dwarnings + cargo doc --all --all-features --no-deps --exclude tide + + - name: cargo fmt + cache: false + before_script: rustup component add rustfmt + script: cargo fmt --all -- --check + + - name: cargo clippy + env: [CACHE_NAME=clippy] + before_script: rustup component add clippy + script: cargo clippy --all --all-targets --exclude examples -- -Dwarnings + + - name: cargo build --no-default-features + env: [CACHE_NAME=no-default-features] + script: + - cargo build --manifest-path tide/Cargo.toml --no-default-features + - cargo build --manifest-path tide-core/Cargo.toml --no-default-features + + - name: cargo test + script: cargo test --all --verbose diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index e0f88be3a..4c1cad9b1 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -289,8 +289,7 @@ mod tests { .header(ACCEPT_ENCODING, hval) .body(Body::empty()) .unwrap(); - let res = server.simulate(req).unwrap(); - res + server.simulate(req).unwrap() } // Generates a decoded response given a request body and the header value representing its encoding. @@ -301,8 +300,7 @@ mod tests { .header(CONTENT_ENCODING, hval) .body(body) .unwrap(); - let res = server.simulate(req).unwrap(); - res + server.simulate(req).unwrap() } #[test] diff --git a/tide-cookies/src/middleware.rs b/tide-cookies/src/middleware.rs index 99142c82a..86f5ff87e 100644 --- a/tide-cookies/src/middleware.rs +++ b/tide-cookies/src/middleware.rs @@ -9,7 +9,7 @@ use tide_core::{ /// Middleware to work with cookies. /// -/// [`CookiesMiddleware`] along with [`ContextExt`](crate::cookies::ContextExt) provide smooth +/// [`CookiesMiddleware`] along with [`ContextExt`](crate::data::ContextExt) provide smooth /// access to request cookies and setting/removing cookies from response. This leverages the /// [cookie](https://crates.io/crates/cookie) crate. /// This middleware parses cookies from request and caches them in the extension. Once the request @@ -74,7 +74,11 @@ mod tests { /// Tide will use the the `Cookies`'s `Extract` implementation to build this parameter. async fn retrieve_cookie(mut cx: Context<()>) -> String { - format!("{}", cx.get_cookie(COOKIE_NAME).unwrap().unwrap().value()) + cx.get_cookie(COOKIE_NAME) + .unwrap() + .unwrap() + .value() + .to_string() } async fn set_cookie(mut cx: Context<()>) { @@ -109,8 +113,7 @@ mod tests { .header(http::header::COOKIE, "testCookie=RequestCookieValue") .body(Body::empty()) .unwrap(); - let res = server.simulate(req).unwrap(); - res + server.simulate(req).unwrap() } #[test] From e7a7dacdddc791d74edd7e4585e18e10541d3e3d Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 20 May 2019 12:09:05 +0530 Subject: [PATCH 14/54] add tide-log --- Cargo.toml | 1 + tide-log/Cargo.toml | 24 +++++++++++++++++ tide-log/src/lib.rs | 65 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 tide-log/Cargo.toml create mode 100644 tide-log/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 3cf23390f..c5df7f535 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] members = [ "tide", + "tide-log", "tide-compression", "tide-cookies", "tide-core", diff --git a/tide-log/Cargo.toml b/tide-log/Cargo.toml new file mode 100644 index 000000000..ac8440f6c --- /dev/null +++ b/tide-log/Cargo.toml @@ -0,0 +1,24 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Tide middleware for logging" +documentation = "https://docs.rs/tide-log" +keywords = ["tide", "web", "async", "middleware", "logging"] +categories = [ + "logging", + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-log" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + + [dependencies] +tide = { path = "../tide" } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs new file mode 100644 index 000000000..3e82ebc77 --- /dev/null +++ b/tide-log/src/lib.rs @@ -0,0 +1,65 @@ +#![feature(async_await)] +#![deny( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +use futures::future::BoxFuture; +use log::{info, trace}; +use tide::{ + middleware::{Middleware, Next}, + Context, Response, +}; + +macro_rules! box_async { + {$($t:tt)*} => { + ::futures::future::FutureExt::boxed(async move { $($t)* }) + }; +} + +/// A simple requests logger +/// +/// # Examples +/// +/// ```rust +/// +/// let mut app = tide::App::new(); +/// app.middleware(tide_log::RequestLogger::new()); +/// ``` +#[derive(Debug, Clone, Default)] +pub struct RequestLogger; + +impl RequestLogger { + pub fn new() -> Self { + Self::default() + } + + async fn log_basic<'a, Data: Send + Sync + 'static>( + &'a self, + ctx: Context, + next: Next<'a, Data>, + ) -> tide::Response { + let path = ctx.uri().path().to_owned(); + let method = ctx.method().as_str().to_owned(); + trace!("IN => {} {}", method, path); + let start = std::time::Instant::now(); + let res = next.run(ctx).await; + let status = res.status(); + info!( + "{} {} {} {}ms", + method, + path, + status.as_str(), + start.elapsed().as_millis() + ); + res + } +} + +impl Middleware for RequestLogger { + fn handle<'a>(&'a self, ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + box_async! { self.log_basic(ctx, next).await } + } +} From 1106a85955aa7b9881d91d63aae121425c0fb99c Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 20 May 2019 12:16:27 +0530 Subject: [PATCH 15/54] change deny attr to warn; consistent enforcement for all crates --- tide-compression/src/lib.rs | 2 +- tide-cookies/src/lib.rs | 2 +- tide-core/src/lib.rs | 2 +- tide-log/src/lib.rs | 4 +++- tide/src/lib.rs | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index 4c1cad9b1..3e0992ac1 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -3,7 +3,7 @@ #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![cfg_attr(test, deny(warnings))] #![feature(async_await)] -#![deny( +#![warn( nonstandard_style, rust_2018_idioms, future_incompatible, diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs index da88a7054..c60f045ef 100644 --- a/tide-cookies/src/lib.rs +++ b/tide-cookies/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(test, deny(warnings))] #![feature(async_await)] -#![deny( +#![warn( nonstandard_style, rust_2018_idioms, future_incompatible, diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index ca0df3282..54577781f 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -1,7 +1,7 @@ #![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(test, deny(warnings))] #![feature(async_await, existential_type)] -#![deny( +#![warn( nonstandard_style, rust_2018_idioms, future_incompatible, diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index 3e82ebc77..20442315e 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -1,5 +1,7 @@ +#![cfg_attr(feature = "nightly", deny(missing_docs))] +#![cfg_attr(test, deny(warnings))] #![feature(async_await)] -#![deny( +#![warn( nonstandard_style, rust_2018_idioms, future_incompatible, diff --git a/tide/src/lib.rs b/tide/src/lib.rs index 6ef962c0b..dbf6c6123 100755 --- a/tide/src/lib.rs +++ b/tide/src/lib.rs @@ -4,7 +4,7 @@ #![cfg_attr(test, deny(warnings))] #![feature(async_await, existential_type)] #![allow(unused_variables)] -#![deny( +#![warn( nonstandard_style, rust_2018_idioms, future_incompatible, From 2878abad4695a9fa85a90818c31eb844363bce35 Mon Sep 17 00:00:00 2001 From: Zihan Liu Date: Mon, 20 May 2019 17:35:31 +0800 Subject: [PATCH 16/54] Add ResultDynErrExt (#216) --- tide-core/src/error.rs | 52 +++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/tide-core/src/error.rs b/tide-core/src/error.rs index eacfe93ac..d7e748b6b 100644 --- a/tide-core/src/error.rs +++ b/tide-core/src/error.rs @@ -1,5 +1,3 @@ -// use core::pin::Pin; -// use futures::future::Future; use http::{HttpTryFrom, Response, StatusCode}; use http_service::Body; @@ -55,6 +53,18 @@ impl From for Error { } } +/// Extends the `Response` type with a method to extract error causes when applicable. +pub trait ResponseExt { + /// Extract the cause of the unsuccessful response, if any + fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)>; +} + +impl ResponseExt for Response { + fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { + self.extensions().get().map(|Cause(c)| &**c) + } +} + /// Extends the `Result` type with convenient methods for constructing Tide errors. pub trait ResultExt: Sized { /// Convert to an `EndpointResult`, treating the `Err` case as a client @@ -76,19 +86,39 @@ pub trait ResultExt: Sized { StatusCode: HttpTryFrom; } -/// Extends the `Response` type with a method to extract error causes when applicable. -pub trait ResponseExt { - /// Extract the cause of the unsuccessful response, if any - fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)>; +impl ResultExt for std::result::Result { + fn with_err_status(self, status: S) -> EndpointResult + where + StatusCode: HttpTryFrom, + { + let r = self.map_err(|e| Box::new(e) as Box); + r.with_err_status(status) + } } -impl ResponseExt for Response { - fn err_cause(&self) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> { - self.extensions().get().map(|Cause(c)| &**c) +/// Extends the `Result` type using `std::error::Error` trait object as the error type with +/// convenient methods for constructing Tide errors. +pub trait ResultDynErrExt: Sized { + /// Convert to an `EndpointResult`, treating the `Err` case as a client + /// error (response code 400). + fn client_err(self) -> EndpointResult { + self.with_err_status(400) } + + /// Convert to an `EndpointResult`, treating the `Err` case as a server + /// error (response code 500). + fn server_err(self) -> EndpointResult { + self.with_err_status(500) + } + + /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom + /// response status. + fn with_err_status(self, status: S) -> EndpointResult + where + StatusCode: HttpTryFrom; } -impl ResultExt for std::result::Result { +impl ResultDynErrExt for std::result::Result> { fn with_err_status(self, status: S) -> EndpointResult where StatusCode: HttpTryFrom, @@ -96,7 +126,7 @@ impl ResultExt for std::resu self.map_err(|e| Error { resp: Response::builder() .status(status) - .extension(Cause(Box::new(e))) + .extension(Cause(e)) .body(Body::empty()) .unwrap(), }) From 4d6da98a7fd2ecc35d645a331a0ed300a87a189a Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Mon, 20 May 2019 18:53:13 +0530 Subject: [PATCH 17/54] remove remaining deny attrs --- tide-compression/src/lib.rs | 2 -- tide-cookies/src/lib.rs | 2 -- tide-core/src/lib.rs | 2 -- tide-log/src/lib.rs | 2 -- tide/src/lib.rs | 2 -- 5 files changed, 10 deletions(-) diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index 3e0992ac1..718425195 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -1,7 +1,5 @@ -#![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] -#![cfg_attr(test, deny(warnings))] #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs index c60f045ef..6137ae114 100644 --- a/tide-cookies/src/lib.rs +++ b/tide-cookies/src/lib.rs @@ -1,5 +1,3 @@ -#![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(test, deny(warnings))] #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index 54577781f..88c3690d3 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -1,5 +1,3 @@ -#![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(test, deny(warnings))] #![feature(async_await, existential_type)] #![warn( nonstandard_style, diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index 20442315e..4811a4676 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -1,5 +1,3 @@ -#![cfg_attr(feature = "nightly", deny(missing_docs))] -#![cfg_attr(test, deny(warnings))] #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide/src/lib.rs b/tide/src/lib.rs index dbf6c6123..1c9880fe1 100755 --- a/tide/src/lib.rs +++ b/tide/src/lib.rs @@ -1,7 +1,5 @@ -#![cfg_attr(feature = "nightly", deny(missing_docs))] #![cfg_attr(any(feature = "nightly", test), feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] -#![cfg_attr(test, deny(warnings))] #![feature(async_await, existential_type)] #![allow(unused_variables)] #![warn( From 011d0db078d74e78ccf53f03605271b1bba60290 Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 20 May 2019 08:28:28 -0700 Subject: [PATCH 18/54] rename trait ExtractForms to ContextExt (#239) as follows https://github.com/rustasync/tide/issues/187 --- examples/src/body_types.rs | 2 +- examples/src/multipart_form/mod.rs | 2 +- tide/src/forms.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/src/body_types.rs b/examples/src/body_types.rs index c2d518903..f85c90a00 100644 --- a/examples/src/body_types.rs +++ b/examples/src/body_types.rs @@ -1,7 +1,7 @@ use serde::{Deserialize, Serialize}; use tide::{ error::ResultExt, - forms::{self, ExtractForms}, + forms::{self, ContextExt}, response, App, Context, EndpointResult, }; diff --git a/examples/src/multipart_form/mod.rs b/examples/src/multipart_form/mod.rs index 1e7e91b45..bf6acc4a4 100644 --- a/examples/src/multipart_form/mod.rs +++ b/examples/src/multipart_form/mod.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; use std::io::Read; -use tide::{forms::ExtractForms, response, App, Context, EndpointResult}; +use tide::{forms::ContextExt, response, App, Context, EndpointResult}; #[derive(Serialize, Deserialize, Clone)] struct Message { diff --git a/tide/src/forms.rs b/tide/src/forms.rs index b7338dab5..a6f4a7b5f 100644 --- a/tide/src/forms.rs +++ b/tide/src/forms.rs @@ -8,7 +8,7 @@ use crate::{ }; /// An extension trait for `Context`, providing form extraction. -pub trait ExtractForms { +pub trait ContextExt { /// Asynchronously extract the entire body as a single form. fn body_form(&mut self) -> BoxTryFuture; @@ -16,7 +16,7 @@ pub trait ExtractForms { fn body_multipart(&mut self) -> BoxTryFuture>>>; } -impl ExtractForms for Context { +impl ContextExt for Context { fn body_form(&mut self) -> BoxTryFuture { let body = self.take_body(); box_async! { From 6637aafb9ac994a3eb68167dcd2bb2d2a568180e Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 20 May 2019 17:15:42 +0200 Subject: [PATCH 19/54] remove async box macro Signed-off-by: Yoshua Wuyts --- tide-compression/src/lib.rs | 15 +++++---------- tide-cookies/src/lib.rs | 3 --- tide-cookies/src/middleware.rs | 5 +++-- tide-core/src/app.rs | 5 +++-- tide-core/src/endpoint.rs | 5 +++-- tide-core/src/lib.rs | 7 ------- tide-core/src/router.rs | 7 ++++--- tide/src/forms.rs | 9 +++++---- tide/src/lib.rs | 6 ------ tide/src/middleware/default_headers.rs | 5 +++-- tide/src/middleware/logger.rs | 5 +++-- 11 files changed, 29 insertions(+), 43 deletions(-) diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index 4c1cad9b1..dd8605533 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -13,6 +13,7 @@ pub use accept_encoding::Encoding; use async_compression::stream; use futures::future::BoxFuture; +use futures::prelude::*; use http::{header::CONTENT_ENCODING, status::StatusCode, HeaderMap}; use http_service::{Body, Request}; use tide::{ @@ -21,12 +22,6 @@ use tide::{ Context, Error, Response, }; -macro_rules! box_async { - {$($t:tt)*} => { - ::futures::future::FutureExt::boxed(async move { $($t)* }) - }; -} - /// Encode settings for the compression middleware. /// /// This can be modified in the case that you want more control over the speed or quality of compression. @@ -137,14 +132,14 @@ impl Compression { impl Middleware for Compression { fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { - box_async! { + FutureExt::boxed(async move { let encoding = match self.preferred_encoding(cx.headers()) { Ok(encoding) => encoding, Err(e) => return e.into_response(), }; let res = next.run(cx).await; self.encode(res, encoding) - } + }) } } @@ -225,13 +220,13 @@ impl Middleware for Decompression { mut cx: Context, next: Next<'a, Data>, ) -> BoxFuture<'a, Response> { - box_async! { + FutureExt::boxed(async move { match self.decode(cx.request_mut()) { Ok(_) => (), Err(e) => return e.into_response(), }; next.run(cx).await - } + }) } } diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs index da88a7054..c7619d222 100644 --- a/tide-cookies/src/lib.rs +++ b/tide-cookies/src/lib.rs @@ -8,9 +8,6 @@ missing_debug_implementations )] -#[macro_use] -extern crate tide_core; - mod data; mod middleware; diff --git a/tide-cookies/src/middleware.rs b/tide-cookies/src/middleware.rs index 86f5ff87e..2d300369f 100644 --- a/tide-cookies/src/middleware.rs +++ b/tide-cookies/src/middleware.rs @@ -1,6 +1,7 @@ use crate::data::CookieData; use futures::future::BoxFuture; use http::header::HeaderValue; +use futures::prelude::*; use tide_core::{ middleware::{Middleware, Next}, @@ -31,7 +32,7 @@ impl Middleware for CookiesMiddleware { mut cx: Context, next: Next<'a, Data>, ) -> BoxFuture<'a, Response> { - box_async! { + FutureExt::boxed(async move { let cookie_data = cx .extensions_mut() .remove() @@ -56,7 +57,7 @@ impl Middleware for CookiesMiddleware { } } res - } + }) } } diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index ef65b35ad..e0e0549a0 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -1,4 +1,5 @@ use futures::future::{self, BoxFuture}; +use futures::prelude::*; use http_service::HttpService; use std::sync::Arc; @@ -292,7 +293,7 @@ impl HttpService for Server { let middleware = self.middleware.clone(); let data = self.data.clone(); - box_async! { + FutureExt::boxed(async move { let fut = { let Selection { endpoint, params } = router.route(&path, method); let cx = Context::new(data, req, params); @@ -306,7 +307,7 @@ impl HttpService for Server { }; Ok(fut.await) - } + }) } } diff --git a/tide-core/src/endpoint.rs b/tide-core/src/endpoint.rs index 8ec5e988b..663654868 100644 --- a/tide-core/src/endpoint.rs +++ b/tide-core/src/endpoint.rs @@ -1,4 +1,5 @@ use futures::future::{BoxFuture, Future}; +use futures::prelude::*; use crate::{response::IntoResponse, Context, Response}; @@ -68,8 +69,8 @@ where type Fut = BoxFuture<'static, Response>; fn call(&self, cx: Context) -> Self::Fut { let fut = (self)(cx); - box_async! { + FutureExt::boxed(async move { fut.await.into_response() - } + }) } } diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index ca0df3282..f1f06a77b 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -11,13 +11,6 @@ // ISSUE: https://github.com/rust-lang/rust-clippy/issues/3988 #![allow(clippy::needless_lifetimes)] -#[macro_export] -macro_rules! box_async { - {$($t:tt)*} => { - ::futures::future::FutureExt::boxed(async move { $($t)* }) - }; -} - mod app; mod context; mod endpoint; diff --git a/tide-core/src/router.rs b/tide-core/src/router.rs index 34710110d..0b028c2fa 100644 --- a/tide-core/src/router.rs +++ b/tide-core/src/router.rs @@ -1,5 +1,6 @@ use fnv::FnvHashMap; -use futures::future::{BoxFuture, FutureExt}; +use futures::future::BoxFuture; +use futures::prelude::*; use http_service::Body; use route_recognizer::{Match, Params, Router as MethodRouter}; @@ -62,7 +63,7 @@ impl Router { } fn not_found_endpoint(_cx: Context) -> BoxFuture<'static, Response> { - box_async! { + FutureExt::boxed(async move { http::Response::builder().status(http::StatusCode::NOT_FOUND).body(Body::empty()).unwrap() - } + }) } diff --git a/tide/src/forms.rs b/tide/src/forms.rs index b7338dab5..2465b2e59 100644 --- a/tide/src/forms.rs +++ b/tide/src/forms.rs @@ -1,5 +1,6 @@ use http_service::Body; use multipart::server::Multipart; +use futures::prelude::*; use std::io::Cursor; use crate::{ @@ -19,10 +20,10 @@ pub trait ExtractForms { impl ExtractForms for Context { fn body_form(&mut self) -> BoxTryFuture { let body = self.take_body(); - box_async! { + FutureExt::boxed(async move { let body = body.into_vec().await.client_err()?; Ok(serde_urlencoded::from_bytes(&body).map_err(|e| err_fmt!("could not decode form: {}", e)).client_err()?) - } + }) } fn body_multipart(&mut self) -> BoxTryFuture>>> { @@ -35,11 +36,11 @@ impl ExtractForms for Context { let body = self.take_body(); - box_async! { + FutureExt::boxed(async move { let body = body.into_vec().await.client_err()?; let boundary = boundary.ok_or_else(|| err_fmt!("no boundary found")).client_err()?; Ok(Multipart::with_body(Cursor::new(body), boundary)) - } + }) } } diff --git a/tide/src/lib.rs b/tide/src/lib.rs index 6ef962c0b..3337ff4f9 100755 --- a/tide/src/lib.rs +++ b/tide/src/lib.rs @@ -22,12 +22,6 @@ #[doc(include = "../../README.md")] const _README: () = (); -macro_rules! box_async { - {$($t:tt)*} => { - ::futures::future::FutureExt::boxed(async move { $($t)* }) - }; -} - #[macro_use] extern crate tide_core; diff --git a/tide/src/middleware/default_headers.rs b/tide/src/middleware/default_headers.rs index d7b5bffe4..d76dbb192 100644 --- a/tide/src/middleware/default_headers.rs +++ b/tide/src/middleware/default_headers.rs @@ -1,4 +1,5 @@ use futures::future::BoxFuture; +use futures::prelude::*; use http::{ header::{HeaderValue, IntoHeaderName}, @@ -41,7 +42,7 @@ impl DefaultHeaders { impl Middleware for DefaultHeaders { fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { - box_async! { + FutureExt::boxed(async move { let mut res = next.run(cx).await; let headers = res.headers_mut(); @@ -49,6 +50,6 @@ impl Middleware for DefaultHeaders { headers.entry(key).unwrap().or_insert_with(|| value.clone()); } res - } + }) } } diff --git a/tide/src/middleware/logger.rs b/tide/src/middleware/logger.rs index 3becf7928..8036cf585 100644 --- a/tide/src/middleware/logger.rs +++ b/tide/src/middleware/logger.rs @@ -3,6 +3,7 @@ use slog_async; use slog_term; use futures::future::BoxFuture; +use futures::prelude::*; use crate::{ middleware::{Middleware, Next}, @@ -37,7 +38,7 @@ impl Default for RootLogger { /// is generated. impl Middleware for RootLogger { fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { - box_async! { + FutureExt::boxed(async move { let path = cx.uri().path().to_owned(); let method = cx.method().as_str().to_owned(); @@ -45,6 +46,6 @@ impl Middleware for RootLogger { let status = res.status(); info!(self.inner_logger, "{} {} {}", method, path, status.as_str()); res - } + }) } } From e7703fddddba990cc450ef7dda92dfe7a90cf2a4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 20 May 2019 23:35:50 +0200 Subject: [PATCH 20/54] rustfmt Signed-off-by: Yoshua Wuyts --- tide-cookies/src/middleware.rs | 2 +- tide-core/src/endpoint.rs | 4 +--- tide-core/src/router.rs | 5 ++++- tide/src/forms.rs | 10 +++++++--- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tide-cookies/src/middleware.rs b/tide-cookies/src/middleware.rs index 2d300369f..e8dafd243 100644 --- a/tide-cookies/src/middleware.rs +++ b/tide-cookies/src/middleware.rs @@ -1,7 +1,7 @@ use crate::data::CookieData; use futures::future::BoxFuture; -use http::header::HeaderValue; use futures::prelude::*; +use http::header::HeaderValue; use tide_core::{ middleware::{Middleware, Next}, diff --git a/tide-core/src/endpoint.rs b/tide-core/src/endpoint.rs index 663654868..c7959b469 100644 --- a/tide-core/src/endpoint.rs +++ b/tide-core/src/endpoint.rs @@ -69,8 +69,6 @@ where type Fut = BoxFuture<'static, Response>; fn call(&self, cx: Context) -> Self::Fut { let fut = (self)(cx); - FutureExt::boxed(async move { - fut.await.into_response() - }) + FutureExt::boxed(async move { fut.await.into_response() }) } } diff --git a/tide-core/src/router.rs b/tide-core/src/router.rs index 0b028c2fa..eb97a90ba 100644 --- a/tide-core/src/router.rs +++ b/tide-core/src/router.rs @@ -64,6 +64,9 @@ impl Router { fn not_found_endpoint(_cx: Context) -> BoxFuture<'static, Response> { FutureExt::boxed(async move { - http::Response::builder().status(http::StatusCode::NOT_FOUND).body(Body::empty()).unwrap() + http::Response::builder() + .status(http::StatusCode::NOT_FOUND) + .body(Body::empty()) + .unwrap() }) } diff --git a/tide/src/forms.rs b/tide/src/forms.rs index 2465b2e59..be4a39367 100644 --- a/tide/src/forms.rs +++ b/tide/src/forms.rs @@ -1,6 +1,6 @@ +use futures::prelude::*; use http_service::Body; use multipart::server::Multipart; -use futures::prelude::*; use std::io::Cursor; use crate::{ @@ -22,7 +22,9 @@ impl ExtractForms for Context { let body = self.take_body(); FutureExt::boxed(async move { let body = body.into_vec().await.client_err()?; - Ok(serde_urlencoded::from_bytes(&body).map_err(|e| err_fmt!("could not decode form: {}", e)).client_err()?) + Ok(serde_urlencoded::from_bytes(&body) + .map_err(|e| err_fmt!("could not decode form: {}", e)) + .client_err()?) }) } @@ -38,7 +40,9 @@ impl ExtractForms for Context { FutureExt::boxed(async move { let body = body.into_vec().await.client_err()?; - let boundary = boundary.ok_or_else(|| err_fmt!("no boundary found")).client_err()?; + let boundary = boundary + .ok_or_else(|| err_fmt!("no boundary found")) + .client_err()?; Ok(Multipart::with_body(Cursor::new(body), boundary)) }) } From 4806db4abfe03f8ae17d09b7d734c7cba19c2242 Mon Sep 17 00:00:00 2001 From: Dalei Date: Tue, 21 May 2019 05:55:41 +0800 Subject: [PATCH 21/54] Fix broken links in readme (#242) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e60e6f65..f2129f50b 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ fn main() -> Result<(), std::io::Error> { - [Hello World](https://github.com/rustasync/tide/blob/master/examples/src/hello.rs) - [Messages](https://github.com/rustasync/tide/blob/master/examples/src/messages.rs) - [Body Types](https://github.com/rustasync/tide/blob/master/examples/src/body_types.rs) -- [Multipart Form](https://github.com/rustasync/tide/blob/master/examples/src/multipart-form/main.rs) +- [Multipart Form](https://github.com/rustasync/tide/blob/master/examples/src/multipart_form/mod.rs) - [Catch All](https://github.com/rustasync/tide/blob/master/examples/src/catch_all.rs) - [Cookies](https://github.com/rustasync/tide/blob/master/examples/src/cookies.rs) - [Default Headers](https://github.com/rustasync/tide/blob/master/examples/src/default_headers.rs) From 114c17cc6eeaededeb4d470ef891088ddf827174 Mon Sep 17 00:00:00 2001 From: Nemo157 Date: Tue, 21 May 2019 01:50:38 +0200 Subject: [PATCH 22/54] Sweep the cache before uploading it (#246) --- .travis.yml | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index cc4c46432..dd540f831 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,29 +2,57 @@ language: rust rust: nightly-2019-05-09 cache: cargo +before_script: +- > + [[ "$(cargo-sweep --version)" == "cargo-sweep 0.4.1" ]] + || cargo install cargo-sweep +- cargo sweep --stamp + +before_cache: +- cargo sweep --file + matrix: include: - name: cargo doc env: [CACHE_NAME=docs] script: - - RUSTDOCFLAGS=-Dwarnings - cargo doc --all --all-features --no-deps --exclude tide + - RUSTDOCFLAGS=-Dwarnings cargo doc + -Zmtime-on-use + --all --all-features + --exclude tide + --no-deps - name: cargo fmt cache: false - before_script: rustup component add rustfmt - script: cargo fmt --all -- --check + before_script: [] + install: + - rustup component add rustfmt + script: + - cargo fmt --all -- --check - name: cargo clippy env: [CACHE_NAME=clippy] - before_script: rustup component add clippy - script: cargo clippy --all --all-targets --exclude examples -- -Dwarnings + install: + - rustup component add clippy + script: + - cargo clippy + -Zmtime-on-use + --all --all-targets + --exclude examples + -- -Dwarnings - name: cargo build --no-default-features env: [CACHE_NAME=no-default-features] script: - - cargo build --manifest-path tide/Cargo.toml --no-default-features - - cargo build --manifest-path tide-core/Cargo.toml --no-default-features + - cargo build + -Zmtime-on-use + --manifest-path tide-core/Cargo.toml + --no-default-features + - cargo build + -Zmtime-on-use + --manifest-path tide/Cargo.toml + --no-default-features - name: cargo test - script: cargo test --all --verbose + script: + - cargo test -Zmtime-on-use --all --verbose From 0d8caa9608691eff161c4dc8df154e65dc7fa8bc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 21 May 2019 01:51:46 +0200 Subject: [PATCH 23/54] move tide/ to src/ Signed-off-by: Yoshua Wuyts --- Cargo.toml | 64 +++++++++++++++++-- examples/Cargo.toml | 32 ---------- examples/{src => }/body_types.rs | 0 examples/{src => }/catch_all.rs | 0 examples/{src => }/cookies.rs | 0 examples/{src => }/default_headers.rs | 0 examples/{src => }/graphql.rs | 0 examples/{src => }/hello.rs | 0 examples/{src => }/lib.rs | 0 examples/{src => }/messages.rs | 0 examples/{src => }/multipart_form/mod.rs | 0 examples/{src => }/multipart_form/test.txt | 0 examples/{src => }/staticfile.rs | 0 {tide/src => src}/error.rs | 0 {tide/src => src}/forms.rs | 0 {tide/src => src}/lib.rs | 0 .../src => src}/middleware/default_headers.rs | 0 {tide/src => src}/middleware/logger.rs | 0 {tide/src => src}/middleware/mod.rs | 0 {tide/src => src}/querystring.rs | 0 {tide/tests => tests}/wildcard.rs | 0 tide-compression/Cargo.toml | 2 +- tide-core/Cargo.toml | 2 +- tide/Cargo.toml | 45 ------------- 24 files changed, 59 insertions(+), 86 deletions(-) delete mode 100644 examples/Cargo.toml rename examples/{src => }/body_types.rs (100%) rename examples/{src => }/catch_all.rs (100%) rename examples/{src => }/cookies.rs (100%) rename examples/{src => }/default_headers.rs (100%) rename examples/{src => }/graphql.rs (100%) rename examples/{src => }/hello.rs (100%) rename examples/{src => }/lib.rs (100%) rename examples/{src => }/messages.rs (100%) rename examples/{src => }/multipart_form/mod.rs (100%) rename examples/{src => }/multipart_form/test.txt (100%) rename examples/{src => }/staticfile.rs (100%) rename {tide/src => src}/error.rs (100%) rename {tide/src => src}/forms.rs (100%) rename {tide/src => src}/lib.rs (100%) rename {tide/src => src}/middleware/default_headers.rs (100%) rename {tide/src => src}/middleware/logger.rs (100%) rename {tide/src => src}/middleware/mod.rs (100%) rename {tide/src => src}/querystring.rs (100%) rename {tide/tests => tests}/wildcard.rs (100%) delete mode 100644 tide/Cargo.toml diff --git a/Cargo.toml b/Cargo.toml index 3cf23390f..8a466bd06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,63 @@ +[package] +authors = [ + "Aaron Turon ", + "Yoshua Wuyts ", +] +description = "WIP modular web framework" +documentation = "https://docs.rs/tide" +keywords = ["tide", "http", "web", "framework", "async"] +categories = [ + "network-programming", + "asynchronous", + "web-programming::http-server" +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.2.0" + +[features] +default = ["hyper", "cookies"] +cookies = ["tide-cookies"] +hyper = ["tide-core/http-service-hyper"] + +[dependencies] +futures-preview = "0.3.0-alpha.16" +http = "0.1" +http-service = "0.2.0" +serde = "1.0.91" +serde_derive = "1.0.91" +slog = "2.4.1" +slog-async = "2.3.0" +slog-term = "2.4.0" +serde_urlencoded = "0.5.5" +tide-cookies = { path = "./tide-cookies", optional = true } +tide-core = { path = "./tide-core" } + +[dependencies.multipart] +default-features = false +features = ["server"] +version = "0.16.1" + +[dev-dependencies] +bytes = "0.4.12" +cookie = { version = "0.12", features = ["percent-encode"] } +futures-fs = "0.0.5" +futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } +http = "0.1" +http-service = "0.2.0" +http-service-mock = "0.2.0" +juniper = "0.11.1" +mime = "0.3.13" +mime_guess = "2.0.0-alpha.6" +percent-encoding = "1.0.1" +serde = { version = "1.0.91", features = ["derive"] } + [workspace] members = [ - "tide", "tide-compression", "tide-cookies", "tide-core", - "examples", ] - -[patch.crates-io] -http-service = { git = "https://github.com/rustasync/http-service", branch = "master" } -http-service-hyper = { git = "https://github.com/rustasync/http-service", branch = "master" } -http-service-mock = { git = "https://github.com/rustasync/http-service", branch = "master" } diff --git a/examples/Cargo.toml b/examples/Cargo.toml deleted file mode 100644 index af1206539..000000000 --- a/examples/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -authors = [ - "Tide Developers", -] -description = "Tide web server examples" -documentation = "https://docs.rs/tide" -edition = "2018" -license = "MIT OR Apache-2.0" -name = "examples" -readme = "README.md" -repository = "https://github.com/rustasync/tide" -version = "0.1.0" -publish = false - -[dependencies] -tide = { path = "../tide" } -cookie = { version = "0.12", features = ["percent-encode"] } -http = "0.1" -http-service = "0.2.0" -bytes = "0.4.12" -futures-fs = "0.0.5" -futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } -juniper = "0.11.1" -mime = "0.3.13" -mime_guess = "2.0.0-alpha.6" -percent-encoding = "1.0.1" -serde = { version = "1.0.91", features = ["derive"] } - -[dependencies.multipart] -default-features = false -features = ["server"] -version = "0.16.1" diff --git a/examples/src/body_types.rs b/examples/body_types.rs similarity index 100% rename from examples/src/body_types.rs rename to examples/body_types.rs diff --git a/examples/src/catch_all.rs b/examples/catch_all.rs similarity index 100% rename from examples/src/catch_all.rs rename to examples/catch_all.rs diff --git a/examples/src/cookies.rs b/examples/cookies.rs similarity index 100% rename from examples/src/cookies.rs rename to examples/cookies.rs diff --git a/examples/src/default_headers.rs b/examples/default_headers.rs similarity index 100% rename from examples/src/default_headers.rs rename to examples/default_headers.rs diff --git a/examples/src/graphql.rs b/examples/graphql.rs similarity index 100% rename from examples/src/graphql.rs rename to examples/graphql.rs diff --git a/examples/src/hello.rs b/examples/hello.rs similarity index 100% rename from examples/src/hello.rs rename to examples/hello.rs diff --git a/examples/src/lib.rs b/examples/lib.rs similarity index 100% rename from examples/src/lib.rs rename to examples/lib.rs diff --git a/examples/src/messages.rs b/examples/messages.rs similarity index 100% rename from examples/src/messages.rs rename to examples/messages.rs diff --git a/examples/src/multipart_form/mod.rs b/examples/multipart_form/mod.rs similarity index 100% rename from examples/src/multipart_form/mod.rs rename to examples/multipart_form/mod.rs diff --git a/examples/src/multipart_form/test.txt b/examples/multipart_form/test.txt similarity index 100% rename from examples/src/multipart_form/test.txt rename to examples/multipart_form/test.txt diff --git a/examples/src/staticfile.rs b/examples/staticfile.rs similarity index 100% rename from examples/src/staticfile.rs rename to examples/staticfile.rs diff --git a/tide/src/error.rs b/src/error.rs similarity index 100% rename from tide/src/error.rs rename to src/error.rs diff --git a/tide/src/forms.rs b/src/forms.rs similarity index 100% rename from tide/src/forms.rs rename to src/forms.rs diff --git a/tide/src/lib.rs b/src/lib.rs similarity index 100% rename from tide/src/lib.rs rename to src/lib.rs diff --git a/tide/src/middleware/default_headers.rs b/src/middleware/default_headers.rs similarity index 100% rename from tide/src/middleware/default_headers.rs rename to src/middleware/default_headers.rs diff --git a/tide/src/middleware/logger.rs b/src/middleware/logger.rs similarity index 100% rename from tide/src/middleware/logger.rs rename to src/middleware/logger.rs diff --git a/tide/src/middleware/mod.rs b/src/middleware/mod.rs similarity index 100% rename from tide/src/middleware/mod.rs rename to src/middleware/mod.rs diff --git a/tide/src/querystring.rs b/src/querystring.rs similarity index 100% rename from tide/src/querystring.rs rename to src/querystring.rs diff --git a/tide/tests/wildcard.rs b/tests/wildcard.rs similarity index 100% rename from tide/tests/wildcard.rs rename to tests/wildcard.rs diff --git a/tide-compression/Cargo.toml b/tide-compression/Cargo.toml index 19bb2c92c..f00c417d5 100644 --- a/tide-compression/Cargo.toml +++ b/tide-compression/Cargo.toml @@ -14,7 +14,7 @@ repository = "https://github.com/rustasync/tide" version = "0.1.0" [dependencies] -tide = { path = "../tide" } +tide = { path = "../" } accept-encoding = "0.2.0-alpha.2" bytes = "0.4.12" futures-preview = "0.3.0-alpha.16" diff --git a/tide-core/Cargo.toml b/tide-core/Cargo.toml index 60ef60fe8..137b65f38 100644 --- a/tide-core/Cargo.toml +++ b/tide-core/Cargo.toml @@ -24,7 +24,7 @@ optional = true version = "0.2.0" [dev-dependencies] -tide = { path = "../tide" } +tide = { path = "../" } serde_derive = "1.0.91" [features] diff --git a/tide/Cargo.toml b/tide/Cargo.toml deleted file mode 100644 index ce2cb9f28..000000000 --- a/tide/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -authors = [ - "Aaron Turon ", - "Yoshua Wuyts ", -] -description = "WIP modular web framework" -documentation = "https://docs.rs/tide" -keywords = ["tide", "http", "web", "framework", "async"] -categories = [ - "network-programming", - "asynchronous", - "web-programming::http-server" -] -edition = "2018" -license = "MIT OR Apache-2.0" -name = "tide" -readme = "README.md" -repository = "https://github.com/rustasync/tide" -version = "0.2.0" - -[dependencies] -futures-preview = "0.3.0-alpha.16" -http = "0.1" -http-service = "0.2.0" -serde = "1.0.91" -serde_derive = "1.0.91" -slog = "2.4.1" -slog-async = "2.3.0" -slog-term = "2.4.0" -serde_urlencoded = "0.5.5" -tide-cookies = { path = "../tide-cookies", optional = true } -tide-core = { path = "../tide-core" } - -[dependencies.multipart] -default-features = false -features = ["server"] -version = "0.16.1" - -[features] -default = ["hyper", "cookies"] -cookies = ["tide-cookies"] -hyper = ["tide-core/http-service-hyper"] - -[dev-dependencies] -http-service-mock = "0.2.0" From e11f71a31877e8c4e35599f3c073d82655c4e45a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 21 May 2019 01:56:58 +0200 Subject: [PATCH 24/54] fix examples Signed-off-by: Yoshua Wuyts --- Cargo.toml | 9 ++++++--- examples/body_types.rs | 3 ++- examples/catch_all.rs | 3 ++- examples/cookies.rs | 3 ++- examples/default_headers.rs | 4 +++- examples/graphql.rs | 3 ++- examples/hello.rs | 3 ++- examples/lib.rs | 13 ------------- examples/messages.rs | 4 +++- examples/multipart_form/mod.rs | 1 + examples/staticfile.rs | 4 +++- tide-compression/examples/simple.rs | 2 +- 12 files changed, 27 insertions(+), 25 deletions(-) delete mode 100644 examples/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 8a466bd06..e8ccfe732 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,10 +29,10 @@ http = "0.1" http-service = "0.2.0" serde = "1.0.91" serde_derive = "1.0.91" +serde_urlencoded = "0.5.5" slog = "2.4.1" slog-async = "2.3.0" slog-term = "2.4.0" -serde_urlencoded = "0.5.5" tide-cookies = { path = "./tide-cookies", optional = true } tide-core = { path = "./tide-core" } @@ -46,8 +46,6 @@ bytes = "0.4.12" cookie = { version = "0.12", features = ["percent-encode"] } futures-fs = "0.0.5" futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } -http = "0.1" -http-service = "0.2.0" http-service-mock = "0.2.0" juniper = "0.11.1" mime = "0.3.13" @@ -61,3 +59,8 @@ members = [ "tide-cookies", "tide-core", ] + +[patch.crates-io] +http-service = { git = "https://github.com/rustasync/http-service", branch = "master" } +http-service-hyper = { git = "https://github.com/rustasync/http-service", branch = "master" } +http-service-mock = { git = "https://github.com/rustasync/http-service", branch = "master" } diff --git a/examples/body_types.rs b/examples/body_types.rs index f85c90a00..10dd46fac 100644 --- a/examples/body_types.rs +++ b/examples/body_types.rs @@ -1,3 +1,4 @@ +#![feature(async_await)] use serde::{Deserialize, Serialize}; use tide::{ error::ResultExt, @@ -35,7 +36,7 @@ async fn echo_form(mut cx: Context<()>) -> EndpointResult { Ok(forms::form(msg)) } -pub fn main() { +fn main() { let mut app = App::new(); app.at("/echo/string").post(echo_string); diff --git a/examples/catch_all.rs b/examples/catch_all.rs index ebdf736c3..7ff1980d5 100644 --- a/examples/catch_all.rs +++ b/examples/catch_all.rs @@ -1,3 +1,4 @@ +#![feature(async_await)] use tide::Context; async fn echo_path(cx: Context<()>) -> String { @@ -5,7 +6,7 @@ async fn echo_path(cx: Context<()>) -> String { format!("Your path is: {}", path) } -pub fn main() { +fn main() { let mut app = tide::App::new(); app.at("/echo_path/*path").get(echo_path); app.run("127.0.0.1:8000").unwrap(); diff --git a/examples/cookies.rs b/examples/cookies.rs index 670c6afd7..92820e6a8 100644 --- a/examples/cookies.rs +++ b/examples/cookies.rs @@ -1,3 +1,4 @@ +#![feature(async_await)] use cookie::Cookie; use tide::{cookies::ContextExt, middleware::CookiesMiddleware, Context}; @@ -14,7 +15,7 @@ async fn remove_cookie(mut cx: Context<()>) { cx.remove_cookie(Cookie::named("hello")).unwrap(); } -pub fn main() { +fn main() { let mut app = tide::App::new(); app.middleware(CookiesMiddleware::new()); diff --git a/examples/default_headers.rs b/examples/default_headers.rs index b76bfddc7..70f4a8d54 100644 --- a/examples/default_headers.rs +++ b/examples/default_headers.rs @@ -1,6 +1,8 @@ +#![feature(async_await)] + use tide::middleware::DefaultHeaders; -pub fn main() { +fn main() { let mut app = tide::App::new(); app.middleware( diff --git a/examples/graphql.rs b/examples/graphql.rs index a85af2e69..a78c71c00 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -2,6 +2,7 @@ // a look at [the Juniper book]. // // [the Juniper book]: https://graphql-rust.github.io/ +#![feature(async_await)] use http::status::StatusCode; use juniper::graphql_object; use std::sync::{atomic, Arc}; @@ -56,7 +57,7 @@ async fn handle_graphql(mut cx: Context) -> EndpointResult { Ok(resp) } -pub fn main() { +fn main() { let mut app = App::with_state(Data::default()); app.at("/graphql").post(handle_graphql); app.run("127.0.0.1:8000").unwrap(); diff --git a/examples/hello.rs b/examples/hello.rs index 1a11e05c9..edcf106e2 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,4 +1,5 @@ -pub fn main() { +#![feature(async_await)] +fn main() { let mut app = tide::App::new(); app.at("/").get(async move |_| "Hello, world!"); app.run("127.0.0.1:8000").unwrap(); diff --git a/examples/lib.rs b/examples/lib.rs deleted file mode 100644 index 31679fc28..000000000 --- a/examples/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(async_await)] -#![warn(clippy::all)] -#![allow(dead_code)] - -mod body_types; -mod catch_all; -mod cookies; -mod default_headers; -mod graphql; -mod hello; -mod messages; -mod multipart_form; -mod staticfile; diff --git a/examples/messages.rs b/examples/messages.rs index 4abdadf61..023528c89 100644 --- a/examples/messages.rs +++ b/examples/messages.rs @@ -1,3 +1,5 @@ +#![feature(async_await)] + use http::status::StatusCode; use serde::{Deserialize, Serialize}; use std::sync::Mutex; @@ -62,7 +64,7 @@ async fn get_message(cx: Context) -> EndpointResult { } } -pub fn main() { +fn main() { let mut app = App::with_state(Database::default()); app.at("/message").post(new_message); app.at("/message/:id").get(get_message).post(set_message); diff --git a/examples/multipart_form/mod.rs b/examples/multipart_form/mod.rs index bf6acc4a4..0abef7c48 100644 --- a/examples/multipart_form/mod.rs +++ b/examples/multipart_form/mod.rs @@ -1,3 +1,4 @@ +#![feature(async_await)] use serde::{Deserialize, Serialize}; use std::io::Read; use tide::{forms::ContextExt, response, App, Context, EndpointResult}; diff --git a/examples/staticfile.rs b/examples/staticfile.rs index cf44ccd97..d712aceb8 100644 --- a/examples/staticfile.rs +++ b/examples/staticfile.rs @@ -1,3 +1,5 @@ +#![feature(async_await)] + use bytes::Bytes; use futures_fs::FsPool; use futures_util::compat::*; @@ -119,7 +121,7 @@ async fn handle_path(ctx: Context) -> EndpointResult { }) } -pub fn main() { +fn main() { let mut app = App::with_state(StaticFile::new("./")); app.at("/*").get(handle_path); app.run("127.0.0.1:8000").unwrap(); diff --git a/tide-compression/examples/simple.rs b/tide-compression/examples/simple.rs index 2f5d0e174..73484eac0 100644 --- a/tide-compression/examples/simple.rs +++ b/tide-compression/examples/simple.rs @@ -12,7 +12,7 @@ async fn echo_bytes(mut cx: Context<()>) -> Vec { cx.body_bytes().await.unwrap() } -pub fn main() { +fn main() { let mut app = App::new(); app.at("/").get(lorem_ipsum); app.at("/echo").post(echo_bytes); From 15eee7708e976c5a8d8afa4c450d4ff52a4be74d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 21 May 2019 02:13:29 +0200 Subject: [PATCH 25/54] fix tests Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 6ef962c0b..ef9650015 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ //! #[cfg(test)] -#[doc(include = "../../README.md")] +#[doc(include = "../README.md")] const _README: () = (); macro_rules! box_async { From 7a8a33bb157682ed16c0336560f751942786fd0b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 21 May 2019 02:14:29 +0200 Subject: [PATCH 26/54] remove certificate Signed-off-by: Yoshua Wuyts --- .github/CONTRIBUTING.md | 8 -------- CERTIFICATE | 37 ------------------------------------- 2 files changed, 45 deletions(-) delete mode 100644 CERTIFICATE diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e3ad56ae0..a11172a7f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -11,7 +11,6 @@ Repository. - [Code of Conduct](#code-of-conduct) - [Bad Actors](#bad-actors) -- [Developer Certificate of Origin](#developer-certificate-of-origin) ## Code of Conduct The project has a [Code of Conduct](./CODE_OF_CONDUCT.md) that *all* @@ -54,10 +53,3 @@ contributors the benefit of the doubt and having a sincere willingness to admit that you *might* be wrong is critical for any successful open collaboration. Don't be a bad actor. - -## Developer Certificate of Origin -All contributors must read and agree to the [Developer Certificate of -Origin (DCO)](../CERTIFICATE). - -The DCO allows us to accept contributions from people to the project, similarly -to how a license allows us to distribute our code. diff --git a/CERTIFICATE b/CERTIFICATE deleted file mode 100644 index 8201f9921..000000000 --- a/CERTIFICATE +++ /dev/null @@ -1,37 +0,0 @@ -Developer Certificate of Origin -Version 1.1 - -Copyright (C) 2004, 2006 The Linux Foundation and its contributors. -1 Letterman Drive -Suite D4700 -San Francisco, CA, 94129 - -Everyone is permitted to copy and distribute verbatim copies of this -license document, but changing it is not allowed. - - -Developer's Certificate of Origin 1.1 - -By making a contribution to this project, I certify that: - -(a) The contribution was created in whole or in part by me and I - have the right to submit it under the open source license - indicated in the file; or - -(b) The contribution is based upon previous work that, to the best - of my knowledge, is covered under an appropriate open source - license and I have the right under that license to submit that - work with modifications, whether created in whole or in part - by me, under the same open source license (unless I am - permitted to submit under a different license), as indicated - in the file; or - -(c) The contribution was provided directly to me by some other - person who certified (a), (b) or (c) and I have not modified - it. - -(d) I understand and agree that this project and the contribution - are public and that a record of the contribution (including all - personal information I submit with it, including my sign-off) is - maintained indefinitely and may be redistributed consistent with - this project or the open source license(s) involved. From e30f184f0ffa666897cac6f2394f58b75cae5bf0 Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 20 May 2019 21:54:38 -0700 Subject: [PATCH 27/54] remove `--manifest-path` `tide` now sits at root of crate --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cc4c46432..cde8a0275 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ matrix: - name: cargo build --no-default-features env: [CACHE_NAME=no-default-features] script: - - cargo build --manifest-path tide/Cargo.toml --no-default-features + - cargo build --no-default-features - cargo build --manifest-path tide-core/Cargo.toml --no-default-features - name: cargo test From 4df808d0290cb2b8212f452335b7371f7dcb0299 Mon Sep 17 00:00:00 2001 From: "Prasanna V. Loganathar" Date: Tue, 21 May 2019 10:53:04 +0530 Subject: [PATCH 28/54] add tide-log (#222) * add tide-log * remove box_async macro --- Cargo.toml | 1 + tide-log/Cargo.toml | 24 ++++++++++++++++++ tide-log/src/lib.rs | 60 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 tide-log/Cargo.toml create mode 100644 tide-log/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index e8ccfe732..a8fdb55e7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ members = [ "tide-compression", "tide-cookies", "tide-core", + "tide-log", ] [patch.crates-io] diff --git a/tide-log/Cargo.toml b/tide-log/Cargo.toml new file mode 100644 index 000000000..515dda102 --- /dev/null +++ b/tide-log/Cargo.toml @@ -0,0 +1,24 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Tide middleware for logging" +documentation = "https://docs.rs/tide-log" +keywords = ["tide", "web", "async", "middleware", "logging"] +categories = [ + "logging", + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-log" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + + [dependencies] +tide = { path = "../" } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs new file mode 100644 index 000000000..865c6f37b --- /dev/null +++ b/tide-log/src/lib.rs @@ -0,0 +1,60 @@ +#![feature(async_await)] +#![deny( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +use futures::future::BoxFuture; +use futures::prelude::*; +use log::{info, trace}; +use tide::{ + middleware::{Middleware, Next}, + Context, Response, +}; + +/// A simple requests logger +/// +/// # Examples +/// +/// ```rust +/// +/// let mut app = tide::App::new(); +/// app.middleware(tide_log::RequestLogger::new()); +/// ``` +#[derive(Debug, Clone, Default)] +pub struct RequestLogger; + +impl RequestLogger { + pub fn new() -> Self { + Self::default() + } + + async fn log_basic<'a, Data: Send + Sync + 'static>( + &'a self, + ctx: Context, + next: Next<'a, Data>, + ) -> tide::Response { + let path = ctx.uri().path().to_owned(); + let method = ctx.method().as_str().to_owned(); + trace!("IN => {} {}", method, path); + let start = std::time::Instant::now(); + let res = next.run(ctx).await; + let status = res.status(); + info!( + "{} {} {} {}ms", + method, + path, + status.as_str(), + start.elapsed().as_millis() + ); + res + } +} + +impl Middleware for RequestLogger { + fn handle<'a>(&'a self, ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + FutureExt::boxed(async move { self.log_basic(ctx, next).await }) + } +} From a9fbc58d2376fc00393edcdc0d1d6fba8eb31f21 Mon Sep 17 00:00:00 2001 From: k-nasa Date: Tue, 21 May 2019 14:52:20 +0900 Subject: [PATCH 29/54] feat: add type annotation --- examples/body_types.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/body_types.rs b/examples/body_types.rs index 10dd46fac..18269bfb0 100644 --- a/examples/body_types.rs +++ b/examples/body_types.rs @@ -25,13 +25,13 @@ async fn echo_bytes(mut cx: Context<()>) -> Vec { } async fn echo_json(mut cx: Context<()>) -> EndpointResult { - let msg = cx.body_json().await.client_err()?; + let msg: Message = cx.body_json().await.client_err()?; println!("JSON: {:?}", msg); Ok(response::json(msg)) } async fn echo_form(mut cx: Context<()>) -> EndpointResult { - let msg = cx.body_form().await?; + let msg: Message = cx.body_form().await?; println!("Form: {:?}", msg); Ok(forms::form(msg)) } From e45a7e5c54d5afaeccf44c403c16b5f6adf36c44 Mon Sep 17 00:00:00 2001 From: Allen Date: Mon, 20 May 2019 23:11:44 -0700 Subject: [PATCH 30/54] Update .travis.yml `examples` is no longer a package in the workspace following #247 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 97dc27fc2..5a15187c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,6 @@ matrix: - cargo clippy -Zmtime-on-use --all --all-targets - --exclude examples -- -Dwarnings - name: cargo build --no-default-features From 1a93fc25ec6314af7e72f5e4e8fd4304b4f24055 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" Date: Tue, 21 May 2019 07:34:25 +0000 Subject: [PATCH 31/54] Update juniper requirement from 0.11.1 to 0.12.0 Updates the requirements on [juniper](https://github.com/graphql-rust/juniper) to permit the latest version. - [Release notes](https://github.com/graphql-rust/juniper/releases) - [Commits](https://github.com/graphql-rust/juniper/commits) Signed-off-by: dependabot[bot] --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a8fdb55e7..d6e03173d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ cookie = { version = "0.12", features = ["percent-encode"] } futures-fs = "0.0.5" futures-util-preview = { version = "0.3.0-alpha.16", features = ["compat"] } http-service-mock = "0.2.0" -juniper = "0.11.1" +juniper = "0.12.0" mime = "0.3.13" mime_guess = "2.0.0-alpha.6" percent-encoding = "1.0.1" From 8b06916e75de05b8ba34021f07b3e897479857e3 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 16:50:14 +0530 Subject: [PATCH 32/54] isolate tide-headers, tide-slog; better; improve loggers --- Cargo.toml | 11 ++-- src/{middleware/mod.rs => middleware.rs} | 10 ++-- tide-headers/Cargo.toml | 23 ++++++++ .../src/lib.rs | 19 +++++-- tide-log/Cargo.toml | 6 +-- tide-log/src/lib.rs | 29 ++++++++-- tide-slog/Cargo.toml | 27 ++++++++++ .../logger.rs => tide-slog/src/lib.rs | 54 +++++++++++++------ 8 files changed, 142 insertions(+), 37 deletions(-) rename src/{middleware/mod.rs => middleware.rs} (53%) create mode 100644 tide-headers/Cargo.toml rename src/middleware/default_headers.rs => tide-headers/src/lib.rs (79%) create mode 100644 tide-slog/Cargo.toml rename src/middleware/logger.rs => tide-slog/src/lib.rs (51%) diff --git a/Cargo.toml b/Cargo.toml index a8fdb55e7..61250f838 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,11 +30,10 @@ http-service = "0.2.0" serde = "1.0.91" serde_derive = "1.0.91" serde_urlencoded = "0.5.5" -slog = "2.4.1" -slog-async = "2.3.0" -slog-term = "2.4.0" tide-cookies = { path = "./tide-cookies", optional = true } tide-core = { path = "./tide-core" } +tide-headers = { path = "./tide-headers" } +tide-log = { path = "./tide-log" } [dependencies.multipart] default-features = false @@ -52,13 +51,19 @@ mime = "0.3.13" mime_guess = "2.0.0-alpha.6" percent-encoding = "1.0.1" serde = { version = "1.0.91", features = ["derive"] } +tide-log = { path = "./tide-log" } +env_logger = "0.6.1" +log4rs = "0.8.3" +log = "0.4.6" [workspace] members = [ "tide-compression", "tide-cookies", "tide-core", + "tide-headers", "tide-log", + "tide-slog", ] [patch.crates-io] diff --git a/src/middleware/mod.rs b/src/middleware.rs similarity index 53% rename from src/middleware/mod.rs rename to src/middleware.rs index aaa20d48b..581792a7a 100644 --- a/src/middleware/mod.rs +++ b/src/middleware.rs @@ -1,7 +1,9 @@ -mod default_headers; -mod logger; +// Core +pub use tide_core::middleware::{Middleware, Next}; + +// Exports from tide repo. +pub use tide_headers::DefaultHeaders; +pub use tide_log::RequestLogger; -pub use self::{default_headers::DefaultHeaders, logger::RootLogger}; #[cfg(feature = "cookies")] pub use tide_cookies::CookiesMiddleware; -pub use tide_core::middleware::{Middleware, Next}; diff --git a/tide-headers/Cargo.toml b/tide-headers/Cargo.toml new file mode 100644 index 000000000..0d02e69a0 --- /dev/null +++ b/tide-headers/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Header related middleware for tide" +documentation = "https://docs.rs/tide-log" +keywords = ["tide", "web", "async", "middleware", "headers"] +categories = [ + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-headers" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + + [dependencies] +tide-core = { path = "../tide-core" } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" diff --git a/src/middleware/default_headers.rs b/tide-headers/src/lib.rs similarity index 79% rename from src/middleware/default_headers.rs rename to tide-headers/src/lib.rs index d76dbb192..36b080567 100644 --- a/src/middleware/default_headers.rs +++ b/tide-headers/src/lib.rs @@ -1,12 +1,23 @@ +//! Crate that provides helpers, and/or middlewares for tide +//! related to http headers. +#![feature(async_await)] +#![warn( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + use futures::future::BoxFuture; use futures::prelude::*; +use log::trace; use http::{ header::{HeaderValue, IntoHeaderName}, HeaderMap, HttpTryFrom, }; -use crate::{ +use tide_core::{ middleware::{Middleware, Next}, Context, Response, }; @@ -20,10 +31,9 @@ pub struct DefaultHeaders { impl DefaultHeaders { /// Construct a new instance with an empty list of headers. pub fn new() -> DefaultHeaders { - DefaultHeaders::default() + Self::default() } - #[inline] /// Add a header to the default header list. pub fn header(mut self, key: K, value: V) -> Self where @@ -35,7 +45,6 @@ impl DefaultHeaders { .expect("Cannot create default header"); self.headers.append(key, value); - self } } @@ -44,9 +53,9 @@ impl Middleware for DefaultHeaders { fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let mut res = next.run(cx).await; - let headers = res.headers_mut(); for (key, value) in self.headers.iter() { + trace!("add default: {} {:?}", &key, &value); headers.entry(key).unwrap().or_insert_with(|| value.clone()); } res diff --git a/tide-log/Cargo.toml b/tide-log/Cargo.toml index 515dda102..24671ec59 100644 --- a/tide-log/Cargo.toml +++ b/tide-log/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "Tide Developers" ] -description = "Tide middleware for logging" +description = "Logging middleware for tide" documentation = "https://docs.rs/tide-log" keywords = ["tide", "web", "async", "middleware", "logging"] categories = [ @@ -18,7 +18,7 @@ repository = "https://github.com/rustasync/tide" version = "0.1.0" [dependencies] -tide = { path = "../" } +tide-core = { path = "../tide-core" } futures-preview = "0.3.0-alpha.16" http = "0.1" -log = "0.4.6" +log = "0.4.6" diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index 91f057031..a7a875014 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and/or middlewares for tide +//! related to logging. +//! #![feature(async_await)] #![warn( nonstandard_style, @@ -9,7 +12,8 @@ use futures::future::BoxFuture; use futures::prelude::*; use log::{info, trace}; -use tide::{ + +use tide_core::{ middleware::{Middleware, Next}, Context, Response, }; @@ -24,25 +28,40 @@ use tide::{ /// app.middleware(tide_log::RequestLogger::new()); /// ``` #[derive(Debug, Clone, Default)] -pub struct RequestLogger; +pub struct RequestLogger { + target: String, +} impl RequestLogger { + /// Create a new instance of logger with default target as + /// "requests" pub fn new() -> Self { - Self::default() + Self { + target: "requests".to_owned(), + } + } + + /// Create a new instance of logger with supplied `target` for + /// logging. + pub fn with_target(target: String) -> Self { + Self { + target, + } } async fn log_basic<'a, Data: Send + Sync + 'static>( &'a self, ctx: Context, next: Next<'a, Data>, - ) -> tide::Response { + ) -> Response { let path = ctx.uri().path().to_owned(); let method = ctx.method().as_str().to_owned(); - trace!("IN => {} {}", method, path); + trace!(target: &self.target, "IN => {} {}", method, path); let start = std::time::Instant::now(); let res = next.run(ctx).await; let status = res.status(); info!( + target: &self.target, "{} {} {} {}ms", method, path, diff --git a/tide-slog/Cargo.toml b/tide-slog/Cargo.toml new file mode 100644 index 000000000..92487dae0 --- /dev/null +++ b/tide-slog/Cargo.toml @@ -0,0 +1,27 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Logging middleware for tide based on slog" +documentation = "https://docs.rs/tide-log" +keywords = ["tide", "web", "async", "middleware", "logging", "slog"] +categories = [ + "logging", + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-slog" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + + [dependencies] +tide-core = { path = "../tide-core" } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" +slog = "2.4.1" +slog-async = "2.3.0" +slog-term = "2.4.0" diff --git a/src/middleware/logger.rs b/tide-slog/src/lib.rs similarity index 51% rename from src/middleware/logger.rs rename to tide-slog/src/lib.rs index 8036cf585..121f45e8c 100644 --- a/src/middleware/logger.rs +++ b/tide-slog/src/lib.rs @@ -1,50 +1,70 @@ -use slog::{info, o, Drain}; +#![feature(async_await)] +#![warn( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +use slog::{info, trace, o, Drain}; use slog_async; use slog_term; use futures::future::BoxFuture; use futures::prelude::*; -use crate::{ +use tide_core::{ middleware::{Middleware, Next}, Context, Response, }; -/// Root logger for Tide. Wraps over logger provided by slog.SimpleLogger +/// RequestLogger based on slog.SimpleLogger #[derive(Debug)] -pub struct RootLogger { +pub struct RequestLogger { // drain: dyn slog::Drain, - inner_logger: slog::Logger, + inner: slog::Logger, } -impl RootLogger { - pub fn new() -> RootLogger { - let decorator = slog_term::TermDecorator::new().build(); - let drain = slog_term::CompactFormat::new(decorator).build().fuse(); - let drain = slog_async::Async::new(drain).build().fuse(); +impl RequestLogger { + pub fn new() -> Self { + Default::default() + } - let log = slog::Logger::root(drain, o!()); - RootLogger { inner_logger: log } + pub fn with_logger(logger: slog::Logger) -> Self { + Self { inner: logger } } } -impl Default for RootLogger { +impl Default for RequestLogger { fn default() -> Self { - Self::new() + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::CompactFormat::new(decorator).build().fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); + + let log = slog::Logger::root(drain, o!()); + Self { inner: log } } } /// Stores information during request phase and logs information once the response /// is generated. -impl Middleware for RootLogger { +impl Middleware for RequestLogger { fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let path = cx.uri().path().to_owned(); let method = cx.method().as_str().to_owned(); - + trace!(self.inner, "IN => {} {}", method, path); + let start = std::time::Instant::now(); let res = next.run(cx).await; let status = res.status(); - info!(self.inner_logger, "{} {} {}", method, path, status.as_str()); + info!( + self.inner, + "{} {} {} {}ms", + method, + path, + status.as_str(), + start.elapsed().as_millis() + ); res }) } From 225a43d0029a04700442e40833475d2cf7184546 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 16:52:50 +0530 Subject: [PATCH 33/54] add logging examples --- examples/hello_envlog.rs | 8 ++++++++ examples/hello_logrs.rs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 examples/hello_envlog.rs create mode 100644 examples/hello_logrs.rs diff --git a/examples/hello_envlog.rs b/examples/hello_envlog.rs new file mode 100644 index 000000000..d57a5a4bd --- /dev/null +++ b/examples/hello_envlog.rs @@ -0,0 +1,8 @@ +#![feature(async_await)] +fn main() { + env_logger::from_env(env_logger::Env::default().default_filter_or("info")).init(); + let mut app = tide::App::new(); + app.middleware(tide::middleware::RequestLogger::new()); + app.at("/").get(async move |_| "Hello, world!"); + app.run("127.0.0.1:8000").unwrap(); +} diff --git a/examples/hello_logrs.rs b/examples/hello_logrs.rs new file mode 100644 index 000000000..d32f2696e --- /dev/null +++ b/examples/hello_logrs.rs @@ -0,0 +1,18 @@ +#![feature(async_await)] +fn main() { + use log::LevelFilter; + use log4rs::append::console::ConsoleAppender; + use log4rs::config::{Appender, Config, Root}; + + let stdout = ConsoleAppender::builder().build(); + let config = Config::builder() + .appender(Appender::builder().build("stdout", Box::new(stdout))) + .build(Root::builder().appender("stdout").build(LevelFilter::Info)) + .unwrap(); + let _handle = log4rs::init_config(config).unwrap(); + + let mut app = tide::App::new(); + app.middleware(tide::middleware::RequestLogger::new()); + app.at("/").get(async move |_| "Hello, world!"); + app.run("127.0.0.1:8000").unwrap(); +} From 7d9e136b8183156c5dd0d96ff4eff78bd537de50 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 17:42:35 +0530 Subject: [PATCH 34/54] cargo fmt --- tide-log/src/lib.rs | 4 +--- tide-slog/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index a7a875014..88edf072b 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -44,9 +44,7 @@ impl RequestLogger { /// Create a new instance of logger with supplied `target` for /// logging. pub fn with_target(target: String) -> Self { - Self { - target, - } + Self { target } } async fn log_basic<'a, Data: Send + Sync + 'static>( diff --git a/tide-slog/src/lib.rs b/tide-slog/src/lib.rs index 121f45e8c..1b1724fa7 100644 --- a/tide-slog/src/lib.rs +++ b/tide-slog/src/lib.rs @@ -6,7 +6,7 @@ missing_debug_implementations )] -use slog::{info, trace, o, Drain}; +use slog::{info, o, trace, Drain}; use slog_async; use slog_term; From 8aac219292e2688779de1d390fde6e447c70f3c6 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 17:59:42 +0530 Subject: [PATCH 35/54] add tide dev-dep to all crates --- tide-compression/Cargo.toml | 1 + tide-cookies/Cargo.toml | 1 + tide-headers/Cargo.toml | 3 +++ tide-log/Cargo.toml | 3 +++ tide-slog/Cargo.toml | 3 +++ 5 files changed, 11 insertions(+) diff --git a/tide-compression/Cargo.toml b/tide-compression/Cargo.toml index f00c417d5..395fff270 100644 --- a/tide-compression/Cargo.toml +++ b/tide-compression/Cargo.toml @@ -27,4 +27,5 @@ features = ["stream", "gzip", "zlib", "brotli", "zstd"] version = "0.1.0-alpha.1" [dev-dependencies] +tide = { path = "../" } http-service-mock = "0.2.0" diff --git a/tide-cookies/Cargo.toml b/tide-cookies/Cargo.toml index 34b388977..e37532055 100644 --- a/tide-cookies/Cargo.toml +++ b/tide-cookies/Cargo.toml @@ -18,4 +18,5 @@ http-service = "0.2.0" tide-core = { path = "../tide-core" } [dev-dependencies] +tide = { path = "../" } http-service-mock = "0.2.0" diff --git a/tide-headers/Cargo.toml b/tide-headers/Cargo.toml index 0d02e69a0..1340c08bd 100644 --- a/tide-headers/Cargo.toml +++ b/tide-headers/Cargo.toml @@ -21,3 +21,6 @@ tide-core = { path = "../tide-core" } futures-preview = "0.3.0-alpha.16" http = "0.1" log = "0.4.6" + +[dev-dependencies] +tide = { path = "../" } \ No newline at end of file diff --git a/tide-log/Cargo.toml b/tide-log/Cargo.toml index 24671ec59..dee006c86 100644 --- a/tide-log/Cargo.toml +++ b/tide-log/Cargo.toml @@ -22,3 +22,6 @@ tide-core = { path = "../tide-core" } futures-preview = "0.3.0-alpha.16" http = "0.1" log = "0.4.6" + +[dev-dependencies] +tide = { path = "../" } diff --git a/tide-slog/Cargo.toml b/tide-slog/Cargo.toml index 92487dae0..ef6ddb36a 100644 --- a/tide-slog/Cargo.toml +++ b/tide-slog/Cargo.toml @@ -25,3 +25,6 @@ log = "0.4.6" slog = "2.4.1" slog-async = "2.3.0" slog-term = "2.4.0" + +[dev-dependencies] +tide = { path = "../" } \ No newline at end of file From f41754bf270c7aba03df9425e9a4cf4adbc3c367 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 18:11:47 +0530 Subject: [PATCH 36/54] fixup manifest documentation links --- tide-cookies/Cargo.toml | 4 ++-- tide-headers/Cargo.toml | 2 +- tide-slog/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tide-cookies/Cargo.toml b/tide-cookies/Cargo.toml index e37532055..012f61f72 100644 --- a/tide-cookies/Cargo.toml +++ b/tide-cookies/Cargo.toml @@ -5,8 +5,8 @@ edition = "2018" authors = [ "Tide Developers", ] -description = "Cookie management for Tide web framework" -documentation = "https://docs.rs/tide-core" +description = "Cookie middleware and extensions for tide" +documentation = "https://docs.rs/tide-cookies" license = "MIT OR Apache-2.0" repository = "https://github.com/rustasync/tide" diff --git a/tide-headers/Cargo.toml b/tide-headers/Cargo.toml index 1340c08bd..04ff51499 100644 --- a/tide-headers/Cargo.toml +++ b/tide-headers/Cargo.toml @@ -3,7 +3,7 @@ authors = [ "Tide Developers" ] description = "Header related middleware for tide" -documentation = "https://docs.rs/tide-log" +documentation = "https://docs.rs/tide-headers" keywords = ["tide", "web", "async", "middleware", "headers"] categories = [ "network-programming", diff --git a/tide-slog/Cargo.toml b/tide-slog/Cargo.toml index ef6ddb36a..dfddda8f3 100644 --- a/tide-slog/Cargo.toml +++ b/tide-slog/Cargo.toml @@ -3,7 +3,7 @@ authors = [ "Tide Developers" ] description = "Logging middleware for tide based on slog" -documentation = "https://docs.rs/tide-log" +documentation = "https://docs.rs/tide-slog" keywords = ["tide", "web", "async", "middleware", "logging", "slog"] categories = [ "logging", From b47e780552bd48e9353355f18c9eefe8b1fc06a5 Mon Sep 17 00:00:00 2001 From: updogliu Date: Tue, 21 May 2019 21:28:14 +0800 Subject: [PATCH 37/54] pub use ResultDynErrExt in tide::error --- src/error.rs | 4 +++- tide-core/src/error.rs | 6 ++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/error.rs b/src/error.rs index 25ae1cc4b..6f987973b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,8 @@ use core::pin::Pin; use futures::future::Future; -pub use tide_core::error::{EndpointResult, Error, ResponseExt, ResultExt, StringError}; +pub use tide_core::error::{ + EndpointResult, Error, ResponseExt, ResultDynErrExt, ResultExt, StringError, +}; pub(crate) type BoxTryFuture = Pin> + Send + 'static>>; diff --git a/tide-core/src/error.rs b/tide-core/src/error.rs index d7e748b6b..fa5d0db22 100644 --- a/tide-core/src/error.rs +++ b/tide-core/src/error.rs @@ -79,8 +79,7 @@ pub trait ResultExt: Sized { self.with_err_status(500) } - /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom - /// response status. + /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom response status. fn with_err_status(self, status: S) -> EndpointResult where StatusCode: HttpTryFrom; @@ -111,8 +110,7 @@ pub trait ResultDynErrExt: Sized { self.with_err_status(500) } - /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom - /// response status. + /// Convert to an `EndpointResult`, wrapping the `Err` case with a custom response status. fn with_err_status(self, status: S) -> EndpointResult where StatusCode: HttpTryFrom; From 476d75b57ffc10ae8ca9c30af15488cd64832870 Mon Sep 17 00:00:00 2001 From: Allen Date: Tue, 21 May 2019 07:43:30 -0700 Subject: [PATCH 38/54] Update Cargo.toml add newline before EOF --- tide-headers/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tide-headers/Cargo.toml b/tide-headers/Cargo.toml index 04ff51499..f53dff0ed 100644 --- a/tide-headers/Cargo.toml +++ b/tide-headers/Cargo.toml @@ -23,4 +23,4 @@ http = "0.1" log = "0.4.6" [dev-dependencies] -tide = { path = "../" } \ No newline at end of file +tide = { path = "../" } From f354835c211a30035ca997b229b9325f35b1bcc2 Mon Sep 17 00:00:00 2001 From: grey Date: Tue, 21 May 2019 07:51:53 -0700 Subject: [PATCH 39/54] capitalize Tide in crate desc --- tide-cookies/Cargo.toml | 2 +- tide-headers/Cargo.toml | 2 +- tide-log/Cargo.toml | 2 +- tide-slog/Cargo.toml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tide-cookies/Cargo.toml b/tide-cookies/Cargo.toml index 012f61f72..6c0742f4e 100644 --- a/tide-cookies/Cargo.toml +++ b/tide-cookies/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" authors = [ "Tide Developers", ] -description = "Cookie middleware and extensions for tide" +description = "Cookie middleware and extensions for Tide" documentation = "https://docs.rs/tide-cookies" license = "MIT OR Apache-2.0" repository = "https://github.com/rustasync/tide" diff --git a/tide-headers/Cargo.toml b/tide-headers/Cargo.toml index f53dff0ed..294bc7791 100644 --- a/tide-headers/Cargo.toml +++ b/tide-headers/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "Tide Developers" ] -description = "Header related middleware for tide" +description = "Header related middleware for Tide" documentation = "https://docs.rs/tide-headers" keywords = ["tide", "web", "async", "middleware", "headers"] categories = [ diff --git a/tide-log/Cargo.toml b/tide-log/Cargo.toml index dee006c86..4a2074b85 100644 --- a/tide-log/Cargo.toml +++ b/tide-log/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "Tide Developers" ] -description = "Logging middleware for tide" +description = "Logging middleware for Tide" documentation = "https://docs.rs/tide-log" keywords = ["tide", "web", "async", "middleware", "logging"] categories = [ diff --git a/tide-slog/Cargo.toml b/tide-slog/Cargo.toml index dfddda8f3..ca23079b8 100644 --- a/tide-slog/Cargo.toml +++ b/tide-slog/Cargo.toml @@ -2,7 +2,7 @@ authors = [ "Tide Developers" ] -description = "Logging middleware for tide based on slog" +description = "Logging middleware for Tide based on slog" documentation = "https://docs.rs/tide-slog" keywords = ["tide", "web", "async", "middleware", "logging", "slog"] categories = [ @@ -27,4 +27,4 @@ slog-async = "2.3.0" slog-term = "2.4.0" [dev-dependencies] -tide = { path = "../" } \ No newline at end of file +tide = { path = "../" } From 34cb65c4df352917431a8d90767ec9dd1475dc43 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Tue, 21 May 2019 21:32:12 +0530 Subject: [PATCH 40/54] refactor tide-forms, tide-querystring --- Cargo.toml | 12 +++---- src/error.rs | 8 ----- src/lib.rs | 31 +++++++++++++------ src/middleware.rs | 9 ------ tide-core/src/internal.rs | 8 +++++ tide-core/src/lib.rs | 3 ++ tide-forms/Cargo.toml | 31 +++++++++++++++++++ src/forms.rs => tide-forms/src/lib.rs | 14 +++++++-- tide-querystring/Cargo.toml | 31 +++++++++++++++++++ .../src/lib.rs | 15 ++++++--- 10 files changed, 120 insertions(+), 42 deletions(-) delete mode 100644 src/error.rs delete mode 100644 src/middleware.rs create mode 100644 tide-core/src/internal.rs create mode 100644 tide-forms/Cargo.toml rename src/forms.rs => tide-forms/src/lib.rs (90%) create mode 100644 tide-querystring/Cargo.toml rename src/querystring.rs => tide-querystring/src/lib.rs (91%) diff --git a/Cargo.toml b/Cargo.toml index 86801ad61..f977500a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,18 +27,12 @@ hyper = ["tide-core/http-service-hyper"] futures-preview = "0.3.0-alpha.16" http = "0.1" http-service = "0.2.0" -serde = "1.0.91" -serde_derive = "1.0.91" -serde_urlencoded = "0.5.5" tide-cookies = { path = "./tide-cookies", optional = true } tide-core = { path = "./tide-core" } tide-headers = { path = "./tide-headers" } tide-log = { path = "./tide-log" } - -[dependencies.multipart] -default-features = false -features = ["server"] -version = "0.16.1" +tide-forms = { path = "./tide-forms" } +tide-querystring = { path = "./tide-querystring" } [dev-dependencies] bytes = "0.4.12" @@ -61,8 +55,10 @@ members = [ "tide-compression", "tide-cookies", "tide-core", + "tide-forms", "tide-headers", "tide-log", + "tide-querystring", "tide-slog", ] diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 6f987973b..000000000 --- a/src/error.rs +++ /dev/null @@ -1,8 +0,0 @@ -use core::pin::Pin; -use futures::future::Future; - -pub use tide_core::error::{ - EndpointResult, Error, ResponseExt, ResultDynErrExt, ResultExt, StringError, -}; - -pub(crate) type BoxTryFuture = Pin> + Send + 'static>>; diff --git a/src/lib.rs b/src/lib.rs index 63609583a..cf3c600b2 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(any(feature = "nightly", test), feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![feature(async_await, existential_type)] -#![allow(unused_variables)] #![warn( nonstandard_style, rust_2018_idioms, @@ -20,21 +19,33 @@ #[doc(include = "../README.md")] const _README: () = (); -#[macro_use] -extern crate tide_core; +pub use http; #[cfg(feature = "cookies")] #[doc(inline)] pub use tide_cookies as cookies; -pub mod error; -pub mod forms; -pub mod middleware; -pub mod querystring; - #[doc(inline)] pub use tide_core::{ - response, App, Context, Endpoint, EndpointResult, Error, Response, Route, Server, + response, App, Context, Endpoint, EndpointResult, Error, Response, Route, Server, err_fmt + // TODO: export Body once it's in turn exported by tide_core }; -pub use http; +pub mod error { + pub use tide_core::error::{EndpointResult, Error, ResponseExt, ResultExt, ResultDynErrExt, StringError}; +} + +pub use tide_forms as forms; +pub use tide_querystring as querystring; + +pub mod middleware { + // Core + pub use tide_core::middleware::{Middleware, Next}; + + // Exports from tide repo. + pub use tide_headers::DefaultHeaders; + pub use tide_log::RequestLogger; + + #[cfg(feature = "cookies")] + pub use tide_cookies::CookiesMiddleware; +} \ No newline at end of file diff --git a/src/middleware.rs b/src/middleware.rs deleted file mode 100644 index 581792a7a..000000000 --- a/src/middleware.rs +++ /dev/null @@ -1,9 +0,0 @@ -// Core -pub use tide_core::middleware::{Middleware, Next}; - -// Exports from tide repo. -pub use tide_headers::DefaultHeaders; -pub use tide_log::RequestLogger; - -#[cfg(feature = "cookies")] -pub use tide_cookies::CookiesMiddleware; diff --git a/tide-core/src/internal.rs b/tide-core/src/internal.rs new file mode 100644 index 000000000..3866fc5d0 --- /dev/null +++ b/tide-core/src/internal.rs @@ -0,0 +1,8 @@ +//! For internal use. These APIs will never be stable and +//! are meant to be used internally by the tide repo. + +use core::pin::Pin; +use futures::future::Future; + +/// Convenience alias for pinned box of Future> + Send + 'static +pub type BoxTryFuture = Pin> + Send + 'static>>; \ No newline at end of file diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index 4bd2d646a..c025d772e 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -18,6 +18,9 @@ pub mod response; mod route; mod router; +// Internal shared API for limited use across crates in our repo +pub mod internal; + pub use crate::{ app::{App, Server}, context::Context, diff --git a/tide-forms/Cargo.toml b/tide-forms/Cargo.toml new file mode 100644 index 000000000..3b5fe4a44 --- /dev/null +++ b/tide-forms/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Form helpers and extensions for Tide" +documentation = "https://docs.rs/tide-forms" +keywords = ["tide", "web", "async", "helpers", "forms"] +categories = [ + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-forms" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + +[dependencies] +tide-core = { path = "../tide-core" } +http-service = "0.2.0" +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" +multipart = { version = "0.16.1", features = ["server"], default-features = false } +serde = { version = "1.0.91", features = ["derive"] } +serde_urlencoded = "0.5.5" + +[dev-dependencies] +tide = { path = "../" } + diff --git a/src/forms.rs b/tide-forms/src/lib.rs similarity index 90% rename from src/forms.rs rename to tide-forms/src/lib.rs index ddfea6d7d..e8829405d 100644 --- a/src/forms.rs +++ b/tide-forms/src/lib.rs @@ -1,11 +1,21 @@ +#![feature(async_await)] +#![warn( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + use futures::prelude::*; use http_service::Body; use multipart::server::Multipart; use std::io::Cursor; -use crate::{ - error::{BoxTryFuture, ResultExt}, +use tide_core::{ + error::ResultExt, Context, Response, + err_fmt, + internal::BoxTryFuture }; /// An extension trait for `Context`, providing form extraction. diff --git a/tide-querystring/Cargo.toml b/tide-querystring/Cargo.toml new file mode 100644 index 000000000..5f5bbbd95 --- /dev/null +++ b/tide-querystring/Cargo.toml @@ -0,0 +1,31 @@ +[package] +authors = [ + "Tide Developers" +] +description = "Query string helpers and extensions for Tide" +documentation = "https://docs.rs/tide-querystring" +keywords = ["tide", "web", "async", "helpers", "querystring"] +categories = [ + "network-programming", + "web-programming::http-server", +] +edition = "2018" +license = "MIT OR Apache-2.0" +name = "tide-querystring" +readme = "README.md" +repository = "https://github.com/rustasync/tide" +version = "0.1.0" + + [dependencies] +tide-core = { path = "../tide-core" } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +log = "0.4.6" +serde = { version = "1.0.91", features = ["derive"] } +serde_urlencoded = "0.5.5" + +[dev-dependencies] +tide = { path = "../" } +http-service = "0.2.0" +http-service-mock = "0.2.0" + diff --git a/src/querystring.rs b/tide-querystring/src/lib.rs similarity index 91% rename from src/querystring.rs rename to tide-querystring/src/lib.rs index 883108a32..d83076a21 100644 --- a/src/querystring.rs +++ b/tide-querystring/src/lib.rs @@ -1,4 +1,12 @@ -use crate::{error::Error, Context}; +#![feature(async_await)] +#![warn( + nonstandard_style, + rust_2018_idioms, + future_incompatible, + missing_debug_implementations +)] + +use tide_core::{error::Error, Context}; use http::StatusCode; use serde::Deserialize; @@ -8,14 +16,11 @@ pub trait ContextExt<'de> { } impl<'de, Data> ContextExt<'de> for Context { - #[inline] fn url_query>(&'de self) -> Result { let query = self.uri().query(); - if query.is_none() { return Err(Error::from(StatusCode::BAD_REQUEST)); } - Ok(serde_urlencoded::from_str(query.unwrap()) .map_err(|_| Error::from(StatusCode::BAD_REQUEST))?) } @@ -27,7 +32,7 @@ mod tests { use futures::executor::block_on; use http_service::Body; use http_service_mock::make_server; - use serde_derive::Deserialize; + use serde::de::Deserialize; #[derive(Deserialize)] struct Params { From 5c483ba6ae3ae4e033d2af8ee98dae30d99ed4c1 Mon Sep 17 00:00:00 2001 From: Kirk Turner Date: Tue, 21 May 2019 23:02:13 +0800 Subject: [PATCH 41/54] Fix the documentation for wildcards for path definitions to match implementation Also add a bunch of tests that demonstrate the implemented behavior --- tests/wildcard.rs | 169 +++++++++++++++++++++++++++++++++++++++++++ tide-core/src/app.rs | 19 +++-- 2 files changed, 180 insertions(+), 8 deletions(-) diff --git a/tests/wildcard.rs b/tests/wildcard.rs index 6d21ccf46..01c364620 100644 --- a/tests/wildcard.rs +++ b/tests/wildcard.rs @@ -10,6 +10,22 @@ async fn add_one(cx: Context<()>) -> Result { Ok((num + 1).to_string()) } +async fn add_two(cx: Context<()>) -> Result { + let one: i64 = cx.param("one").client_err()?; + let two: i64 = cx.param("two").client_err()?; + Ok((one + two).to_string()) +} + +async fn echo_path(cx: Context<()>) -> Result { + let path: String = cx.param("path").client_err()?; + Ok(path) +} + +async fn echo_empty(cx: Context<()>) -> Result { + let path: String = cx.param("").client_err()?; + Ok(path) +} + #[test] fn wildcard() { let mut app = tide::App::new(); @@ -56,3 +72,156 @@ fn not_found_error() { let res = server.simulate(req).unwrap(); assert_eq!(res.status(), 404); } + +#[test] +fn wildpath() { + let mut app = tide::App::new(); + app.at("/echo/*path").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/some_path") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"some_path"); + + let req = http::Request::get("/echo/multi/segment/path") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"multi/segment/path"); + + let req = http::Request::get("/echo/").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b""); +} + +#[test] +fn multi_wildcard() { + let mut app = tide::App::new(); + app.at("/add_two/:one/:two/").get(add_two); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/add_two/1/2/") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"3"); + + let req = http::Request::get("/add_two/-1/2/") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"1"); + let req = http::Request::get("/add_two/1") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); +} + +#[test] +fn wild_last_segment() { + let mut app = tide::App::new(); + app.at("/echo/:path/*").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); + + let req = http::Request::get("/echo/one/two/three/four") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); +} + +#[test] +fn invalid_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/*path/:one/").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); +} + +#[test] +fn nameless_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/:").get(async move |_| ""); + + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + + let req = http::Request::get("/echo/one").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); +} + +#[test] +fn nameless_internal_wildcard() { + let mut app = tide::App::new(); + app.at("/echo/:/:path").get(echo_path); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one").body(Body::empty()).unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 404); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"two"); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"two"); +} + +#[test] +fn nameless_internal_wildcard2() { + let mut app = tide::App::new(); + app.at("/echo/:/:path").get(echo_empty); + let mut server = make_server(app.into_http_service()).unwrap(); + + let req = http::Request::get("/echo/one/two") + .body(Body::empty()) + .unwrap(); + let res = server.simulate(req).unwrap(); + assert_eq!(res.status(), 200); + let body = block_on(res.into_body().into_vec()).unwrap(); + assert_eq!(&*body, &*b"one"); +} diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index e0e0549a0..8f7b64bc7 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -41,7 +41,7 @@ use crate::{ /// # Routing and parameters /// /// Tide's routing system is simple and similar to many other frameworks. It -/// uses `:foo` for "wildcard" URL segments, and `:foo*` to match the rest of a +/// uses `:foo` for "wildcard" URL segments, and `*foo` to match the rest of a /// URL (which may include multiple segments). Here's an example using wildcard /// segments as parameters to endpoints: /// @@ -183,12 +183,13 @@ impl App { /// parameter called `name`. It is not possible to define wildcard segments /// with different names for otherwise identical paths. /// - /// Wildcard definitions can be followed by an optional *wildcard - /// modifier*. Currently, there is only one modifier: `*`, which means that - /// the wildcard will match to the end of given path, no matter how many - /// segments are left, even nothing. It is an error to define two wildcard - /// segments with different wildcard modifiers, or to write other path - /// segment after a segment with wildcard modifier. + /// Alternatively a wildcard definitions can start with a `*`, for example + /// `*path`, which means that the wildcard will match to the end of given + /// path, no matter how many segments are left, even nothing. + /// + /// The name of the parameter can be omitted to define a path that matches + /// the required structure, but where the parameters are not required. + /// `:` will match a segment, and `*` will match an entire path. /// /// Here are some examples omitting the HTTP verb based endpoint selection: /// @@ -197,7 +198,9 @@ impl App { /// app.at("/"); /// app.at("/hello"); /// app.at("add_two/:num"); - /// app.at("static/:path*"); + /// app.at("files/:user/*"); + /// app.at("static/*path"); + /// app.at("static/:context/:"); /// ``` /// /// There is no fallback route matching, i.e. either a resource is a full From 60834dc6e763ba6701619d8db1d069408017dc3c Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 22 May 2019 00:49:25 +0530 Subject: [PATCH 42/54] fix err_fmt macro --- tide-core/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tide-core/src/error.rs b/tide-core/src/error.rs index fa5d0db22..a64f53edd 100644 --- a/tide-core/src/error.rs +++ b/tide-core/src/error.rs @@ -16,7 +16,7 @@ impl std::fmt::Display for StringError { #[macro_export] macro_rules! err_fmt { {$($t:tt)*} => { - crate::error::StringError(format!($($t)*)) + $crate::error::StringError(format!($($t)*)) } } From d187372e7a765d8bbbfde006eb18d0942d1ebf69 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 22 May 2019 00:50:22 +0530 Subject: [PATCH 43/54] fix querystring tests --- tide-querystring/src/lib.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tide-querystring/src/lib.rs b/tide-querystring/src/lib.rs index d83076a21..3fa9f2af5 100644 --- a/tide-querystring/src/lib.rs +++ b/tide-querystring/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and extensions for Tide +//! related to query strings. + #![feature(async_await)] #![warn( nonstandard_style, @@ -32,20 +35,20 @@ mod tests { use futures::executor::block_on; use http_service::Body; use http_service_mock::make_server; - use serde::de::Deserialize; + use serde::Deserialize; #[derive(Deserialize)] struct Params { msg: String, } - async fn handler(cx: crate::Context<()>) -> Result { + async fn handler(cx: tide::Context<()>) -> Result { let p = cx.url_query::()?; Ok(p.msg) } - fn app() -> crate::App<()> { - let mut app = crate::App::new(); + fn app() -> tide::App<()> { + let mut app = tide::App::new(); app.at("/").get(handler); app } From 676132e6c2f188c6670732058aa2cf05400c5c84 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 22 May 2019 00:59:52 +0530 Subject: [PATCH 44/54] add basic crate level doc --- tide-compression/src/lib.rs | 3 +++ tide-cookies/src/lib.rs | 3 +++ tide-core/src/lib.rs | 2 ++ tide-forms/src/lib.rs | 3 +++ tide-headers/src/lib.rs | 3 ++- tide-log/src/lib.rs | 4 ++-- tide-slog/src/lib.rs | 3 +++ 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index ec4a989a4..31f7327c2 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and/or middlewares for Tide +//! related to compression. + #![cfg_attr(feature = "nightly", feature(external_doc))] #![cfg_attr(feature = "nightly", doc(include = "../README.md"))] #![feature(async_await)] diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs index a2c37ef26..128b2d923 100644 --- a/tide-cookies/src/lib.rs +++ b/tide-cookies/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and/or middlewares for Tide +//! related to cookies. + #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index c025d772e..2a38ab555 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -1,3 +1,5 @@ +//! Core types and traits from Tide + #![feature(async_await, existential_type)] #![warn( nonstandard_style, diff --git a/tide-forms/src/lib.rs b/tide-forms/src/lib.rs index e8829405d..4d8baf270 100644 --- a/tide-forms/src/lib.rs +++ b/tide-forms/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and extensions for Tide +//! related to forms. + #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-headers/src/lib.rs b/tide-headers/src/lib.rs index 36b080567..1303ad1d0 100644 --- a/tide-headers/src/lib.rs +++ b/tide-headers/src/lib.rs @@ -1,5 +1,6 @@ -//! Crate that provides helpers, and/or middlewares for tide +//! Crate that provides helpers and/or middlewares for Tide //! related to http headers. + #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index 88edf072b..9237d74b9 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -1,6 +1,6 @@ -//! Crate that provides helpers and/or middlewares for tide +//! Crate that provides helpers and/or middlewares for Tide //! related to logging. -//! + #![feature(async_await)] #![warn( nonstandard_style, diff --git a/tide-slog/src/lib.rs b/tide-slog/src/lib.rs index 1b1724fa7..04ef2e6ec 100644 --- a/tide-slog/src/lib.rs +++ b/tide-slog/src/lib.rs @@ -1,3 +1,6 @@ +//! Crate that provides helpers and/or middlewares for Tide +//! related to structured logging with slog. + #![feature(async_await)] #![warn( nonstandard_style, From d15bb7a026c0c179bec78e65213a6ae749645bf1 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 22 May 2019 01:01:08 +0530 Subject: [PATCH 45/54] cargo fmt --- src/lib.rs | 19 +++++++++++++++---- tide-core/src/internal.rs | 5 +++-- tide-core/src/lib.rs | 2 +- tide-forms/src/lib.rs | 7 +------ tide-querystring/src/lib.rs | 2 +- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cf3c600b2..a67c722f7 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,12 +27,23 @@ pub use tide_cookies as cookies; #[doc(inline)] pub use tide_core::{ - response, App, Context, Endpoint, EndpointResult, Error, Response, Route, Server, err_fmt - // TODO: export Body once it's in turn exported by tide_core + err_fmt, + response, + App, + Context, + Endpoint, + EndpointResult, + Error, + Response, + Route, + Server, + // TODO: export Body once it's in turn exported by tide_core }; pub mod error { - pub use tide_core::error::{EndpointResult, Error, ResponseExt, ResultExt, ResultDynErrExt, StringError}; + pub use tide_core::error::{ + EndpointResult, Error, ResponseExt, ResultDynErrExt, ResultExt, StringError, + }; } pub use tide_forms as forms; @@ -48,4 +59,4 @@ pub mod middleware { #[cfg(feature = "cookies")] pub use tide_cookies::CookiesMiddleware; -} \ No newline at end of file +} diff --git a/tide-core/src/internal.rs b/tide-core/src/internal.rs index 3866fc5d0..9a2ad59f1 100644 --- a/tide-core/src/internal.rs +++ b/tide-core/src/internal.rs @@ -1,8 +1,9 @@ -//! For internal use. These APIs will never be stable and +//! For internal use. These APIs will never be stable and //! are meant to be used internally by the tide repo. use core::pin::Pin; use futures::future::Future; /// Convenience alias for pinned box of Future> + Send + 'static -pub type BoxTryFuture = Pin> + Send + 'static>>; \ No newline at end of file +pub type BoxTryFuture = + Pin> + Send + 'static>>; diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index 2a38ab555..ecdf21390 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -1,5 +1,5 @@ //! Core types and traits from Tide - + #![feature(async_await, existential_type)] #![warn( nonstandard_style, diff --git a/tide-forms/src/lib.rs b/tide-forms/src/lib.rs index 4d8baf270..67165c2e6 100644 --- a/tide-forms/src/lib.rs +++ b/tide-forms/src/lib.rs @@ -14,12 +14,7 @@ use http_service::Body; use multipart::server::Multipart; use std::io::Cursor; -use tide_core::{ - error::ResultExt, - Context, Response, - err_fmt, - internal::BoxTryFuture -}; +use tide_core::{err_fmt, error::ResultExt, internal::BoxTryFuture, Context, Response}; /// An extension trait for `Context`, providing form extraction. pub trait ContextExt { diff --git a/tide-querystring/src/lib.rs b/tide-querystring/src/lib.rs index 3fa9f2af5..b7ae83bdf 100644 --- a/tide-querystring/src/lib.rs +++ b/tide-querystring/src/lib.rs @@ -9,9 +9,9 @@ missing_debug_implementations )] -use tide_core::{error::Error, Context}; use http::StatusCode; use serde::Deserialize; +use tide_core::{error::Error, Context}; /// An extension trait for `Context`, providing query string deserialization. pub trait ContextExt<'de> { From c121673f87c8fe7ffe0cffccee7696a58e7772b7 Mon Sep 17 00:00:00 2001 From: "Richard Dodd (dodj)" Date: Tue, 21 May 2019 21:35:41 +0100 Subject: [PATCH 46/54] Fix links in README --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f2129f50b..f8297ef00 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,15 @@ fn main() -> Result<(), std::io::Error> { **More Examples** -- [Hello World](https://github.com/rustasync/tide/blob/master/examples/src/hello.rs) -- [Messages](https://github.com/rustasync/tide/blob/master/examples/src/messages.rs) -- [Body Types](https://github.com/rustasync/tide/blob/master/examples/src/body_types.rs) -- [Multipart Form](https://github.com/rustasync/tide/blob/master/examples/src/multipart_form/mod.rs) -- [Catch All](https://github.com/rustasync/tide/blob/master/examples/src/catch_all.rs) -- [Cookies](https://github.com/rustasync/tide/blob/master/examples/src/cookies.rs) -- [Default Headers](https://github.com/rustasync/tide/blob/master/examples/src/default_headers.rs) -- [GraphQL](https://github.com/rustasync/tide/blob/master/examples/src/graphql.rs) -- [Staticfile](https://github.com/rustasync/tide/blob/master/examples/src/staticfile.rs) +- [Hello World](https://github.com/rustasync/tide/blob/master/examples/hello.rs) +- [Messages](https://github.com/rustasync/tide/blob/master/examples/messages.rs) +- [Body Types](https://github.com/rustasync/tide/blob/master/examples/body_types.rs) +- [Multipart Form](https://github.com/rustasync/tide/blob/master/examples/multipart_form/mod.rs) +- [Catch All](https://github.com/rustasync/tide/blob/master/examples/catch_all.rs) +- [Cookies](https://github.com/rustasync/tide/blob/master/examples/cookies.rs) +- [Default Headers](https://github.com/rustasync/tide/blob/master/examples/default_headers.rs) +- [GraphQL](https://github.com/rustasync/tide/blob/master/examples/graphql.rs) +- [Staticfile](https://github.com/rustasync/tide/blob/master/examples/staticfile.rs) ## Resources From 8ac86ec991fd85bb40c8caa1870d3c5f071ba861 Mon Sep 17 00:00:00 2001 From: Prasanna Loganathar Date: Wed, 22 May 2019 02:10:11 +0530 Subject: [PATCH 47/54] fix tide-slog keywords limit --- tide-slog/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tide-slog/Cargo.toml b/tide-slog/Cargo.toml index ca23079b8..dd00a8b04 100644 --- a/tide-slog/Cargo.toml +++ b/tide-slog/Cargo.toml @@ -4,7 +4,7 @@ authors = [ ] description = "Logging middleware for Tide based on slog" documentation = "https://docs.rs/tide-slog" -keywords = ["tide", "web", "async", "middleware", "logging", "slog"] +keywords = ["tide", "web", "middleware", "logging", "slog"] categories = [ "logging", "network-programming", From ce2167ac7449d457381dc76f5ecbbd51a61ef870 Mon Sep 17 00:00:00 2001 From: Maniarr Date: Wed, 22 May 2019 20:25:42 +0200 Subject: [PATCH 48/54] Rename Context to Context --- examples/graphql.rs | 16 ++++++++-------- src/querystring.rs | 2 +- tide-compression/src/lib.rs | 10 +++++----- tide-cookies/src/middleware.rs | 6 +++--- tide-core/src/app.rs | 20 ++++++++++---------- tide-core/src/context.rs | 4 ++-- tide-core/src/middleware.rs | 9 ++++++--- tide-core/src/router.rs | 2 +- tide-headers/src/lib.rs | 4 ++-- tide-log/src/lib.rs | 10 +++++----- tide-slog/src/lib.rs | 4 ++-- 11 files changed, 45 insertions(+), 42 deletions(-) diff --git a/examples/graphql.rs b/examples/graphql.rs index a78c71c00..f79527c12 100644 --- a/examples/graphql.rs +++ b/examples/graphql.rs @@ -8,19 +8,19 @@ use juniper::graphql_object; use std::sync::{atomic, Arc}; use tide::{error::ResultExt, response, App, Context, EndpointResult}; -// First, we define `Data` that holds accumulator state. This is accessible as App data in +// First, we define `State` that holds accumulator state. This is accessible as state in // Tide, and as executor context in Juniper. #[derive(Clone, Default)] -struct Data(Arc); +struct State(Arc); -impl juniper::Context for Data {} +impl juniper::Context for State {} // We define `Query` unit struct here. GraphQL queries will refer to this struct. The struct itself -// doesn't have any associated data (and there's no need to do so), but instead it exposes the +// doesn't have any associated state (and there's no need to do so), but instead it exposes the // accumulator state from the context. struct Query; -graphql_object!(Query: Data |&self| { +graphql_object!(Query: State |&self| { // GraphQL integers are signed and 32 bits long. field accumulator(&executor) -> i32 as "Current value of the accumulator" { executor.context().0.load(atomic::Ordering::Relaxed) as i32 @@ -31,7 +31,7 @@ graphql_object!(Query: Data |&self| { // `Query`, but it provides the way to "mutate" the accumulator state. struct Mutation; -graphql_object!(Mutation: Data |&self| { +graphql_object!(Mutation: State |&self| { field add(&executor, by: i32) -> i32 as "Add given value to the accumulator." { executor.context().0.fetch_add(by as isize, atomic::Ordering::Relaxed) as i32 + by } @@ -43,7 +43,7 @@ type Schema = juniper::RootNode<'static, Query, Mutation>; // Finally, we'll bridge between Tide and Juniper. `GraphQLRequest` from Juniper implements // `Deserialize`, so we use `Json` extractor to deserialize the request body. -async fn handle_graphql(mut cx: Context) -> EndpointResult { +async fn handle_graphql(mut cx: Context) -> EndpointResult { let query: juniper::http::GraphQLRequest = cx.body_json().await.client_err()?; let schema = Schema::new(Query, Mutation); let response = query.execute(&schema, cx.state()); @@ -58,7 +58,7 @@ async fn handle_graphql(mut cx: Context) -> EndpointResult { } fn main() { - let mut app = App::with_state(Data::default()); + let mut app = App::with_state(State::default()); app.at("/graphql").post(handle_graphql); app.run("127.0.0.1:8000").unwrap(); } diff --git a/src/querystring.rs b/src/querystring.rs index 883108a32..ba28b443b 100644 --- a/src/querystring.rs +++ b/src/querystring.rs @@ -7,7 +7,7 @@ pub trait ContextExt<'de> { fn url_query>(&'de self) -> Result; } -impl<'de, Data> ContextExt<'de> for Context { +impl<'de, State> ContextExt<'de> for Context { #[inline] fn url_query>(&'de self) -> Result { let query = self.uri().query(); diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index ec4a989a4..a162748a4 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -128,8 +128,8 @@ impl Compression { } } -impl Middleware for Compression { - fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { +impl Middleware for Compression { + fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let encoding = match self.preferred_encoding(cx.headers()) { Ok(encoding) => encoding, @@ -212,11 +212,11 @@ impl Decompression { } } -impl Middleware for Decompression { +impl Middleware for Decompression { fn handle<'a>( &'a self, - mut cx: Context, - next: Next<'a, Data>, + mut cx: Context, + next: Next<'a, State>, ) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { match self.decode(cx.request_mut()) { diff --git a/tide-cookies/src/middleware.rs b/tide-cookies/src/middleware.rs index e8dafd243..9c34ff332 100644 --- a/tide-cookies/src/middleware.rs +++ b/tide-cookies/src/middleware.rs @@ -26,11 +26,11 @@ impl CookiesMiddleware { } } -impl Middleware for CookiesMiddleware { +impl Middleware for CookiesMiddleware { fn handle<'a>( &'a self, - mut cx: Context, - next: Next<'a, Data>, + mut cx: Context, + next: Next<'a, State>, ) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let cookie_data = cx diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index e0e0549a0..484fd81db 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -133,7 +133,7 @@ use crate::{ pub struct App { router: Router, middleware: Vec>>, - data: State, + state: State, } impl App<()> { @@ -155,7 +155,7 @@ impl App { App { router: Router::new(), middleware: Vec::new(), - data: state, + state, } } @@ -229,7 +229,7 @@ impl App { pub fn into_http_service(self) -> Server { Server { router: Arc::new(self.router), - data: Arc::new(self.data), + state: Arc::new(self.state), middleware: Arc::new(self.middleware), } } @@ -273,7 +273,7 @@ impl App { #[allow(missing_debug_implementations)] pub struct Server { router: Arc>, - data: Arc, + state: Arc, middleware: Arc>>>, } @@ -291,12 +291,12 @@ impl HttpService for Server { let method = req.method().to_owned(); let router = self.router.clone(); let middleware = self.middleware.clone(); - let data = self.data.clone(); + let state = self.state.clone(); FutureExt::boxed(async move { let fut = { let Selection { endpoint, params } = router.route(&path, method); - let cx = Context::new(data, req, params); + let cx = Context::new(state, req, params); let next = Next { endpoint, @@ -319,19 +319,19 @@ mod tests { use super::*; use crate::{middleware::Next, router::Selection, Context, Response}; - fn simulate_request<'a, Data: Default + Clone + Send + Sync + 'static>( - app: &'a App, + fn simulate_request<'a, State: Default + Clone + Send + Sync + 'static>( + app: &'a App, path: &'a str, method: http::Method, ) -> BoxFuture<'a, Response> { let Selection { endpoint, params } = app.router.route(path, method.clone()); - let data = Arc::new(Data::default()); + let state = Arc::new(State::default()); let req = http::Request::builder() .method(method) .body(http_service::Body::empty()) .unwrap(); - let cx = Context::new(data, req, params); + let cx = Context::new(state, req, params); let next = Next { endpoint, next_middleware: &app.middleware, diff --git a/tide-core/src/context.rs b/tide-core/src/context.rs index 543ac8a5d..d3e1bacd5 100644 --- a/tide-core/src/context.rs +++ b/tide-core/src/context.rs @@ -3,7 +3,7 @@ use http_service::Body; use route_recognizer::Params; use std::{str::FromStr, sync::Arc}; -/// Data associated with a request-response lifecycle. +/// State associated with a request-response lifecycle. /// /// The `Context` gives endpoints access to basic information about the incoming /// request, route parameters, and various ways of accessing the request's body. @@ -60,7 +60,7 @@ impl Context { &mut self.request } - /// Access app-global data. + /// Access the state. pub fn state(&self) -> &State { &self.state } diff --git a/tide-core/src/middleware.rs b/tide-core/src/middleware.rs index 47d1caa13..2b881f27f 100644 --- a/tide-core/src/middleware.rs +++ b/tide-core/src/middleware.rs @@ -9,11 +9,14 @@ pub trait Middleware: 'static + Send + Sync { fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response>; } -impl Middleware for F +impl Middleware for F where - F: Send + Sync + 'static + for<'a> Fn(Context, Next<'a, Data>) -> BoxFuture<'a, Response>, + F: Send + + Sync + + 'static + + for<'a> Fn(Context, Next<'a, State>) -> BoxFuture<'a, Response>, { - fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response> { (self)(cx, next) } } diff --git a/tide-core/src/router.rs b/tide-core/src/router.rs index eb97a90ba..1ce768dad 100644 --- a/tide-core/src/router.rs +++ b/tide-core/src/router.rs @@ -62,7 +62,7 @@ impl Router { } } -fn not_found_endpoint(_cx: Context) -> BoxFuture<'static, Response> { +fn not_found_endpoint(_cx: Context) -> BoxFuture<'static, Response> { FutureExt::boxed(async move { http::Response::builder() .status(http::StatusCode::NOT_FOUND) diff --git a/tide-headers/src/lib.rs b/tide-headers/src/lib.rs index 36b080567..d5692bd19 100644 --- a/tide-headers/src/lib.rs +++ b/tide-headers/src/lib.rs @@ -49,8 +49,8 @@ impl DefaultHeaders { } } -impl Middleware for DefaultHeaders { - fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { +impl Middleware for DefaultHeaders { + fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let mut res = next.run(cx).await; let headers = res.headers_mut(); diff --git a/tide-log/src/lib.rs b/tide-log/src/lib.rs index 88edf072b..cc0787657 100644 --- a/tide-log/src/lib.rs +++ b/tide-log/src/lib.rs @@ -47,10 +47,10 @@ impl RequestLogger { Self { target } } - async fn log_basic<'a, Data: Send + Sync + 'static>( + async fn log_basic<'a, State: Send + Sync + 'static>( &'a self, - ctx: Context, - next: Next<'a, Data>, + ctx: Context, + next: Next<'a, State>, ) -> Response { let path = ctx.uri().path().to_owned(); let method = ctx.method().as_str().to_owned(); @@ -70,8 +70,8 @@ impl RequestLogger { } } -impl Middleware for RequestLogger { - fn handle<'a>(&'a self, ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { +impl Middleware for RequestLogger { + fn handle<'a>(&'a self, ctx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { self.log_basic(ctx, next).await }) } } diff --git a/tide-slog/src/lib.rs b/tide-slog/src/lib.rs index 1b1724fa7..b0b782208 100644 --- a/tide-slog/src/lib.rs +++ b/tide-slog/src/lib.rs @@ -48,8 +48,8 @@ impl Default for RequestLogger { /// Stores information during request phase and logs information once the response /// is generated. -impl Middleware for RequestLogger { - fn handle<'a>(&'a self, cx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { +impl Middleware for RequestLogger { + fn handle<'a>(&'a self, cx: Context, next: Next<'a, State>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { let path = cx.uri().path().to_owned(); let method = cx.method().as_str().to_owned(); From 9fbd88f4a717eb08f184b112bd53f11d48058040 Mon Sep 17 00:00:00 2001 From: grey Date: Wed, 22 May 2019 15:38:15 -0700 Subject: [PATCH 49/54] propagate hyper error on serve failure --- tide-core/src/app.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index 484fd81db..d61988c37 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -257,11 +257,9 @@ impl App { .next() .ok_or(std::io::ErrorKind::InvalidInput)?; - // TODO: propagate the error from hyper http_service_hyper::serve(self.into_http_service(), addr) .await - .ok(); - Ok(()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)) } } From 279e0f05a0a24fba9fc192f1d7956ad60fa0cf24 Mon Sep 17 00:00:00 2001 From: nasa Date: Mon, 27 May 2019 20:03:16 +0900 Subject: [PATCH 50/54] Add doc comment (#250) * doc: Add doc comment to tide-core * doc: Add documantation to tide-cookie * doc: Add doc to tide-compression * doc: Add doc comment * fix doc links * fix path * doc: Add doc comment --- src/lib.rs | 7 ++++++- tide-compression/src/lib.rs | 5 ++++- tide-cookies/src/lib.rs | 5 ++++- tide-cookies/src/middleware.rs | 6 +++++- tide-core/src/app.rs | 4 ++++ tide-core/src/endpoint.rs | 2 +- tide-core/src/error.rs | 7 +++++-- tide-core/src/lib.rs | 9 ++++++++- tide-core/src/middleware.rs | 3 ++- tide-core/src/response.rs | 3 +++ tide-core/src/route.rs | 1 + tide-querystring/src/lib.rs | 1 + 12 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a67c722f7..5b8ef56ac 100755 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,8 @@ nonstandard_style, rust_2018_idioms, future_incompatible, - missing_debug_implementations + missing_debug_implementations, + missing_docs )] //! @@ -41,6 +42,8 @@ pub use tide_core::{ }; pub mod error { + //! Module to export tide_core errors + pub use tide_core::error::{ EndpointResult, Error, ResponseExt, ResultDynErrExt, ResultExt, StringError, }; @@ -50,6 +53,8 @@ pub use tide_forms as forms; pub use tide_querystring as querystring; pub mod middleware { + //! Module to export tide_core middleware + // Core pub use tide_core::middleware::{Middleware, Next}; diff --git a/tide-compression/src/lib.rs b/tide-compression/src/lib.rs index 721caa9bb..07c3c78ee 100644 --- a/tide-compression/src/lib.rs +++ b/tide-compression/src/lib.rs @@ -8,9 +8,12 @@ nonstandard_style, rust_2018_idioms, future_incompatible, - missing_debug_implementations + missing_debug_implementations, + missing_docs )] +//! Compression-related middleware for Tide + pub use accept_encoding::Encoding; use async_compression::stream; use futures::future::BoxFuture; diff --git a/tide-cookies/src/lib.rs b/tide-cookies/src/lib.rs index 128b2d923..c1c72a651 100644 --- a/tide-cookies/src/lib.rs +++ b/tide-cookies/src/lib.rs @@ -6,9 +6,12 @@ nonstandard_style, rust_2018_idioms, future_incompatible, - missing_debug_implementations + missing_debug_implementations, + missing_docs )] +//! Cookie management for Tide web framework + mod data; mod middleware; diff --git a/tide-cookies/src/middleware.rs b/tide-cookies/src/middleware.rs index 9c34ff332..41c3e10db 100644 --- a/tide-cookies/src/middleware.rs +++ b/tide-cookies/src/middleware.rs @@ -10,17 +10,21 @@ use tide_core::{ /// Middleware to work with cookies. /// -/// [`CookiesMiddleware`] along with [`ContextExt`](crate::data::ContextExt) provide smooth +/// [`CookiesMiddleware`] along with [`ContextExt`] provide smooth /// access to request cookies and setting/removing cookies from response. This leverages the /// [cookie](https://crates.io/crates/cookie) crate. /// This middleware parses cookies from request and caches them in the extension. Once the request /// is processed by endpoints and other middlewares, all the added and removed cookies are set on /// on the response. You will need to add this middle before any other middlewares that might need /// to access Cookies. +/// +/// [`CookiesMiddleware`]: crate::middleware::CookiesMiddleware +/// [`ContextExt`]: ../../tide/cookies/trait.ContextExt.html #[derive(Clone, Default, Debug)] pub struct CookiesMiddleware {} impl CookiesMiddleware { + /// CookieMiddleware constructor pub fn new() -> Self { Self {} } diff --git a/tide-core/src/app.rs b/tide-core/src/app.rs index 181ef649e..b036fb0d0 100644 --- a/tide-core/src/app.rs +++ b/tide-core/src/app.rs @@ -220,6 +220,8 @@ impl App { /// /// Middleware can only be added at the "top level" of an application, /// and is processed in the order in which it is applied. + /// + /// [`Middleware`]: crate::middleware::Middleware pub fn middleware(&mut self, m: impl Middleware) -> &mut Self { self.middleware.push(Arc::new(m)); self @@ -270,6 +272,8 @@ impl App { /// /// This type is useful only in conjunction with the [`HttpService`] trait, /// i.e. for hosting a Tide app within some custom HTTP server. +/// +/// [`HttpService`]: http_service::HttpService #[derive(Clone)] #[allow(missing_debug_implementations)] pub struct Server { diff --git a/tide-core/src/endpoint.rs b/tide-core/src/endpoint.rs index c7959b469..2b379295c 100644 --- a/tide-core/src/endpoint.rs +++ b/tide-core/src/endpoint.rs @@ -9,7 +9,7 @@ use crate::{response::IntoResponse, Context, Response}; /// directly by Tide users. /// /// In practice, endpoints are functions that take a `Context` as an argument and -/// return a type `T` that implements [`IntoResponse`]. +/// return a type `T` that implements [`IntoResponse`](crate::response::IntoResponse). /// /// # Examples /// diff --git a/tide-core/src/error.rs b/tide-core/src/error.rs index a64f53edd..5062939ca 100644 --- a/tide-core/src/error.rs +++ b/tide-core/src/error.rs @@ -1,9 +1,11 @@ -use http::{HttpTryFrom, Response, StatusCode}; -use http_service::Body; +//! Error and Result module use crate::response::IntoResponse; +use http::{HttpTryFrom, Response, StatusCode}; +use http_service::Body; #[derive(Debug)] +/// A string error, which can be display pub struct StringError(pub String); impl std::error::Error for StringError {} @@ -14,6 +16,7 @@ impl std::fmt::Display for StringError { } #[macro_export] +/// Macro that generates StringError immediately macro_rules! err_fmt { {$($t:tt)*} => { $crate::error::StringError(format!($($t)*)) diff --git a/tide-core/src/lib.rs b/tide-core/src/lib.rs index ecdf21390..4ede67a03 100644 --- a/tide-core/src/lib.rs +++ b/tide-core/src/lib.rs @@ -5,12 +5,19 @@ nonstandard_style, rust_2018_idioms, future_incompatible, - missing_debug_implementations + missing_debug_implementations, + missing_docs )] // TODO: Remove this after clippy bug due to async await is resolved. // ISSUE: https://github.com/rust-lang/rust-clippy/issues/3988 #![allow(clippy::needless_lifetimes)] +//! +//! Tide core api document +//! +//! The [`App`] docs are a good place to get started. +//! + mod app; mod context; mod endpoint; diff --git a/tide-core/src/middleware.rs b/tide-core/src/middleware.rs index 2b881f27f..12129edd0 100644 --- a/tide-core/src/middleware.rs +++ b/tide-core/src/middleware.rs @@ -1,6 +1,7 @@ +//! Middlewares + use crate::{endpoint::DynEndpoint, Context, Response}; use futures::future::BoxFuture; - use std::sync::Arc; /// Middleware that wraps around remaining middleware chain. diff --git a/tide-core/src/response.rs b/tide-core/src/response.rs index 90751b25d..0adea7e63 100644 --- a/tide-core/src/response.rs +++ b/tide-core/src/response.rs @@ -1,5 +1,8 @@ +//! Multiple types of response modules + use http_service::Body; +/// An Http response pub type Response = http_service::Response; /// Serialize `t` into a JSON-encoded response. diff --git a/tide-core/src/route.rs b/tide-core/src/route.rs index 0406e793a..dc5297073 100644 --- a/tide-core/src/route.rs +++ b/tide-core/src/route.rs @@ -37,6 +37,7 @@ impl<'a, State: 'static> Route<'a, State> { } } + /// Add endpoint nested routes pub fn nest(&mut self, f: impl FnOnce(&mut Route<'a, State>)) -> &mut Self { f(self); self diff --git a/tide-querystring/src/lib.rs b/tide-querystring/src/lib.rs index 48023f061..15e735a7f 100644 --- a/tide-querystring/src/lib.rs +++ b/tide-querystring/src/lib.rs @@ -15,6 +15,7 @@ use tide_core::{error::Error, Context}; /// An extension trait for `Context`, providing query string deserialization. pub trait ContextExt<'de> { + /// Analyze url and extract query parameters fn url_query>(&'de self) -> Result; } From 8470e060aa806effb1f14de3e2118ba8b5d9af12 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 27 May 2019 13:46:53 -0700 Subject: [PATCH 51/54] feat: add session middleware with pluggable backing --- Cargo.toml | 1 + tide-sessions/Cargo.toml | 23 +++++++ tide-sessions/src/lib.rs | 111 +++++++++++++++++++++++++++++++ tide-sessions/src/session_map.rs | 48 +++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 tide-sessions/Cargo.toml create mode 100644 tide-sessions/src/lib.rs create mode 100644 tide-sessions/src/session_map.rs diff --git a/Cargo.toml b/Cargo.toml index f977500a5..133e63621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -54,6 +54,7 @@ log = "0.4.6" members = [ "tide-compression", "tide-cookies", + "tide-sessions", "tide-core", "tide-forms", "tide-headers", diff --git a/tide-sessions/Cargo.toml b/tide-sessions/Cargo.toml new file mode 100644 index 000000000..254d588df --- /dev/null +++ b/tide-sessions/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "tide-sessions" +version = "0.0.1" +edition = "2018" +authors = [ + "Tide Developers", +] +description = "Session middleware and extensions for Tide" +documentation = "https://docs.rs/tide-sessions" +license = "MIT OR Apache-2.0" +repository = "https://github.com/rustasync/tide" + +[dependencies] +tide = { path = "../" } +cookie = { version = "0.12", features = ["percent-encode"] } +futures-preview = "0.3.0-alpha.16" +http = "0.1" +http-service = "0.2.0" +tide-core = { path = "../tide-core" } + +[dev-dependencies] +tide = { path = "../" } +http-service-mock = "0.2.0" diff --git a/tide-sessions/src/lib.rs b/tide-sessions/src/lib.rs new file mode 100644 index 000000000..6e3d62c40 --- /dev/null +++ b/tide-sessions/src/lib.rs @@ -0,0 +1,111 @@ +#![feature(async_await)] +use std::{ + cell::{ RefCell, Ref, RefMut }, + sync::Arc +}; +use tide::{ + Context, + Response, + cookies::ContextExt, + middleware::{ Middleware, Next } +}; +use futures::future::BoxFuture; +use futures::prelude::*; +use http::header::HeaderValue; + +mod session_map; +use self::session_map::SessionMap; + +pub trait SessionStore { + fn load_session(&self, key: &str) -> SessionMap; + fn create_session(&self) -> SessionMap { + SessionMap::new() + } + fn commit(&self, session: Ref>) -> Result; +} + +pub struct SessionMiddleware { + pub session_key: String, + pub store: Store +} + +#[derive(Clone)] +pub struct SessionCell(RefCell>); + +// We're copying actix, here. I need to understand this better, because +// this strikes me as dangerous. +#[doc(hidden)] +unsafe impl Send for SessionCell {} +#[doc(hidden)] +unsafe impl Sync for SessionCell {} + +// If a handler needs access to the session (mutably or immutably) it can +// import this trait. +pub trait SessionExt { + fn session(&self) -> Ref>; + fn session_mut(&self) -> RefMut>; +} + +impl< + Data: Clone + Send + Sync + 'static +> SessionExt for Context { + fn session(&self) -> Ref> { + let session_cell = self.extensions().get::>().unwrap(); + session_cell.0.borrow() + } + + fn session_mut(&self) -> RefMut> { + let session_cell = self.extensions().get::>().unwrap(); + session_cell.0.borrow_mut() + } +} + +impl< + Data: Clone + Send + Sync + 'static, + S: SessionStore + Send + Sync + 'static +> Middleware for SessionMiddleware { + fn handle<'a>(&'a self, mut ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { + + FutureExt::boxed(async move { + let result_maybe_session = ctx.get_cookie(&self.session_key); + let mut has_session = false; + + let session = match result_maybe_session { + Ok(maybe_session) => match maybe_session { + Some(cookie) => { + has_session = true; + self.store.load_session(cookie.value()) + }, + None => self.store.create_session() + }, + Err(_) => self.store.create_session() + }; + + // Create a ref-counted cell (yay interior mutability.) Attach + // a clone of that arc'd cell to the context and send it + // through. At the same time, keep our local copy of the arc + // ready for inspection after we're done processing the + // request. + let cell = Arc::new(SessionCell(RefCell::new(Box::new(session)))); + ctx.extensions_mut().insert(cell.clone()); + let mut res = next.run(ctx).await; + + // Borrow the session map and check to see if we need to commit + // it and/or send a new cookie. + let session_cell = &cell.0; + let session = session_cell.borrow(); + if !SessionMap::dirty(&session) { + return res + } + + if let Ok(key) = self.store.commit(session) { + if !has_session { + let hm = res.headers_mut(); + hm.insert("Set-Cookie", key); + } + } + + res + }) + } +} diff --git a/tide-sessions/src/session_map.rs b/tide-sessions/src/session_map.rs new file mode 100644 index 000000000..d02ff4caa --- /dev/null +++ b/tide-sessions/src/session_map.rs @@ -0,0 +1,48 @@ +use std::{ + cell::Ref, + ops::{ Deref, DerefMut } +}; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct SessionMap { + is_dirty: bool, + data: HashMap // XXX: this could be made more generic +} + +// Provide associated functions a la Box or Arc, so we can +// Deref directly to the internal HashMap. +impl SessionMap { + pub fn dirty(target: &Ref>) -> bool { + target.is_dirty + } + + pub fn rotate(target: &mut Self) { + target.is_dirty = true + } + + pub fn new() -> Self { + Self { + is_dirty: false, + data: HashMap::new() + } + } +} + +impl Deref for SessionMap { + type Target = HashMap; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + +impl DerefMut for SessionMap { + // XXX: A tweet linked to this line in master earlier. If you're + // coming in from that link, the original comment is preserved + // at this URL: https://github.com/chrisdickinson/blog-rs/blob/6dfbe91a4fa09714ce6a975e4663e3e1efdaf9fa/src/session.rs#L45 + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data + } +} + From 16584b9b7f7dc10ac11a5548e1dd7925fb52df7a Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 27 May 2019 14:05:30 -0700 Subject: [PATCH 52/54] fix: clean up lifetimes a bit; add TODO comments --- tide-sessions/src/lib.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tide-sessions/src/lib.rs b/tide-sessions/src/lib.rs index 6e3d62c40..0c1899f82 100644 --- a/tide-sessions/src/lib.rs +++ b/tide-sessions/src/lib.rs @@ -21,7 +21,7 @@ pub trait SessionStore { fn create_session(&self) -> SessionMap { SessionMap::new() } - fn commit(&self, session: Ref>) -> Result; + fn commit(&self, key: Option<&str>, session: Ref>) -> Result; } pub struct SessionMiddleware { @@ -68,17 +68,20 @@ impl< FutureExt::boxed(async move { let result_maybe_session = ctx.get_cookie(&self.session_key); - let mut has_session = false; - let session = match result_maybe_session { + let session_key = match result_maybe_session { Ok(maybe_session) => match maybe_session { - Some(cookie) => { - has_session = true; - self.store.load_session(cookie.value()) - }, - None => self.store.create_session() + Some(cookie) => Some(String::from(cookie.value())), + None => None }, - Err(_) => self.store.create_session() + Err(_) => None + }; + + let session = match session_key.as_ref() { + Some(value) => { + self.store.load_session(&value) + }, + None => self.store.create_session() }; // Create a ref-counted cell (yay interior mutability.) Attach @@ -98,9 +101,15 @@ impl< return res } - if let Ok(key) = self.store.commit(session) { - if !has_session { + if let Ok(key) = self.store.commit(session_key.as_ref().map(String::as_str), session) { + if session_key.is_none() { let hm = res.headers_mut(); + // TODO: set the cookie's domain, path, expiry, sameSite, + // etc. properties via options set in the middleware. + // TODO: is there a good way to play nicely with cookie + // middleware? Can we rely on additions to context that + // other middleware add during the response half of the + // request lifecycle? hm.insert("Set-Cookie", key); } } From 75f97bc73041e59d61b59d0b151a23341a1f5ee8 Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 27 May 2019 22:08:52 -0700 Subject: [PATCH 53/54] fix: rotate session on deref_mut --- tide-sessions/src/lib.rs | 4 ++++ tide-sessions/src/session_map.rs | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tide-sessions/src/lib.rs b/tide-sessions/src/lib.rs index 0c1899f82..1f9eb28ab 100644 --- a/tide-sessions/src/lib.rs +++ b/tide-sessions/src/lib.rs @@ -118,3 +118,7 @@ impl< }) } } + +#[cfg(test)] +mod tests { +} diff --git a/tide-sessions/src/session_map.rs b/tide-sessions/src/session_map.rs index d02ff4caa..298670ab2 100644 --- a/tide-sessions/src/session_map.rs +++ b/tide-sessions/src/session_map.rs @@ -38,11 +38,8 @@ impl Deref for SessionMap { } impl DerefMut for SessionMap { - // XXX: A tweet linked to this line in master earlier. If you're - // coming in from that link, the original comment is preserved - // at this URL: https://github.com/chrisdickinson/blog-rs/blob/6dfbe91a4fa09714ce6a975e4663e3e1efdaf9fa/src/session.rs#L45 fn deref_mut(&mut self) -> &mut Self::Target { + SessionMap::rotate(self); &mut self.data } } - From fd6dbb077da5f036ea0dc4f1ae9923d704895cdc Mon Sep 17 00:00:00 2001 From: Chris Dickinson Date: Mon, 27 May 2019 23:17:25 -0700 Subject: [PATCH 54/54] wip: split into multiple files, start adding a test --- tide-sessions/src/ext.rs | 27 ++++++ tide-sessions/src/lib.rs | 147 +++++++++++++++++++----------- tide-sessions/src/session_cell.rs | 19 ++++ 3 files changed, 139 insertions(+), 54 deletions(-) create mode 100644 tide-sessions/src/ext.rs create mode 100644 tide-sessions/src/session_cell.rs diff --git a/tide-sessions/src/ext.rs b/tide-sessions/src/ext.rs new file mode 100644 index 000000000..d426156e1 --- /dev/null +++ b/tide-sessions/src/ext.rs @@ -0,0 +1,27 @@ +use crate::session_cell::SessionCell; +use crate::session_map::SessionMap; +use std::cell::{ Ref, RefMut }; +use std::sync::Arc; +use tide::Context; + +// If a handler needs access to the session (mutably or immutably) it can +// import this trait. +pub trait SessionExt { + fn session(&self) -> Ref>; + fn session_mut(&self) -> RefMut>; +} + +impl< + Data: Clone + Send + Sync + 'static +> SessionExt for Context { + fn session(&self) -> Ref> { + let session_cell = self.extensions().get::>().unwrap(); + session_cell.0.borrow() + } + + fn session_mut(&self) -> RefMut> { + let session_cell = self.extensions().get::>().unwrap(); + session_cell.0.borrow_mut() + } +} + diff --git a/tide-sessions/src/lib.rs b/tide-sessions/src/lib.rs index 1f9eb28ab..4ca6f4cdc 100644 --- a/tide-sessions/src/lib.rs +++ b/tide-sessions/src/lib.rs @@ -1,8 +1,5 @@ #![feature(async_await)] -use std::{ - cell::{ RefCell, Ref, RefMut }, - sync::Arc -}; +use std::cell::Ref; use tide::{ Context, Response, @@ -12,58 +9,37 @@ use tide::{ use futures::future::BoxFuture; use futures::prelude::*; use http::header::HeaderValue; +use cookie::{ Cookie, CookieBuilder }; +mod session_cell; mod session_map; -use self::session_map::SessionMap; +mod ext; + +pub use crate::session_map::SessionMap; +pub use crate::ext::SessionExt; + +use self::session_cell::SessionCell; pub trait SessionStore { fn load_session(&self, key: &str) -> SessionMap; fn create_session(&self) -> SessionMap { SessionMap::new() } - fn commit(&self, key: Option<&str>, session: Ref>) -> Result; + fn commit(&self, key: Option<&str>, session: Ref>) -> Result; } -pub struct SessionMiddleware { +pub struct SessionMiddleware CookieBuilder + 'static> { pub session_key: String, - pub store: Store -} - -#[derive(Clone)] -pub struct SessionCell(RefCell>); - -// We're copying actix, here. I need to understand this better, because -// this strikes me as dangerous. -#[doc(hidden)] -unsafe impl Send for SessionCell {} -#[doc(hidden)] -unsafe impl Sync for SessionCell {} - -// If a handler needs access to the session (mutably or immutably) it can -// import this trait. -pub trait SessionExt { - fn session(&self) -> Ref>; - fn session_mut(&self) -> RefMut>; -} + pub store: Store, -impl< - Data: Clone + Send + Sync + 'static -> SessionExt for Context { - fn session(&self) -> Ref> { - let session_cell = self.extensions().get::>().unwrap(); - session_cell.0.borrow() - } - - fn session_mut(&self) -> RefMut> { - let session_cell = self.extensions().get::>().unwrap(); - session_cell.0.borrow_mut() - } + pub configure_session_cookie: Configure } impl< Data: Clone + Send + Sync + 'static, - S: SessionStore + Send + Sync + 'static -> Middleware for SessionMiddleware { + S: SessionStore + Send + Sync + 'static, + C: Send + Sync + Fn(CookieBuilder) -> CookieBuilder + 'static +> Middleware for SessionMiddleware { fn handle<'a>(&'a self, mut ctx: Context, next: Next<'a, Data>) -> BoxFuture<'a, Response> { FutureExt::boxed(async move { @@ -84,12 +60,11 @@ impl< None => self.store.create_session() }; - // Create a ref-counted cell (yay interior mutability.) Attach - // a clone of that arc'd cell to the context and send it - // through. At the same time, keep our local copy of the arc - // ready for inspection after we're done processing the + // Create a ref-counted cell. Attach a clone of that ARC'd cell to + // the context and send it through. Meanwhile, keep our local copy + // of the arc ready for inspection after we're done processing the // request. - let cell = Arc::new(SessionCell(RefCell::new(Box::new(session)))); + let cell = SessionCell::new(session); ctx.extensions_mut().insert(cell.clone()); let mut res = next.run(ctx).await; @@ -101,16 +76,22 @@ impl< return res } - if let Ok(key) = self.store.commit(session_key.as_ref().map(String::as_str), session) { + if let Ok(sid) = self.store.commit(session_key.as_ref().map(String::as_str), session) { if session_key.is_none() { - let hm = res.headers_mut(); - // TODO: set the cookie's domain, path, expiry, sameSite, - // etc. properties via options set in the middleware. - // TODO: is there a good way to play nicely with cookie - // middleware? Can we rely on additions to context that - // other middleware add during the response half of the - // request lifecycle? - hm.insert("Set-Cookie", key); + let builder = Cookie::build(self.session_key.clone(), sid); + let c = (self.configure_session_cookie)( + builder + ).finish(); + + if let Ok(value) = HeaderValue::from_str(&c.to_string()) { + // TODO: is there a good way to play nicely with cookie + // middleware? Can we rely on additions to context that + // other middleware add during the response half of the + // request lifecycle? + + let headers = res.headers_mut(); + headers.insert("Set-Cookie", value); + } } } @@ -121,4 +102,62 @@ impl< #[cfg(test)] mod tests { + use super::*; + use futures::{ + executor::{block_on, block_on_stream}, + stream::StreamExt, + }; + use http_service::Body; + use http_service_mock::make_server; + use cookie::CookieBuilder; + + // Generates the app. + fn app(mw: SessionMiddleware) -> tide::App<()> where + S: SessionStore + Send + Sync + 'static, + C: Fn(CookieBuilder) -> CookieBuilder + Send + Sync { + let mut app = tide::App::new(); + app.at("/session/:key").get(async move |ctx: Context<()>| { + let session = ctx.session(); + let key: String = ctx.param("key").expect("failed to parse url param"); + session.get(&key).expect("expected to be able to read key").clone() + }).post(async move |mut ctx: Context<()>| { + let key: String = ctx.param("key").expect("failed to parse url param"); + let body = ctx.body_string().await.expect("failed to read test request body"); + let mut session = ctx.session_mut(); + session.insert(key, body); + "ok" + }); + app.middleware(mw); + app + } + + fn configure_cookie (builder: CookieBuilder) -> CookieBuilder { + builder + } + + struct InMemorySessionStore; + impl SessionStore for InMemorySessionStore { + fn load_session(&self, key: &str) -> SessionMap { + SessionMap::new() + } + + fn commit(&self, key: Option<&str>, session: Ref>) -> Result { + Ok(String::from("hi")) + } + } + + + #[test] + fn set_cookie_creates_new_session_id() { + let app = app(SessionMiddleware { + session_key: String::from("sid"), + store: InMemorySessionStore { }, + configure_session_cookie: configure_cookie + }); + let mut server = make_server(app.into_http_service()).unwrap(); + let req = http::Request::post("/session/testkey") + .body(Body::from("hello world")) + .unwrap(); + server.simulate(req).unwrap(); + } } diff --git a/tide-sessions/src/session_cell.rs b/tide-sessions/src/session_cell.rs new file mode 100644 index 000000000..e4dc8af1a --- /dev/null +++ b/tide-sessions/src/session_cell.rs @@ -0,0 +1,19 @@ +use crate::session_map::SessionMap; +use std::cell::RefCell; +use std::sync::Arc; + +#[derive(Clone)] +pub struct SessionCell(pub RefCell>); + +// We're copying actix, here. I need to understand this better, because +// this strikes me as dangerous. +#[doc(hidden)] +unsafe impl Send for SessionCell {} +#[doc(hidden)] +unsafe impl Sync for SessionCell {} + +impl SessionCell { + pub fn new (map: SessionMap) -> Arc { + Arc::new(Self(RefCell::new(Box::new(map)))) + } +}