WIP - ActixWeb multi-tenant blog and newsletter API server. Originally forked from LukeMathWalker/zero-to-production.

Update to latest version of actix-web, tracing-bunyan-formatter and tracing-subscriber. Use tokio macros instead of actix-web's macros.

+96 -106
+66 -80
Cargo.lock
··· 4 4 5 5 [[package]] 6 6 name = "actix-codec" 7 - version = "0.4.0" 7 + version = "0.4.1" 8 8 source = "registry+https://github.com/rust-lang/crates.io-index" 9 - checksum = "1d5dbeb2d9e51344cb83ca7cc170f1217f9fe25bfc50160e6e200b5c31c1019a" 9 + checksum = "13895df506faee81e423febbae3a33b27fca71831b96bb3d60adf16ebcfea952" 10 10 dependencies = [ 11 11 "bitflags", 12 12 "bytes", 13 13 "futures-core", 14 14 "futures-sink", 15 15 "log", 16 + "memchr", 16 17 "pin-project-lite", 17 18 "tokio", 18 19 "tokio-util", ··· 20 21 21 22 [[package]] 22 23 name = "actix-http" 23 - version = "3.0.0-beta.10" 24 + version = "3.0.0-beta.16" 24 25 source = "registry+https://github.com/rust-lang/crates.io-index" 25 - checksum = "dd38a862fa7fead2b47ee55e550982aba583ebc7365ccf0155b49934ad6f16f9" 26 + checksum = "6294c508c1413346857838356f53f45dbfd257ea31dca19470d9ce78750a7d37" 26 27 dependencies = [ 27 28 "actix-codec", 28 29 "actix-rt", 29 30 "actix-service", 30 - "actix-tls", 31 31 "actix-utils", 32 32 "ahash", 33 33 "base64", ··· 39 39 "encoding_rs", 40 40 "flate2", 41 41 "futures-core", 42 - "futures-util", 42 + "futures-task", 43 43 "h2", 44 44 "http", 45 45 "httparse", 46 + "httpdate", 46 47 "itoa", 47 48 "language-tags", 48 49 "local-channel", 49 50 "log", 50 51 "mime", 51 - "once_cell", 52 52 "percent-encoding", 53 - "pin-project", 54 53 "pin-project-lite", 55 54 "rand 0.8.4", 56 - "regex", 57 - "serde", 58 55 "sha-1", 59 56 "smallvec", 60 - "time 0.2.27", 61 - "tokio", 62 57 "zstd", 63 58 ] 64 59 65 60 [[package]] 66 61 name = "actix-macros" 67 - version = "0.2.1" 62 + version = "0.2.3" 68 63 source = "registry+https://github.com/rust-lang/crates.io-index" 69 - checksum = "c2f86cd6857c135e6e9fe57b1619a88d1f94a7df34c00e11fe13e64fd3438837" 64 + checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" 70 65 dependencies = [ 71 66 "quote", 72 67 "syn", ··· 74 69 75 70 [[package]] 76 71 name = "actix-router" 77 - version = "0.5.0-beta.2" 72 + version = "0.5.0-beta.3" 78 73 source = "registry+https://github.com/rust-lang/crates.io-index" 79 - checksum = "36b95ce0d76d1aa2f98b681702807475ade0f99bd4552546a6843a966d42ea3d" 74 + checksum = "ddd9f117b910fbcce6e9f45092ffd4ff017785a346d09e2d4fd049f4e20384f4" 80 75 dependencies = [ 81 76 "bytestring", 82 77 "firestorm", ··· 88 83 89 84 [[package]] 90 85 name = "actix-rt" 91 - version = "2.2.0" 86 + version = "2.5.0" 92 87 source = "registry+https://github.com/rust-lang/crates.io-index" 93 - checksum = "bc7d7cd957c9ed92288a7c3c96af81fa5291f65247a76a34dac7b6af74e52ba0" 88 + checksum = "05c2f80ce8d0c990941c7a7a931f69fd0701b76d521f8d36298edf59cd3fbf1f" 94 89 dependencies = [ 95 90 "actix-macros", 96 91 "futures-core", ··· 99 94 100 95 [[package]] 101 96 name = "actix-server" 102 - version = "2.0.0-beta.5" 97 + version = "2.0.0-rc.1" 103 98 source = "registry+https://github.com/rust-lang/crates.io-index" 104 - checksum = "26369215fcc3b0176018b3b68756a8bcc275bb000e6212e454944913a1f9bf87" 99 + checksum = "78c9b22794b8af1c2e02434873ef858f2a7db40dbbf861ce77a04cd81ac6b767" 105 100 dependencies = [ 106 101 "actix-rt", 107 102 "actix-service", 108 103 "actix-utils", 109 104 "futures-core", 105 + "futures-util", 110 106 "log", 111 - "mio", 107 + "mio 0.8.0", 112 108 "num_cpus", 113 - "slab", 109 + "socket2", 114 110 "tokio", 115 111 ] 116 112 ··· 126 122 ] 127 123 128 124 [[package]] 129 - name = "actix-tls" 130 - version = "3.0.0-beta.5" 131 - source = "registry+https://github.com/rust-lang/crates.io-index" 132 - checksum = "65b7bb60840962ef0332f7ea01a57d73a24d2cb663708511ff800250bbfef569" 133 - dependencies = [ 134 - "actix-codec", 135 - "actix-rt", 136 - "actix-service", 137 - "actix-utils", 138 - "derive_more", 139 - "futures-core", 140 - "http", 141 - "log", 142 - "tokio-util", 143 - ] 144 - 145 - [[package]] 146 125 name = "actix-utils" 147 126 version = "3.0.0" 148 127 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 154 133 155 134 [[package]] 156 135 name = "actix-web" 157 - version = "4.0.0-beta.9" 136 + version = "4.0.0-beta.15" 158 137 source = "registry+https://github.com/rust-lang/crates.io-index" 159 - checksum = "d34aa2b23ec9c7c9a799b3cf9258f67c91b18ac3f0f5f484e175c7ac46739bb5" 138 + checksum = "4609cf57246040316642d4dc4c03d7f3d4a083a892122829dbd9e6ec8db7cd67" 160 139 dependencies = [ 161 140 "actix-codec", 162 141 "actix-http", ··· 172 151 "cfg-if", 173 152 "cookie", 174 153 "derive_more", 175 - "either", 176 154 "encoding_rs", 177 155 "futures-core", 178 156 "futures-util", ··· 182 160 "mime", 183 161 "once_cell", 184 162 "paste", 185 - "pin-project", 163 + "pin-project-lite", 186 164 "regex", 187 165 "serde", 188 166 "serde_json", 189 167 "serde_urlencoded", 190 168 "smallvec", 191 169 "socket2", 192 - "time 0.2.27", 170 + "time 0.3.5", 193 171 "url", 194 172 ] 195 173 196 174 [[package]] 197 175 name = "actix-web-codegen" 198 - version = "0.5.0-beta.4" 176 + version = "0.5.0-beta.6" 199 177 source = "registry+https://github.com/rust-lang/crates.io-index" 200 - checksum = "4a11fd6f322120a74b23327e778ef0a4950b1f44a2b76468a69316a150f5c6dd" 178 + checksum = "30a90b7f6c2fde9a1fe3df4da758c2c3c9d620dfa3eae4da0b6925dc0a13444a" 201 179 dependencies = [ 202 180 "actix-router", 203 181 "proc-macro2", ··· 885 863 886 864 [[package]] 887 865 name = "h2" 888 - version = "0.3.6" 866 + version = "0.3.9" 889 867 source = "registry+https://github.com/rust-lang/crates.io-index" 890 - checksum = "6c06815895acec637cd6ed6e9662c935b866d20a106f8361892893a7d9234964" 868 + checksum = "8f072413d126e57991455e0a922b31e4c8ba7c2ffbebf6b78b4f8521397d65cd" 891 869 dependencies = [ 892 870 "bytes", 893 871 "fnv", ··· 1207 1185 1208 1186 [[package]] 1209 1187 name = "matchers" 1210 - version = "0.0.1" 1188 + version = "0.1.0" 1211 1189 source = "registry+https://github.com/rust-lang/crates.io-index" 1212 - checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" 1190 + checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" 1213 1191 dependencies = [ 1214 1192 "regex-automata", 1215 1193 ] ··· 1264 1242 version = "0.7.13" 1265 1243 source = "registry+https://github.com/rust-lang/crates.io-index" 1266 1244 checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" 1245 + dependencies = [ 1246 + "libc", 1247 + "log", 1248 + "miow", 1249 + "ntapi", 1250 + "winapi", 1251 + ] 1252 + 1253 + [[package]] 1254 + name = "mio" 1255 + version = "0.8.0" 1256 + source = "registry+https://github.com/rust-lang/crates.io-index" 1257 + checksum = "ba272f85fa0b41fc91872be579b3bbe0f56b792aa361a380eb669469f68dafb2" 1267 1258 dependencies = [ 1268 1259 "libc", 1269 1260 "log", ··· 2169 2160 ] 2170 2161 2171 2162 [[package]] 2163 + name = "time" 2164 + version = "0.3.5" 2165 + source = "registry+https://github.com/rust-lang/crates.io-index" 2166 + checksum = "41effe7cfa8af36f439fac33861b66b049edc6f9a32331e2312660529c1c24ad" 2167 + dependencies = [ 2168 + "itoa", 2169 + "libc", 2170 + ] 2171 + 2172 + [[package]] 2172 2173 name = "time-macros" 2173 2174 version = "0.1.1" 2174 2175 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2216 2217 "bytes", 2217 2218 "libc", 2218 2219 "memchr", 2219 - "mio", 2220 + "mio 0.7.13", 2220 2221 "num_cpus", 2221 2222 "once_cell", 2222 2223 "parking_lot", ··· 2294 2295 2295 2296 [[package]] 2296 2297 name = "tracing-actix-web" 2297 - version = "0.4.0-beta.13" 2298 + version = "0.5.0-beta.6" 2298 2299 source = "registry+https://github.com/rust-lang/crates.io-index" 2299 - checksum = "64be7b20ed6dfa15ddd89a964f28724360f9769eee60712666f20924d9a82c35" 2300 + checksum = "940f76971e1a1ca2c64e8f915aec14acbc61a5b1c1c177dedf64eb8dd41b204d" 2300 2301 dependencies = [ 2301 2302 "actix-web", 2303 + "pin-project", 2302 2304 "tracing", 2303 2305 "tracing-futures", 2304 2306 "uuid", ··· 2317 2319 2318 2320 [[package]] 2319 2321 name = "tracing-bunyan-formatter" 2320 - version = "0.2.6" 2322 + version = "0.3.1" 2321 2323 source = "registry+https://github.com/rust-lang/crates.io-index" 2322 - checksum = "c408910c9b7eabc0215fe2b4a89f8ec95581a91cea1f7619f7c78caf14cbc2a1" 2324 + checksum = "1cb2ad6aa9b1c637d84c54db002275bbf72a7f3c6fed80f8b33f5af0c39027e9" 2323 2325 dependencies = [ 2324 - "chrono", 2325 2326 "gethostname", 2326 2327 "log", 2327 2328 "serde", 2328 2329 "serde_json", 2330 + "time 0.3.5", 2329 2331 "tracing", 2330 2332 "tracing-core", 2331 2333 "tracing-log", ··· 2363 2365 ] 2364 2366 2365 2367 [[package]] 2366 - name = "tracing-serde" 2367 - version = "0.1.2" 2368 - source = "registry+https://github.com/rust-lang/crates.io-index" 2369 - checksum = "fb65ea441fbb84f9f6748fd496cf7f63ec9af5bca94dd86456978d055e8eb28b" 2370 - dependencies = [ 2371 - "serde", 2372 - "tracing-core", 2373 - ] 2374 - 2375 - [[package]] 2376 2368 name = "tracing-subscriber" 2377 - version = "0.2.24" 2369 + version = "0.3.4" 2378 2370 source = "registry+https://github.com/rust-lang/crates.io-index" 2379 - checksum = "fdd0568dbfe3baf7048b7908d2b32bca0d81cd56bec6d2a8f894b01d74f86be3" 2371 + checksum = "d5e6136799e1079699e0d9784c883e03af55cf6a1bee48fe1d79ca552c1bc36f" 2380 2372 dependencies = [ 2381 2373 "ansi_term", 2382 - "chrono", 2383 2374 "lazy_static", 2384 2375 "matchers", 2385 2376 "regex", 2386 - "serde", 2387 - "serde_json", 2388 2377 "sharded-slab", 2389 2378 "smallvec", 2390 2379 "thread_local", 2391 2380 "tracing", 2392 2381 "tracing-core", 2393 2382 "tracing-log", 2394 - "tracing-serde", 2395 2383 ] 2396 2384 2397 2385 [[package]] ··· 2700 2688 name = "zero2prod" 2701 2689 version = "0.1.0" 2702 2690 dependencies = [ 2703 - "actix-http", 2704 - "actix-rt", 2705 2691 "actix-web", 2706 2692 "anyhow", 2707 2693 "argon2", ··· 2737 2723 2738 2724 [[package]] 2739 2725 name = "zstd" 2740 - version = "0.7.0+zstd.1.4.9" 2726 + version = "0.9.1+zstd.1.5.1" 2741 2727 source = "registry+https://github.com/rust-lang/crates.io-index" 2742 - checksum = "9428752481d8372e15b1bf779ea518a179ad6c771cca2d2c60e4fbff3cc2cd52" 2728 + checksum = "538b8347df9257b7fbce37677ef7535c00a3c7bf1f81023cc328ed7fe4b41de8" 2743 2729 dependencies = [ 2744 2730 "zstd-safe", 2745 2731 ] 2746 2732 2747 2733 [[package]] 2748 2734 name = "zstd-safe" 2749 - version = "3.1.0+zstd.1.4.9" 2735 + version = "4.1.2+zstd.1.5.1" 2750 2736 source = "registry+https://github.com/rust-lang/crates.io-index" 2751 - checksum = "5aa1926623ad7fe406e090555387daf73db555b948134b4d73eac5eb08fb666d" 2737 + checksum = "9fb4cfe2f6e6d35c5d27ecd9d256c4b6f7933c4895654917460ec56c29336cc1" 2752 2738 dependencies = [ 2753 2739 "libc", 2754 2740 "zstd-sys", ··· 2756 2742 2757 2743 [[package]] 2758 2744 name = "zstd-sys" 2759 - version = "1.5.0+zstd.1.4.9" 2745 + version = "1.6.2+zstd.1.5.1" 2760 2746 source = "registry+https://github.com/rust-lang/crates.io-index" 2761 - checksum = "4e6c094340240369025fc6b731b054ee2a834328fa584310ac96aa4baebdc465" 2747 + checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" 2762 2748 dependencies = [ 2763 2749 "cc", 2764 2750 "libc",
+5 -7
Cargo.toml
··· 13 13 name = "zero2prod" 14 14 15 15 [dependencies] 16 - actix-http = "=3.0.0-beta.10" 17 - actix-web = "=4.0.0-beta.9" 16 + actix-web = "=4.0.0-beta.15" 17 + tokio = { version = "1", features = ["macros"] } 18 18 19 19 serde = "1.0.115" 20 20 config = { version = "0.10.1", default-features = false, features = ["yaml"] } ··· 25 25 log = "0.4" 26 26 tracing = "0.1.19" 27 27 tracing-futures = "0.2.4" 28 - tracing-subscriber = { version = "0.2.12", features = ["registry", "env-filter"] } 29 - tracing-bunyan-formatter = "0.2.2" 28 + tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] } 29 + tracing-bunyan-formatter = "0.3" 30 30 tracing-log = "0.1.1" 31 31 thiserror = "1.0.24" 32 32 serde-aux = "1.0.1" 33 33 unicode-segmentation = "1.7.1" 34 34 validator = "0.12.0" 35 35 rand = { version = "0.8", features= ["std_rng"] } 36 - tracing-actix-web = "0.4.0-beta.12" 36 + tracing-actix-web = "0.5.0-beta.6" 37 37 anyhow = "1.0.40" 38 38 base64 = "0.13.0" 39 39 argon2 = { version = "0.3", features = ["std"] } ··· 46 46 fake = "~2.3.0" 47 47 wiremock = "0.5" 48 48 serde_json = "1.0.61" 49 - actix-rt = "2" 50 - tokio = { version = "1", features = ["macros"] } 51 49 linkify = "0.5.0"
+1 -1
src/main.rs
··· 2 2 use zero2prod::startup::Application; 3 3 use zero2prod::telemetry::{get_subscriber, init_subscriber}; 4 4 5 - #[actix_web::main] 5 + #[tokio::main] 6 6 async fn main() -> std::io::Result<()> { 7 7 let subscriber = get_subscriber("zero2prod".into(), "info".into(), std::io::stdout); 8 8 init_subscriber(subscriber);
+4 -1
src/routes/newsletters.rs
··· 2 2 use crate::email_client::EmailClient; 3 3 use crate::routes::error_chain_fmt; 4 4 use crate::telemetry::spawn_blocking_with_tracing; 5 - use actix_web::http::{HeaderMap, HeaderValue, StatusCode}; 5 + use actix_web::http::{ 6 + header::{HeaderMap, HeaderValue}, 7 + StatusCode, 8 + }; 6 9 use actix_web::{web, HttpResponse, ResponseError}; 7 10 use anyhow::Context; 8 11 use argon2::{Argon2, PasswordHash, PasswordVerifier};
+6 -3
src/telemetry.rs
··· 12 12 /// 13 13 /// We are using `impl Subscriber` as return type to avoid having to spell out the actual 14 14 /// type of the returned subscriber, which is indeed quite complex. 15 - pub fn get_subscriber( 15 + pub fn get_subscriber<Sink>( 16 16 name: String, 17 17 env_filter: String, 18 - sink: impl MakeWriter + Send + Sync + 'static, 19 - ) -> impl Subscriber + Sync + Send { 18 + sink: Sink, 19 + ) -> impl Subscriber + Sync + Send 20 + where 21 + Sink: for<'a> MakeWriter<'a> + Send + Sync + 'static, 22 + { 20 23 let env_filter = 21 24 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter)); 22 25 let formatting_layer = BunyanFormattingLayer::new(name, sink);
+1 -1
tests/api/health_check.rs
··· 1 1 use crate::helpers::spawn_app; 2 2 3 - #[actix_rt::test] 3 + #[tokio::test] 4 4 async fn health_check_works() { 5 5 // Arrange 6 6 let app = spawn_app().await;
+3 -3
tests/api/newsletter.rs
··· 128 128 } 129 129 } 130 130 131 - #[actix_rt::test] 131 + #[tokio::test] 132 132 async fn requests_missing_authorization_are_rejected() { 133 133 // Arrange 134 134 let app = spawn_app().await; ··· 154 154 ); 155 155 } 156 156 157 - #[actix_rt::test] 157 + #[tokio::test] 158 158 async fn non_existing_user_is_rejected() { 159 159 // Arrange 160 160 let app = spawn_app().await; ··· 184 184 ); 185 185 } 186 186 187 - #[actix_rt::test] 187 + #[tokio::test] 188 188 async fn invalid_password_is_rejected() { 189 189 // Arrange 190 190 let app = spawn_app().await;
+7 -7
tests/api/subscriptions.rs
··· 2 2 use wiremock::matchers::{method, path}; 3 3 use wiremock::{Mock, ResponseTemplate}; 4 4 5 - #[actix_rt::test] 5 + #[tokio::test] 6 6 async fn subscribe_returns_a_200_for_valid_form_data() { 7 7 // Arrange 8 8 let app = spawn_app().await; ··· 21 21 assert_eq!(200, response.status().as_u16()); 22 22 } 23 23 24 - #[actix_rt::test] 24 + #[tokio::test] 25 25 async fn subscribe_persists_the_new_subscriber() { 26 26 // Arrange 27 27 let app = spawn_app().await; ··· 41 41 assert_eq!(saved.status, "pending_confirmation"); 42 42 } 43 43 44 - #[actix_rt::test] 44 + #[tokio::test] 45 45 async fn subscribe_fails_if_there_is_a_fatal_database_error() { 46 46 // Arrange 47 47 let app = spawn_app().await; ··· 59 59 assert_eq!(response.status().as_u16(), 500); 60 60 } 61 61 62 - #[actix_rt::test] 62 + #[tokio::test] 63 63 async fn subscribe_sends_a_confirmation_email_for_valid_data() { 64 64 // Arrange 65 65 let app = spawn_app().await; ··· 79 79 // Mock asserts on drop 80 80 } 81 81 82 - #[actix_rt::test] 82 + #[tokio::test] 83 83 async fn subscribe_sends_a_confirmation_email_with_a_link() { 84 84 // Arrange 85 85 let app = spawn_app().await; ··· 102 102 assert_eq!(confirmation_links.html, confirmation_links.plain_text); 103 103 } 104 104 105 - #[actix_rt::test] 105 + #[tokio::test] 106 106 async fn subscribe_returns_a_400_when_data_is_missing() { 107 107 // Arrange 108 108 let app = spawn_app().await; ··· 127 127 } 128 128 } 129 129 130 - #[actix_rt::test] 130 + #[tokio::test] 131 131 async fn subscribe_returns_a_400_when_fields_are_present_but_invalid() { 132 132 // Arrange 133 133 let app = spawn_app().await;
+3 -3
tests/api/subscriptions_confirm.rs
··· 2 2 use wiremock::matchers::{method, path}; 3 3 use wiremock::{Mock, ResponseTemplate}; 4 4 5 - #[actix_rt::test] 5 + #[tokio::test] 6 6 async fn confirmations_without_token_are_rejected_with_a_400() { 7 7 // Arrange 8 8 let app = spawn_app().await; ··· 16 16 assert_eq!(response.status().as_u16(), 400); 17 17 } 18 18 19 - #[actix_rt::test] 19 + #[tokio::test] 20 20 async fn the_link_returned_by_subscribe_returns_a_200_if_called() { 21 21 // Arrange 22 22 let app = spawn_app().await; ··· 39 39 assert_eq!(response.status().as_u16(), 200); 40 40 } 41 41 42 - #[actix_rt::test] 42 + #[tokio::test] 43 43 async fn clicking_on_the_confirmation_link_confirms_a_subscriber() { 44 44 // Arrange 45 45 let app = spawn_app().await;