From f14ed30cedde89f3c76b62c535297600d5232e9e Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Mon, 24 Jul 2023 21:06:40 +0700 Subject: [PATCH 1/7] Init --- cache/redis-cache-middleware/Cargo.toml | 12 ++ cache/redis-cache-middleware/README.md | 34 +++++ cache/redis-cache-middleware/src/main.rs | 162 +++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 cache/redis-cache-middleware/Cargo.toml create mode 100644 cache/redis-cache-middleware/README.md create mode 100644 cache/redis-cache-middleware/src/main.rs diff --git a/cache/redis-cache-middleware/Cargo.toml b/cache/redis-cache-middleware/Cargo.toml new file mode 100644 index 00000000..ab2e2543 --- /dev/null +++ b/cache/redis-cache-middleware/Cargo.toml @@ -0,0 +1,12 @@ +[package] +authors = ["Jatutep Bunupuradah "] +name = "main" +version = "0.1.0" +edition = "2021" + +[dependencies] +actix-web = "*" +actix-rt = "*" +env_logger = "*" +actix-web-lab = "*" +redis = { version = "*", features = ["tls-native-tls"] } diff --git a/cache/redis-cache-middleware/README.md b/cache/redis-cache-middleware/README.md new file mode 100644 index 00000000..fb7adbf7 --- /dev/null +++ b/cache/redis-cache-middleware/README.md @@ -0,0 +1,34 @@ +# Redis Cache Middleware + +This project demonstrates how to implement Redis cache middleware to handle cache responses synchronously. +The application should be able to function properly even if Redis is not running; however, the caching process will be disabled in such cases. + + +## Setting up +Configure the environment variable `REDIS_HOST`, or if not set, the default host `redis://localhost:4379` will be used. TLS is supported using the `rediss://` protocol. +Run the server using `cargo run`. + +## Endpoints + +### `GET /fibonacci/{number}` + +To test the demo, send a GET request to `/fibonacci/{number}`, where {number} is a positive integer of type u64. + +## Request Directives + +Setting `Cache-Control: no-cache` will return the most up-to-date response while still caching it. This will always yield a `miss` cache status. +Using `Cache-Control: no-store` will prevent caching, ensuring you always receive the latest response. + +## Verify Redis Contents + +When making the first GET request to `/fibonacci/47`, it may take around 8 seconds to respond. +If Redis is running and the connection is established, subsequent requests should return the cached result immediately, but with content type `application/json`. + +## Known issues + +- Connecting to a remote Redis server might introduce significant overhead and could lead to prolonged connection times or even failure to reach the remote server. + +## Further implementations + +- Implement asynchronous insertion of cache responses. +- Explore using an in-memory datastore within the application process to reduce reliance on Redis. diff --git a/cache/redis-cache-middleware/src/main.rs b/cache/redis-cache-middleware/src/main.rs new file mode 100644 index 00000000..128167db --- /dev/null +++ b/cache/redis-cache-middleware/src/main.rs @@ -0,0 +1,162 @@ +use actix_web::{ + body::{self, MessageBody}, + dev::{ServiceRequest, ServiceResponse}, + http::{ + header::{CacheDirective, HeaderValue, CACHE_CONTROL, CACHE_STATUS, CONTENT_TYPE}, + Method, StatusCode, + }, + middleware, web, App, Error, HttpResponse, HttpServer, Responder, +}; +use actix_web_lab::middleware::{from_fn, Next}; +use redis::{Client as RedisClient, Commands, RedisError}; +use std::env; + +fn fib(n: u64) -> u64 { + if n <= 1 { + return n; + } + fib(n - 1) + fib(n - 2) +} + +async fn an_expensive_function(n: web::Path) -> impl Responder { + let result = fib(n.to_owned()); + HttpResponse::Ok().body(result.to_string()) +} + +#[actix_rt::main] +async fn main() -> std::result::Result<(), std::io::Error> { + env_logger::init(); + + let redis_client = + redis::Client::open(env::var("REDIS_HOST").unwrap_or("redis://localhost:6379".to_owned())) + .unwrap(); + + let listen_port: String = env::var("LISTEN_PORT").unwrap_or(8080.to_string()); + let listen_address: String = ["0.0.0.0", &listen_port].join(":"); + + println!("Server is listening at {}...", listen_address); + HttpServer::new(move || { + App::new() + .wrap(from_fn(cache_middleware)) + .app_data(redis_client.to_owned()) + .service(web::resource("/fibonacci/{n}").route(web::get().to(an_expensive_function))) + .wrap(middleware::Logger::default()) + }) + .bind(listen_address)? + .run() + .await?; + + Ok(()) +} + +async fn cache_middleware( + req: ServiceRequest, + next: Next, +) -> Result, Error> { + // Defining cache key based on requst path and query string + let key = format!("{}?{}", req.path(), req.query_string()); + println!("cache key: {key:?}"); + + // Get "Cache-Control" request header and get cache directive + let headers = req.headers().to_owned(); + let cache_directive = match headers.get(CACHE_CONTROL) { + Some(cache_control_header) => cache_control_header.to_str().unwrap_or(""), + None => "", + }; + + // Initialize Redis Client from App Data + let redis_client = req.app_data::(); + // This should always be Some, so let's unwrap + let mut redis_conn = redis_client.unwrap().get_connection(); + let redis_ok = redis_conn.is_ok(); + + // If Redis connection succeeded and request method is GET + // Also, if cache directive is not "no-cache" and not "no-store" + if redis_ok + && req.method() == Method::GET + && cache_directive != CacheDirective::NoCache.to_string() + && cache_directive != CacheDirective::NoStore.to_string() + { + // Unwrap the connection + let redis_conn = redis_conn.as_mut().unwrap(); + + // Try to get the cached response by defined key + let cached_response: Result, RedisError> = redis_conn.get(key.to_owned()); + if let Err(e) = cached_response { + // If cache cannot be deserialized + println!("cache get error: {}", e); + } else if cached_response.as_ref().unwrap().is_empty() { + // If cache body is empty + println!("cache not found"); + } else { + // If cache is found + println!("cache found"); + + // Prepare response body + let res = HttpResponse::new(StatusCode::OK).set_body(cached_response.unwrap()); + let mut res = ServiceResponse::new(req.request().to_owned(), res); + + // Define content-type and headers here + res.headers_mut() + .append(CONTENT_TYPE, HeaderValue::from_static("application/json")); + res.headers_mut() + .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); + res.headers_mut() + .append(CACHE_STATUS, HeaderValue::from_static("hit")); + + return Ok(res); + } + } + + // If Redis connection fails or cache could not be found + // Call the next service + let res = next.call(req).await?; + + // deconstruct response into parts + let (req, res) = res.into_parts(); + let (res, body) = res.into_parts(); + + // Convert body to Bytes + let body = body::to_bytes(body).await.ok().unwrap(); + // Use bytes directly for caching instead of converting to a String + let res_body_enc = body.to_vec(); + + // Prepare response body + let res = res.set_body(res_body_enc.to_owned()); + let mut res = ServiceResponse::new(req.to_owned(), res); + + // If a GET request succeeded and cache directive is not "no-store" + if req.method() == Method::GET + && StatusCode::is_success(&res.status()) + && cache_directive != CacheDirective::NoStore.to_string() + { + // Define response headers here + res.headers_mut() + .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); + res.headers_mut() + .append(CACHE_STATUS, HeaderValue::from_static("miss")); + + // If Redis connection succeeded + if redis_ok { + // Try to insert the response body into Redis + let mut redis_conn = redis_conn.unwrap(); + let insert = redis::Cmd::set(key, res_body_enc); + let insert = insert.query::(&mut redis_conn); + + if let Err(e) = insert { + // If cache insertion failed + println!("cache insert error: {}", e); + } else { + // This should print "cache insert success: OK" + println!("cache insert success: {}", insert.unwrap()); + } + } else if let Err(e) = redis_conn { + // If Redis connection failed + println!("RedisError: {}", e); + } + } else { + // If the request method is not "GET" or the operation failed or cache directive is "no-store" + println!("not inserting cache"); + } + Ok(res) +} From 28f0a9d8316ae947809951c5c18896e501e13768 Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Mon, 24 Jul 2023 21:13:14 +0700 Subject: [PATCH 2/7] Update README.md --- cache/redis-cache-middleware/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cache/redis-cache-middleware/README.md b/cache/redis-cache-middleware/README.md index fb7adbf7..cafb498a 100644 --- a/cache/redis-cache-middleware/README.md +++ b/cache/redis-cache-middleware/README.md @@ -16,8 +16,8 @@ To test the demo, send a GET request to `/fibonacci/{number}`, where {number} is ## Request Directives -Setting `Cache-Control: no-cache` will return the most up-to-date response while still caching it. This will always yield a `miss` cache status. -Using `Cache-Control: no-store` will prevent caching, ensuring you always receive the latest response. +- `Cache-Control: no-cache` will return the most up-to-date response while still caching it. This will always yield a `miss` cache status. +- `Cache-Control: no-store` will prevent caching, ensuring you always receive the latest response. ## Verify Redis Contents From da645894d966210739a3314529c570a17fbf4fc7 Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Mon, 24 Jul 2023 21:14:24 +0700 Subject: [PATCH 3/7] Update README.md --- cache/redis-cache-middleware/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/redis-cache-middleware/README.md b/cache/redis-cache-middleware/README.md index cafb498a..2192b378 100644 --- a/cache/redis-cache-middleware/README.md +++ b/cache/redis-cache-middleware/README.md @@ -22,7 +22,7 @@ To test the demo, send a GET request to `/fibonacci/{number}`, where {number} is ## Verify Redis Contents When making the first GET request to `/fibonacci/47`, it may take around 8 seconds to respond. -If Redis is running and the connection is established, subsequent requests should return the cached result immediately, but with content type `application/json`. +If Redis is running and the connection is established, subsequent requests should return the cached result immediately, a `hit` cache status will be returned, but with content type `application/json`. ## Known issues From 510697c8db42cb696d697372c89eb7439107880d Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Tue, 25 Jul 2023 18:58:10 +0700 Subject: [PATCH 4/7] Don't get connection on no-store directive If no-store directive is specified, don't get Redis connection so it won't have any bottleneck connecting to the Redis server. --- cache/redis-cache-middleware/src/main.rs | 111 ++++++++++++++--------- 1 file changed, 69 insertions(+), 42 deletions(-) diff --git a/cache/redis-cache-middleware/src/main.rs b/cache/redis-cache-middleware/src/main.rs index 128167db..85b853e1 100644 --- a/cache/redis-cache-middleware/src/main.rs +++ b/cache/redis-cache-middleware/src/main.rs @@ -11,15 +11,36 @@ use actix_web_lab::middleware::{from_fn, Next}; use redis::{Client as RedisClient, Commands, RedisError}; use std::env; -fn fib(n: u64) -> u64 { +fn fib_recursive(n: u64) -> u64 { if n <= 1 { return n; } - fib(n - 1) + fib(n - 2) + let a = n - 1; + let b = n - 2; + println!("{a} + {b}"); + fib_recursive(a) + fib_recursive(b) } +/* fn fib_iterative(n: u64) -> u64 { + let mut a = 0; + let mut b = 1; + let mut c; + + if n == 0 { + return a; + } + + for i in 2..=n { + print!("{i} "); + c = a + b; + a = b; + b = c; + } + b +} */ + async fn an_expensive_function(n: web::Path) -> impl Responder { - let result = fib(n.to_owned()); + let result = fib_recursive(n.to_owned()); HttpResponse::Ok().body(result.to_string()) } @@ -64,47 +85,47 @@ async fn cache_middleware( None => "", }; - // Initialize Redis Client from App Data - let redis_client = req.app_data::(); - // This should always be Some, so let's unwrap - let mut redis_conn = redis_client.unwrap().get_connection(); - let redis_ok = redis_conn.is_ok(); - - // If Redis connection succeeded and request method is GET - // Also, if cache directive is not "no-cache" and not "no-store" - if redis_ok - && req.method() == Method::GET - && cache_directive != CacheDirective::NoCache.to_string() + // If cache directive is not "no-cache" and not "no-store" + if cache_directive != CacheDirective::NoCache.to_string() && cache_directive != CacheDirective::NoStore.to_string() { - // Unwrap the connection - let redis_conn = redis_conn.as_mut().unwrap(); - - // Try to get the cached response by defined key - let cached_response: Result, RedisError> = redis_conn.get(key.to_owned()); - if let Err(e) = cached_response { - // If cache cannot be deserialized - println!("cache get error: {}", e); - } else if cached_response.as_ref().unwrap().is_empty() { - // If cache body is empty - println!("cache not found"); - } else { - // If cache is found - println!("cache found"); - - // Prepare response body - let res = HttpResponse::new(StatusCode::OK).set_body(cached_response.unwrap()); - let mut res = ServiceResponse::new(req.request().to_owned(), res); - - // Define content-type and headers here - res.headers_mut() - .append(CONTENT_TYPE, HeaderValue::from_static("application/json")); - res.headers_mut() - .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); - res.headers_mut() - .append(CACHE_STATUS, HeaderValue::from_static("hit")); - - return Ok(res); + // Initialize Redis Client from App Data + let redis_client = req.app_data::(); + // This should always be Some, so let's unwrap + let mut redis_conn = redis_client.unwrap().get_connection(); + let redis_ok = redis_conn.is_ok(); + + // If Redis connection succeeded and request method is GET + if redis_ok && req.method() == Method::GET { + // Unwrap the connection + let redis_conn = redis_conn.as_mut().unwrap(); + + // Try to get the cached response by defined key + let cached_response: Result, RedisError> = redis_conn.get(key.to_owned()); + if let Err(e) = cached_response { + // If cache cannot be deserialized + println!("cache get error: {}", e); + } else if cached_response.as_ref().unwrap().is_empty() { + // If cache body is empty + println!("cache not found"); + } else { + // If cache is found + println!("cache found"); + + // Prepare response body + let res = HttpResponse::new(StatusCode::OK).set_body(cached_response.unwrap()); + let mut res = ServiceResponse::new(req.request().to_owned(), res); + + // Define content-type and headers here + res.headers_mut() + .append(CONTENT_TYPE, HeaderValue::from_static("application/json")); + res.headers_mut() + .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); + res.headers_mut() + .append(CACHE_STATUS, HeaderValue::from_static("hit")); + + return Ok(res); + } } } @@ -136,6 +157,12 @@ async fn cache_middleware( res.headers_mut() .append(CACHE_STATUS, HeaderValue::from_static("miss")); + // Initialize Redis Client from App Data + let redis_client = req.app_data::(); + // This should always be Some, so let's unwrap + let redis_conn = redis_client.unwrap().get_connection(); + let redis_ok = redis_conn.is_ok(); + // If Redis connection succeeded if redis_ok { // Try to insert the response body into Redis From 9e018052197d53e197b918274a79e8caf84e3636 Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Sun, 6 Aug 2023 01:43:05 +0700 Subject: [PATCH 5/7] versioned dependencies --- cache/redis-cache-middleware/Cargo.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cache/redis-cache-middleware/Cargo.toml b/cache/redis-cache-middleware/Cargo.toml index ab2e2543..0d1ba4a9 100644 --- a/cache/redis-cache-middleware/Cargo.toml +++ b/cache/redis-cache-middleware/Cargo.toml @@ -1,12 +1,12 @@ [package] authors = ["Jatutep Bunupuradah "] -name = "main" +name = "actix-cache-middleware" version = "0.1.0" edition = "2021" [dependencies] -actix-web = "*" -actix-rt = "*" -env_logger = "*" -actix-web-lab = "*" -redis = { version = "*", features = ["tls-native-tls"] } +actix-web = "4.3.1" +actix-rt = "2.8.0" +env_logger = "0.10.0" +actix-web-lab = "0.19.1" +redis = { version = "0.23.1", features = ["tls-native-tls"] } From d14d4fba0df5ad1c0af5891ecdf41a419a16d961 Mon Sep 17 00:00:00 2001 From: Jatutep Bunupuradah Date: Sun, 6 Aug 2023 01:43:58 +0700 Subject: [PATCH 6/7] remove dead code, cache_max_age --- cache/redis-cache-middleware/src/main.rs | 46 +++++++++--------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/cache/redis-cache-middleware/src/main.rs b/cache/redis-cache-middleware/src/main.rs index 85b853e1..c30abd2a 100644 --- a/cache/redis-cache-middleware/src/main.rs +++ b/cache/redis-cache-middleware/src/main.rs @@ -15,30 +15,9 @@ fn fib_recursive(n: u64) -> u64 { if n <= 1 { return n; } - let a = n - 1; - let b = n - 2; - println!("{a} + {b}"); - fib_recursive(a) + fib_recursive(b) + fib_recursive(n - 1) + fib_recursive(n - 2) } -/* fn fib_iterative(n: u64) -> u64 { - let mut a = 0; - let mut b = 1; - let mut c; - - if n == 0 { - return a; - } - - for i in 2..=n { - print!("{i} "); - c = a + b; - a = b; - b = c; - } - b -} */ - async fn an_expensive_function(n: web::Path) -> impl Responder { let result = fib_recursive(n.to_owned()); HttpResponse::Ok().body(result.to_string()) @@ -70,12 +49,19 @@ async fn main() -> std::result::Result<(), std::io::Error> { Ok(()) } -async fn cache_middleware( +pub async fn cache_middleware( req: ServiceRequest, next: Next, ) -> Result, Error> { + // Adjust cache expiry here + const MAX_AGE: usize = 86400; + let cache_max_age = format!("max-age={MAX_AGE}").parse::().unwrap(); // Defining cache key based on requst path and query string - let key = format!("{}?{}", req.path(), req.query_string()); + let key = if req.query_string().is_empty() { + req.path().to_owned() + } else { + format!("{}?{}", req.path(), req.query_string()) + }; println!("cache key: {key:?}"); // Get "Cache-Control" request header and get cache directive @@ -88,6 +74,7 @@ async fn cache_middleware( // If cache directive is not "no-cache" and not "no-store" if cache_directive != CacheDirective::NoCache.to_string() && cache_directive != CacheDirective::NoStore.to_string() + && key != "/metrics" { // Initialize Redis Client from App Data let redis_client = req.app_data::(); @@ -119,8 +106,7 @@ async fn cache_middleware( // Define content-type and headers here res.headers_mut() .append(CONTENT_TYPE, HeaderValue::from_static("application/json")); - res.headers_mut() - .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); + res.headers_mut().append(CACHE_CONTROL, cache_max_age); res.headers_mut() .append(CACHE_STATUS, HeaderValue::from_static("hit")); @@ -150,10 +136,10 @@ async fn cache_middleware( if req.method() == Method::GET && StatusCode::is_success(&res.status()) && cache_directive != CacheDirective::NoStore.to_string() + && key != "/metrics" { // Define response headers here - res.headers_mut() - .append(CACHE_CONTROL, HeaderValue::from_static("max-age=86400")); + res.headers_mut().append(CACHE_CONTROL, cache_max_age); res.headers_mut() .append(CACHE_STATUS, HeaderValue::from_static("miss")); @@ -167,7 +153,9 @@ async fn cache_middleware( if redis_ok { // Try to insert the response body into Redis let mut redis_conn = redis_conn.unwrap(); - let insert = redis::Cmd::set(key, res_body_enc); + let insert = redis::Cmd::set_ex(key, res_body_enc, MAX_AGE); + // Or keep the cache forever: + // let insert = redis::Cmd::set(key, res_body_enc); let insert = insert.query::(&mut redis_conn); if let Err(e) = insert { From e28b0388363988584da44c302c047b50e416f21b Mon Sep 17 00:00:00 2001 From: Rob Ede Date: Sun, 6 Aug 2023 01:30:20 +0100 Subject: [PATCH 7/7] rename under cache/redis --- Cargo.lock | 28 +++++++++++++++++-- Cargo.toml | 1 + .../Cargo.toml | 12 ++++---- .../README.md | 0 .../src/main.rs | 7 +++-- 5 files changed, 37 insertions(+), 11 deletions(-) rename cache/{redis-cache-middleware => redis}/Cargo.toml (53%) rename cache/{redis-cache-middleware => redis}/README.md (100%) rename cache/{redis-cache-middleware => redis}/src/main.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 36c2c58b..d49149b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1908,6 +1908,16 @@ dependencies = [ "bytes 1.4.0", ] +[[package]] +name = "cache-redis" +version = "1.0.0" +dependencies = [ + "actix-web", + "actix-web-lab", + "env_logger", + "redis", +] + [[package]] name = "casbin" version = "2.0.9" @@ -5834,9 +5844,9 @@ dependencies = [ [[package]] name = "redis" -version = "0.23.0" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea8c51b5dc1d8e5fd3350ec8167f464ec0995e79f2e90a075b63371500d557f" +checksum = "ff5d95dd18a4d76650f0c2607ed8ebdbf63baf9cb934e1c233cd220c694db1d7" dependencies = [ "arc-swap", "async-trait", @@ -5845,11 +5855,14 @@ dependencies = [ "futures 0.3.28", "futures-util", "itoa 1.0.9", + "native-tls", "percent-encoding", "pin-project-lite 0.2.10", "ryu", "sha1_smol", + "socket2 0.4.9", "tokio 1.29.1", + "tokio-retry", "tokio-util 0.7.8", "url", ] @@ -7946,6 +7959,17 @@ dependencies = [ "tokio-sync", ] +[[package]] +name = "tokio-retry" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f57eb36ecbe0fc510036adff84824dd3c24bb781e21bfa67b69d556aa85214f" +dependencies = [ + "pin-project", + "rand 0.8.5", + "tokio 1.29.1", +] + [[package]] name = "tokio-rustls" version = "0.23.4" diff --git a/Cargo.toml b/Cargo.toml index d2564f91..9916c0a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "basics/state", "basics/static-files", "basics/todo", + "cache/redis", "cors/backend", "data-factory", "databases/diesel", diff --git a/cache/redis-cache-middleware/Cargo.toml b/cache/redis/Cargo.toml similarity index 53% rename from cache/redis-cache-middleware/Cargo.toml rename to cache/redis/Cargo.toml index 0d1ba4a9..29f50352 100644 --- a/cache/redis-cache-middleware/Cargo.toml +++ b/cache/redis/Cargo.toml @@ -1,12 +1,12 @@ [package] +name = "cache-redis" +version = "1.0.0" +publish = false authors = ["Jatutep Bunupuradah "] -name = "actix-cache-middleware" -version = "0.1.0" edition = "2021" [dependencies] -actix-web = "4.3.1" -actix-rt = "2.8.0" -env_logger = "0.10.0" -actix-web-lab = "0.19.1" +actix-web-lab.workspace = true +actix-web.workspace = true +env_logger.workspace = true redis = { version = "0.23.1", features = ["tls-native-tls"] } diff --git a/cache/redis-cache-middleware/README.md b/cache/redis/README.md similarity index 100% rename from cache/redis-cache-middleware/README.md rename to cache/redis/README.md diff --git a/cache/redis-cache-middleware/src/main.rs b/cache/redis/src/main.rs similarity index 98% rename from cache/redis-cache-middleware/src/main.rs rename to cache/redis/src/main.rs index c30abd2a..bef115d5 100644 --- a/cache/redis-cache-middleware/src/main.rs +++ b/cache/redis/src/main.rs @@ -1,3 +1,5 @@ +use std::env; + use actix_web::{ body::{self, MessageBody}, dev::{ServiceRequest, ServiceResponse}, @@ -9,7 +11,6 @@ use actix_web::{ }; use actix_web_lab::middleware::{from_fn, Next}; use redis::{Client as RedisClient, Commands, RedisError}; -use std::env; fn fib_recursive(n: u64) -> u64 { if n <= 1 { @@ -23,7 +24,7 @@ async fn an_expensive_function(n: web::Path) -> impl Responder { HttpResponse::Ok().body(result.to_string()) } -#[actix_rt::main] +#[actix_web::main] async fn main() -> std::result::Result<(), std::io::Error> { env_logger::init(); @@ -56,7 +57,7 @@ pub async fn cache_middleware( // Adjust cache expiry here const MAX_AGE: usize = 86400; let cache_max_age = format!("max-age={MAX_AGE}").parse::().unwrap(); - // Defining cache key based on requst path and query string + // Defining cache key based on request path and query string let key = if req.query_string().is_empty() { req.path().to_owned() } else {