From 5d9035ddc1346c43ae1b1038500e548ef46d61d4 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Wed, 14 Oct 2020 21:37:16 -0700 Subject: [PATCH] Keep an op-log for sync 'CookieJar'. In brief, this commit: * Updates to the latest upstream 'cookie', fixing a memory leak. * Make changes to 'CookieJar' observable only through 'pending()'. * Deprecates 'Client::new()' in favor of 'Client::tracked()'. * Makes 'dispatch()' on tracked 'Client's synchronize on cookies. * Makes 'Client::untracked()' actually untracked. This commit updates to the latest 'cookie' which removes support for 'Sync' cookie jars. Instead of relying on 'cookie', this commit implements an op-log based 'CookieJar' which internally keeps track of changes. The API is such that changes are only observable through specialized '_pending()' methods. --- contrib/lib/src/databases.rs | 2 - contrib/lib/src/templates/mod.rs | 2 +- .../lib/tests/compress_responder.rs.disabled | 16 +- .../lib/tests/compression_fairing.rs.disabled | 20 +- contrib/lib/tests/helmet.rs | 2 +- contrib/lib/tests/static_files.rs | 12 +- contrib/lib/tests/templates.rs | 6 +- core/codegen/tests/expansion.rs | 4 +- core/codegen/tests/responder.rs | 6 +- core/codegen/tests/route-data.rs | 2 +- core/codegen/tests/route-format.rs | 4 +- core/codegen/tests/route-ranking.rs | 4 +- core/codegen/tests/route.rs | 2 +- .../ui-fail-nightly/responder-types.stderr | 6 - .../ui-fail-stable/responder-types.stderr | 6 - core/http/Cargo.toml | 3 +- core/http/src/cookies.rs | 247 ++++++++++++++---- core/http/src/lib.rs | 2 +- core/lib/Cargo.toml | 6 +- core/lib/benches/format-routing.rs | 8 +- core/lib/benches/ranked-routing.rs | 4 +- core/lib/benches/simple-routing.rs | 10 +- core/lib/src/local/asynchronous/client.rs | 40 ++- core/lib/src/local/asynchronous/request.rs | 33 +-- core/lib/src/local/asynchronous/response.rs | 6 +- core/lib/src/local/blocking/client.rs | 10 +- core/lib/src/local/blocking/request.rs | 2 +- core/lib/src/local/blocking/response.rs | 2 +- core/lib/src/local/client.rs | 44 ++-- core/lib/src/local/mod.rs | 12 +- core/lib/src/local/request.rs | 12 +- core/lib/src/request/request.rs | 4 + core/lib/src/response/flash.rs | 4 +- core/lib/src/response/response.rs | 2 +- core/lib/src/response/status.rs | 6 +- core/lib/src/rocket.rs | 8 +- .../lib/tests/absolute-uris-okay-issue-443.rs | 2 +- core/lib/tests/catcher-cookies-1213.rs | 4 +- .../conditionally-set-server-header-996.rs | 2 +- core/lib/tests/derive-reexports.rs | 2 +- .../fairing_before_head_strip-issue-546.rs | 4 +- .../lib/tests/flash-lazy-removes-issue-466.rs | 2 +- core/lib/tests/form_method-issue-45.rs | 4 +- .../lib/tests/form_value_decoding-issue-82.rs | 2 +- core/lib/tests/head_handling.rs | 4 +- core/lib/tests/limits.rs | 8 +- .../local-request-content-type-issue-505.rs | 4 +- .../local_request_private_cookie-issue-368.rs | 4 +- core/lib/tests/many-cookie-jars-at-once.rs | 4 +- core/lib/tests/mapped-base-issue-1262.rs | 4 +- core/lib/tests/nested-fairing-attaches.rs | 2 +- .../tests/precise-content-type-matching.rs | 2 +- .../tests/redirect_from_catcher-issue-113.rs | 3 +- core/lib/tests/route_guard.rs | 2 +- core/lib/tests/segments-issues-41-86.rs | 2 +- core/lib/tests/strict_and_lenient_forms.rs | 2 +- core/lib/tests/unsound-local-request-1312.rs | 2 +- .../tests/uri-percent-encoding-issue-808.rs | 4 +- examples/config/tests/common/mod.rs | 2 +- examples/content_types/src/tests.rs | 2 +- examples/cookies/src/tests.rs | 6 +- examples/errors/src/tests.rs | 6 +- examples/fairings/src/tests.rs | 6 +- examples/form_kitchen_sink/src/tests.rs | 8 +- examples/form_validation/src/tests.rs | 4 +- examples/handlebars_templates/src/tests.rs | 2 +- examples/hello_2018/src/tests.rs | 6 +- examples/hello_person/src/tests.rs | 4 +- examples/hello_world/src/tests.rs | 2 +- examples/json/src/tests.rs | 4 +- examples/managed_queue/src/tests.rs | 2 +- examples/manual_routes/src/tests.rs | 4 +- examples/msgpack/src/tests.rs | 4 +- examples/optional_redirect/src/tests.rs | 4 +- examples/pastebin/src/tests.rs | 4 +- examples/query_params/src/tests.rs | 2 +- examples/ranking/src/tests.rs | 2 +- examples/raw_sqlite/src/tests.rs | 2 +- examples/raw_upload/src/tests.rs | 4 +- examples/redirect/src/tests.rs | 2 +- examples/request_guard/src/main.rs | 2 +- examples/request_local_state/src/tests.rs | 2 +- examples/session/src/tests.rs | 8 +- examples/state/src/tests.rs | 2 +- examples/static_files/src/tests.rs | 2 +- examples/stream/src/tests.rs | 4 +- examples/tera_templates/src/tests.rs | 2 +- examples/testing/src/async_required.rs | 2 +- examples/testing/src/main.rs | 2 +- examples/tls/src/tests.rs | 2 +- examples/todo/src/tests.rs | 2 +- examples/uuid/src/tests.rs | 4 +- site/guide/8-testing.md | 16 +- 93 files changed, 465 insertions(+), 295 deletions(-) diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index 2a71e6cc..c416ef7b 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -1130,7 +1130,5 @@ mod tests { assert_eq!(pool_size, database_config.pool_size); assert_eq!(true, database_config.extras.contains_key("certs")); assert_eq!(true, database_config.extras.contains_key("key")); - - println!("{:#?}", database_config); } } diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 09ebb7f2..44ed31a4 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -330,7 +330,7 @@ impl Template { /// /// fn main() { /// let rocket = rocket::ignite().attach(Template::fairing()); - /// let client = Client::new(rocket).expect("valid rocket"); + /// let client = Client::untracked(rocket).expect("valid rocket"); /// /// // Create a `context`. Here, just an empty `HashMap`. /// let mut context = HashMap::new(); diff --git a/contrib/lib/tests/compress_responder.rs.disabled b/contrib/lib/tests/compress_responder.rs.disabled index 2256b0bf..345400e3 100644 --- a/contrib/lib/tests/compress_responder.rs.disabled +++ b/contrib/lib/tests/compress_responder.rs.disabled @@ -66,7 +66,7 @@ mod compress_responder_tests { #[test] fn test_prioritizes_brotli() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -90,7 +90,7 @@ mod compress_responder_tests { #[test] fn test_br_font() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/font") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -114,7 +114,7 @@ mod compress_responder_tests { #[test] fn test_fallback_gzip() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate, gzip")) @@ -137,7 +137,7 @@ mod compress_responder_tests { #[test] fn test_does_not_recompress() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/already_encoded") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -160,7 +160,7 @@ mod compress_responder_tests { #[test] fn test_does_not_compress_explicit_identity() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/identity") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -178,7 +178,7 @@ mod compress_responder_tests { #[test] fn test_ignore_exceptions() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/image") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -202,7 +202,7 @@ mod compress_responder_tests { #[test] fn test_ignores_unimplemented_encodings() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate")) @@ -220,7 +220,7 @@ mod compress_responder_tests { #[test] fn test_respects_identity_only() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "identity")) diff --git a/contrib/lib/tests/compression_fairing.rs.disabled b/contrib/lib/tests/compression_fairing.rs.disabled index 03aba384..00224d64 100644 --- a/contrib/lib/tests/compression_fairing.rs.disabled +++ b/contrib/lib/tests/compression_fairing.rs.disabled @@ -85,7 +85,7 @@ mod compression_fairing_tests { #[test] fn test_prioritizes_brotli() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -109,7 +109,7 @@ mod compression_fairing_tests { #[test] fn test_br_font() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/font") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -133,7 +133,7 @@ mod compression_fairing_tests { #[test] fn test_fallback_gzip() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate, gzip")) @@ -156,7 +156,7 @@ mod compression_fairing_tests { #[test] fn test_does_not_recompress() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/already_encoded") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -179,7 +179,7 @@ mod compression_fairing_tests { #[test] fn test_does_not_compress_explicit_identity() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/identity") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -197,7 +197,7 @@ mod compression_fairing_tests { #[test] fn test_does_not_compress_image() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/image") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -215,7 +215,7 @@ mod compression_fairing_tests { #[test] fn test_ignores_unimplemented_encodings() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "deflate")) @@ -233,7 +233,7 @@ mod compression_fairing_tests { #[test] fn test_respects_identity_only() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client .get("/") .header(Header::new("Accept-Encoding", "identity")) @@ -251,7 +251,7 @@ mod compression_fairing_tests { #[test] fn test_does_not_compress_custom_exception() { - let client = Client::new(rocket_tar_exception()).expect("valid rocket instance"); + let client = Client::tracked(rocket_tar_exception()).expect("valid rocket instance"); let mut response = client .get("/tar") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) @@ -269,7 +269,7 @@ mod compression_fairing_tests { #[test] fn test_compress_custom_removed_exception() { - let client = Client::new(rocket_tar_exception()).expect("valid rocket instance"); + let client = Client::tracked(rocket_tar_exception()).expect("valid rocket instance"); let mut response = client .get("/image") .header(Header::new("Accept-Encoding", "deflate, gzip, br")) diff --git a/contrib/lib/tests/helmet.rs b/contrib/lib/tests/helmet.rs index fc869c95..12b7ee8b 100644 --- a/contrib/lib/tests/helmet.rs +++ b/contrib/lib/tests/helmet.rs @@ -32,7 +32,7 @@ mod helmet_tests { macro_rules! dispatch { ($helmet:expr, $closure:expr) => {{ let rocket = rocket::ignite().mount("/", routes![hello]).attach($helmet); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); $closure(response) diff --git a/contrib/lib/tests/static_files.rs b/contrib/lib/tests/static_files.rs index 4827294e..be2cbee7 100644 --- a/contrib/lib/tests/static_files.rs +++ b/contrib/lib/tests/static_files.rs @@ -69,7 +69,7 @@ mod static_tests { #[test] fn test_static_no_index() { - let client = Client::new(rocket()).expect("valid rocket"); + let client = Client::tracked(rocket()).expect("valid rocket"); assert_all(&client, "no_index", REGULAR_FILES, true); assert_all(&client, "no_index", HIDDEN_FILES, false); assert_all(&client, "no_index", INDEXED_DIRECTORIES, false); @@ -77,7 +77,7 @@ mod static_tests { #[test] fn test_static_hidden() { - let client = Client::new(rocket()).expect("valid rocket"); + let client = Client::tracked(rocket()).expect("valid rocket"); assert_all(&client, "dots", REGULAR_FILES, true); assert_all(&client, "dots", HIDDEN_FILES, true); assert_all(&client, "dots", INDEXED_DIRECTORIES, false); @@ -85,7 +85,7 @@ mod static_tests { #[test] fn test_static_index() { - let client = Client::new(rocket()).expect("valid rocket"); + let client = Client::tracked(rocket()).expect("valid rocket"); assert_all(&client, "index", REGULAR_FILES, true); assert_all(&client, "index", HIDDEN_FILES, false); assert_all(&client, "index", INDEXED_DIRECTORIES, true); @@ -97,7 +97,7 @@ mod static_tests { #[test] fn test_static_all() { - let client = Client::new(rocket()).expect("valid rocket"); + let client = Client::tracked(rocket()).expect("valid rocket"); assert_all(&client, "both", REGULAR_FILES, true); assert_all(&client, "both", HIDDEN_FILES, true); assert_all(&client, "both", INDEXED_DIRECTORIES, true); @@ -129,7 +129,7 @@ mod static_tests { fn catch_two(a: &RawStr, b: &RawStr) -> String { format!("{}/{}", a, b) } let rocket = rocket().mount("/default", routes![catch_one, catch_two]); - let client = Client::new(rocket).expect("valid rocket"); + let client = Client::tracked(rocket).expect("valid rocket"); let response = client.get("/default/ireallydontexist").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -146,7 +146,7 @@ mod static_tests { #[test] fn test_redirection() { - let client = Client::new(rocket()).expect("valid rocket"); + let client = Client::tracked(rocket()).expect("valid rocket"); // Redirection only happens if enabled, and doesn't affect index behaviour. let response = client.get("/no_index/inner").dispatch(); diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs index 199454e7..e92d90bc 100644 --- a/contrib/lib/tests/templates.rs +++ b/contrib/lib/tests/templates.rs @@ -66,7 +66,7 @@ mod templates_tests { #[test] fn test_template_metadata_with_tera() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/tera/txt_test").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -107,7 +107,7 @@ mod templates_tests { #[test] fn test_template_metadata_with_handlebars() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/hbs/test").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -144,7 +144,7 @@ mod templates_tests { write_file(&reload_path, INITIAL_TEXT); // set up the client. if we can't reload templates, then just quit - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let res = client.get("/is_reloading").dispatch(); if res.status() != Status::Ok { return; diff --git a/core/codegen/tests/expansion.rs b/core/codegen/tests/expansion.rs index 50af1712..5aa16ec7 100644 --- a/core/codegen/tests/expansion.rs +++ b/core/codegen/tests/expansion.rs @@ -34,7 +34,7 @@ foo!("/hello/", name); #[test] fn test_reexpansion() { let rocket = rocket::ignite().mount("/", routes![easy, hard, hi]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/easy/327").dispatch(); assert_eq!(response.into_string().unwrap(), "easy id: 327"); @@ -60,7 +60,7 @@ index!(i32); #[test] fn test_index() { let rocket = rocket::ignite().mount("/", routes![index]).manage(100i32); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string().unwrap(), "Thing: 100"); diff --git a/core/codegen/tests/responder.rs b/core/codegen/tests/responder.rs index 8e4ea834..af00a15e 100644 --- a/core/codegen/tests/responder.rs +++ b/core/codegen/tests/responder.rs @@ -21,7 +21,7 @@ pub enum Foo<'r> { #[rocket::async_test] async fn responder_foo() { - let client = Client::new(rocket::ignite()).await.expect("valid rocket"); + let client = Client::tracked(rocket::ignite()).await.expect("valid rocket"); let local_req = client.get("/"); let req = local_req.inner(); @@ -70,7 +70,7 @@ pub struct Bar<'r> { #[rocket::async_test] async fn responder_bar() { - let client = Client::new(rocket::ignite()).await.expect("valid rocket"); + let client = Client::tracked(rocket::ignite()).await.expect("valid rocket"); let local_req = client.get("/"); let req = local_req.inner(); @@ -95,7 +95,7 @@ pub struct Baz { #[rocket::async_test] async fn responder_baz() { - let client = Client::new(rocket::ignite()).await.expect("valid rocket"); + let client = Client::tracked(rocket::ignite()).await.expect("valid rocket"); let local_req = client.get("/"); let req = local_req.inner(); diff --git a/core/codegen/tests/route-data.rs b/core/codegen/tests/route-data.rs index f036d3c5..afccfcef 100644 --- a/core/codegen/tests/route-data.rs +++ b/core/codegen/tests/route-data.rs @@ -36,7 +36,7 @@ fn simple(simple: Simple) -> String { simple.0 } #[test] fn test_data() { let rocket = rocket::ignite().mount("/", routes![form, simple]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.post("/f") .header(ContentType::Form) diff --git a/core/codegen/tests/route-format.rs b/core/codegen/tests/route-format.rs index 8fe4806d..1c991b84 100644 --- a/core/codegen/tests/route-format.rs +++ b/core/codegen/tests/route-format.rs @@ -37,7 +37,7 @@ fn test_formats() { .mount("/", routes![json, xml, json_long, msgpack_long, msgpack, plain, binary, other]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.post("/").header(ContentType::JSON).dispatch(); assert_eq!(response.into_string().unwrap(), "json"); @@ -83,7 +83,7 @@ fn test_custom_formats() { let rocket = rocket::ignite() .mount("/", routes![get_foo, post_foo, get_bar_baz, put_bar_baz]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let foo_a = Accept::new(&[MediaType::new("application", "foo").into()]); let foo_ct = ContentType::new("application", "foo"); diff --git a/core/codegen/tests/route-ranking.rs b/core/codegen/tests/route-ranking.rs index a4bc85de..68d1038a 100644 --- a/core/codegen/tests/route-ranking.rs +++ b/core/codegen/tests/route-ranking.rs @@ -19,7 +19,7 @@ fn get3(_number: u64) -> &'static str { "3" } #[test] fn test_ranking() { let rocket = rocket::ignite().mount("/", routes![get0, get1, get2, get3]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/0").dispatch(); assert_eq!(response.into_string().unwrap(), "0"); @@ -44,7 +44,7 @@ fn test_rank_collision() { use rocket::error::LaunchErrorKind; let rocket = rocket::ignite().mount("/", routes![get0, get0b]); - let client_result = Client::new(rocket); + let client_result = Client::tracked(rocket); match client_result.as_ref().map_err(|e| e.kind()) { Err(LaunchErrorKind::Collision(..)) => { /* o.k. */ }, Ok(_) => panic!("client succeeded unexpectedly"), diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index 1020847e..1eab6ed6 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -77,7 +77,7 @@ fn test_full_route() { .mount("/1", routes![post1]) .mount("/2", routes![post2]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let a = "A%20A"; let name = "Bob%20McDonald"; diff --git a/core/codegen/tests/ui-fail-nightly/responder-types.stderr b/core/codegen/tests/ui-fail-nightly/responder-types.stderr index 80b97b8c..4d871850 100644 --- a/core/codegen/tests/ui-fail-nightly/responder-types.stderr +++ b/core/codegen/tests/ui-fail-nightly/responder-types.stderr @@ -14,9 +14,7 @@ error[E0277]: the trait bound `Header<'_>: From` is not satisfied | = help: the following implementations were found: as From<&Cookie<'_>>> - as From<&CookieCrumb>> as From>> - as From> = note: required because of the requirements on the impl of `Into>` for `u8` error[E0277]: the trait bound `u8: Responder<'_, '_>` is not satisfied @@ -35,9 +33,7 @@ error[E0277]: the trait bound `Header<'_>: From` is not satisfied | = help: the following implementations were found: as From<&Cookie<'_>>> - as From<&CookieCrumb>> as From>> - as From> = note: required because of the requirements on the impl of `Into>` for `u8` error[E0277]: the trait bound `Header<'_>: From` is not satisfied @@ -48,9 +44,7 @@ error[E0277]: the trait bound `Header<'_>: From` is not sat | = help: the following implementations were found: as From<&Cookie<'_>>> - as From<&CookieCrumb>> as From>> - as From> = note: required because of the requirements on the impl of `Into>` for `std::string::String` error[E0277]: the trait bound `usize: Responder<'_, '_>` is not satisfied diff --git a/core/codegen/tests/ui-fail-stable/responder-types.stderr b/core/codegen/tests/ui-fail-stable/responder-types.stderr index 026ad119..f414b4cb 100644 --- a/core/codegen/tests/ui-fail-stable/responder-types.stderr +++ b/core/codegen/tests/ui-fail-stable/responder-types.stderr @@ -14,9 +14,7 @@ error[E0277]: the trait bound `rocket::http::Header<'_>: std::convert::From` | = help: the following implementations were found: as std::convert::From<&rocket::http::Cookie<'_>>> - as std::convert::From<&rocket::http::CookieCrumb>> as std::convert::From>> - as std::convert::From> = note: required because of the requirements on the impl of `std::convert::Into>` for `u8` error[E0277]: the trait bound `u8: rocket::response::Responder<'_, '_>` is not satisfied @@ -35,9 +33,7 @@ error[E0277]: the trait bound `rocket::http::Header<'_>: std::convert::From` | = help: the following implementations were found: as std::convert::From<&rocket::http::Cookie<'_>>> - as std::convert::From<&rocket::http::CookieCrumb>> as std::convert::From>> - as std::convert::From> = note: required because of the requirements on the impl of `std::convert::Into>` for `u8` error[E0277]: the trait bound `rocket::http::Header<'_>: std::convert::From` is not satisfied @@ -48,9 +44,7 @@ error[E0277]: the trait bound `rocket::http::Header<'_>: std::convert::From as std::convert::From<&rocket::http::Cookie<'_>>> - as std::convert::From<&rocket::http::CookieCrumb>> as std::convert::From>> - as std::convert::From> = note: required because of the requirements on the impl of `std::convert::Into>` for `std::string::String` error[E0277]: the trait bound `usize: rocket::response::Responder<'_, '_>` is not satisfied diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index c50d0b74..7ce24ca6 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -34,10 +34,11 @@ unicode-xid = "0.2" log = "0.4" ref-cast = "1.0" uncased = "0.9" +parking_lot = "0.11" [dependencies.cookie] git = "https://github.com/SergioBenitez/cookie-rs.git" -rev = "3795f2e" +rev = "9675944" features = ["percent-encode"] [dependencies.pear] diff --git a/core/http/src/cookies.rs b/core/http/src/cookies.rs index 708bbee1..935c432f 100644 --- a/core/http/src/cookies.rs +++ b/core/http/src/cookies.rs @@ -1,8 +1,10 @@ use std::fmt; +use parking_lot::Mutex; + use crate::Header; -pub use cookie::{Cookie, CookieCrumb, SameSite, Iter}; +pub use cookie::{Cookie, SameSite, Iter}; #[doc(hidden)] pub use self::key::*; /// Types and methods to manage a `Key` when private cookies are enabled. @@ -26,21 +28,68 @@ mod key { /// Collection of one or more HTTP cookies. /// -/// The `CookieJar` type allows for retrieval of cookies from an incoming -/// request as well as modifications to cookies to be reflected by Rocket on -/// outgoing responses. +/// `CookieJar` allows for retrieval of cookies from an incoming request. It +/// also tracks modifications (additions and removals) and marks them as +/// pending. +/// +/// # Pending +/// +/// Changes to a `CookieJar` are _not_ visible via the normal [`get()`] and +/// [`get_private()`] methods. This is typically the desired effect as a +/// `CookieJar` always reflects the cookies in an incoming request. In cases +/// where this is not desired, the [`get_pending()`] and +/// [`get_private_pending()`] methods are available, which always return the +/// latest changes. +/// +/// ```rust +/// # #[macro_use] extern crate rocket; +/// use rocket::http::{CookieJar, Cookie}; +/// +/// #[get("/message")] +/// fn message(jar: &CookieJar<'_>) { +/// jar.add(Cookie::new("message", "hello!")); +/// jar.add(Cookie::new("other", "bye!")); +/// +/// // `get()` does not reflect changes. +/// assert!(jar.get("other").is_none()); +/// # assert_eq!(jar.get("message").map(|c| c.value()), Some("hi")); +/// +/// // `get_pending()` does. +/// let other_pending = jar.get_pending("other"); +/// let message_pending = jar.get_pending("message"); +/// assert_eq!(other_pending.as_ref().map(|c| c.value()), Some("bye!")); +/// assert_eq!(message_pending.as_ref().map(|c| c.value()), Some("hello!")); +/// # jar.remove(Cookie::named("message")); +/// # assert_eq!(jar.get("message").map(|c| c.value()), Some("hi")); +/// # assert!(jar.get_pending("message").is_none()); +/// } +/// # fn main() { +/// # use rocket::local::blocking::Client; +/// # let rocket = rocket::ignite().mount("/", routes![message]); +/// # let client = Client::tracked(rocket).unwrap(); +/// # let response = client.get("/message") +/// # .cookie(Cookie::new("message", "hi")) +/// # .dispatch(); +/// # +/// # assert!(response.status().class().is_success()); +/// # } +/// ``` /// /// # Usage /// /// A type of `&CookieJar` can be retrieved via its `FromRequest` implementation /// as a request guard or via the [`Request::cookies()`] method. Individual /// cookies can be retrieved via the [`get()`] and [`get_private()`] methods. -/// Cookies can be added or removed via the [`add()`], [`add_private()`], -/// [`remove()`], and [`remove_private()`] methods. +/// Pending changes can be observed via the [`get_pending()`] and +/// [`get_private_pending()`] methods. Cookies can be added or removed via the +/// [`add()`], [`add_private()`], [`remove()`], and [`remove_private()`] +/// methods. /// /// [`Request::cookies()`]: rocket::Request::cookies() /// [`get()`]: #method.get /// [`get_private()`]: #method.get_private +/// [`get_pending()`]: #method.get_pending +/// [`get_private_pending()`]: #method.get_private_pending /// [`add()`]: #method.add /// [`add_private()`]: #method.add_private /// [`remove()`]: #method.remove @@ -56,8 +105,8 @@ mod key { /// use rocket::http::CookieJar; /// /// #[get("/message")] -/// fn message(jar: &CookieJar<'_>) -> Option { -/// jar.get("message").map(|c| format!("Message: {}", c.value())) +/// fn message<'a>(jar: &'a CookieJar<'_>) -> Option<&'a str> { +/// jar.get("message").map(|cookie| cookie.value()) /// } /// # fn main() { } /// ``` @@ -123,15 +172,43 @@ mod key { /// is usually done through tools like `openssl`. Using `openssl`, for instance, /// a 256-bit base64 key can be generated with the command `openssl rand -base64 /// 32`. -#[derive(Clone)] pub struct CookieJar<'a> { jar: cookie::CookieJar, key: &'a Key, + ops: Mutex>, +} + +impl<'a> Clone for CookieJar<'a> { + fn clone(&self) -> Self { + CookieJar { + jar: self.jar.clone(), + key: self.key, + ops: Mutex::new(self.ops.lock().clone()), + } + } +} + +#[derive(Clone)] +enum Op { + Add(Cookie<'static>, bool), + Remove(Cookie<'static>, bool), +} + +impl Op { + fn cookie(&self) -> &Cookie<'static> { + match self { + Op::Add(c, _) | Op::Remove(c, _) => c + } + } } impl<'a> CookieJar<'a> { - /// Returns a reference to the `Cookie` inside this container with the name - /// `name`. If no such cookie exists, returns `None`. + /// Returns a reference to the _original_ `Cookie` inside this container + /// with the name `name`. If no such cookie exists, returns `None`. + /// + /// **Note:** This method _does not_ obverse changes made via additions and + /// removals to the cookie jar. To observe those changes, use + /// [`CookieJar::get_pending()`]. /// /// # Example /// @@ -144,14 +221,18 @@ impl<'a> CookieJar<'a> { /// let cookie = jar.get("name"); /// } /// ``` - pub fn get(&self, name: &str) -> Option { + pub fn get(&self, name: &str) -> Option<&Cookie<'static>> { self.jar.get(name) } - /// Returns a reference to the `Cookie` inside this collection with the name - /// `name` and authenticates and decrypts the cookie's value, returning a - /// `Cookie` with the decrypted value. If the cookie cannot be found, or the - /// cookie fails to authenticate or decrypt, `None` is returned. + /// Retrives the _original_ `Cookie` inside this collection with the name + /// `name` and authenticates and decrypts the cookie's value. If the cookie + /// cannot be found, or the cookie fails to authenticate or decrypt, `None` + /// is returned. + /// + /// **Note:** This method _does not_ obverse changes made via additions and + /// removals to the cookie jar. To observe those changes, use + /// [`CookieJar::get_private_pending()`]. /// /// # Example /// @@ -170,6 +251,55 @@ impl<'a> CookieJar<'a> { self.jar.private(&*self.key).get(name) } + /// Returns a reference to the _original or pending_ `Cookie` inside this + /// container with the name `name`. If no such cookie exists, returns + /// `None`. + /// + /// # Example + /// + /// ```rust + /// # #[macro_use] extern crate rocket; + /// use rocket::http::{Cookie, CookieJar}; + /// + /// #[get("/")] + /// fn handler(jar: &CookieJar<'_>) { + /// let pending_cookie = jar.get_pending("name"); + /// } + /// ``` + pub fn get_pending(&self, name: &str) -> Option> { + let ops = self.ops.lock(); + for op in ops.iter().rev().filter(|op| op.cookie().name() == name) { + match op { + Op::Add(c, _) => return Some(c.clone()), + Op::Remove(_, _) => return None, + } + } + + drop(ops); + self.get(name).map(|c| c.clone()) + } + + /// Retrives the _original or pending_ `Cookie` inside this collection with + /// the name `name` and authenticates and decrypts the cookie's value. If + /// the cookie cannot be found, or the cookie fails to authenticate or + /// decrypt, `None` is returned. + /// + /// # Example + /// + /// ```rust + /// # #[macro_use] extern crate rocket; + /// use rocket::http::{Cookie, CookieJar}; + /// + /// #[get("/")] + /// fn handler(jar: &CookieJar<'_>) { + /// let pending_cookie = jar.get_private_pending("name"); + /// } + /// ``` + pub fn get_private_pending(&self, name: &str) -> Option> { + let cookie = self.get_pending(name)?; + self.jar.private(&*self.key).decrypt(cookie) + } + /// Adds `cookie` to this collection. /// /// Unless a value is set for the given property, the following defaults are @@ -198,7 +328,7 @@ impl<'a> CookieJar<'a> { /// ``` pub fn add(&self, mut cookie: Cookie<'static>) { Self::set_defaults(&mut cookie); - self.jar.add(cookie) + self.ops.lock().push(Op::Add(cookie, false)); } /// Adds `cookie` to the collection. The cookie's value is encrypted with @@ -233,7 +363,7 @@ impl<'a> CookieJar<'a> { #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] pub fn add_private(&self, mut cookie: Cookie<'static>) { Self::set_private_defaults(&mut cookie); - self.jar.private(&*self.key).add(cookie) + self.ops.lock().push(Op::Add(cookie, true)); } /// Removes `cookie` from this collection and generates a "removal" cookies @@ -263,7 +393,7 @@ impl<'a> CookieJar<'a> { cookie.set_path("/"); } - self.jar.remove(cookie) + self.ops.lock().push(Op::Remove(cookie, false)); } /// Removes the private `cookie` from the collection. @@ -290,10 +420,14 @@ impl<'a> CookieJar<'a> { cookie.set_path("/"); } - self.jar.private(&*self.key).remove(cookie) + self.ops.lock().push(Op::Remove(cookie, true)); } - /// Returns an iterator over all of the cookies present in this collection. + /// Returns an iterator over all of the _original_ cookies present in this + /// collection. + /// + /// **Note:** This method _does not_ obverse changes made via additions and + /// removals to the cookie jar. /// /// # Example /// @@ -308,38 +442,64 @@ impl<'a> CookieJar<'a> { /// } /// } /// ``` - pub fn iter(&self) -> impl Iterator + '_ { + pub fn iter(&self) -> impl Iterator> { self.jar.iter() } } -/// WARNING: These is unstable! Do not use outside of Rocket! +/// WARNING: These are unstable! Do not use outside of Rocket! #[doc(hidden)] impl<'a> CookieJar<'a> { #[inline(always)] - pub fn new(key: &'a Key) -> CookieJar<'a> { - CookieJar { jar: cookie::CookieJar::new(), key } + pub fn new(key: &'a Key) -> Self { + CookieJar { + jar: cookie::CookieJar::new(), + key, ops: Mutex::new(Vec::new()), + } } #[inline(always)] pub fn from(jar: cookie::CookieJar, key: &'a Key) -> CookieJar<'a> { - CookieJar { jar, key } + CookieJar { jar, key, ops: Mutex::new(Vec::new()) } } /// Removes all delta cookies. #[inline(always)] pub fn reset_delta(&self) { - self.jar.reset_delta() + self.ops.lock().clear(); } + /// TODO: This could be faster by just returning the cookies directly via + /// an ordered hash-set of sorts. #[inline(always)] - pub fn delta(&self) -> cookie::Delta { - self.jar.delta() + pub fn take_delta_jar(&self) -> cookie::CookieJar { + let ops = std::mem::replace(&mut *self.ops.lock(), Vec::new()); + let mut jar = cookie::CookieJar::new(); + + for op in ops { + match op { + Op::Add(c, false) => jar.add(c), + #[cfg(feature = "private-cookies")] + Op::Add(c, true) => jar.private_mut(self.key).add(c), + Op::Remove(mut c, _) => { + if self.jar.get(c.name()).is_some() { + c.make_removal(); + jar.add(c); + } else { + jar.remove(c); + } + } + #[allow(unreachable_patterns)] + _ => unreachable!() + } + } + + jar } /// Adds an original `cookie` to this collection. #[inline(always)] - pub fn add_original(&self, cookie: Cookie<'static>) { + pub fn add_original(&mut self, cookie: Cookie<'static>) { self.jar.add_original(cookie) } @@ -347,8 +507,8 @@ impl<'a> CookieJar<'a> { #[inline(always)] #[cfg(feature = "private-cookies")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] - pub fn add_original_private(&self, cookie: Cookie<'static>) { - self.jar.private(&*self.key).add_original(cookie); + pub fn add_original_private(&mut self, cookie: Cookie<'static>) { + self.jar.private_mut(&*self.key).add_original(cookie); } /// For each property mentioned below, this method checks if there is a @@ -400,7 +560,16 @@ impl<'a> CookieJar<'a> { impl fmt::Debug for CookieJar<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.jar.fmt(f) + let pending: Vec<_> = self.ops.lock() + .iter() + .map(|c| c.cookie()) + .cloned() + .collect(); + + f.debug_struct("CookieJar") + .field("original", &self.jar) + .field("pending", &pending) + .finish() } } @@ -415,15 +584,3 @@ impl From<&Cookie<'_>> for Header<'static> { Header::new("Set-Cookie", cookie.encoded().to_string()) } } - -impl From for Header<'static> { - fn from(cookie: CookieCrumb) -> Header<'static> { - Header::new("Set-Cookie", cookie.encoded().to_string()) - } -} - -impl From<&CookieCrumb> for Header<'static> { - fn from(cookie: &CookieCrumb) -> Header<'static> { - Header::new("Set-Cookie", cookie.encoded().to_string()) - } -} diff --git a/core/http/src/lib.rs b/core/http/src/lib.rs index 426d8f37..98be6039 100644 --- a/core/http/src/lib.rs +++ b/core/http/src/lib.rs @@ -74,4 +74,4 @@ pub use crate::status::{Status, StatusClass}; pub use crate::header::{Header, HeaderMap}; pub use crate::raw_str::RawStr; pub use crate::media_type::MediaType; -pub use crate::cookies::{Cookie, CookieJar, CookieCrumb, SameSite}; +pub use crate::cookies::{Cookie, CookieJar, SameSite}; diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 43c07407..08579884 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -40,11 +40,7 @@ async-trait = "0.1" ref-cast = "1.0" atomic = "0.5" ubyte = "0.10" - -[dependencies.cookie] -git = "https://github.com/SergioBenitez/cookie-rs.git" -rev = "3795f2e" -features = ["percent-encode"] +parking_lot = "0.11" [dependencies.pear] git = "https://github.com/SergioBenitez/Pear.git" diff --git a/core/lib/benches/format-routing.rs b/core/lib/benches/format-routing.rs index 75d3e80b..77c4113e 100644 --- a/core/lib/benches/format-routing.rs +++ b/core/lib/benches/format-routing.rs @@ -19,25 +19,25 @@ use bencher::Bencher; use rocket::http::{Accept, ContentType}; fn accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let request = client.get("/").header(Accept::JSON); b.iter(|| { request.clone().dispatch(); }); } fn wrong_accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let request = client.get("/").header(Accept::HTML); b.iter(|| { request.clone().dispatch(); }); } fn content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let request = client.post("/").header(ContentType::JSON); b.iter(|| { request.clone().dispatch(); }); } fn wrong_content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let request = client.post("/").header(ContentType::Plain); b.iter(|| { request.clone().dispatch(); }); } diff --git a/core/lib/benches/ranked-routing.rs b/core/lib/benches/ranked-routing.rs index f17fba7b..662776a8 100644 --- a/core/lib/benches/ranked-routing.rs +++ b/core/lib/benches/ranked-routing.rs @@ -33,7 +33,7 @@ use rocket::local::blocking::Client; use rocket::http::{Accept, ContentType}; fn accept_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let requests = vec![ client.get("/").header(Accept::JSON), client.get("/").header(Accept::HTML), @@ -48,7 +48,7 @@ fn accept_format(b: &mut Bencher) { } fn content_type_format(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let requests = vec![ client.post("/").header(ContentType::JSON), client.post("/").header(ContentType::HTML), diff --git a/core/lib/benches/simple-routing.rs b/core/lib/benches/simple-routing.rs index 717a8e0b..062792f0 100644 --- a/core/lib/benches/simple-routing.rs +++ b/core/lib/benches/simple-routing.rs @@ -44,7 +44,7 @@ use bencher::Bencher; use rocket::local::blocking::Client; fn bench_hello_world(b: &mut Bencher) { - let client = Client::new(hello_world_rocket()).unwrap(); + let client = Client::tracked(hello_world_rocket()).unwrap(); b.iter(|| { client.get("/").dispatch(); @@ -52,7 +52,7 @@ fn bench_hello_world(b: &mut Bencher) { } fn bench_single_get_index(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); b.iter(|| { client.get("/").dispatch(); @@ -60,7 +60,7 @@ fn bench_single_get_index(b: &mut Bencher) { } fn bench_get_put_post_index(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; @@ -76,7 +76,7 @@ fn bench_get_put_post_index(b: &mut Bencher) { } fn bench_dynamic(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; @@ -92,7 +92,7 @@ fn bench_dynamic(b: &mut Bencher) { } fn bench_simple_routing(b: &mut Bencher) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Hold all of the requests we're going to make during the benchmark. let mut requests = vec![]; diff --git a/core/lib/src/local/asynchronous/client.rs b/core/lib/src/local/asynchronous/client.rs index c18af310..4c2cb4d5 100644 --- a/core/lib/src/local/asynchronous/client.rs +++ b/core/lib/src/local/asynchronous/client.rs @@ -1,5 +1,7 @@ use std::borrow::Cow; +use parking_lot::RwLock; + use crate::local::asynchronous::{LocalRequest, LocalResponse}; use crate::rocket::{Rocket, Cargo}; use crate::http::{private::cookie, Method}; @@ -11,7 +13,7 @@ use crate::error::LaunchError; /// For the `blocking` version, see /// [`blocking::Client`](crate::local::blocking::Client). /// -/// ## Multithreaded Synchronization Pitfalls +/// ## Multithreaded Tracking Synchronization Pitfalls /// /// Unlike its [`blocking`](crate::local::blocking) variant, this `async` /// `Client` implements `Sync`. However, using it in a multithreaded environment @@ -19,11 +21,14 @@ use crate::error::LaunchError; /// This is because while cookie modifications are serialized, the ordering /// depends on the ordering of request dispatch. /// -/// If possible, refrain from sharing a single instance of `Client` across -/// multiple threads. Instead, prefer to create a unique instance of `Client` -/// per thread. If this is not possible, ensure that you are not depending on -/// the ordering of cookie modifications or have arranged for request dispatch -/// to occur in a deterministic manner. +/// If possible, refrain from sharing a single instance of a tracking `Client` +/// across multiple threads. Instead, prefer to create a unique instance of +/// `Client` per thread. If this is not possible, ensure that you are not +/// depending on the ordering of cookie modifications or have arranged for +/// request dispatch to occur in a deterministic manner. +/// +/// Alternatively, use an untracked client, which does not suffer from these +/// pitfalls. /// /// ## Example /// @@ -35,7 +40,7 @@ use crate::error::LaunchError; /// /// # rocket::async_test(async { /// let rocket = rocket::ignite(); -/// let client = Client::new(rocket).await.expect("valid rocket"); +/// let client = Client::tracked(rocket).await.expect("valid rocket"); /// let response = client.post("/") /// .body("Hello, world!") /// .dispatch() @@ -44,8 +49,8 @@ use crate::error::LaunchError; /// ``` pub struct Client { cargo: Cargo, + cookies: RwLock, pub(in super) tracked: bool, - pub(in super) cookies: cookie::CookieJar, } impl Client { @@ -55,8 +60,8 @@ impl Client { ) -> Result { rocket.prelaunch_check().await?; let cargo = rocket.into_cargo().await; - - Ok(Client { cargo, tracked, cookies: cookie::CookieJar::new() }) + let cookies = RwLock::new(cookie::CookieJar::new()); + Ok(Client { cargo, tracked, cookies }) } // WARNING: This is unstable! Do not use this method outside of Rocket! @@ -66,7 +71,7 @@ impl Client { { crate::async_test(async { let rocket = crate::ignite(); - let client = Client::new(rocket).await.expect("valid rocket"); + let client = Client::untracked(rocket).await.expect("valid rocket"); let request = client.get("/"); let response = request.clone().dispatch().await; f(&client, request, response) @@ -79,8 +84,17 @@ impl Client { } #[inline(always)] - pub(crate) fn _cookies(&self) -> &cookie::CookieJar { - &self.cookies + pub(crate) fn _with_raw_cookies(&self, f: F) -> T + where F: FnOnce(&cookie::CookieJar) -> T + { + f(&*self.cookies.read()) + } + + #[inline(always)] + pub(crate) fn _with_raw_cookies_mut(&self, f: F) -> T + where F: FnOnce(&mut cookie::CookieJar) -> T + { + f(&mut *self.cookies.write()) } #[inline(always)] diff --git a/core/lib/src/local/asynchronous/request.rs b/core/lib/src/local/asynchronous/request.rs index f81f5e2c..b16dd1fc 100644 --- a/core/lib/src/local/asynchronous/request.rs +++ b/core/lib/src/local/asynchronous/request.rs @@ -19,7 +19,7 @@ use super::{Client, LocalResponse}; /// use rocket::http::{ContentType, Cookie}; /// /// # rocket::async_test(async { -/// let client = Client::new(rocket::ignite()).await.expect("valid rocket"); +/// let client = Client::tracked(rocket::ignite()).await.expect("valid rocket"); /// let req = client.post("/") /// .header(ContentType::JSON) /// .remote("127.0.0.1:8000".parse().unwrap()) @@ -45,13 +45,15 @@ impl<'c> LocalRequest<'c> { // We try to validate the URI now so that the inner `Request` contains a // valid URI. If it doesn't, we set a dummy one. let origin = Origin::parse(&uri).unwrap_or_else(|_| Origin::dummy()); - let request = Request::new(client.rocket(), method, origin.into_owned()); + let mut request = Request::new(client.rocket(), method, origin.into_owned()); // Add any cookies we know about. if client.tracked { - for crumb in client.cookies.iter() { - request.cookies().add_original(crumb.into_cookie()); - } + client._with_raw_cookies(|jar| { + for cookie in jar.iter() { + request.cookies_mut().add_original(cookie.clone()); + } + }) } LocalRequest { client, request, uri, data: vec![] } @@ -92,18 +94,19 @@ impl<'c> LocalRequest<'c> { // If the client is tracking cookies, updates the internal cookie jar // with the changes reflected by `response`. if self.client.tracked { - let jar = &self.client.cookies; - let current_time = time::OffsetDateTime::now_utc(); - for cookie in response.cookies().iter() { - if let Some(expires) = cookie.expires() { - if expires <= current_time { - jar.force_remove(&cookie); - continue; + self.client._with_raw_cookies_mut(|jar| { + let current_time = time::OffsetDateTime::now_utc(); + for cookie in response.cookies().iter() { + if let Some(expires) = cookie.expires() { + if expires <= current_time { + jar.force_remove(cookie); + continue; + } } - } - jar.add(cookie.into_cookie()); - } + jar.add_original(cookie.clone()); + } + }) } response diff --git a/core/lib/src/local/asynchronous/response.rs b/core/lib/src/local/asynchronous/response.rs index a92a09de..cd842a7c 100644 --- a/core/lib/src/local/asynchronous/response.rs +++ b/core/lib/src/local/asynchronous/response.rs @@ -36,7 +36,7 @@ use crate::{Request, Response}; /// /// # async fn read_body_manually() -> io::Result<()> { /// // Dispatch a `GET /` request. -/// let client = Client::new(rocket()).await.expect("valid rocket"); +/// let client = Client::tracked(rocket()).await.expect("valid rocket"); /// let mut response = client.get("/").dispatch().await; /// /// // Check metadata validity. @@ -89,9 +89,9 @@ impl<'c> LocalResponse<'c> { async move { let response: Response<'c> = f(request).await; - let cookies = CookieJar::new(request.state.config.secret_key()); + let mut cookies = CookieJar::new(request.state.config.secret_key()); for cookie in response.cookies() { - cookies.add(cookie.into_owned()); + cookies.add_original(cookie.into_owned()); } LocalResponse { cookies, _request: boxed_req, response, } diff --git a/core/lib/src/local/blocking/client.rs b/core/lib/src/local/blocking/client.rs index b68a1ce8..8959e37b 100644 --- a/core/lib/src/local/blocking/client.rs +++ b/core/lib/src/local/blocking/client.rs @@ -20,7 +20,7 @@ use crate::http::Method; /// use rocket::local::blocking::Client; /// /// let rocket = rocket::ignite(); -/// let client = Client::new(rocket).expect("valid rocket"); +/// let client = Client::tracked(rocket).expect("valid rocket"); /// let response = client.post("/") /// .body("Hello, world!") /// .dispatch(); @@ -49,7 +49,7 @@ impl Client { where F: FnOnce(&Self, LocalRequest<'_>, LocalResponse<'_>) -> T + Send { let rocket = crate::ignite(); - let client = Client::new(rocket).expect("valid rocket"); + let client = Client::untracked(rocket).expect("valid rocket"); let request = client.get("/"); let response = request.clone().dispatch(); f(&client, request, response) @@ -68,8 +68,10 @@ impl Client { } #[inline(always)] - fn _cookies(&self) -> &cookie::CookieJar { - self.inner._cookies() + pub(crate) fn _with_raw_cookies(&self, f: F) -> T + where F: FnOnce(&crate::http::private::cookie::CookieJar) -> T + { + self.inner._with_raw_cookies(f) } #[inline(always)] diff --git a/core/lib/src/local/blocking/request.rs b/core/lib/src/local/blocking/request.rs index 45efd3bd..01cb4535 100644 --- a/core/lib/src/local/blocking/request.rs +++ b/core/lib/src/local/blocking/request.rs @@ -17,7 +17,7 @@ use super::{Client, LocalResponse}; /// use rocket::local::blocking::{Client, LocalRequest}; /// use rocket::http::{ContentType, Cookie}; /// -/// let client = Client::new(rocket::ignite()).expect("valid rocket"); +/// let client = Client::tracked(rocket::ignite()).expect("valid rocket"); /// let req = client.post("/") /// .header(ContentType::JSON) /// .remote("127.0.0.1:8000".parse().unwrap()) diff --git a/core/lib/src/local/blocking/response.rs b/core/lib/src/local/blocking/response.rs index 74048f5c..beb77a8b 100644 --- a/core/lib/src/local/blocking/response.rs +++ b/core/lib/src/local/blocking/response.rs @@ -33,7 +33,7 @@ use super::Client; /// /// # fn read_body_manually() -> io::Result<()> { /// // Dispatch a `GET /` request. -/// let client = Client::new(rocket()).expect("valid rocket"); +/// let client = Client::tracked(rocket()).expect("valid rocket"); /// let mut response = client.get("/").dispatch(); /// /// // Check metadata validity. diff --git a/core/lib/src/local/client.rs b/core/lib/src/local/client.rs index 352c0f08..ad07dbe1 100644 --- a/core/lib/src/local/client.rs +++ b/core/lib/src/local/client.rs @@ -41,22 +41,19 @@ macro_rules! req_method { macro_rules! pub_client_impl { ($import:literal $(@$prefix:tt $suffix:tt)?) => { - /// Construct a new `Client` from an instance of `Rocket` with cookie - /// tracking. + /// Construct a new `Client` from an instance of `Rocket` _with_ cookie + /// tracking. This is typically the desired mode of operation for testing. /// /// # Cookie Tracking /// - /// By default, a `Client` propagates cookie changes made by responses - /// to previously dispatched requests. In other words, if a previously - /// dispatched request resulted in a response that adds a cookie, any - /// future requests will contain the new cookies. Similarly, cookies - /// removed by a response won't be propagated further. + /// With cookie tracking enabled, a `Client` propagates cookie changes made + /// by responses to previously dispatched requests. In other words, + /// succeeding requests reflect changes (additions and removals) made by any + /// prior responses. /// - /// This is typically the desired mode of operation for a `Client` as it - /// removes the burden of manually tracking cookies. Under some - /// circumstances, however, disabling tracking may be desired. The - /// [`untracked()`](Client::untracked()) method creates a `Client` that - /// _will not_ track cookies. + /// Cookie tracking requires synchronization between dispatches. **As such, + /// cookie tracking _should not_ be enabled if a local client is being used + /// to serve requests on multiple threads.** /// /// # Errors /// @@ -67,10 +64,10 @@ macro_rules! pub_client_impl { #[doc = $import] /// /// let rocket = rocket::ignite(); - /// let client = Client::new(rocket); + /// let client = Client::tracked(rocket); /// ``` #[inline(always)] - pub $($prefix)? fn new(rocket: Rocket) -> Result { + pub $($prefix)? fn tracked(rocket: Rocket) -> Result { Self::_new(rocket, true) $(.$suffix)? } @@ -79,8 +76,9 @@ macro_rules! pub_client_impl { /// /// # Cookie Tracking /// - /// Unlike the [`new()`](Client::new()) constructor, a `Client` returned - /// from this method _does not_ automatically propagate cookie changes. + /// Unlike the [`tracked()`](Client::tracked()) constructor, a `Client` + /// returned from this method _does not_ automatically propagate cookie + /// changes and thus requires no synchronization between dispatches. /// /// # Errors /// @@ -94,7 +92,16 @@ macro_rules! pub_client_impl { /// let client = Client::untracked(rocket); /// ``` pub $($prefix)? fn untracked(rocket: Rocket) -> Result { - Self::_new(rocket, true) $(.$suffix)? + Self::_new(rocket, false) $(.$suffix)? + } + + /// Deprecated alias to [`Client::tracked()`]. + #[deprecated( + since = "0.5", + note = "choose between `Client::untracked()` and `Client::tracked()`" + )] + pub $($prefix)? fn new(rocket: Rocket) -> Result { + Self::tracked(rocket) $(.$suffix)? } /// Returns a reference to the `Rocket` this client is creating requests @@ -153,7 +160,8 @@ macro_rules! pub_client_impl { #[inline(always)] pub fn cookies(&self) -> crate::http::CookieJar<'_> { let key = self.rocket().config.secret_key(); - crate::http::CookieJar::from(self._cookies().clone(), key) + let jar = self._with_raw_cookies(|jar| jar.clone()); + crate::http::CookieJar::from(jar, key) } req_method!($import, "GET", get, Method::Get); diff --git a/core/lib/src/local/mod.rs b/core/lib/src/local/mod.rs index 93a60bbe..6a8daf10 100644 --- a/core/lib/src/local/mod.rs +++ b/core/lib/src/local/mod.rs @@ -55,7 +55,7 @@ //! fn test_hello_world_blocking() { //! // Construct a client to use for dispatching requests. //! use rocket::local::blocking::Client; -//! let client = Client::new(super::rocket()) +//! let client = Client::tracked(super::rocket()) //! .expect("valid `Rocket`"); //! //! // Dispatch a request to 'GET /' and validate the response. @@ -68,7 +68,7 @@ //! async fn test_hello_world_async() { //! // Construct a client to use for dispatching requests. //! use rocket::local::asynchronous::Client; -//! let client = Client::new(super::rocket()).await +//! let client = Client::tracked(super::rocket()).await //! .expect("valid `Rocket`"); //! //! // Dispatch a request to 'GET /' and validate the response. @@ -91,7 +91,7 @@ //! //! **Usage** //! -//! A `Client` is constructed via the [`new()`] ([`async` `new()`]) or +//! A `Client` is constructed via the [`tracked()`] ([`async` `tracked()`]) or //! [`untracked()`] ([`async` `untracked()`]) methods from an already //! constructed `Rocket` instance. Once a value of `Client` has been //! constructed, [`get()`], [`put()`], [`post()`], and so on ([`async` `get()`], @@ -100,7 +100,7 @@ //! //! **Cookie Tracking** //! -//! A `Client` constructed using `new()` propagates cookie changes made by +//! A `Client` constructed using `tracked()` propagates cookie changes made by //! responses to previously dispatched requests. In other words, if a previously //! dispatched request resulted in a response that adds a cookie, any future //! requests will contain that cookie. Similarly, cookies removed by a response @@ -117,9 +117,9 @@ //! [`LocalRequest`]: blocking::LocalRequest //! [`async` `LocalRequest`]: asynchronous::LocalRequest //! -//! [`new()`]: blocking::Client::new() +//! [`tracked()`]: blocking::Client::tracked() //! [`untracked()`]: blocking::Client::untracked() -//! [`async` `new()`]: asynchronous::Client::new() +//! [`async` `tracked()`]: asynchronous::Client::tracked() //! [`async` `untracked()`]: asynchronous::Client::untracked() //! //! [`get()`]: blocking::Client::get() diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 8fb7e0ba..3c43e2ee 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -111,8 +111,8 @@ macro_rules! pub_request_impl { /// # }); /// ``` #[inline] - pub fn cookie(self, cookie: crate::http::Cookie<'_>) -> Self { - self._request().cookies().add_original(cookie.into_owned()); + pub fn cookie(mut self, cookie: crate::http::Cookie<'_>) -> Self { + self._request_mut().cookies_mut().add_original(cookie.into_owned()); self } @@ -131,11 +131,11 @@ macro_rules! pub_request_impl { /// # }); /// ``` #[inline] - pub fn cookies<'a, C>(self, cookies: C) -> Self + pub fn cookies<'a, C>(mut self, cookies: C) -> Self where C: IntoIterator> { for cookie in cookies { - self._request().cookies().add_original(cookie.into_owned()); + self._request_mut().cookies_mut().add_original(cookie.into_owned()); } self @@ -161,8 +161,8 @@ macro_rules! pub_request_impl { #[cfg(feature = "secrets")] #[cfg_attr(nightly, doc(cfg(feature = "secrets")))] #[inline] - pub fn private_cookie(self, cookie: crate::http::Cookie<'static>) -> Self { - self._request().cookies().add_original_private(cookie); + pub fn private_cookie(mut self, cookie: crate::http::Cookie<'static>) -> Self { + self._request_mut().cookies_mut().add_original_private(cookie); self } diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 33e3caa8..fc3c4973 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -832,6 +832,10 @@ impl<'r> Request<'r> { self.method.store(method, Ordering::Release) } + pub(crate) fn cookies_mut(&mut self) -> &mut CookieJar<'r> { + &mut self.state.cookies + } + /// Convert from Hyper types into a Rocket Request. pub(crate) fn from_hyp( rocket: &'r Rocket, diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 46480b57..9eee6649 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -182,7 +182,6 @@ impl Flash { Cookie::build(FLASH_COOKIE_NAME, content) .max_age(Duration::minutes(5)) - .path("/") .finish() } } @@ -215,8 +214,7 @@ impl<'a, 'r> Flash<&'a Request<'r>> { fn clear_cookie_if_needed(&self) { // Remove the cookie if it hasn't already been removed. if !self.consumed.swap(true, Ordering::Relaxed) { - let cookie = Cookie::build(FLASH_COOKIE_NAME, "").path("/").finish(); - self.inner.cookies().remove(cookie); + self.inner.cookies().remove(Cookie::named(FLASH_COOKIE_NAME)); } } diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 82729c76..0f46253f 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -731,7 +731,7 @@ impl<'r> Response<'r> { self.status = Some(Status::new(code, reason)); } - /// Returns a vector of the cookies set in `self` as identified by the + /// Returns an iterator over the cookies in `self` as identified by the /// `Set-Cookie` header. Malformed cookies are skipped. /// /// # Example diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index a5e66976..342c5590 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -50,7 +50,7 @@ impl<'r, R> Created { /// } /// /// # let rocket = rocket::ignite().mount("/", routes![create]); - /// # let client = Client::new(rocket).unwrap(); + /// # let client = Client::tracked(rocket).unwrap(); /// let response = client.get("/").dispatch(); /// /// let loc = response.headers().get_one("Location"); @@ -79,7 +79,7 @@ impl<'r, R> Created { /// } /// /// # let rocket = rocket::ignite().mount("/", routes![create]); - /// # let client = Client::new(rocket).unwrap(); + /// # let client = Client::tracked(rocket).unwrap(); /// let response = client.get("/").dispatch(); /// /// let loc = response.headers().get_one("Location"); @@ -112,7 +112,7 @@ impl<'r, R> Created { /// } /// /// # let rocket = rocket::ignite().mount("/", routes![create]); - /// # let client = Client::new(rocket).unwrap(); + /// # let client = Client::tracked(rocket).unwrap(); /// let response = client.get("/").dispatch(); /// /// let loc = response.headers().get_one("Location"); diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index d9f57ce0..810fefc0 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -347,7 +347,8 @@ impl Rocket { request._set_method(Method::Get); // Return early so we don't set cookies twice. - let try_next: BoxFuture<'_, _> = Box::pin(self.route_and_process(request, data)); + let try_next: BoxFuture<'_, _> = + Box::pin(self.route_and_process(request, data)); return try_next.await; } else { // No match was found and it can't be autohandled. 404. @@ -359,8 +360,9 @@ impl Rocket { // Set the cookies. Note that error responses will only include // cookies set by the error handler. See `handle_error` for more. - for crumb in request.cookies().delta() { - response.adjoin_header(crumb) + let delta_jar = request.cookies().take_delta_jar(); + for cookie in delta_jar.delta() { + response.adjoin_header(cookie); } response diff --git a/core/lib/tests/absolute-uris-okay-issue-443.rs b/core/lib/tests/absolute-uris-okay-issue-443.rs index 1c2d791c..be3b0c30 100644 --- a/core/lib/tests/absolute-uris-okay-issue-443.rs +++ b/core/lib/tests/absolute-uris-okay-issue-443.rs @@ -19,7 +19,7 @@ mod test_absolute_uris_okay { #[test] fn redirect_works() { let rocket = rocket::ignite().mount("/", routes![google, redirect]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/google").dispatch(); let location = response.headers().get_one("Location"); diff --git a/core/lib/tests/catcher-cookies-1213.rs b/core/lib/tests/catcher-cookies-1213.rs index 3032e03a..877e30fa 100644 --- a/core/lib/tests/catcher-cookies-1213.rs +++ b/core/lib/tests/catcher-cookies-1213.rs @@ -25,11 +25,11 @@ mod tests { let rocket = rocket::ignite() .mount("/", routes![index]) .register(catchers![not_found]) - .attach(AdHoc::on_request("Add Fairing Cookie", |req, _| Box::pin(async move { + .attach(AdHoc::on_request("Add Cookie", |req, _| Box::pin(async move { req.cookies().add(Cookie::new("fairing", "woo")); }))); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); // Check that the index returns the `index` and `fairing` cookie. let response = client.get("/").dispatch(); diff --git a/core/lib/tests/conditionally-set-server-header-996.rs b/core/lib/tests/conditionally-set-server-header-996.rs index a8c8cd16..711d4ba8 100644 --- a/core/lib/tests/conditionally-set-server-header-996.rs +++ b/core/lib/tests/conditionally-set-server-header-996.rs @@ -20,7 +20,7 @@ mod conditionally_set_server_header { #[test] fn do_not_overwrite_server_header() { let rocket = rocket::ignite().mount("/", routes![do_not_overwrite, use_default]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/do_not_overwrite").dispatch(); let server = response.headers().get_one("Server"); diff --git a/core/lib/tests/derive-reexports.rs b/core/lib/tests/derive-reexports.rs index f63d6d9f..ee7f2a15 100644 --- a/core/lib/tests/derive-reexports.rs +++ b/core/lib/tests/derive-reexports.rs @@ -46,7 +46,7 @@ fn test_derive_reexports() { use rocket::local::blocking::Client; let rocket = rocket::ignite().mount("/", routes![index, number]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string().unwrap(), "hello"); diff --git a/core/lib/tests/fairing_before_head_strip-issue-546.rs b/core/lib/tests/fairing_before_head_strip-issue-546.rs index fa8a0f01..85380b28 100644 --- a/core/lib/tests/fairing_before_head_strip-issue-546.rs +++ b/core/lib/tests/fairing_before_head_strip-issue-546.rs @@ -40,7 +40,7 @@ mod fairing_before_head_strip { }) })); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert!(response.body().is_none()); @@ -71,7 +71,7 @@ mod fairing_before_head_strip { }) })); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert!(response.body().is_none()); diff --git a/core/lib/tests/flash-lazy-removes-issue-466.rs b/core/lib/tests/flash-lazy-removes-issue-466.rs index 748b96bd..4eca7a2f 100644 --- a/core/lib/tests/flash-lazy-removes-issue-466.rs +++ b/core/lib/tests/flash-lazy-removes-issue-466.rs @@ -28,7 +28,7 @@ mod flash_lazy_remove_tests { fn test() { use super::*; let r = rocket::ignite().mount("/", routes![set, unused, used]); - let client = Client::new(r).unwrap(); + let client = Client::tracked(r).unwrap(); // Ensure the cookie's not there at first. let response = client.get("/unused").dispatch(); diff --git a/core/lib/tests/form_method-issue-45.rs b/core/lib/tests/form_method-issue-45.rs index 8c6eaed6..c60b5224 100644 --- a/core/lib/tests/form_method-issue-45.rs +++ b/core/lib/tests/form_method-issue-45.rs @@ -20,7 +20,7 @@ mod tests { #[test] fn method_eval() { - let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap(); + let client = Client::tracked(rocket::ignite().mount("/", routes![bug])).unwrap(); let response = client.post("/") .header(ContentType::Form) .body("_method=patch&form_data=Form+data") @@ -31,7 +31,7 @@ mod tests { #[test] fn get_passes_through() { - let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap(); + let client = Client::tracked(rocket::ignite().mount("/", routes![bug])).unwrap(); let response = client.get("/") .header(ContentType::Form) .body("_method=patch&form_data=Form+data") diff --git a/core/lib/tests/form_value_decoding-issue-82.rs b/core/lib/tests/form_value_decoding-issue-82.rs index 61db2c7a..8a2c7960 100644 --- a/core/lib/tests/form_value_decoding-issue-82.rs +++ b/core/lib/tests/form_value_decoding-issue-82.rs @@ -19,7 +19,7 @@ mod tests { use rocket::http::Status; fn check_decoding(raw: &str, decoded: &str) { - let client = Client::new(rocket::ignite().mount("/", routes![bug])).unwrap(); + let client = Client::tracked(rocket::ignite().mount("/", routes![bug])).unwrap(); let response = client.post("/") .header(ContentType::Form) .body(format!("form_data={}", raw)) diff --git a/core/lib/tests/head_handling.rs b/core/lib/tests/head_handling.rs index 22940ecf..8daa3569 100644 --- a/core/lib/tests/head_handling.rs +++ b/core/lib/tests/head_handling.rs @@ -30,7 +30,7 @@ mod head_handling_tests { #[test] fn auto_head() { - let client = Client::new(rocket::ignite().mount("/", routes())).unwrap(); + let client = Client::tracked(rocket::ignite().mount("/", routes())).unwrap(); let response = client.head("/").dispatch(); let content_type: Vec<_> = response.headers().get("Content-Type").collect(); @@ -46,7 +46,7 @@ mod head_handling_tests { #[test] fn user_head() { - let client = Client::new(rocket::ignite().mount("/", routes())).unwrap(); + let client = Client::tracked(rocket::ignite().mount("/", routes())).unwrap(); let response = client.head("/other").dispatch(); let content_type: Vec<_> = response.headers().get("Content-Type").collect(); diff --git a/core/lib/tests/limits.rs b/core/lib/tests/limits.rs index 5fe7804c..cf48f862 100644 --- a/core/lib/tests/limits.rs +++ b/core/lib/tests/limits.rs @@ -29,7 +29,7 @@ mod limits_tests { #[test] fn large_enough() { - let client = Client::new(rocket_with_forms_limit(128)).unwrap(); + let client = Client::tracked(rocket_with_forms_limit(128)).unwrap(); let response = client.post("/") .body("value=Hello+world") .header(ContentType::Form) @@ -40,7 +40,7 @@ mod limits_tests { #[test] fn just_large_enough() { - let client = Client::new(rocket_with_forms_limit(17)).unwrap(); + let client = Client::tracked(rocket_with_forms_limit(17)).unwrap(); let response = client.post("/") .body("value=Hello+world") .header(ContentType::Form) @@ -51,7 +51,7 @@ mod limits_tests { #[test] fn much_too_small() { - let client = Client::new(rocket_with_forms_limit(4)).unwrap(); + let client = Client::tracked(rocket_with_forms_limit(4)).unwrap(); let response = client.post("/") .body("value=Hello+world") .header(ContentType::Form) @@ -62,7 +62,7 @@ mod limits_tests { #[test] fn contracted() { - let client = Client::new(rocket_with_forms_limit(10)).unwrap(); + let client = Client::tracked(rocket_with_forms_limit(10)).unwrap(); let response = client.post("/") .body("value=Hello+world") .header(ContentType::Form) diff --git a/core/lib/tests/local-request-content-type-issue-505.rs b/core/lib/tests/local-request-content-type-issue-505.rs index fafb3053..b56a98e6 100644 --- a/core/lib/tests/local-request-content-type-issue-505.rs +++ b/core/lib/tests/local-request-content-type-issue-505.rs @@ -54,7 +54,7 @@ mod local_request_content_type_tests { #[test] fn has_no_ct() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let req = client.post("/"); assert_eq!(req.clone().dispatch().into_string(), Some("Absent".to_string())); @@ -69,7 +69,7 @@ mod local_request_content_type_tests { #[test] fn has_ct() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let req = client.post("/").header(ContentType::JSON); assert_eq!(req.clone().dispatch().into_string(), Some("Present".to_string())); diff --git a/core/lib/tests/local_request_private_cookie-issue-368.rs b/core/lib/tests/local_request_private_cookie-issue-368.rs index fc446d57..664a1c32 100644 --- a/core/lib/tests/local_request_private_cookie-issue-368.rs +++ b/core/lib/tests/local_request_private_cookie-issue-368.rs @@ -20,7 +20,7 @@ mod private_cookies { fn private_cookie_is_returned() { let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); let response = req.dispatch(); @@ -32,7 +32,7 @@ mod private_cookies { fn regular_cookie_is_not_returned() { let rocket = rocket::ignite().mount("/", routes![return_private_cookie]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let req = client.get("/").cookie(Cookie::new("cookie_name", "cookie_value")); let response = req.dispatch(); diff --git a/core/lib/tests/many-cookie-jars-at-once.rs b/core/lib/tests/many-cookie-jars-at-once.rs index 3c0a4c07..68febbd8 100644 --- a/core/lib/tests/many-cookie-jars-at-once.rs +++ b/core/lib/tests/many-cookie-jars-at-once.rs @@ -28,7 +28,7 @@ mod many_cookie_jars_tests { #[test] fn test_mutli_add() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.post("/").dispatch(); let cookies = response.cookies(); assert_eq!(cookies.iter().count(), 2); @@ -38,7 +38,7 @@ mod many_cookie_jars_tests { #[test] fn test_mutli_get() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/") .cookie(Cookie::new("a", "a_val")) .cookie(Cookie::new("b", "hi!")) diff --git a/core/lib/tests/mapped-base-issue-1262.rs b/core/lib/tests/mapped-base-issue-1262.rs index a78e7bbb..2cfa23dc 100644 --- a/core/lib/tests/mapped-base-issue-1262.rs +++ b/core/lib/tests/mapped-base-issue-1262.rs @@ -30,7 +30,7 @@ mod mapped_base_tests { #[test] fn only_prefix() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get("/a/b/3").dispatch(); assert_eq!(response.into_string().unwrap(), "3"); @@ -44,7 +44,7 @@ mod mapped_base_tests { #[test] fn prefix_and_base() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get("/foo/a/b/23").dispatch(); assert_eq!(response.into_string().unwrap(), "23"); diff --git a/core/lib/tests/nested-fairing-attaches.rs b/core/lib/tests/nested-fairing-attaches.rs index 9ae736ad..b959453d 100644 --- a/core/lib/tests/nested-fairing-attaches.rs +++ b/core/lib/tests/nested-fairing-attaches.rs @@ -46,7 +46,7 @@ mod nested_fairing_attaches_tests { #[test] fn test_counts() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("1, 1".into())); diff --git a/core/lib/tests/precise-content-type-matching.rs b/core/lib/tests/precise-content-type-matching.rs index bc44bcdf..599b359a 100644 --- a/core/lib/tests/precise-content-type-matching.rs +++ b/core/lib/tests/precise-content-type-matching.rs @@ -35,7 +35,7 @@ mod tests { macro_rules! check_dispatch { ($mount:expr, $ct:expr, $body:expr) => ( - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let mut req = client.post($mount); let ct: Option = $ct; if let Some(ct) = ct { diff --git a/core/lib/tests/redirect_from_catcher-issue-113.rs b/core/lib/tests/redirect_from_catcher-issue-113.rs index 62978bf3..9867e548 100644 --- a/core/lib/tests/redirect_from_catcher-issue-113.rs +++ b/core/lib/tests/redirect_from_catcher-issue-113.rs @@ -14,9 +14,8 @@ mod tests { #[test] fn error_catcher_redirect() { - let client = Client::new(rocket::ignite().register(catchers![not_found])).unwrap(); + let client = Client::tracked(rocket::ignite().register(catchers![not_found])).unwrap(); let response = client.get("/unknown").dispatch(); - println!("Response:\n{:?}", response); let location: Vec<_> = response.headers().get("location").collect(); assert_eq!(response.status(), Status::SeeOther); diff --git a/core/lib/tests/route_guard.rs b/core/lib/tests/route_guard.rs index ce6e8ea5..17d45169 100644 --- a/core/lib/tests/route_guard.rs +++ b/core/lib/tests/route_guard.rs @@ -24,7 +24,7 @@ mod route_guard_tests { .mount("/first", routes![files]) .mount("/second", routes![files]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); assert_path(&client, "/first/some/path"); assert_path(&client, "/second/some/path"); assert_path(&client, "/first/second/b/c"); diff --git a/core/lib/tests/segments-issues-41-86.rs b/core/lib/tests/segments-issues-41-86.rs index bfbccff5..680c8019 100644 --- a/core/lib/tests/segments-issues-41-86.rs +++ b/core/lib/tests/segments-issues-41-86.rs @@ -36,7 +36,7 @@ mod tests { let rocket = rocket::ignite() .mount("/", routes![test, two, one_two, none, dual]) .mount("/point", routes![test, two, one_two, dual]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); // We construct a path that matches each of the routes above. We ensure the // prefix is stripped, confirming that dynamic segments are working. diff --git a/core/lib/tests/strict_and_lenient_forms.rs b/core/lib/tests/strict_and_lenient_forms.rs index 4b2d4a0d..04fe1b72 100644 --- a/core/lib/tests/strict_and_lenient_forms.rs +++ b/core/lib/tests/strict_and_lenient_forms.rs @@ -26,7 +26,7 @@ mod strict_and_lenient_forms_tests { const FIELD_VALUE: &str = "just_some_value"; fn client() -> Client { - Client::new(rocket::ignite().mount("/", routes![strict, lenient])).unwrap() + Client::tracked(rocket::ignite().mount("/", routes![strict, lenient])).unwrap() } #[test] diff --git a/core/lib/tests/unsound-local-request-1312.rs b/core/lib/tests/unsound-local-request-1312.rs index 5bea7354..dd91db83 100644 --- a/core/lib/tests/unsound-local-request-1312.rs +++ b/core/lib/tests/unsound-local-request-1312.rs @@ -3,7 +3,7 @@ use rocket::local::blocking::Client; #[test] fn test_local_request_clone_soundness() { - let client = Client::new(rocket::ignite()).unwrap(); + let client = Client::tracked(rocket::ignite()).unwrap(); // creates two LocalRequest instances that shouldn't share the same req let r1 = client.get("/").header(Header::new("key", "val1")); diff --git a/core/lib/tests/uri-percent-encoding-issue-808.rs b/core/lib/tests/uri-percent-encoding-issue-808.rs index eb0dbb05..1ec976e1 100644 --- a/core/lib/tests/uri-percent-encoding-issue-808.rs +++ b/core/lib/tests/uri-percent-encoding-issue-808.rs @@ -33,7 +33,7 @@ mod tests { #[test] fn uri_percent_encoding_redirect() { let expected_location = vec!["/hello/John%5B%5D%7C%5C%25@%5E"]; - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/raw").dispatch(); let location: Vec<_> = response.headers().get("location").collect(); @@ -48,7 +48,7 @@ mod tests { #[test] fn uri_percent_encoding_get() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let name = Uri::percent_encode(NAME); let response = client.get(format!("/hello/{}", name)).dispatch(); assert_eq!(response.status(), Status::Ok); diff --git a/examples/config/tests/common/mod.rs b/examples/config/tests/common/mod.rs index 5ff74661..4a248fd3 100644 --- a/examples/config/tests/common/mod.rs +++ b/examples/config/tests/common/mod.rs @@ -62,7 +62,7 @@ pub fn test_config(environment: Environment) { })) .mount("/", routes![check_config]); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); let response = client.get("/check_config").dispatch(); assert_eq!(response.status(), Status::Ok); } diff --git a/examples/content_types/src/tests.rs b/examples/content_types/src/tests.rs index 2e5c2a06..bb9b2a9e 100644 --- a/examples/content_types/src/tests.rs +++ b/examples/content_types/src/tests.rs @@ -5,7 +5,7 @@ use rocket::local::blocking::Client; fn test(method: Method, uri: &str, header: H, status: Status, body: String) where H: Into> { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.req(method, uri).header(header).dispatch(); assert_eq!(response.status(), status); assert_eq!(response.into_string(), Some(body)); diff --git a/examples/cookies/src/tests.rs b/examples/cookies/src/tests.rs index 209b0905..d9418a3e 100644 --- a/examples/cookies/src/tests.rs +++ b/examples/cookies/src/tests.rs @@ -7,7 +7,7 @@ use rocket_contrib::templates::Template; #[test] fn test_submit() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.post("/submit") .header(ContentType::Form) .body("message=Hello from Rocket!") @@ -24,7 +24,7 @@ fn test_submit() { fn test_body(optional_cookie: Option>, expected_body: String) { // Attach a cookie if one is given. - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = match optional_cookie { Some(cookie) => client.get("/").cookie(cookie).dispatch(), None => client.get("/").dispatch(), @@ -36,7 +36,7 @@ fn test_body(optional_cookie: Option>, expected_body: String) { #[test] fn test_index() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Render the template with an empty context. let mut context: HashMap<&str, &str> = HashMap::new(); diff --git a/examples/errors/src/tests.rs b/examples/errors/src/tests.rs index d1f0f6d3..7d5e6c24 100644 --- a/examples/errors/src/tests.rs +++ b/examples/errors/src/tests.rs @@ -3,7 +3,7 @@ use rocket::http::Status; #[test] fn test_hello() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let (name, age) = ("Arthur", 42); let uri = format!("/hello/{}/{}", name, age); @@ -15,7 +15,7 @@ fn test_hello() { #[test] fn forced_error_and_default_catcher() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let request = client.get("/404"); let expected = super::not_found(request.inner()); @@ -44,7 +44,7 @@ fn forced_error_and_default_catcher() { #[test] fn test_hello_invalid_age() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); for &(name, age) in &[("Ford", -129), ("Trillian", 128)] { let request = client.get(format!("/hello/{}/{}", name, age)); diff --git a/examples/fairings/src/tests.rs b/examples/fairings/src/tests.rs index ae6d61e9..cb8fe922 100644 --- a/examples/fairings/src/tests.rs +++ b/examples/fairings/src/tests.rs @@ -3,14 +3,14 @@ use rocket::local::blocking::Client; #[test] fn rewrite_get_put() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Hello, fairings!".into())); } #[test] fn counts() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Issue 1 GET request. client.get("/").dispatch(); @@ -30,7 +30,7 @@ fn counts() { #[test] fn token() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Ensure the token is '123', which is what we have in `Rocket.toml`. let res = client.get("/token").dispatch(); diff --git a/examples/form_kitchen_sink/src/tests.rs b/examples/form_kitchen_sink/src/tests.rs index db0c80db..d106245c 100644 --- a/examples/form_kitchen_sink/src/tests.rs +++ b/examples/form_kitchen_sink/src/tests.rs @@ -42,7 +42,7 @@ macro_rules! assert_valid_raw_form { #[test] fn test_good_forms() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let mut input = FormInput { checkbox: true, number: 310, @@ -121,7 +121,7 @@ macro_rules! assert_invalid_raw_form { #[test] fn check_semantically_invalid_forms() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let mut form_vals = ["true", "1", "a", "hi", "hey", "b"]; form_vals[0] = "not true"; @@ -177,7 +177,7 @@ fn check_semantically_invalid_forms() { #[test] fn check_structurally_invalid_forms() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); assert_invalid_raw_form!(&client, "==&&&&&&=="); assert_invalid_raw_form!(&client, "a&=b"); assert_invalid_raw_form!(&client, "="); @@ -185,7 +185,7 @@ fn check_structurally_invalid_forms() { #[test] fn check_bad_utf8() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); unsafe { let bad_str = std::str::from_utf8_unchecked(b"a=\xff"); assert_form_eq!(&client, bad_str, "Form input was invalid UTF-8.".into()); diff --git a/examples/form_validation/src/tests.rs b/examples/form_validation/src/tests.rs index 73362b63..105ca041 100644 --- a/examples/form_validation/src/tests.rs +++ b/examples/form_validation/src/tests.rs @@ -5,7 +5,7 @@ use rocket::http::{ContentType, Status}; fn test_login(user: &str, pass: &str, age: &str, status: Status, body: T) where T: Into> + Send { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let query = format!("username={}&password={}&age={}", user, pass, age); let response = client.post("/login") .header(ContentType::Form) @@ -44,7 +44,7 @@ fn test_invalid_age() { } fn check_bad_form(form_str: &str, status: Status) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.post("/login") .header(ContentType::Form) .body(form_str) diff --git a/examples/handlebars_templates/src/tests.rs b/examples/handlebars_templates/src/tests.rs index 19546baf..aa860b6f 100644 --- a/examples/handlebars_templates/src/tests.rs +++ b/examples/handlebars_templates/src/tests.rs @@ -7,7 +7,7 @@ use rocket_contrib::templates::Template; macro_rules! dispatch { ($method:expr, $path:expr, |$client:ident, $response:ident| $body:expr) => ({ - let $client = Client::new(rocket()).unwrap(); + let $client = Client::tracked(rocket()).unwrap(); let $response = $client.req($method, $path).dispatch(); $body }) diff --git a/examples/hello_2018/src/tests.rs b/examples/hello_2018/src/tests.rs index 540ed946..38ca2adf 100644 --- a/examples/hello_2018/src/tests.rs +++ b/examples/hello_2018/src/tests.rs @@ -2,7 +2,7 @@ use rocket::{self, local::blocking::Client}; #[test] fn hello_world() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Hello, Rust 2018!".into())); } @@ -35,14 +35,14 @@ mod scoped_uri_tests { #[test] fn test_inner_hello() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Hello! Try /Rust%202018.".into())); } #[test] fn test_hello_name() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/Rust%202018").dispatch(); assert_eq!(response.into_string().unwrap(), "Hello, Rust 2018! This is /Rust%202018."); } diff --git a/examples/hello_person/src/tests.rs b/examples/hello_person/src/tests.rs index badcc648..c223ed20 100644 --- a/examples/hello_person/src/tests.rs +++ b/examples/hello_person/src/tests.rs @@ -2,12 +2,12 @@ use rocket::local::blocking::Client; use rocket::http::Status; fn test(uri: String, expected: String) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); assert_eq!(client.get(&uri).dispatch().into_string(), Some(expected)); } fn test_404(uri: &'static str) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); assert_eq!(client.get(uri).dispatch().status(), Status::NotFound); } diff --git a/examples/hello_world/src/tests.rs b/examples/hello_world/src/tests.rs index 104863a2..a4951f4d 100644 --- a/examples/hello_world/src/tests.rs +++ b/examples/hello_world/src/tests.rs @@ -2,7 +2,7 @@ use rocket::local::blocking::Client; #[test] fn hello_world() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Hello, world!".into())); } diff --git a/examples/json/src/tests.rs b/examples/json/src/tests.rs index 94cd19e5..40b874b4 100644 --- a/examples/json/src/tests.rs +++ b/examples/json/src/tests.rs @@ -4,7 +4,7 @@ use rocket::http::{Status, ContentType}; #[test] fn bad_get_put() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Try to get a message with an ID that doesn't exist. let res = client.get("/message/99").header(ContentType::JSON).dispatch(); @@ -34,7 +34,7 @@ fn bad_get_put() { #[test] fn post_get_put_get() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Check that a message with ID 1 doesn't exist. let res = client.get("/message/1").header(ContentType::JSON).dispatch(); diff --git a/examples/managed_queue/src/tests.rs b/examples/managed_queue/src/tests.rs index 05a5a8e7..a00c8a2f 100644 --- a/examples/managed_queue/src/tests.rs +++ b/examples/managed_queue/src/tests.rs @@ -3,7 +3,7 @@ use rocket::http::Status; #[test] fn test_push_pop() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.put("/push?event=test1").dispatch(); assert_eq!(response.status(), Status::Ok); diff --git a/examples/manual_routes/src/tests.rs b/examples/manual_routes/src/tests.rs index 3cc4123e..578a076a 100644 --- a/examples/manual_routes/src/tests.rs +++ b/examples/manual_routes/src/tests.rs @@ -3,7 +3,7 @@ use rocket::local::blocking::Client; use rocket::http::{ContentType, Status}; fn test(uri: &str, content_type: ContentType, status: Status, body: String) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get(uri).header(content_type).dispatch(); assert_eq!(response.status(), status); assert_eq!(response.into_string(), Some(body)); @@ -30,7 +30,7 @@ fn test_echo() { #[test] fn test_upload() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let expected_body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, \ sed do eiusmod tempor incididunt ut labore et dolore \ magna aliqua".to_string(); diff --git a/examples/msgpack/src/tests.rs b/examples/msgpack/src/tests.rs index 50c2f71f..f33660ac 100644 --- a/examples/msgpack/src/tests.rs +++ b/examples/msgpack/src/tests.rs @@ -12,7 +12,7 @@ struct Message { #[test] fn msgpack_get() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let res = client.get("/message/1").header(ContentType::MsgPack).dispatch(); assert_eq!(res.status(), Status::Ok); assert_eq!(res.content_type(), Some(ContentType::MsgPack)); @@ -25,7 +25,7 @@ fn msgpack_get() { #[test] fn msgpack_post() { // Dispatch request with a message of `[2, "Goodbye, world!"]`. - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let res = client.post("/message") .header(ContentType::MsgPack) .body(&[146, 2, 175, 71, 111, 111, 100, 98, 121, 101, 44, 32, 119, 111, 114, 108, 100, 33]) diff --git a/examples/optional_redirect/src/tests.rs b/examples/optional_redirect/src/tests.rs index 3c916726..21137bba 100644 --- a/examples/optional_redirect/src/tests.rs +++ b/examples/optional_redirect/src/tests.rs @@ -2,14 +2,14 @@ use rocket::local::blocking::Client; use rocket::http::Status; fn test_200(uri: &str, expected_body: &str) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get(uri).dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string(), Some(expected_body.to_string())); } fn test_303(uri: &str, expected_location: &str) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get(uri).dispatch(); let location_headers: Vec<_> = response.headers().get("Location").collect(); assert_eq!(response.status(), Status::SeeOther); diff --git a/examples/pastebin/src/tests.rs b/examples/pastebin/src/tests.rs index cf27a518..6bfe6a85 100644 --- a/examples/pastebin/src/tests.rs +++ b/examples/pastebin/src/tests.rs @@ -8,7 +8,7 @@ fn extract_id(from: &str) -> Option { #[test] fn check_index() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Ensure the index returns what we expect. let response = client.get("/").dispatch(); @@ -32,7 +32,7 @@ fn download_paste(client: &Client, id: &str) -> String { #[test] fn pasting() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); // Do a trivial upload, just to make sure it works. let body_1 = "Hello, world!"; diff --git a/examples/query_params/src/tests.rs b/examples/query_params/src/tests.rs index 03931c88..0ad92d6e 100644 --- a/examples/query_params/src/tests.rs +++ b/examples/query_params/src/tests.rs @@ -4,7 +4,7 @@ use rocket::http::Status; macro_rules! run_test { ($query:expr, |$response:ident| $body:expr) => ({ - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); #[allow(unused_mut)] let mut $response = client.get(format!("/hello{}", $query)).dispatch(); $body diff --git a/examples/ranking/src/tests.rs b/examples/ranking/src/tests.rs index cbdced5a..6f6f3dc7 100644 --- a/examples/ranking/src/tests.rs +++ b/examples/ranking/src/tests.rs @@ -1,7 +1,7 @@ use rocket::local::blocking::Client; fn test(uri: String, expected: String) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get(&uri).dispatch(); assert_eq!(response.into_string(), Some(expected)); } diff --git a/examples/raw_sqlite/src/tests.rs b/examples/raw_sqlite/src/tests.rs index 3ddf4707..df334da7 100644 --- a/examples/raw_sqlite/src/tests.rs +++ b/examples/raw_sqlite/src/tests.rs @@ -3,7 +3,7 @@ use rocket::local::blocking::Client; #[test] fn hello() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Rocketeer".into())); } diff --git a/examples/raw_upload/src/tests.rs b/examples/raw_upload/src/tests.rs index 89d37f5f..13ec8e3e 100644 --- a/examples/raw_upload/src/tests.rs +++ b/examples/raw_upload/src/tests.rs @@ -9,7 +9,7 @@ const UPLOAD_CONTENTS: &str = "Hey! I'm going to be uploaded. :D Yay!"; #[test] fn test_index() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let res = client.get("/").dispatch(); assert_eq!(res.into_string(), Some(super::index().to_string())); } @@ -21,7 +21,7 @@ fn test_raw_upload() { let _ = fs::remove_file(&upload_file); // Do the upload. Make sure we get the expected results. - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let res = client.post("/upload") .header(ContentType::Plain) .body(UPLOAD_CONTENTS) diff --git a/examples/redirect/src/tests.rs b/examples/redirect/src/tests.rs index 11973a06..5884dd32 100644 --- a/examples/redirect/src/tests.rs +++ b/examples/redirect/src/tests.rs @@ -3,7 +3,7 @@ use rocket::http::Status; fn client() -> Client { let rocket = rocket::ignite().mount("/", routes![super::root, super::login]); - Client::new(rocket).unwrap() + Client::tracked(rocket).unwrap() } #[test] diff --git a/examples/request_guard/src/main.rs b/examples/request_guard/src/main.rs index 20b17b0d..b0a4ec91 100644 --- a/examples/request_guard/src/main.rs +++ b/examples/request_guard/src/main.rs @@ -31,7 +31,7 @@ mod test { use rocket::http::Header; fn test_header_count<'h>(headers: Vec>) { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let mut req = client.get("/"); for header in headers.iter().cloned() { req.add_header(header); diff --git a/examples/request_local_state/src/tests.rs b/examples/request_local_state/src/tests.rs index 9dee2fe1..d9bb875f 100644 --- a/examples/request_local_state/src/tests.rs +++ b/examples/request_local_state/src/tests.rs @@ -5,7 +5,7 @@ use rocket::local::blocking::Client; #[test] fn test() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); client.get("/sync").dispatch(); let atomics = client.cargo().state::().unwrap(); diff --git a/examples/session/src/tests.rs b/examples/session/src/tests.rs index 97771d6b..51ace29f 100644 --- a/examples/session/src/tests.rs +++ b/examples/session/src/tests.rs @@ -23,7 +23,7 @@ fn login(client: &Client, user: &str, pass: &str) -> Option> { #[test] fn redirect_on_index() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.status(), Status::SeeOther); assert_eq!(response.headers().get_one("Location"), Some("/login")); @@ -31,7 +31,7 @@ fn redirect_on_index() { #[test] fn can_login() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/login").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -41,14 +41,14 @@ fn can_login() { #[test] fn login_fails() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); assert!(login(&client, "Seergio", "password").is_none()); assert!(login(&client, "Sergio", "idontknow").is_none()); } #[test] fn login_logout_succeeds() { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let login_cookie = login(&client, "Sergio", "password").expect("logged in"); // Ensure we're logged in. diff --git a/examples/state/src/tests.rs b/examples/state/src/tests.rs index d8bf17b9..8a022090 100644 --- a/examples/state/src/tests.rs +++ b/examples/state/src/tests.rs @@ -13,7 +13,7 @@ fn get_count(client: &Client) -> usize { #[test] fn test_count() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); // Count should start at 0. assert_eq!(get_count(&client), 0); diff --git a/examples/static_files/src/tests.rs b/examples/static_files/src/tests.rs index 8552cecb..939f6735 100644 --- a/examples/static_files/src/tests.rs +++ b/examples/static_files/src/tests.rs @@ -9,7 +9,7 @@ use super::rocket; fn test_query_file (path: &str, file: T, status: Status) where T: Into> { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get(path).dispatch(); assert_eq!(response.status(), status); diff --git a/examples/stream/src/tests.rs b/examples/stream/src/tests.rs index 1e9e4728..6fe44846 100644 --- a/examples/stream/src/tests.rs +++ b/examples/stream/src/tests.rs @@ -5,7 +5,7 @@ use rocket::local::blocking::Client; #[test] fn test_root() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let res = client.get("/").dispatch(); // Check that we have exactly 25,000 'a'. @@ -24,7 +24,7 @@ fn test_file() { file.write_all(CONTENTS.as_bytes()).expect("write to big_file"); // Get the big file contents, hopefully. - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let res = client.get("/big_file").dispatch(); assert_eq!(res.into_string(), Some(CONTENTS.into())); diff --git a/examples/tera_templates/src/tests.rs b/examples/tera_templates/src/tests.rs index ad9b6aad..d6602276 100644 --- a/examples/tera_templates/src/tests.rs +++ b/examples/tera_templates/src/tests.rs @@ -6,7 +6,7 @@ use rocket_contrib::templates::Template; macro_rules! dispatch { ($method:expr, $path:expr, |$client:ident, $response:ident| $body:expr) => ({ - let $client = Client::new(rocket()).unwrap(); + let $client = Client::tracked(rocket()).unwrap(); let $response = $client.req($method, $path).dispatch(); $body }) diff --git a/examples/testing/src/async_required.rs b/examples/testing/src/async_required.rs index fa46e2ee..3d29a13f 100644 --- a/examples/testing/src/async_required.rs +++ b/examples/testing/src/async_required.rs @@ -26,7 +26,7 @@ mod test { async fn test_rendezvous() { use rocket::local::asynchronous::Client; - let client = Client::new(rocket()).await.unwrap(); + let client = Client::tracked(rocket()).await.unwrap(); let req = client.get("/barrier"); let (r1, r2) = rocket::tokio::join!(req.clone().dispatch(), req.dispatch()); diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index d69f8710..1576a7e4 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -21,7 +21,7 @@ mod test { fn test_hello() { use rocket::local::blocking::Client; - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string(), Some("Hello, world!".into())); diff --git a/examples/tls/src/tests.rs b/examples/tls/src/tests.rs index 104863a2..a4951f4d 100644 --- a/examples/tls/src/tests.rs +++ b/examples/tls/src/tests.rs @@ -2,7 +2,7 @@ use rocket::local::blocking::Client; #[test] fn hello_world() { - let client = Client::new(super::rocket()).unwrap(); + let client = Client::tracked(super::rocket()).unwrap(); let response = client.get("/").dispatch(); assert_eq!(response.into_string(), Some("Hello, world!".into())); } diff --git a/examples/todo/src/tests.rs b/examples/todo/src/tests.rs index a184945b..94a588a3 100644 --- a/examples/todo/src/tests.rs +++ b/examples/todo/src/tests.rs @@ -17,7 +17,7 @@ macro_rules! run_test { rocket::async_test(async move { let rocket = super::rocket(); - let $client = Client::new(rocket).await.expect("Rocket client"); + let $client = Client::tracked(rocket).await.expect("Rocket client"); let db = super::DbConn::get_one($client.cargo()).await; let $conn = db.expect("failed to get database connection for testing"); Task::delete_all(&$conn).await.expect("failed to delete all tasks for testing"); diff --git a/examples/uuid/src/tests.rs b/examples/uuid/src/tests.rs index 77f49a55..4ba39c64 100644 --- a/examples/uuid/src/tests.rs +++ b/examples/uuid/src/tests.rs @@ -3,13 +3,13 @@ use rocket::local::blocking::Client; use rocket::http::Status; fn test(uri: &str, expected: &str) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let res = client.get(uri).dispatch(); assert_eq!(res.into_string(), Some(expected.into())); } fn test_404(uri: &str) { - let client = Client::new(rocket()).unwrap(); + let client = Client::tracked(rocket()).unwrap(); let res = client.get(uri).dispatch(); assert_eq!(res.status(), Status::NotFound); } diff --git a/site/guide/8-testing.md b/site/guide/8-testing.md index 671c9aab..5bfc2de1 100644 --- a/site/guide/8-testing.md +++ b/site/guide/8-testing.md @@ -24,7 +24,7 @@ instance. Usage is straightforward: ```rust # use rocket::local::blocking::Client; # let rocket = rocket::ignite(); - let client = Client::new(rocket).unwrap(); + let client = Client::tracked(rocket).unwrap(); # let _ = client; ``` @@ -33,7 +33,7 @@ instance. Usage is straightforward: ```rust # use rocket::local::blocking::Client; # let rocket = rocket::ignite(); - # let client = Client::new(rocket).unwrap(); + # let client = Client::tracked(rocket).unwrap(); let req = client.get("/"); # let _ = req; ``` @@ -43,7 +43,7 @@ instance. Usage is straightforward: ```rust # use rocket::local::blocking::Client; # let rocket = rocket::ignite(); - # let client = Client::new(rocket).unwrap(); + # let client = Client::tracked(rocket).unwrap(); # let req = client.get("/"); let response = req.dispatch(); # let _ = response; @@ -101,7 +101,7 @@ use rocket::local::blocking::Client; use rocket::http::{ContentType, Status}; let rocket = rocket::ignite().mount("/", routes![hello]); -let client = Client::new(rocket).expect("valid rocket instance"); +let client = Client::tracked(rocket).expect("valid rocket instance"); let mut response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -168,7 +168,7 @@ testing: we _want_ our tests to panic when something goes wrong. # fn rocket() -> rocket::Rocket { rocket::ignite() } # use rocket::local::blocking::Client; -let client = Client::new(rocket()).expect("valid rocket instance"); +let client = Client::tracked(rocket()).expect("valid rocket instance"); ``` Then, we create a new `GET /` request and dispatch it, getting back our @@ -177,7 +177,7 @@ application's response: ```rust # fn rocket() -> rocket::Rocket { rocket::ignite() } # use rocket::local::blocking::Client; -# let client = Client::new(rocket()).expect("valid rocket instance"); +# let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client.get("/").dispatch(); ``` @@ -199,7 +199,7 @@ We do this by checking the `Response` object directly: use rocket::http::{ContentType, Status}; # # let rocket = rocket::ignite().mount("/", routes![hello]); -# let client = Client::new(rocket).expect("valid rocket instance"); +# let client = Client::tracked(rocket).expect("valid rocket instance"); # let mut response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); @@ -232,7 +232,7 @@ mod test { #[test] # */ pub fn hello_world() { - let client = Client::new(rocket()).expect("valid rocket instance"); + let client = Client::tracked(rocket()).expect("valid rocket instance"); let mut response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.into_string(), Some("Hello, world!".into()));