From 312735279791fe6f0b79239479a3d750506e2c0d Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 7 Dec 2019 23:59:24 +0600 Subject: [PATCH] upgrade to 2.0 alpha.3 --- Cargo.toml | 84 ++++----- actix_todo/src/api.rs | 168 ------------------ async_db/Cargo.toml | 10 +- async_db/src/db.rs | 6 +- async_db/src/main.rs | 74 +++----- async_ex1/Cargo.toml | 10 +- async_ex1/src/main.rs | 67 ++++--- async_ex2/Cargo.toml | 12 +- async_ex2/src/appconfig.rs | 16 +- async_ex2/src/bin/main.rs | 6 +- async_ex2/src/handlers/parts.rs | 25 +-- async_ex2/src/handlers/products.rs | 36 ++-- basics/Cargo.toml | 15 +- basics/src/main.rs | 62 +++---- cookie-auth/Cargo.toml | 7 +- cookie-auth/src/main.rs | 12 +- cookie-session/Cargo.toml | 10 +- cookie-session/src/main.rs | 13 +- diesel/Cargo.toml | 7 +- diesel/src/main.rs | 109 ++++++------ error_handling/Cargo.toml | 10 +- error_handling/src/main.rs | 26 +-- form/Cargo.toml | 6 +- form/src/main.rs | 102 +++++------ graphql-demo/Cargo.toml | 8 +- graphql-demo/src/handlers.rs | 30 ++-- graphql-demo/src/main.rs | 18 +- graphql-demo/src/schemas/mod.rs | 2 +- graphql-demo/src/schemas/product.rs | 6 +- graphql-demo/src/schemas/root.rs | 95 +++++----- graphql-demo/src/schemas/user.rs | 39 ++-- hello-world/Cargo.toml | 5 +- hello-world/src/main.rs | 24 +-- http-proxy/Cargo.toml | 8 +- http-proxy/src/main.rs | 48 +++-- json/Cargo.toml | 7 +- json/src/main.rs | 92 +++++----- json_error/Cargo.toml | 8 +- json_error/src/main.rs | 41 ++--- jsonrpc/Cargo.toml | 10 +- jsonrpc/src/main.rs | 94 +++++----- middleware/Cargo.toml | 12 +- middleware/src/main.rs | 26 +-- middleware/src/read_request_body.rs | 46 ++--- middleware/src/read_response_body.rs | 53 +++--- middleware/src/redirect.rs | 19 +- middleware/src/simple.rs | 23 ++- multipart/Cargo.toml | 1 + multipart/src/main.rs | 22 +-- openssl/Cargo.toml | 12 ++ {tls => openssl}/README.md | 0 {tls => openssl}/cert.pem | 0 {tls => openssl}/key.pem | 0 {tls => openssl}/src/main.rs | 15 +- r2d2/Cargo.toml | 8 +- r2d2/src/main.rs | 26 ++- rustls/Cargo.toml | 9 +- rustls/src/main.rs | 14 +- server-sent-events/Cargo.toml | 10 +- server-sent-events/src/main.rs | 60 ++++--- simple-auth-server/Cargo.toml | 12 +- simple-auth-server/src/auth_handler.rs | 52 +++--- simple-auth-server/src/invitation_handler.rs | 23 +-- simple-auth-server/src/main.rs | 20 ++- simple-auth-server/src/register_handler.rs | 13 +- state/Cargo.toml | 7 +- state/src/main.rs | 8 +- static_index/Cargo.toml | 11 +- static_index/src/main.rs | 6 +- template_askama/Cargo.toml | 5 +- template_askama/src/main.rs | 8 +- template_handlebars/Cargo.toml | 5 +- template_handlebars/src/main.rs | 13 +- template_tera/Cargo.toml | 5 +- template_tera/src/main.rs | 8 +- tls/Cargo.toml | 16 -- {actix_todo => todo}/.env | 0 {actix_todo => todo}/Cargo.toml | 11 +- {actix_todo => todo}/README.md | 0 {actix_todo => todo}/diesel.toml | 0 {actix_todo => todo}/migrations/.gitkeep | 0 .../down.sql | 0 .../up.sql | 0 .../down.sql | 0 .../up.sql | 0 todo/src/api.rs | 135 ++++++++++++++ {actix_todo => todo}/src/db.rs | 0 {actix_todo => todo}/src/main.rs | 13 +- {actix_todo => todo}/src/model.rs | 0 {actix_todo => todo}/src/schema.rs | 0 {actix_todo => todo}/src/session.rs | 0 {actix_todo => todo}/static/css/normalize.css | 0 {actix_todo => todo}/static/css/skeleton.css | 0 {actix_todo => todo}/static/css/style.css | 0 {actix_todo => todo}/static/errors/400.html | 0 {actix_todo => todo}/static/errors/404.html | 0 {actix_todo => todo}/static/errors/500.html | 0 .../templates/index.html.tera | 0 unix-socket/Cargo.toml | 9 +- unix-socket/src/main.rs | 18 +- 100 files changed, 1075 insertions(+), 1107 deletions(-) delete mode 100644 actix_todo/src/api.rs create mode 100644 openssl/Cargo.toml rename {tls => openssl}/README.md (100%) rename {tls => openssl}/cert.pem (100%) rename {tls => openssl}/key.pem (100%) rename {tls => openssl}/src/main.rs (84%) delete mode 100644 tls/Cargo.toml rename {actix_todo => todo}/.env (100%) rename {actix_todo => todo}/Cargo.toml (68%) rename {actix_todo => todo}/README.md (100%) rename {actix_todo => todo}/diesel.toml (100%) rename {actix_todo => todo}/migrations/.gitkeep (100%) rename {actix_todo => todo}/migrations/00000000000000_diesel_initial_setup/down.sql (100%) rename {actix_todo => todo}/migrations/00000000000000_diesel_initial_setup/up.sql (100%) rename {actix_todo => todo}/migrations/2018-07-05-163612_create_tasks_table/down.sql (100%) rename {actix_todo => todo}/migrations/2018-07-05-163612_create_tasks_table/up.sql (100%) create mode 100644 todo/src/api.rs rename {actix_todo => todo}/src/db.rs (100%) rename {actix_todo => todo}/src/main.rs (84%) rename {actix_todo => todo}/src/model.rs (100%) rename {actix_todo => todo}/src/schema.rs (100%) rename {actix_todo => todo}/src/session.rs (100%) rename {actix_todo => todo}/static/css/normalize.css (100%) rename {actix_todo => todo}/static/css/skeleton.css (100%) rename {actix_todo => todo}/static/css/style.css (100%) rename {actix_todo => todo}/static/errors/400.html (100%) rename {actix_todo => todo}/static/errors/404.html (100%) rename {actix_todo => todo}/static/errors/500.html (100%) rename {actix_todo => todo}/templates/index.html.tera (100%) diff --git a/Cargo.toml b/Cargo.toml index 2fe1c8f..890673e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,44 +1,46 @@ [workspace] members = [ - "./", - "actix_redis", - "actix_todo", - "async_db", - "async_ex1", - "async_ex2", - "basics", - "cookie-auth", - "cookie-session", - "diesel", - "error_handling", - "form", - "graphql-demo", - "hello-world", - "http-proxy", - "json", - "json_error", - "jsonrpc", - "juniper", - "middleware", - "multipart", - "protobuf", - "r2d2", - "redis-session", - "rustls", - "server-sent-events", - "simple-auth-server", - "state", - "static_index", - "template_askama", - "template_tera", - "template_yarte", - "template_handlebars", - "tls", - "udp-echo", - "unix-socket", - "web-cors/backend", - "websocket", - "websocket-chat", - "websocket-chat-broker", - "websocket-tcp-chat", +# "actix_redis", + "async_db", + "async_ex1", + "async_ex2", + "basics", + "cookie-auth", + "cookie-session", + "diesel", + "error_handling", + "form", + "graphql-demo", + "hello-world", + "http-proxy", + "json", + "json_error", + "jsonrpc", + "juniper", + "middleware", + "multipart", + "openssl", +# "protobuf", + "r2d2", +# "redis-session", + "rustls", + "server-sent-events", + "simple-auth-server", + "state", + "static_index", + "template_askama", + "template_handlebars", + "template_tera", +# "template_yarte", + "todo", +# "udp-echo", + "unix-socket", +# "web-cors/backend", +# "websocket", +# "websocket-chat", +# "websocket-chat-broker", +# "websocket-tcp-chat", ] + +[patch.crates-io] +actix-http = { git = "https://github.com/actix/actix-web.git" } diff --git a/actix_todo/src/api.rs b/actix_todo/src/api.rs deleted file mode 100644 index cb3e15a..0000000 --- a/actix_todo/src/api.rs +++ /dev/null @@ -1,168 +0,0 @@ -use actix_files::NamedFile; -use actix_session::Session; -use actix_web::middleware::errhandlers::ErrorHandlerResponse; -use actix_web::{dev, error, http, web, Error, HttpResponse, Responder, Result}; -use futures::future::{err, Either, Future, IntoFuture}; -use tera::{Context, Tera}; - -use crate::db; -use crate::session::{self, FlashMessage}; - -pub fn index( - pool: web::Data, - tmpl: web::Data, - session: Session, -) -> impl Future { - web::block(move || db::get_all_tasks(&pool)) - .from_err() - .then(move |res| match res { - Ok(tasks) => { - let mut context = Context::new(); - context.insert("tasks", &tasks); - - //Session is set during operations on other endpoints - //that can redirect to index - if let Some(flash) = session::get_flash(&session)? { - context.insert("msg", &(flash.kind, flash.message)); - session::clear_flash(&session); - } - - let rendered = - tmpl.render("index.html.tera", &context).map_err(|e| { - error::ErrorInternalServerError(e.description().to_owned()) - })?; - - Ok(HttpResponse::Ok().body(rendered)) - } - Err(e) => Err(e), - }) -} - -#[derive(Deserialize)] -pub struct CreateForm { - description: String, -} - -pub fn create( - params: web::Form, - pool: web::Data, - session: Session, -) -> impl Future { - if params.description.is_empty() { - Either::A( - session::set_flash( - &session, - FlashMessage::error("Description cannot be empty"), - ) - .map(|_| redirect_to("/")) - .into_future(), - ) - } else { - Either::B( - web::block(move || db::create_task(params.into_inner().description, &pool)) - .from_err() - .then(move |res| match res { - Ok(_) => { - session::set_flash( - &session, - FlashMessage::success("Task successfully added"), - )?; - Ok(redirect_to("/")) - } - Err(e) => Err(e), - }), - ) - } -} - -#[derive(Deserialize)] -pub struct UpdateParams { - id: i32, -} - -#[derive(Deserialize)] -pub struct UpdateForm { - _method: String, -} - -pub fn update( - db: web::Data, - params: web::Path, - form: web::Form, - session: Session, -) -> impl Future { - match form._method.as_ref() { - "put" => Either::A(Either::A(toggle(db, params))), - "delete" => Either::A(Either::B(delete(db, params, session))), - unsupported_method => { - let msg = format!("Unsupported HTTP method: {}", unsupported_method); - Either::B(err(error::ErrorBadRequest(msg))) - } - } -} - -fn toggle( - pool: web::Data, - params: web::Path, -) -> impl Future { - web::block(move || db::toggle_task(params.id, &pool)) - .from_err() - .then(move |res| match res { - Ok(_) => Ok(redirect_to("/")), - Err(e) => Err(e), - }) -} - -fn delete( - pool: web::Data, - params: web::Path, - session: Session, -) -> impl Future { - web::block(move || db::delete_task(params.id, &pool)) - .from_err() - .then(move |res| match res { - Ok(_) => { - session::set_flash( - &session, - FlashMessage::success("Task was deleted."), - )?; - Ok(redirect_to("/")) - } - Err(e) => Err(e), - }) -} - -fn redirect_to(location: &str) -> HttpResponse { - HttpResponse::Found() - .header(http::header::LOCATION, location) - .finish() -} - -pub fn bad_request(res: dev::ServiceResponse) -> Result> { - let new_resp = NamedFile::open("static/errors/400.html")? - .set_status_code(res.status()) - .respond_to(res.request())?; - Ok(ErrorHandlerResponse::Response( - res.into_response(new_resp.into_body()), - )) -} - -pub fn not_found(res: dev::ServiceResponse) -> Result> { - let new_resp = NamedFile::open("static/errors/404.html")? - .set_status_code(res.status()) - .respond_to(res.request())?; - Ok(ErrorHandlerResponse::Response( - res.into_response(new_resp.into_body()), - )) -} - -pub fn internal_server_error( - res: dev::ServiceResponse, -) -> Result> { - let new_resp = NamedFile::open("static/errors/500.html")? - .set_status_code(res.status()) - .respond_to(res.request())?; - Ok(ErrorHandlerResponse::Response( - res.into_response(new_resp.into_body()), - )) -} diff --git a/async_db/Cargo.toml b/async_db/Cargo.toml index b7ef39f..ce72dc7 100644 --- a/async_db/Cargo.toml +++ b/async_db/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "async_db" -version = "0.1.0" +version = "2.0.0" authors = ["Darin Gordon "] edition = "2018" workspace = ".." [dependencies] -actix-rt = "0.2.2" -actix-web = "1.0.0" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" dotenv = "0.10" -env_logger = "0.5" +env_logger = "0.6" failure = "0.1.1" -futures = "0.1" +futures = "0.3.1" num_cpus = "1.10.0" r2d2 = "0.8.2" r2d2_sqlite = "0.8.0" diff --git a/async_db/src/db.rs b/async_db/src/db.rs index 1a0f865..7d3f634 100644 --- a/async_db/src/db.rs +++ b/async_db/src/db.rs @@ -1,6 +1,6 @@ use actix_web::{web, Error as AWError}; use failure::Error; -use futures::Future; +use futures::{Future, TryFutureExt}; use r2d2; use r2d2_sqlite; use rusqlite::NO_PARAMS; @@ -26,7 +26,7 @@ pub enum Queries { pub fn execute( pool: &Pool, query: Queries, -) -> impl Future, Error = AWError> { +) -> impl Future, AWError>> { let pool = pool.clone(); web::block(move || match query { Queries::GetTopTenHottestYears => get_hottest_years(pool.get()?), @@ -34,7 +34,7 @@ pub fn execute( Queries::GetTopTenHottestMonths => get_hottest_months(pool.get()?), Queries::GetTopTenColdestMonths => get_coldest_months(pool.get()?), }) - .from_err() + .map_err(AWError::from) } fn get_hottest_years(conn: Connection) -> Result, Error> { diff --git a/async_db/src/main.rs b/async_db/src/main.rs index 78ab4cc..ec40433 100644 --- a/async_db/src/main.rs +++ b/async_db/src/main.rs @@ -14,64 +14,42 @@ This project illustrates two examples: use std::io; use actix_web::{middleware, web, App, Error as AWError, HttpResponse, HttpServer}; -use futures::future::{join_all, ok as fut_ok, Future}; -use r2d2_sqlite; -use r2d2_sqlite::SqliteConnectionManager; +use futures::future::join_all; +use r2d2_sqlite::{self, SqliteConnectionManager}; mod db; -use db::{Pool, Queries, WeatherAgg}; +use db::{Pool, Queries}; /// Version 1: Calls 4 queries in sequential order, as an asynchronous handler -fn asyncio_weather( - db: web::Data, -) -> impl Future { - let mut result: Vec> = vec![]; +async fn asyncio_weather(db: web::Data) -> Result { + let result = vec![ + db::execute(&db, Queries::GetTopTenHottestYears).await?, + db::execute(&db, Queries::GetTopTenColdestYears).await?, + db::execute(&db, Queries::GetTopTenHottestMonths).await?, + db::execute(&db, Queries::GetTopTenColdestMonths).await?, + ]; - db::execute(&db, Queries::GetTopTenHottestYears) - .from_err() - .and_then(move |res| { - result.push(res); - db::execute(&db, Queries::GetTopTenColdestYears) - .from_err() - .and_then(move |res| { - result.push(res); - db::execute(&db, Queries::GetTopTenHottestMonths) - .from_err() - .and_then(move |res| { - result.push(res); - db::execute(&db, Queries::GetTopTenColdestMonths) - .from_err() - .and_then(move |res| { - result.push(res); - fut_ok(result) - }) - }) - }) - }) - .and_then(|res| Ok(HttpResponse::Ok().json(res))) + Ok(HttpResponse::Ok().json(result)) } /// Version 2: Calls 4 queries in parallel, as an asynchronous handler /// Returning Error types turn into None values in the response -fn parallel_weather( - db: web::Data, -) -> impl Future { +async fn parallel_weather(db: web::Data) -> Result { let fut_result = vec![ - Box::new(db::execute(&db, Queries::GetTopTenHottestYears)), - Box::new(db::execute(&db, Queries::GetTopTenColdestYears)), - Box::new(db::execute(&db, Queries::GetTopTenHottestMonths)), - Box::new(db::execute(&db, Queries::GetTopTenColdestMonths)), + Box::pin(db::execute(&db, Queries::GetTopTenHottestYears)), + Box::pin(db::execute(&db, Queries::GetTopTenColdestYears)), + Box::pin(db::execute(&db, Queries::GetTopTenHottestMonths)), + Box::pin(db::execute(&db, Queries::GetTopTenColdestMonths)), ]; + let result: Result, _> = join_all(fut_result).await.into_iter().collect(); - join_all(fut_result) - .map_err(AWError::from) - .map(|result| HttpResponse::Ok().json(result)) + Ok(HttpResponse::Ok().json(result.map_err(AWError::from)?)) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix_rt::System::new("parallel_db_example"); // Start N db executor actors (N = number of cores avail) let manager = SqliteConnectionManager::file("weather.db"); @@ -80,20 +58,18 @@ fn main() -> io::Result<()> { // Start http server HttpServer::new(move || { App::new() + // store db pool as Data object .data(pool.clone()) .wrap(middleware::Logger::default()) .service( - web::resource("/asyncio_weather") - .route(web::get().to_async(asyncio_weather)), + web::resource("/asyncio_weather").route(web::get().to(asyncio_weather)), ) .service( web::resource("/parallel_weather") - .route(web::get().to_async(parallel_weather)), + .route(web::get().to(parallel_weather)), ) }) .bind("127.0.0.1:8080")? - .start(); - - println!("Started http server: 127.0.0.1:8080"); - sys.run() + .start() + .await } diff --git a/async_ex1/Cargo.toml b/async_ex1/Cargo.toml index e5b9716..edccbab 100644 --- a/async_ex1/Cargo.toml +++ b/async_ex1/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "awc_examples" -version = "0.1.0" +version = "2.0.0" authors = ["dowwie "] edition = "2018" workspace = ".." [dependencies] -actix-rt = "0.2.2" -actix-web = { version="1.0.0", features=["ssl"] } +actix-rt = "1.0.0-alpha.3" +actix-web = { version="2.0.0-alpha.3", features=["openssl"] } -futures = "0.1" +futures = "0.3.1" serde = "1.0.43" serde_derive = "1.0.43" serde_json = "1.0.16" validator = "0.6.3" validator_derive = "0.6.5" -env_logger = "0.5.9" +env_logger = "0.6" diff --git a/async_ex1/src/main.rs b/async_ex1/src/main.rs index 25fa3bc..3f049ec 100644 --- a/async_ex1/src/main.rs +++ b/async_ex1/src/main.rs @@ -26,7 +26,7 @@ use actix_web::{ web::{self, BytesMut}, App, Error, HttpResponse, HttpServer, }; -use futures::{Future, Stream}; +use futures::StreamExt; use validator::Validate; #[derive(Debug, Validate, Deserialize, Serialize)] @@ -50,56 +50,51 @@ struct HttpBinResponse { } /// validate data, post json to httpbin, get it back in the response body, return deserialized -fn step_x( - data: SomeData, - client: &Client, -) -> impl Future { - let validation = futures::future::result(data.validate()).map_err(ErrorBadRequest); - let post_response = client +async fn step_x(data: SomeData, client: &Client) -> Result { + // validate data + data.validate().map_err(ErrorBadRequest)?; + + let mut res = client .post("https://httpbin.org/post") .send_json(&data) - .map_err(Error::from) // <- convert SendRequestError to an Error - .and_then(|resp| { - resp.from_err() - .fold(BytesMut::new(), |mut acc, chunk| { - acc.extend_from_slice(&chunk); - Ok::<_, Error>(acc) - }) - .map(|body| { - let body: HttpBinResponse = serde_json::from_slice(&body).unwrap(); - body.json - }) - }); + .await + .map_err(Error::from)?; // <- convert SendRequestError to an Error - validation.and_then(|_| post_response) + let mut body = BytesMut::new(); + while let Some(chunk) = res.next().await { + body.extend_from_slice(&chunk?); + } + + let body: HttpBinResponse = serde_json::from_slice(&body).unwrap(); + Ok(body.json) } -fn create_something( +async fn create_something( some_data: web::Json, client: web::Data, -) -> impl Future { - step_x(some_data.into_inner(), &client).and_then(move |some_data_2| { - step_x(some_data_2, &client).and_then(move |some_data_3| { - step_x(some_data_3, &client).and_then(|d| { - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(serde_json::to_string(&d).unwrap())) - }) - }) - }) +) -> Result { + let some_data_2 = step_x(some_data.into_inner(), &client).await?; + let some_data_3 = step_x(some_data_2, &client).await?; + let d = step_x(some_data_3, &client).await?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(serde_json::to_string(&d).unwrap())) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); let endpoint = "127.0.0.1:8080"; println!("Starting server at: {:?}", endpoint); HttpServer::new(|| { - App::new().data(Client::default()).service( - web::resource("/something").route(web::post().to_async(create_something)), - ) + App::new() + .data(Client::default()) + .service(web::resource("/something").route(web::post().to(create_something))) }) .bind(endpoint)? - .run() + .start() + .await } diff --git a/async_ex2/Cargo.toml b/async_ex2/Cargo.toml index c1492b9..c97f0a4 100644 --- a/async_ex2/Cargo.toml +++ b/async_ex2/Cargo.toml @@ -6,13 +6,13 @@ edition = "2018" workspace = ".." [dependencies] -actix-rt = "0.2.2" -actix-web = { version="1.0.0", features=["ssl"] } -actix-multipart = "0.1.1" -actix-service = "0.4.1" -bytes = "0.4.12" +actix-rt = "1.0.0-alpha.3" +actix-web = { version="2.0.0-alpha.3", features=["openssl"] } +actix-multipart = "0.2.0-alpha.2" +actix-service = "1.0.0-alpha.3" +bytes = "0.5.2" env_logger = "0.6.1" -futures = "0.1" +futures = "0.3.1" serde = { version = "^1.0", features = ["derive"] } serde_derive = "1.0.90" serde_json = "1.0.39" diff --git a/async_ex2/src/appconfig.rs b/async_ex2/src/appconfig.rs index f007003..3161576 100644 --- a/async_ex2/src/appconfig.rs +++ b/async_ex2/src/appconfig.rs @@ -8,27 +8,27 @@ pub fn config_app(cfg: &mut web::ServiceConfig) { web::scope("/products") .service( web::resource("") - .route(web::get().to_async(products::get_products)) - .route(web::post().to_async(products::add_product)), + .route(web::get().to(products::get_products)) + .route(web::post().to(products::add_product)), ) .service( web::scope("/{product_id}") .service( web::resource("") - .route(web::get().to_async(products::get_product_detail)) - .route(web::delete().to_async(products::remove_product)), + .route(web::get().to(products::get_product_detail)) + .route(web::delete().to(products::remove_product)), ) .service( web::scope("/parts") .service( web::resource("") - .route(web::get().to_async(parts::get_parts)) - .route(web::post().to_async(parts::add_part)), + .route(web::get().to(parts::get_parts)) + .route(web::post().to(parts::add_part)), ) .service( web::resource("/{part_id}") - .route(web::get().to_async(parts::get_part_detail)) - .route(web::delete().to_async(parts::remove_part)), + .route(web::get().to(parts::get_part_detail)) + .route(web::delete().to(parts::remove_part)), ), ), ), diff --git a/async_ex2/src/bin/main.rs b/async_ex2/src/bin/main.rs index f5ee4fb..be8ef02 100644 --- a/async_ex2/src/bin/main.rs +++ b/async_ex2/src/bin/main.rs @@ -2,7 +2,8 @@ use actix_web::{middleware, App, HttpServer}; use async_ex2::appconfig::config_app; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -12,5 +13,6 @@ fn main() -> std::io::Result<()> { .wrap(middleware::Logger::default()) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/async_ex2/src/handlers/parts.rs b/async_ex2/src/handlers/parts.rs index 3e3874f..9e8725b 100644 --- a/async_ex2/src/handlers/parts.rs +++ b/async_ex2/src/handlers/parts.rs @@ -1,28 +1,19 @@ use actix_web::{web, Error, HttpResponse}; -use futures::{future::ok as fut_ok, Future}; use crate::common::{Part, Product}; -pub fn get_parts( - query: web::Query>, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn get_parts(query: web::Query>) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn add_part( - new_part: web::Json, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn add_part(new_part: web::Json) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn get_part_detail( - id: web::Path, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn get_part_detail(id: web::Path) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn remove_part( - id: web::Path, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn remove_part(id: web::Path) -> Result { + Ok(HttpResponse::Ok().finish()) } diff --git a/async_ex2/src/handlers/products.rs b/async_ex2/src/handlers/products.rs index 36b774a..624b820 100644 --- a/async_ex2/src/handlers/products.rs +++ b/async_ex2/src/handlers/products.rs @@ -1,45 +1,39 @@ use actix_web::{web, Error, HttpResponse}; -use futures::{future::ok as fut_ok, Future}; use crate::common::{Part, Product}; -pub fn get_products( +pub async fn get_products( query: web::Query>, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn add_product( +pub async fn add_product( new_product: web::Json, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn get_product_detail( - id: web::Path, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn get_product_detail(id: web::Path) -> Result { + Ok(HttpResponse::Ok().finish()) } -pub fn remove_product( - id: web::Path, -) -> impl Future { - fut_ok(HttpResponse::Ok().finish()) +pub async fn remove_product(id: web::Path) -> Result { + Ok(HttpResponse::Ok().finish()) } #[cfg(test)] mod tests { - use super::*; use crate::appconfig::config_app; use actix_service::Service; use actix_web::{ http::{header, StatusCode}, - test, web, App, HttpRequest, HttpResponse, + test, App, }; - #[test] - fn test_add_product() { - let mut app = test::init_service(App::new().configure(config_app)); + #[actix_rt::test] + async fn test_add_product() { + let mut app = test::init_service(App::new().configure(config_app)).await; let payload = r#"{"id":12345,"product_type":"fancy","name":"test"}"#.as_bytes(); @@ -49,7 +43,7 @@ mod tests { .set_payload(payload) .to_request(); - let resp = test::block_on(app.call(req)).unwrap(); + let resp = app.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); } diff --git a/basics/Cargo.toml b/basics/Cargo.toml index dd46235..94eba64 100644 --- a/basics/Cargo.toml +++ b/basics/Cargo.toml @@ -1,16 +1,17 @@ [package] name = "basics" -version = "1.0.0" +version = "2.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-rt = "0.2.2" -actix-web = "1.0.0" -actix-files = "0.1.1" -actix-session = "0.2.0" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" +actix-files = "0.2.0-alpha.3" +actix-session = "0.3.0-alpha.3" +actix-utils = "1.0.0-alpha.3" -futures = "0.1.25" +futures = "0.3.1" env_logger = "0.5" -bytes = "0.4" +bytes = "0.5" diff --git a/basics/src/main.rs b/basics/src/main.rs index ab98ecb..9d55d06 100644 --- a/basics/src/main.rs +++ b/basics/src/main.rs @@ -5,24 +5,23 @@ use std::{env, io}; use actix_files as fs; use actix_session::{CookieSession, Session}; +use actix_utils::mpsc; use actix_web::http::{header, Method, StatusCode}; use actix_web::{ error, guard, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, Result, }; use bytes::Bytes; -use futures::unsync::mpsc; -use futures::{future::ok, Future, Stream}; /// favicon handler #[get("/favicon")] -fn favicon() -> Result { +async fn favicon() -> Result { Ok(fs::NamedFile::open("static/favicon.ico")?) } /// simple index handler #[get("/welcome")] -fn welcome(session: Session, req: HttpRequest) -> Result { +async fn welcome(session: Session, req: HttpRequest) -> Result { println!("{:?}", req); // session @@ -42,32 +41,22 @@ fn welcome(session: Session, req: HttpRequest) -> Result { } /// 404 handler -fn p404() -> Result { +async fn p404() -> Result { Ok(fs::NamedFile::open("static/404.html")?.set_status_code(StatusCode::NOT_FOUND)) } -/// async handler -fn index_async(req: HttpRequest) -> impl Future { - println!("{:?}", req); - - ok(HttpResponse::Ok() - .content_type("text/html") - .body(format!("Hello {}!", req.match_info().get("name").unwrap()))) -} - -/// async body -fn index_async_body(path: web::Path) -> HttpResponse { +/// response body +async fn response_body(path: web::Path) -> HttpResponse { let text = format!("Hello {}!", *path); - let (tx, rx_body) = mpsc::unbounded(); - let _ = tx.unbounded_send(Bytes::from(text.as_bytes())); + let (tx, rx_body) = mpsc::channel(); + let _ = tx.send(Ok::<_, Error>(Bytes::from(text))); - HttpResponse::Ok() - .streaming(rx_body.map_err(|_| error::ErrorBadRequest("bad request"))) + HttpResponse::Ok().streaming(rx_body) } /// handler with path parameters like `/user/{name}/` -fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse { +async fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse { println!("{:?}", req); HttpResponse::Ok() @@ -75,10 +64,10 @@ fn with_param(req: HttpRequest, path: web::Path<(String,)>) -> HttpResponse { .body(format!("Hello {}!", path.0)) } -fn main() -> io::Result<()> { - env::set_var("RUST_LOG", "actix_web=debug"); +#[actix_rt::main] +async fn main() -> io::Result<()> { + env::set_var("RUST_LOG", "actix_web=debug;actix_server=info"); env_logger::init(); - let sys = actix_rt::System::new("basic-example"); HttpServer::new(|| { App::new() @@ -92,14 +81,9 @@ fn main() -> io::Result<()> { .service(welcome) // with path parameters .service(web::resource("/user/{name}").route(web::get().to(with_param))) - // async handler + // async response body .service( - web::resource("/async/{name}").route(web::get().to_async(index_async)), - ) - // async handler - .service( - web::resource("/async-body/{name}") - .route(web::get().to(index_async_body)), + web::resource("/async-body/{name}").route(web::get().to(response_body)), ) .service( web::resource("/test").to(|req: HttpRequest| match *req.method() { @@ -109,10 +93,12 @@ fn main() -> io::Result<()> { }), ) .service(web::resource("/error").to(|| { - error::InternalError::new( - io::Error::new(io::ErrorKind::Other, "test"), - StatusCode::INTERNAL_SERVER_ERROR, - ) + async { + error::InternalError::new( + io::Error::new(io::ErrorKind::Other, "test"), + StatusCode::INTERNAL_SERVER_ERROR, + ) + } })) // static files .service(fs::Files::new("/static", "static").show_files_listing()) @@ -137,8 +123,6 @@ fn main() -> io::Result<()> { ) }) .bind("127.0.0.1:8080")? - .start(); - - println!("Starting http server: 127.0.0.1:8080"); - sys.run() + .start() + .await } diff --git a/cookie-auth/Cargo.toml b/cookie-auth/Cargo.toml index 12f8cf5..177ac1b 100644 --- a/cookie-auth/Cargo.toml +++ b/cookie-auth/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "cookie-auth" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim "] edition = "2018" workspace = ".." [dependencies] -actix-web = "1.0.0" -actix-identity = "0.1.0" +actix-web = "2.0.0-alpha.3" +actix-identity = "0.2.0-alpha.1" +actix-rt = "1.0.0-alpha.3" env_logger = "0.6" diff --git a/cookie-auth/src/main.rs b/cookie-auth/src/main.rs index 9da2da5..cb14cce 100644 --- a/cookie-auth/src/main.rs +++ b/cookie-auth/src/main.rs @@ -2,24 +2,25 @@ use actix_identity::Identity; use actix_identity::{CookieIdentityPolicy, IdentityService}; use actix_web::{middleware, web, App, HttpResponse, HttpServer}; -fn index(id: Identity) -> String { +async fn index(id: Identity) -> String { format!( "Hello {}", id.identity().unwrap_or_else(|| "Anonymous".to_owned()) ) } -fn login(id: Identity) -> HttpResponse { +async fn login(id: Identity) -> HttpResponse { id.remember("user1".to_owned()); HttpResponse::Found().header("location", "/").finish() } -fn logout(id: Identity) -> HttpResponse { +async fn logout(id: Identity) -> HttpResponse { id.forget(); HttpResponse::Found().header("location", "/").finish() } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -37,5 +38,6 @@ fn main() -> std::io::Result<()> { .service(web::resource("/").route(web::get().to(index))) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/cookie-session/Cargo.toml b/cookie-session/Cargo.toml index a927124..6093563 100644 --- a/cookie-session/Cargo.toml +++ b/cookie-session/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "cookie-session" -version = "0.1.0" +version = "1.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" -actix-session = "0.2.0" -actix-rt = "0.2.5" +actix-web = "2.0.0-alpha.3" +actix-session = "0.3.0-alpha.3" +actix-rt = "1.0.0-alpha.3" -futures = "0.1" +futures = "0.3.1" time = "0.1" env_logger = "0.6" diff --git a/cookie-session/src/main.rs b/cookie-session/src/main.rs index f0eac8c..e4c81dc 100644 --- a/cookie-session/src/main.rs +++ b/cookie-session/src/main.rs @@ -9,7 +9,7 @@ use actix_session::{CookieSession, Session}; use actix_web::{middleware::Logger, web, App, HttpRequest, HttpServer, Result}; /// simple index handler with session -fn index(session: Session, req: HttpRequest) -> Result<&'static str> { +async fn index(session: Session, req: HttpRequest) -> Result<&'static str> { println!("{:?}", req); // RequestSession trait is used for session access @@ -25,10 +25,11 @@ fn index(session: Session, req: HttpRequest) -> Result<&'static str> { Ok("welcome!") } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); - let sys = actix_rt::System::new("cookie-session"); + println!("Starting http server: 127.0.0.1:8080"); HttpServer::new(|| { App::new() @@ -39,8 +40,6 @@ fn main() -> std::io::Result<()> { .service(web::resource("/").to(index)) }) .bind("127.0.0.1:8080")? - .start(); - - println!("Starting http server: 127.0.0.1:8080"); - sys.run() + .start() + .await } diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml index 5065ff3..15acf84 100644 --- a/diesel/Cargo.toml +++ b/diesel/Cargo.toml @@ -1,16 +1,17 @@ [package] name = "diesel-example" -version = "0.1.0" +version = "1.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" bytes = "0.4" env_logger = "0.6" -futures = "0.1" +futures = "0.3.1" uuid = { version = "0.5", features = ["serde", "v4"] } serde = "1.0" serde_json = "1.0" diff --git a/diesel/src/main.rs b/diesel/src/main.rs index 3645349..5e68345 100644 --- a/diesel/src/main.rs +++ b/diesel/src/main.rs @@ -14,8 +14,7 @@ use bytes::BytesMut; use diesel::prelude::*; use diesel::r2d2::{self, ConnectionManager}; use dotenv; -use futures::future::{err, Either}; -use futures::{Future, Stream}; +use futures::StreamExt; mod models; mod schema; @@ -43,15 +42,15 @@ fn query( } /// Async request handler -fn add( +async fn add( name: web::Path, pool: web::Data, -) -> impl Future { +) -> Result { // run diesel blocking code - web::block(move || query(name.into_inner(), pool)).then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) + Ok(web::block(move || query(name.into_inner(), pool)) + .await + .map(|user| HttpResponse::Ok().json(user)) + .map_err(|_| HttpResponse::InternalServerError())?) } #[derive(Debug, Serialize, Deserialize)] @@ -62,62 +61,53 @@ struct MyUser { const MAX_SIZE: usize = 262_144; // max payload size is 256k /// This handler manually load request payload and parse json object -fn index_add( - pl: web::Payload, +async fn index_add( + mut pl: web::Payload, pool: web::Data, -) -> impl Future { - pl - // `Future::from_err` acts like `?` in that it coerces the error type from - // the future into the final error type - .from_err() - // `fold` will asynchronously read each chunk of the request body and - // call supplied closure, then it resolves to result of closure - .fold(BytesMut::new(), move |mut body, chunk| { - // limit max size of in-memory payload - if (body.len() + chunk.len()) > MAX_SIZE { - Err(error::ErrorBadRequest("overflow")) - } else { - body.extend_from_slice(&chunk); - Ok(body) - } - }) - // `Future::and_then` can be used to merge an asynchronous workflow with a - // synchronous workflow - // - // Douman NOTE: - // The return value in this closure helps, to clarify result for compiler - // as otheriwse it cannot understand it - .and_then(move |body| { - // body is loaded, now we can deserialize serde-json - let r_obj = serde_json::from_slice::(&body); +) -> Result { + let mut body = BytesMut::new(); + // pl.next() gets next item from asynchronous stream of byte chunks + // it resolves to `Option>` object + // `None` - indicaets end of stream + while let Some(chunk) = pl.next().await { + let chunk = chunk?; - // Send to the db for create - match r_obj { - Ok(obj) => { - Either::A(web::block(move || query(obj.name, pool)).then(|res| { - match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - } - })) - } - Err(_) => Either::B(err(error::ErrorBadRequest("Json Decode Failed"))), - } - }) + // limit max size of in-memory payload + if (body.len() + chunk.len()) > MAX_SIZE { + return Err(error::ErrorBadRequest("overflow")); + } + body.extend_from_slice(&chunk); + } + + // body is loaded, now we can deserialize serde-json + let r_obj = serde_json::from_slice::(&body); + + // Send to the db for create return response to peer + match r_obj { + Ok(obj) => { + let user = web::block(move || query(obj.name, pool)) + .await + .map_err(|_| Error::from(HttpResponse::InternalServerError()))?; + Ok(HttpResponse::Ok().json(user)) + } + Err(_) => Err(error::ErrorBadRequest("Json Decode Failed")), + } } -fn add2( +async fn add2( item: web::Json, pool: web::Data, -) -> impl Future { +) -> Result { // run diesel blocking code - web::block(move || query(item.into_inner().name, pool)).then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) + let user = web::block(move || query(item.into_inner().name, pool)) + .await + .map_err(|_| HttpResponse::InternalServerError())?; // convert diesel error to http response + + Ok(HttpResponse::Ok().json(user)) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -153,14 +143,15 @@ fn main() -> std::io::Result<()> { .into() }), ) - .route(web::post().to_async(add2)), + .route(web::post().to(add2)), ) // Manual parsing would allow custom error construction, use of // other parsers *beside* json (for example CBOR, protobuf, xml), and allows // an application to standardise on a single parser implementation. - .service(web::resource("/add").route(web::post().to_async(index_add))) - .service(web::resource("/add/{name}").route(web::get().to_async(add))) + .service(web::resource("/add").route(web::post().to(index_add))) + .service(web::resource("/add/{name}").route(web::get().to(add))) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/error_handling/Cargo.toml b/error_handling/Cargo.toml index 808e733..b675696 100644 --- a/error_handling/Cargo.toml +++ b/error_handling/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "error_handling" -version = "0.1.0" +version = "1.0.0" authors = ["dowwie "] edition = "2018" workspace = ".." [dependencies] -actix-web = "1.0.0" - -derive_more = "0.14.0" -futures = "0.1.23" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +derive_more = "0.99.2" +futures = "0.3.1" rand = "0.5.4" env_logger = "0.6" diff --git a/error_handling/src/main.rs b/error_handling/src/main.rs index 676a8ce..5821980 100644 --- a/error_handling/src/main.rs +++ b/error_handling/src/main.rs @@ -14,7 +14,6 @@ http errors will be chosen, each with an equal chance of being selected: use actix_web::{web, App, Error, HttpResponse, HttpServer, ResponseError}; use derive_more::Display; // naming it clearly for illustration purposes -use futures::future::{err, ok, Future}; use rand::{ distributions::{Distribution, Standard}, thread_rng, Rng, @@ -71,32 +70,33 @@ impl ResponseError for CustomError { } /// randomly returns either () or one of the 4 CustomError variants -fn do_something_random() -> impl Future { +async fn do_something_random() -> Result<(), CustomError> { let mut rng = thread_rng(); // 20% chance that () will be returned by this function if rng.gen_bool(2.0 / 10.0) { - ok(()) + Ok(()) } else { - err(rand::random::()) + Err(rand::random::()) } } -fn do_something() -> impl Future { - do_something_random().from_err().and_then(|_| { - HttpResponse::Ok().body("Nothing interesting happened. Try again.") - }) +async fn do_something() -> Result { + do_something_random().await?; + + Ok(HttpResponse::Ok().body("Nothing interesting happened. Try again.")) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); HttpServer::new(move || { - App::new().service( - web::resource("/something").route(web::get().to_async(do_something)), - ) + App::new() + .service(web::resource("/something").route(web::get().to(do_something))) }) .bind("127.0.0.1:8088")? - .run() + .start() + .await } diff --git a/form/Cargo.toml b/form/Cargo.toml index 7e21dad..74fb020 100644 --- a/form/Cargo.toml +++ b/form/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "form-example" -version = "0.1.0" +version = "1.0.0" authors = ["Gorm Casper "] edition = "2018" workspace = ".." [dependencies] -actix-web = "1.0.8" - +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" serde = "1.0" serde_derive = "1.0" diff --git a/form/src/main.rs b/form/src/main.rs index d2ae5b9..d2e0cff 100644 --- a/form/src/main.rs +++ b/form/src/main.rs @@ -9,14 +9,16 @@ struct AppState { foo: String, } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .wrap(middleware::Logger::default()) .configure(app_config) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } fn app_config(config: &mut web::ServiceConfig) { @@ -28,11 +30,11 @@ fn app_config(config: &mut web::ServiceConfig) { .service(web::resource("/").route(web::get().to(index))) .service(web::resource("/post1").route(web::post().to(handle_post_1))) .service(web::resource("/post2").route(web::post().to(handle_post_2))) - .service(web::resource("/post3").route(web::post().to(handle_post_3))) + .service(web::resource("/post3").route(web::post().to(handle_post_3))), ); } -fn index() -> Result { +async fn index() -> Result { Ok(HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(include_str!("../static/form.html"))) @@ -44,14 +46,14 @@ pub struct MyParams { } /// Simple handle POST request -fn handle_post_1(params: web::Form) -> Result { +async fn handle_post_1(params: web::Form) -> Result { Ok(HttpResponse::Ok() .content_type("text/plain") .body(format!("Your name is {}", params.name))) } /// State and POST Params -fn handle_post_2( +async fn handle_post_2( state: web::Data, params: web::Form, ) -> HttpResponse { @@ -62,7 +64,7 @@ fn handle_post_2( } /// Request and POST Params -fn handle_post_3(req: HttpRequest, params: web::Form) -> impl Responder { +async fn handle_post_3(req: HttpRequest, params: web::Form) -> impl Responder { println!("Handling POST request: {:?}", req); HttpResponse::Ok() @@ -74,10 +76,10 @@ fn handle_post_3(req: HttpRequest, params: web::Form) -> impl Responde mod tests { use super::*; - use actix_web::body::{Body , ResponseBody}; + use actix_web::body::{Body, ResponseBody}; use actix_web::dev::{HttpResponseBuilder, Service, ServiceResponse}; use actix_web::http::{header::CONTENT_TYPE, HeaderValue, StatusCode}; - use actix_web::test::{self, block_on, TestRequest}; + use actix_web::test::{self, TestRequest}; use actix_web::web::Form; trait BodyTest { @@ -99,47 +101,42 @@ mod tests { } } - #[test] - fn handle_post_1_unit_test() { + #[actix_rt::test] + async fn handle_post_1_unit_test() { let params = Form(MyParams { name: "John".to_string(), }); - let result = handle_post_1(params); - let resp = block_on(result).unwrap(); + let resp = handle_post_1(params).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap(), HeaderValue::from_static("text/plain") ); - assert_eq!( - resp.body().as_str(), - "Your name is John" - ); + assert_eq!(resp.body().as_str(), "Your name is John"); } - #[test] - fn handle_post_1_integration_test() { - let mut app = test::init_service(App::new().configure(app_config)); + #[actix_rt::test] + async fn handle_post_1_integration_test() { + let mut app = test::init_service(App::new().configure(app_config)).await; let req = test::TestRequest::post() .uri("/post1") - .set_form(&MyParams {name: "John".to_string()}) + .set_form(&MyParams { + name: "John".to_string(), + }) .to_request(); - let resp: ServiceResponse = block_on(app.call(req)).unwrap(); + let resp: ServiceResponse = app.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap(), HeaderValue::from_static("text/plain") ); - assert_eq!( - resp.response().body().as_str(), - "Your name is John" - ); + assert_eq!(resp.response().body().as_str(), "Your name is John"); } - #[test] - fn handle_post_2_unit_test() { + #[actix_rt::test] + async fn handle_post_2_unit_test() { let state = TestRequest::default() .data(AppState { foo: "bar".to_string(), @@ -150,8 +147,7 @@ mod tests { let params = Form(MyParams { name: "John".to_string(), }); - let result = handle_post_2(state, params); - let resp = block_on(result).unwrap(); + let resp = handle_post_2(state, params).await; assert_eq!(resp.status(), StatusCode::OK); assert_eq!( @@ -164,14 +160,16 @@ mod tests { ); } - #[test] - fn handle_post_2_integration_test() { - let mut app = test::init_service(App::new().configure(app_config)); + #[actix_rt::test] + async fn handle_post_2_integration_test() { + let mut app = test::init_service(App::new().configure(app_config)).await; let req = test::TestRequest::post() .uri("/post2") - .set_form(&MyParams {name: "John".to_string()}) + .set_form(&MyParams { + name: "John".to_string(), + }) .to_request(); - let resp: ServiceResponse = block_on(app.call(req)).unwrap(); + let resp: ServiceResponse = app.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!( @@ -184,16 +182,18 @@ mod tests { ); } - #[test] - fn handle_post_3_unit_test() { + #[actix_rt::test] + async fn handle_post_3_unit_test() { let req = TestRequest::default().to_http_request(); let params = Form(MyParams { name: "John".to_string(), }); - let result = handle_post_3(req.clone(), params); - let resp = match block_on(result.respond_to(&req)) { + let result = handle_post_3(req.clone(), params).await; + let resp = match result.respond_to(&req).await { Ok(t) => t, - Err(_) => HttpResponseBuilder::new(StatusCode::INTERNAL_SERVER_ERROR).finish(), + Err(_) => { + HttpResponseBuilder::new(StatusCode::INTERNAL_SERVER_ERROR).finish() + } }; assert_eq!(resp.status(), StatusCode::OK); @@ -201,29 +201,25 @@ mod tests { resp.headers().get(CONTENT_TYPE).unwrap(), HeaderValue::from_static("text/plain") ); - assert_eq!( - resp.body().as_str(), - "Your name is John" - ); + assert_eq!(resp.body().as_str(), "Your name is John"); } - #[test] - fn handle_post_3_integration_test() { - let mut app = test::init_service(App::new().configure(app_config)); + #[actix_rt::test] + async fn handle_post_3_integration_test() { + let mut app = test::init_service(App::new().configure(app_config)).await; let req = test::TestRequest::post() .uri("/post3") - .set_form(&MyParams {name: "John".to_string()}) + .set_form(&MyParams { + name: "John".to_string(), + }) .to_request(); - let resp: ServiceResponse = block_on(app.call(req)).unwrap(); + let resp: ServiceResponse = app.call(req).await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert_eq!( resp.headers().get(CONTENT_TYPE).unwrap(), HeaderValue::from_static("text/plain") ); - assert_eq!( - resp.response().body().as_str(), - "Your name is John" - ); + assert_eq!(resp.response().body().as_str(), "Your name is John"); } } diff --git a/graphql-demo/Cargo.toml b/graphql-demo/Cargo.toml index 311db02..e9c10b8 100644 --- a/graphql-demo/Cargo.toml +++ b/graphql-demo/Cargo.toml @@ -1,14 +1,16 @@ [package] name = "actix-graphql-demo" -version = "0.1.0" +version = "2.0.0" authors = ["Dwi Sulfahnur "] edition = "2018" +workspace = ".." # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-web = "1.0" -futures = "0.1" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +futures = "0.3.1" juniper = "0.13" diff --git a/graphql-demo/src/handlers.rs b/graphql-demo/src/handlers.rs index 29372d0..edc06c1 100644 --- a/graphql-demo/src/handlers.rs +++ b/graphql-demo/src/handlers.rs @@ -1,44 +1,42 @@ use std::sync::Arc; -use actix_web::{Error, HttpResponse, web}; -use futures::Future; +use actix_web::{web, Error, HttpResponse}; use juniper::http::graphiql::graphiql_source; use juniper::http::GraphQLRequest; use crate::db::Pool; -use crate::schemas::root::{Context, create_schema, Schema}; +use crate::schemas::root::{create_schema, Context, Schema}; -pub fn graphql( +pub async fn graphql( pool: web::Data, schema: web::Data>, data: web::Json, -) -> impl Future { +) -> Result { let ctx = Context { dbpool: pool.get_ref().to_owned(), }; - web::block(move || { + let res = web::block(move || { let res = data.execute(&schema, &ctx); Ok::<_, serde_json::error::Error>(serde_json::to_string(&res)?) }) - .map_err(Error::from) - .and_then(|res| { - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(res)) - }) + .await + .map_err(Error::from)?; + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(res)) } -pub fn graphql_playground() -> HttpResponse { +pub async fn graphql_playground() -> HttpResponse { HttpResponse::Ok() .content_type("text/html; charset=utf-8") .body(graphiql_source("/graphql")) } - pub fn register(config: &mut web::ServiceConfig) { let schema = std::sync::Arc::new(create_schema()); config .data(schema) - .route("/graphql", web::post().to_async(graphql)) + .route("/graphql", web::post().to(graphql)) .route("/graphiql", web::get().to(graphql_playground)); -} \ No newline at end of file +} diff --git a/graphql-demo/src/main.rs b/graphql-demo/src/main.rs index 9f0ab66..57ded8f 100644 --- a/graphql-demo/src/main.rs +++ b/graphql-demo/src/main.rs @@ -4,16 +4,17 @@ extern crate r2d2; extern crate r2d2_mysql; extern crate serde_json; -use actix_web::{App, HttpServer, middleware, web}; +use actix_web::{middleware, web, App, HttpServer}; use crate::db::get_db_pool; -use crate::handlers::{register}; +use crate::handlers::register; +mod db; mod handlers; mod schemas; -mod db; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { dotenv::dotenv().ok(); std::env::set_var("RUST_LOG", "actix_web=info,info"); env_logger::init(); @@ -24,8 +25,9 @@ fn main() -> std::io::Result<()> { .data(pool.clone()) .wrap(middleware::Logger::default()) .configure(register) - .default_service(web::to(|| "404")) + .default_service(web::to(|| async { "404" })) }) - .bind("127.0.0.1:8080")? - .run() -} \ No newline at end of file + .bind("127.0.0.1:8080")? + .start() + .await +} diff --git a/graphql-demo/src/schemas/mod.rs b/graphql-demo/src/schemas/mod.rs index b064732..01b88de 100644 --- a/graphql-demo/src/schemas/mod.rs +++ b/graphql-demo/src/schemas/mod.rs @@ -1,3 +1,3 @@ +pub mod product; pub mod root; pub mod user; -pub mod product; diff --git a/graphql-demo/src/schemas/product.rs b/graphql-demo/src/schemas/product.rs index da0bb8d..0fe3744 100644 --- a/graphql-demo/src/schemas/product.rs +++ b/graphql-demo/src/schemas/product.rs @@ -1,5 +1,5 @@ use juniper; -use mysql::{Error as DBError, from_row, params, Row}; +use mysql::{from_row, params, Error as DBError, Row}; use crate::schemas::root::Context; use crate::schemas::user::User; @@ -36,7 +36,7 @@ impl Product { ); if let Err(err) = user { None - }else{ + } else { let (id, name, email) = from_row(user.unwrap().unwrap()); Some(User { id, name, email }) } @@ -49,4 +49,4 @@ pub struct ProductInput { pub user_id: String, pub name: String, pub price: f64, -} \ No newline at end of file +} diff --git a/graphql-demo/src/schemas/root.rs b/graphql-demo/src/schemas/root.rs index ef223e2..ad3e890 100644 --- a/graphql-demo/src/schemas/root.rs +++ b/graphql-demo/src/schemas/root.rs @@ -1,6 +1,6 @@ -use juniper::{FieldError, FieldResult, RootNode}; use juniper; -use mysql::{Error as DBError, from_row, params, Row}; +use juniper::{FieldError, FieldResult, RootNode}; +use mysql::{from_row, params, Error as DBError, Row}; use crate::db::Pool; @@ -8,7 +8,7 @@ use super::product::{Product, ProductInput}; use super::user::{User, UserInput}; pub struct Context { - pub dbpool: Pool + pub dbpool: Pool, } impl juniper::Context for Context {} @@ -20,13 +20,18 @@ impl QueryRoot { #[graphql(description = "List of all users")] fn users(context: &Context) -> FieldResult> { let mut conn = context.dbpool.get().unwrap(); - let users = conn.prep_exec("select * from user", ()) + let users = conn + .prep_exec("select * from user", ()) .map(|result| { - result.map(|x| x.unwrap()).map(|mut row| { - let (id, name, email) = from_row(row); - User { id, name, email } - }).collect() - }).unwrap(); + result + .map(|x| x.unwrap()) + .map(|mut row| { + let (id, name, email) = from_row(row); + User { id, name, email } + }) + .collect() + }) + .unwrap(); Ok(users) } @@ -34,10 +39,8 @@ impl QueryRoot { fn user(context: &Context, id: String) -> FieldResult { let mut conn = context.dbpool.get().unwrap(); - let user: Result, DBError> = conn.first_exec( - "SELECT * FROM user WHERE id=:id", - params! {"id" => id}, - ); + let user: Result, DBError> = + conn.first_exec("SELECT * FROM user WHERE id=:id", params! {"id" => id}); if let Err(err) = user { return Err(FieldError::new( @@ -53,23 +56,31 @@ impl QueryRoot { #[graphql(description = "List of all users")] fn products(context: &Context) -> FieldResult> { let mut conn = context.dbpool.get().unwrap(); - let products = conn.prep_exec("select * from product", ()) + let products = conn + .prep_exec("select * from product", ()) .map(|result| { - result.map(|x| x.unwrap()).map(|mut row| { - let (id, user_id, name, price) = from_row(row); - Product { id, user_id, name, price } - }).collect() - }).unwrap(); + result + .map(|x| x.unwrap()) + .map(|mut row| { + let (id, user_id, name, price) = from_row(row); + Product { + id, + user_id, + name, + price, + } + }) + .collect() + }) + .unwrap(); Ok(products) } #[graphql(description = "Get Single user reference by user ID")] fn product(context: &Context, id: String) -> FieldResult { let mut conn = context.dbpool.get().unwrap(); - let product: Result, DBError> = conn.first_exec( - "SELECT * FROM user WHERE id=:id", - params! {"id" => id}, - ); + let product: Result, DBError> = + conn.first_exec("SELECT * FROM user WHERE id=:id", params! {"id" => id}); if let Err(err) = product { return Err(FieldError::new( "Product Not Found", @@ -78,11 +89,15 @@ impl QueryRoot { } let (id, user_id, name, price) = from_row(product.unwrap().unwrap()); - Ok(Product { id, user_id, name, price }) + Ok(Product { + id, + user_id, + name, + price, + }) } } - pub struct MutationRoot; #[juniper::object(Context = Context)] @@ -101,17 +116,15 @@ impl MutationRoot { ); match insert { - Ok(opt_row) => { - Ok(User { - id: new_id, - name: user.name, - email: user.email, - }) - } + Ok(opt_row) => Ok(User { + id: new_id, + name: user.name, + email: user.email, + }), Err(err) => { let msg = match err { DBError::MySqlError(err) => err.message, - _ => "internal error".to_owned() + _ => "internal error".to_owned(), }; Err(FieldError::new( "Failed to create new user", @@ -136,18 +149,16 @@ impl MutationRoot { ); match insert { - Ok(opt_row) => { - Ok(Product { - id: new_id, - user_id: product.user_id, - name: product.name, - price: product.price, - }) - } + Ok(opt_row) => Ok(Product { + id: new_id, + user_id: product.user_id, + name: product.name, + price: product.price, + }), Err(err) => { let msg = match err { DBError::MySqlError(err) => err.message, - _ => "internal error".to_owned() + _ => "internal error".to_owned(), }; Err(FieldError::new( "Failed to create new product", diff --git a/graphql-demo/src/schemas/user.rs b/graphql-demo/src/schemas/user.rs index 120bd17..4d71ae8 100644 --- a/graphql-demo/src/schemas/user.rs +++ b/graphql-demo/src/schemas/user.rs @@ -19,27 +19,42 @@ pub struct UserInput { pub email: String, } - #[juniper::object(Context = Context)] impl User { - fn id(&self) -> &str { &self.id } + fn id(&self) -> &str { + &self.id + } fn name(&self) -> &str { &self.name } - fn email(&self) -> &str { &self.email } + fn email(&self) -> &str { + &self.email + } fn products(&self, context: &Context) -> Vec { let mut conn = context.dbpool.get().unwrap(); - let products = conn.prep_exec( - "select * from product where user_id=:user_id", params! { - "user_id" => &self.id - }) + let products = conn + .prep_exec( + "select * from product where user_id=:user_id", + params! { + "user_id" => &self.id + }, + ) .map(|result| { - result.map(|x| x.unwrap()).map(|mut row| { - let (id, user_id, name, price) = from_row(row); - Product { id, user_id, name, price } - }).collect() - }).unwrap(); + result + .map(|x| x.unwrap()) + .map(|mut row| { + let (id, user_id, name, price) = from_row(row); + Product { + id, + user_id, + name, + price, + } + }) + .collect() + }) + .unwrap(); products } } diff --git a/hello-world/Cargo.toml b/hello-world/Cargo.toml index 396062b..a678c6e 100644 --- a/hello-world/Cargo.toml +++ b/hello-world/Cargo.toml @@ -1,10 +1,11 @@ [package] name = "hello-world" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" env_logger = "0.6" diff --git a/hello-world/src/main.rs b/hello-world/src/main.rs index b671ff0..981e6d1 100644 --- a/hello-world/src/main.rs +++ b/hello-world/src/main.rs @@ -1,11 +1,12 @@ use actix_web::{middleware, web, App, HttpRequest, HttpServer}; -fn index(req: HttpRequest) -> &'static str { +async fn index(req: HttpRequest) -> &'static str { println!("REQ: {:?}", req); "Hello world!" } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -13,28 +14,27 @@ fn main() -> std::io::Result<()> { App::new() // enable logger .wrap(middleware::Logger::default()) - .service(web::resource("/index.html").to(|| "Hello world!")) + .service(web::resource("/index.html").to(|| async { "Hello world!" })) .service(web::resource("/").to(index)) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } #[cfg(test)] mod tests { use super::*; use actix_web::dev::Service; - use actix_web::{test, web, App, http, Error}; + use actix_web::{http, test, web, App, Error}; - #[test] - fn test_index() -> Result<(), Error> { + #[actix_rt::test] + async fn test_index() -> Result<(), Error> { let app = App::new().route("/", web::get().to(index)); - let mut app = test::init_service(app); + let mut app = test::init_service(app).await; - let req = test::TestRequest::get() - .uri("/") - .to_request(); - let resp = test::block_on(app.call(req)).unwrap(); + let req = test::TestRequest::get().uri("/").to_request(); + let resp = app.call(req).await.unwrap(); assert_eq!(resp.status(), http::StatusCode::OK); diff --git a/http-proxy/Cargo.toml b/http-proxy/Cargo.toml index a2c8740..d4f8f3a 100644 --- a/http-proxy/Cargo.toml +++ b/http-proxy/Cargo.toml @@ -1,14 +1,14 @@ [package] name = "http-proxy" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim ", "Rotem Yaari "] workspace = ".." edition = "2018" [dependencies] -actix-rt = "0.2" -actix-web = { version = "1.0.0", features=["ssl"] } +actix-rt = "1.0.0-alpha.3" +actix-web = { version = "2.0.0-alpha.3", features=["openssl"] } clap = "2.32.0" -futures = "0.1.25" +futures = "0.3.1" failure = "0.1.3" url = "1.7.1" diff --git a/http-proxy/src/main.rs b/http-proxy/src/main.rs index 51f4440..b5f50fc 100644 --- a/http-proxy/src/main.rs +++ b/http-proxy/src/main.rs @@ -1,17 +1,16 @@ +use std::net::ToSocketAddrs; + use actix_web::client::Client; use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use clap::{value_t, Arg}; -use futures::stream::Stream; -use futures::Future; -use std::net::ToSocketAddrs; use url::Url; -fn forward( +async fn forward( req: HttpRequest, body: web::Bytes, url: web::Data, client: web::Data, -) -> impl Future { +) -> Result { let mut new_url = url.get_ref().clone(); new_url.set_path(req.uri().path()); new_url.set_query(req.uri().query()); @@ -27,28 +26,22 @@ fn forward( forwarded_req }; - forwarded_req - .send_body(body) - .map_err(Error::from) - .map(|mut res| { - let mut client_resp = HttpResponse::build(res.status()); - // Remove `Connection` as per - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection#Directives - for (header_name, header_value) in - res.headers().iter().filter(|(h, _)| *h != "connection") - { - client_resp.header(header_name.clone(), header_value.clone()); - } - res.body() - .into_stream() - .concat2() - .map(move |b| client_resp.body(b)) - .map_err(|e| e.into()) - }) - .flatten() + let mut res = forwarded_req.send_body(body).await.map_err(Error::from)?; + + let mut client_resp = HttpResponse::build(res.status()); + // Remove `Connection` as per + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection#Directives + for (header_name, header_value) in + res.headers().iter().filter(|(h, _)| *h != "connection") + { + client_resp.header(header_name.clone(), header_value.clone()); + } + + Ok(client_resp.body(res.body().await?)) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { let matches = clap::App::new("HTTP Proxy") .arg( Arg::with_name("listen_addr") @@ -102,9 +95,10 @@ fn main() -> std::io::Result<()> { .data(Client::new()) .data(forward_url.clone()) .wrap(middleware::Logger::default()) - .default_service(web::route().to_async(forward)) + .default_service(web::route().to(forward)) }) .bind((listen_addr, listen_port))? .system_exit() - .run() + .start() + .await } diff --git a/json/Cargo.toml b/json/Cargo.toml index d7196f2..c0ba58d 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -6,10 +6,11 @@ workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" -bytes = "0.4" -futures = "0.1" +bytes = "0.5.2" +futures = "0.3.1" env_logger = "*" serde = "1.0" diff --git a/json/src/main.rs b/json/src/main.rs index 293cf5e..791f574 100644 --- a/json/src/main.rs +++ b/json/src/main.rs @@ -1,8 +1,8 @@ use actix_web::{ error, middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer, }; -use bytes::BytesMut; -use futures::{Future, Stream}; +use bytes::{Bytes, BytesMut}; +use futures::StreamExt; use json::JsonValue; use serde_derive::{Deserialize, Serialize}; @@ -13,13 +13,13 @@ struct MyObj { } /// This handler uses json extractor -fn index(item: web::Json) -> HttpResponse { +async fn index(item: web::Json) -> HttpResponse { println!("model: {:?}", &item); HttpResponse::Ok().json(item.0) // <- send response } /// This handler uses json extractor with limit -fn extract_item(item: web::Json, req: HttpRequest) -> HttpResponse { +async fn extract_item(item: web::Json, req: HttpRequest) -> HttpResponse { println!("request: {:?}", req); println!("model: {:?}", item); @@ -29,50 +29,38 @@ fn extract_item(item: web::Json, req: HttpRequest) -> HttpResponse { const MAX_SIZE: usize = 262_144; // max payload size is 256k /// This handler manually load request payload and parse json object -fn index_manual( - payload: web::Payload, -) -> impl Future { +async fn index_manual(mut payload: web::Payload) -> Result { // payload is a stream of Bytes objects - payload - // `Future::from_err` acts like `?` in that it coerces the error type from - // the future into the final error type - .from_err() - // `fold` will asynchronously read each chunk of the request body and - // call supplied closure, then it resolves to result of closure - .fold(BytesMut::new(), move |mut body, chunk| { - // limit max size of in-memory payload - if (body.len() + chunk.len()) > MAX_SIZE { - Err(error::ErrorBadRequest("overflow")) - } else { - body.extend_from_slice(&chunk); - Ok(body) - } - }) - // `Future::and_then` can be used to merge an asynchronous workflow with a - // synchronous workflow - .and_then(|body| { - // body is loaded, now we can deserialize serde-json - let obj = serde_json::from_slice::(&body)?; - Ok(HttpResponse::Ok().json(obj)) // <- send response - }) + let mut body = BytesMut::new(); + while let Some(chunk) = payload.next().await { + let chunk = chunk?; + // limit max size of in-memory payload + if (body.len() + chunk.len()) > MAX_SIZE { + return Err(error::ErrorBadRequest("overflow")); + } + body.extend_from_slice(&chunk); + } + + // body is loaded, now we can deserialize serde-json + let obj = serde_json::from_slice::(&body)?; + Ok(HttpResponse::Ok().json(obj)) // <- send response } /// This handler manually load request payload and parse json-rust -fn index_mjsonrust(pl: web::Payload) -> impl Future { - pl.concat2().from_err().and_then(|body| { - // body is loaded, now we can deserialize json-rust - let result = json::parse(std::str::from_utf8(&body).unwrap()); // return Result - let injson: JsonValue = match result { - Ok(v) => v, - Err(e) => json::object! {"err" => e.to_string() }, - }; - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(injson.dump())) - }) +async fn index_mjsonrust(body: Bytes) -> Result { + // body is loaded, now we can deserialize json-rust + let result = json::parse(std::str::from_utf8(&body).unwrap()); // return Result + let injson: JsonValue = match result { + Ok(v) => v, + Err(e) => json::object! {"err" => e.to_string() }, + }; + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(injson.dump())) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -85,32 +73,34 @@ fn main() -> std::io::Result<()> { .service( web::resource("/extractor2") .data(web::JsonConfig::default().limit(1024)) // <- limit size of the payload (resource level) - .route(web::post().to_async(extract_item)), - ) - .service(web::resource("/manual").route(web::post().to_async(index_manual))) - .service( - web::resource("/mjsonrust").route(web::post().to_async(index_mjsonrust)), + .route(web::post().to(extract_item)), ) + .service(web::resource("/manual").route(web::post().to(index_manual))) + .service(web::resource("/mjsonrust").route(web::post().to(index_mjsonrust))) .service(web::resource("/").route(web::post().to(index))) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } #[cfg(test)] mod tests { use super::*; use actix_web::dev::Service; - use actix_web::{test, web, App, http}; + use actix_web::{http, test, web, App}; #[test] - fn test_index() -> Result<(), Error> { + fn test_index() -> Result<(), Error> { let app = App::new().route("/", web::post().to(index)); let mut app = test::init_service(app); let req = test::TestRequest::post() .uri("/") - .set_json(&MyObj { name: "my-name".to_owned(), number: 43 }) + .set_json(&MyObj { + name: "my-name".to_owned(), + number: 43, + }) .to_request(); let resp = test::block_on(app.call(req)).unwrap(); diff --git a/json_error/Cargo.toml b/json_error/Cargo.toml index 59f6ff8..88c59b7 100644 --- a/json_error/Cargo.toml +++ b/json_error/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "json_error" -version = "0.1.0" +version = "1.0.0" authors = ["Kai Yao "] edition = "2018" [dependencies] -actix = "0.8" -actix-web = "1.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" failure = "0.1" -futures = "0.1" +futures = "0.3" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/json_error/src/main.rs b/json_error/src/main.rs index 6af1c10..85a0421 100644 --- a/json_error/src/main.rs +++ b/json_error/src/main.rs @@ -1,16 +1,12 @@ // This example is meant to show how to automatically generate a json error response when something goes wrong. - -use actix::System; -use actix_web::http::StatusCode; -use actix_web::web::{get, resource, HttpRequest, HttpResponse}; -use actix_web::{App, HttpServer, ResponseError}; -use futures::future::err; -use futures::Future; -use serde::Serialize; -use serde_json::{json, to_string_pretty}; use std::fmt::{Display, Formatter, Result as FmtResult}; use std::io; +use actix_web::http::StatusCode; +use actix_web::{web, App, HttpServer, ResponseError}; +use serde::Serialize; +use serde_json::{json, to_string_pretty}; + #[derive(Debug, Serialize)] struct Error { msg: String, @@ -25,29 +21,30 @@ impl Display for Error { impl ResponseError for Error { // builds the actual response to send back when an error occurs - fn render_response(&self) -> HttpResponse { + fn error_response(&self) -> web::HttpResponse { let err_json = json!({ "error": self.msg }); - HttpResponse::build(StatusCode::from_u16(self.status).unwrap()).json(err_json) + web::HttpResponse::build(StatusCode::from_u16(self.status).unwrap()) + .json(err_json) } } -fn index(_: HttpRequest) -> impl Future { - err(Error { +async fn index() -> Result { + Err(Error { msg: "an example error message".to_string(), status: 400, }) } -fn main() -> io::Result<()> { - let sys = System::new("json_error_example"); +#[actix_rt::main] +async fn main() -> io::Result<()> { let ip_address = "127.0.0.1:8000"; - - HttpServer::new(|| App::new().service(resource("/").route(get().to_async(index)))) - .bind(ip_address) - .expect("Can not bind to port 8000") - .start(); - println!("Running server on {}", ip_address); - sys.run() + HttpServer::new(|| { + App::new().service(web::resource("/").route(web::get().to(index))) + }) + .bind(ip_address) + .expect("Can not bind to port 8000") + .start() + .await } diff --git a/jsonrpc/Cargo.toml b/jsonrpc/Cargo.toml index 8b1a3dc..2401a37 100644 --- a/jsonrpc/Cargo.toml +++ b/jsonrpc/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "jsonrpc-example" -version = "0.1.0" +version = "2.0.0" authors = ["mohanson "] edition = "2018" workspace = ".." [dependencies] -actix = "0.8.2" -actix-web = "1.0.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +bytes = "0.5" env_logger = "0.6" -futures = "0.1.23" -futures-timer = "0.1" +futures = "0.3.1" log = "0.4" serde = "1.0" serde_derive = "1.0" diff --git a/jsonrpc/src/main.rs b/jsonrpc/src/main.rs index fd4f095..8a5260b 100644 --- a/jsonrpc/src/main.rs +++ b/jsonrpc/src/main.rs @@ -1,11 +1,12 @@ use std::error; -use std::sync::Arc; -use std::sync::RwLock; +use std::pin::Pin; +use std::sync::{Arc, RwLock}; use std::time::Duration; +use actix_rt::time::delay_for; use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; -use futures::{future, Future, Stream}; -use futures_timer::Delay; +use bytes::Bytes; +use futures::{Future, FutureExt}; use serde_json; use serde_json::Value; @@ -13,41 +14,36 @@ use serde_json::Value; mod convention; /// The main handler for JSONRPC server. -fn rpc_handler( - req: HttpRequest, - payload: web::Payload, -) -> impl Future { - payload.concat2().from_err().and_then(move |body| { - let reqjson: convention::Request = match serde_json::from_slice(body.as_ref()) { - Ok(ok) => ok, - Err(_) => { - let r = convention::Response { - jsonrpc: String::from(convention::JSONRPC_VERSION), - result: Value::Null, - error: Some(convention::ErrorData::std(-32700)), - id: Value::Null, - }; - return Ok(HttpResponse::Ok() - .content_type("application/json") - .body(r.dump())); - } - }; - let app_state = req.app_data().unwrap(); - let mut result = convention::Response::default(); - result.id = reqjson.id.clone(); - - match rpc_select(&app_state, reqjson.method.as_str(), reqjson.params) { - Ok(ok) => result.result = ok, - Err(e) => result.error = Some(e), +async fn rpc_handler(req: HttpRequest, body: Bytes) -> Result { + let reqjson: convention::Request = match serde_json::from_slice(body.as_ref()) { + Ok(ok) => ok, + Err(_) => { + let r = convention::Response { + jsonrpc: String::from(convention::JSONRPC_VERSION), + result: Value::Null, + error: Some(convention::ErrorData::std(-32700)), + id: Value::Null, + }; + return Ok(HttpResponse::Ok() + .content_type("application/json") + .body(r.dump())); } + }; + let app_state = req.app_data().unwrap(); + let mut result = convention::Response::default(); + result.id = reqjson.id.clone(); - Ok(HttpResponse::Ok() - .content_type("application/json") - .body(result.dump())) - }) + match rpc_select(&app_state, reqjson.method.as_str(), reqjson.params).await { + Ok(ok) => result.result = ok, + Err(e) => result.error = Some(e), + } + + Ok(HttpResponse::Ok() + .content_type("application/json") + .body(result.dump())) } -fn rpc_select( +async fn rpc_select( app_state: &AppState, method: &str, params: Vec, @@ -66,7 +62,7 @@ fn rpc_select( .read() .unwrap() .wait(params[0].as_u64().unwrap()) - .wait() + .await { Ok(ok) => Ok(Value::from(ok)), Err(e) => Err(convention::ErrorData::new(500, &format!("{:?}", e)[..])), @@ -89,7 +85,7 @@ pub trait ImplNetwork { fn wait( &self, d: u64, - ) -> Box>>; + ) -> Pin>>>>; fn get(&self) -> u32; fn inc(&mut self); @@ -113,12 +109,12 @@ impl ImplNetwork for ObjNetwork { fn wait( &self, d: u64, - ) -> Box>> { - if let Err(e) = Delay::new(Duration::from_secs(d)).wait() { - let e: Box = Box::new(e); - return Box::new(future::err(e)); - }; - Box::new(future::ok(String::from("pong"))) + ) -> Pin>>>> { + async move { + delay_for(Duration::from_secs(d)).await; + Ok(String::from("pong")) + } + .boxed_local() } fn get(&self) -> u32 { @@ -141,24 +137,22 @@ impl AppState { } } -fn main() { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "info"); env_logger::init(); let network = Arc::new(RwLock::new(ObjNetwork::new())); - let sys = actix::System::new("actix_jrpc"); HttpServer::new(move || { let app_state = AppState::new(network.clone()); App::new() .data(app_state) .wrap(middleware::Logger::default()) - .service(web::resource("/").route(web::post().to_async(rpc_handler))) + .service(web::resource("/").route(web::post().to(rpc_handler))) }) .bind("127.0.0.1:8080") .unwrap() - .workers(1) - .start(); - - let _ = sys.run(); + .start() + .await } diff --git a/middleware/Cargo.toml b/middleware/Cargo.toml index 84e6643..7452455 100644 --- a/middleware/Cargo.toml +++ b/middleware/Cargo.toml @@ -1,13 +1,15 @@ [package] name = "middleware-example" -version = "0.1.0" +version = "1.0.0" authors = ["Gorm Casper ", "Sven-Hendrik Haase "] edition = "2018" workspace = ".." [dependencies] -actix-service = "0.4.1" -actix-web = "1.0.0" -futures = "0.1.25" +actix-service = "1.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" +futures = "0.3.1" env_logger = "0.6" -bytes = "0.4" +bytes = "0.5" +pin-project = "0.4.6" \ No newline at end of file diff --git a/middleware/src/main.rs b/middleware/src/main.rs index d4ff9e3..0fc3db0 100644 --- a/middleware/src/main.rs +++ b/middleware/src/main.rs @@ -1,17 +1,18 @@ -use actix_web::{web, App, HttpServer}; use actix_service::Service; -use futures::future::Future; +use actix_web::{web, App, HttpServer}; +use futures::future::FutureExt; -#[allow(dead_code)] -mod redirect; #[allow(dead_code)] mod read_request_body; #[allow(dead_code)] mod read_response_body; #[allow(dead_code)] +mod redirect; +#[allow(dead_code)] mod simple; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=debug"); env_logger::init(); @@ -30,14 +31,15 @@ fn main() -> std::io::Result<()> { }) }) .service(web::resource("/login").to(|| { - "You are on /login. Go to src/redirect.rs to change this behavior." + async { + "You are on /login. Go to src/redirect.rs to change this behavior." + } + })) + .service(web::resource("/").to(|| { + async { "Hello, middleware! Check the console where the server is run." } })) - .service( - web::resource("/").to(|| { - "Hello, middleware! Check the console where the server is run." - }), - ) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/middleware/src/read_request_body.rs b/middleware/src/read_request_body.rs index 48aed2c..c18a7a4 100644 --- a/middleware/src/read_request_body.rs +++ b/middleware/src/read_request_body.rs @@ -1,12 +1,13 @@ +use std::cell::RefCell; +use std::pin::Pin; +use std::rc::Rc; +use std::task::{Context, Poll}; + use actix_service::{Service, Transform}; -use actix_web::error::PayloadError; use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error, HttpMessage}; use bytes::BytesMut; -use futures::future::{ok, FutureResult}; -use futures::stream::Stream; -use futures::{Future, Poll}; -use std::cell::RefCell; -use std::rc::Rc; +use futures::future::{ok, Future, Ready}; +use futures::stream::StreamExt; pub struct Logging; @@ -21,7 +22,7 @@ where type Error = Error; type InitError = (); type Transform = LoggingMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(LoggingMiddleware { @@ -45,26 +46,27 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = Pin>>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, mut req: ServiceRequest) -> Self::Future { let mut svc = self.service.clone(); - Box::new( - req.take_payload() - .fold(BytesMut::new(), move |mut body, chunk| { - body.extend_from_slice(&chunk); - Ok::<_, PayloadError>(body) - }) - .map_err(|e| e.into()) - .and_then(move |bytes| { - println!("request body: {:?}", bytes); - svc.call(req).and_then(|res| Ok(res)) - }), - ) + Box::pin(async move { + let mut body = BytesMut::new(); + let mut stream = req.take_payload(); + while let Some(chunk) = stream.next().await { + body.extend_from_slice(&chunk?); + } + + println!("request body: {:?}", body); + let res = svc.call(req).await?; + + println!("response: {:?}", res.headers()); + Ok(res) + }) } } diff --git a/middleware/src/read_response_body.rs b/middleware/src/read_response_body.rs index 26497be..54834dc 100644 --- a/middleware/src/read_response_body.rs +++ b/middleware/src/read_response_body.rs @@ -1,11 +1,13 @@ +use std::future::Future; +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_service::{Service, Transform}; use actix_web::body::{BodySize, MessageBody, ResponseBody}; -use std::marker::PhantomData; use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; use bytes::{Bytes, BytesMut}; -use futures::Async; -use futures::future::{ok, FutureResult}; -use futures::{Future, Poll}; +use futures::future::{ok, Ready}; pub struct Logging; @@ -19,12 +21,10 @@ where type Error = Error; type InitError = (); type Transform = LoggingMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { - ok(LoggingMiddleware { - service, - }) + ok(LoggingMiddleware { service }) } } @@ -42,8 +42,8 @@ where type Error = Error; type Future = WrapperStream; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { @@ -54,11 +54,13 @@ where } } +#[pin_project::pin_project] pub struct WrapperStream where B: MessageBody, S: Service, { + #[pin] fut: S::Future, _t: PhantomData<(B,)>, } @@ -68,18 +70,19 @@ where B: MessageBody, S: Service, Error = Error>, { - type Item = ServiceResponse>; - type Error = Error; + type Output = Result>, Error>; - fn poll(&mut self) -> Poll { - let res = futures::try_ready!(self.fut.poll()); + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let res = futures::ready!(self.project().fut.poll(cx)); - Ok(Async::Ready(res.map_body(move |_, body| { - ResponseBody::Body(BodyLogger { - body, - body_accum: BytesMut::new(), + Poll::Ready(res.map(|res| { + res.map_body(move |_, body| { + ResponseBody::Body(BodyLogger { + body, + body_accum: BytesMut::new(), + }) }) - }))) + })) } } @@ -99,13 +102,15 @@ impl MessageBody for BodyLogger { self.body.size() } - fn poll_next(&mut self) -> Poll, Error> { - match self.body.poll_next()? { - Async::Ready(Some(chunk)) => { + fn poll_next(&mut self, cx: &mut Context<'_>) -> Poll>> { + match self.body.poll_next(cx) { + Poll::Ready(Some(Ok(chunk))) => { self.body_accum.extend_from_slice(&chunk); - Ok(Async::Ready(Some(chunk))) + Poll::Ready(Some(Ok(chunk))) } - val => Ok(val), + Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, } } } diff --git a/middleware/src/redirect.rs b/middleware/src/redirect.rs index f5c0e61..bbd56d2 100644 --- a/middleware/src/redirect.rs +++ b/middleware/src/redirect.rs @@ -1,8 +1,9 @@ +use std::task::{Context, Poll}; + use actix_service::{Service, Transform}; use actix_web::dev::{ServiceRequest, ServiceResponse}; use actix_web::{http, Error, HttpResponse}; -use futures::future::{ok, Either, FutureResult}; -use futures::Poll; +use futures::future::{ok, Either, Ready}; pub struct CheckLogin; @@ -16,7 +17,7 @@ where type Error = Error; type InitError = (); type Transform = CheckLoginMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(CheckLoginMiddleware { service }) @@ -34,10 +35,10 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Either>; + type Future = Either>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { @@ -46,13 +47,13 @@ where let is_logged_in = false; // Change this to see the change in outcome in the browser if is_logged_in { - Either::A(self.service.call(req)) + Either::Left(self.service.call(req)) } else { // Don't forward to /login if we are already on /login if req.path() == "/login" { - Either::A(self.service.call(req)) + Either::Left(self.service.call(req)) } else { - Either::B(ok(req.into_response( + Either::Right(ok(req.into_response( HttpResponse::Found() .header(http::header::LOCATION, "/login") .finish() diff --git a/middleware/src/simple.rs b/middleware/src/simple.rs index a96e156..d9333cc 100644 --- a/middleware/src/simple.rs +++ b/middleware/src/simple.rs @@ -1,7 +1,10 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + use actix_service::{Service, Transform}; use actix_web::{dev::ServiceRequest, dev::ServiceResponse, Error}; -use futures::future::{ok, FutureResult}; -use futures::{Future, Poll}; +use futures::future::{ok, Ready}; +use futures::Future; // There are two steps in middleware processing. // 1. Middleware initialization, middleware factory gets called with @@ -23,7 +26,7 @@ where type Error = Error; type InitError = (); type Transform = SayHiMiddleware; - type Future = FutureResult; + type Future = Ready>; fn new_transform(&self, service: S) -> Self::Future { ok(SayHiMiddleware { service }) @@ -43,18 +46,22 @@ where type Request = ServiceRequest; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = Pin>>>; - fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.service.poll_ready() + fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll> { + self.service.poll_ready(cx) } fn call(&mut self, req: ServiceRequest) -> Self::Future { println!("Hi from start. You requested: {}", req.path()); - Box::new(self.service.call(req).and_then(|res| { + let fut = self.service.call(req); + + Box::pin(async move { + let res = fut.await?; + println!("Hi from response"); Ok(res) - })) + }) } } diff --git a/multipart/Cargo.toml b/multipart/Cargo.toml index 76e8eab..afe802f 100644 --- a/multipart/Cargo.toml +++ b/multipart/Cargo.toml @@ -13,3 +13,4 @@ readme = "README.md" futures = "0.3.1" actix-multipart = "0.2.0-alpha.3" actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" diff --git a/multipart/src/main.rs b/multipart/src/main.rs index 3b902e1..1395557 100644 --- a/multipart/src/main.rs +++ b/multipart/src/main.rs @@ -1,7 +1,7 @@ -use std::io::Write; use actix_multipart::Multipart; use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; -use futures::{StreamExt}; +use futures::StreamExt; +use std::io::Write; async fn save_file(mut payload: Multipart) -> Result { // iterate over multipart stream @@ -38,19 +38,21 @@ fn index() -> HttpResponse { HttpResponse::Ok().body(html) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); std::fs::create_dir_all("./tmp").unwrap(); + let ip = "0.0.0.0:3000"; + HttpServer::new(|| { - App::new() - .wrap(middleware::Logger::default()) - .service( - web::resource("/") - .route(web::get().to(index)) - .route(web::post().to(save_file)), + App::new().wrap(middleware::Logger::default()).service( + web::resource("/") + .route(web::get().to(index)) + .route(web::post().to(save_file)), ) }) .bind(ip)? - .run() + .start() + .await } diff --git a/openssl/Cargo.toml b/openssl/Cargo.toml new file mode 100644 index 0000000..7f65b7a --- /dev/null +++ b/openssl/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "openssl-example" +version = "0.2.0" +authors = ["Nikolay Kim "] +edition = "2018" +workspace = ".." + +[dependencies] +actix-rt = "1.0.0-alpha.3" +actix-web = { version="2.0.0-alpha.3", features=["openssl"] } +env_logger = "0.6" +openssl = "0.10" diff --git a/tls/README.md b/openssl/README.md similarity index 100% rename from tls/README.md rename to openssl/README.md diff --git a/tls/cert.pem b/openssl/cert.pem similarity index 100% rename from tls/cert.pem rename to openssl/cert.pem diff --git a/tls/key.pem b/openssl/key.pem similarity index 100% rename from tls/key.pem rename to openssl/key.pem diff --git a/tls/src/main.rs b/openssl/src/main.rs similarity index 84% rename from tls/src/main.rs rename to openssl/src/main.rs index 7e95309..3ab12f5 100644 --- a/tls/src/main.rs +++ b/openssl/src/main.rs @@ -4,18 +4,19 @@ use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServ use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod}; /// simple handle -fn index(req: HttpRequest) -> Result { +async fn index(req: HttpRequest) -> Result { println!("{:?}", req); Ok(HttpResponse::Ok() .content_type("text/plain") .body("Welcome!")) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=debug"); env_logger::init(); - let sys = actix_rt::System::new("tls-example"); + println!("Started http server: 127.0.0.1:8443"); // load ssl keys let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap(); @@ -37,9 +38,7 @@ fn main() -> io::Result<()> { .finish() }))) }) - .bind_ssl("127.0.0.1:8443", builder)? - .start(); - - println!("Started http server: 127.0.0.1:8443"); - sys.run() + .bind_openssl("127.0.0.1:8443", builder)? + .start() + .await } diff --git a/r2d2/Cargo.toml b/r2d2/Cargo.toml index bfc5146..818db61 100644 --- a/r2d2/Cargo.toml +++ b/r2d2/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "r2d2-example" -version = "0.1.0" +version = "1.0.0" authors = ["Nikolay Kim "] edition = "2018" workspace = ".." [dependencies] -actix-rt = "0.2" -actix-web = "1.0.0" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" -futures = "0.1" +futures = "0.3.1" env_logger = "0.6" uuid = { version = "0.7", features = ["v4"] } diff --git a/r2d2/src/main.rs b/r2d2/src/main.rs index c323784..086025a 100644 --- a/r2d2/src/main.rs +++ b/r2d2/src/main.rs @@ -2,18 +2,17 @@ use std::io; use actix_web::{middleware, web, App, Error, HttpResponse, HttpServer}; -use futures::Future; use r2d2::Pool; use r2d2_sqlite::SqliteConnectionManager; use uuid; /// Async request handler. Ddb pool is stored in application state. -fn index( +async fn index( path: web::Path, db: web::Data>, -) -> impl Future { +) -> Result { // execute sync code in threadpool - web::block(move || { + let res = web::block(move || { let conn = db.get().unwrap(); let uuid = format!("{}", uuid::Uuid::new_v4()); @@ -27,16 +26,16 @@ fn index( row.get::<_, String>(0) }) }) - .then(|res| match res { - Ok(user) => Ok(HttpResponse::Ok().json(user)), - Err(_) => Ok(HttpResponse::InternalServerError().into()), - }) + .await + .map(|user| HttpResponse::Ok().json(user)) + .map_err(|_| HttpResponse::InternalServerError())?; + Ok(res) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=debug"); env_logger::init(); - let sys = actix_rt::System::new("r2d2-example"); // r2d2 pool let manager = SqliteConnectionManager::file("test.db"); @@ -47,10 +46,9 @@ fn main() -> io::Result<()> { App::new() .data(pool.clone()) // <- store db pool in app state .wrap(middleware::Logger::default()) - .route("/{name}", web::get().to_async(index)) + .route("/{name}", web::get().to(index)) }) .bind("127.0.0.1:8080")? - .start(); - - sys.run() + .start() + .await } diff --git a/rustls/Cargo.toml b/rustls/Cargo.toml index 8e70930..482efa7 100644 --- a/rustls/Cargo.toml +++ b/rustls/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustls-example" -version = "0.1.0" +version = "1.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" @@ -11,6 +11,7 @@ path = "src/main.rs" [dependencies] env_logger = "0.5" -rustls = "0.15" -actix-web = { version = "1.0.0", features=["rust-tls"] } -actix-files = "0.1.0" +rustls = "0.16" +actix-web = { version = "2.0.0-alpha.3", features=["rustls"] } +actix-files = "0.2.0-alpha.3" +actix-rt = "1.0.0-alpha.3" diff --git a/rustls/src/main.rs b/rustls/src/main.rs index 8ef4566..abdd45d 100644 --- a/rustls/src/main.rs +++ b/rustls/src/main.rs @@ -2,19 +2,20 @@ use std::fs::File; use std::io::BufReader; use actix_files::Files; -use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; +use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer}; use rustls::internal::pemfile::{certs, rsa_private_keys}; use rustls::{NoClientAuth, ServerConfig}; /// simple handle -fn index(req: HttpRequest) -> Result { +async fn index(req: HttpRequest) -> HttpResponse { println!("{:?}", req); - Ok(HttpResponse::Ok() + HttpResponse::Ok() .content_type("text/plain") - .body("Welcome!")) + .body("Welcome!") } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { if std::env::var("RUST_LOG").is_err() { std::env::set_var("RUST_LOG", "actix_web=info"); } @@ -43,5 +44,6 @@ fn main() -> std::io::Result<()> { .service(Files::new("/static", "static")) }) .bind_rustls("127.0.0.1:8443", config)? - .run() + .start() + .await } diff --git a/server-sent-events/Cargo.toml b/server-sent-events/Cargo.toml index a21366d..60b8c62 100644 --- a/server-sent-events/Cargo.toml +++ b/server-sent-events/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "server-sent-events" -version = "0.1.0" +version = "1.0.0" authors = ["Arve Seljebu"] workspace = ".." edition = "2018" [dependencies] -actix-rt = "0.2" -actix-web = "1.0" +actix-rt = "1.0.0-alpha.3" +actix-web = "2.0.0-alpha.3" env_logger = "0.6" -futures = "0.1" -tokio = "0.1" +futures = "0.3.1" +tokio = "0.2" \ No newline at end of file diff --git a/server-sent-events/src/main.rs b/server-sent-events/src/main.rs index aea38de..15d75ab 100644 --- a/server-sent-events/src/main.rs +++ b/server-sent-events/src/main.rs @@ -1,17 +1,17 @@ -use actix_rt::Arbiter; -use actix_web::error::ErrorInternalServerError; +use std::pin::Pin; +use std::sync::Mutex; +use std::task::{Context, Poll}; +use std::time::Duration; + use actix_web::web::{Bytes, Data, Path}; use actix_web::{web, App, Error, HttpResponse, HttpServer, Responder}; - use env_logger; -use tokio::prelude::*; +use futures::{Stream, StreamExt}; use tokio::sync::mpsc::{channel, Receiver, Sender}; -use tokio::timer::Interval; +use tokio::time::{interval_at, Instant}; -use std::sync::Mutex; -use std::time::{Duration, Instant}; - -fn main() { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { env_logger::init(); let data = Broadcaster::create(); @@ -22,13 +22,12 @@ fn main() { .route("/events", web::get().to(new_client)) .route("/broadcast/{msg}", web::get().to(broadcast)) }) - .bind("127.0.0.1:8080") - .expect("Unable to bind port") - .run() - .unwrap(); + .bind("127.0.0.1:8080")? + .start() + .await } -fn index() -> impl Responder { +async fn index() -> impl Responder { let content = include_str!("index.html"); HttpResponse::Ok() @@ -36,7 +35,7 @@ fn index() -> impl Responder { .body(content) } -fn new_client(broadcaster: Data>) -> impl Responder { +async fn new_client(broadcaster: Data>) -> impl Responder { let rx = broadcaster.lock().unwrap().new_client(); HttpResponse::Ok() @@ -45,7 +44,10 @@ fn new_client(broadcaster: Data>) -> impl Responder { .streaming(rx) } -fn broadcast(msg: Path, broadcaster: Data>) -> impl Responder { +async fn broadcast( + msg: Path, + broadcaster: Data>, +) -> impl Responder { broadcaster.lock().unwrap().send(&msg.into_inner()); HttpResponse::Ok().body("msg sent") @@ -73,14 +75,12 @@ impl Broadcaster { } fn spawn_ping(me: Data>) { - let task = Interval::new(Instant::now(), Duration::from_secs(10)) - .for_each(move |_| { + actix_rt::spawn(async move { + let mut task = interval_at(Instant::now(), Duration::from_secs(10)); + while let Some(_) = task.next().await { me.lock().unwrap().remove_stale_clients(); - Ok(()) - }) - .map_err(|e| panic!("interval errored; err={:?}", e)); - - Arbiter::spawn(task); + } + }) } fn remove_stale_clients(&mut self) { @@ -119,10 +119,16 @@ impl Broadcaster { struct Client(Receiver); impl Stream for Client { - type Item = Bytes; - type Error = Error; + type Item = Result; - fn poll(&mut self) -> Poll, Self::Error> { - self.0.poll().map_err(ErrorInternalServerError) + fn poll_next( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + match Pin::new(&mut self.0).poll_next(cx) { + Poll::Ready(Some(v)) => Poll::Ready(Some(Ok(v))), + Poll::Ready(None) => Poll::Ready(None), + Poll::Pending => Poll::Pending, + } } } diff --git a/simple-auth-server/Cargo.toml b/simple-auth-server/Cargo.toml index 9a96ced..40cb167 100644 --- a/simple-auth-server/Cargo.toml +++ b/simple-auth-server/Cargo.toml @@ -1,20 +1,21 @@ [package] name = "simple-auth-server" -version = "0.1.0" +version = "2.0.0" authors = ["mygnu "] edition = "2018" workspace = ".." [dependencies] -actix-identity = "0.1.0" -actix-web = "1.0.3" +actix-identity = "0.2.0-alpha.1" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" argonautica = "0.2.0" chrono = { version = "0.4.6", features = ["serde"] } -derive_more = "0.15.0" +derive_more = "0.99.0" diesel = { version = "1.4.2", features = ["postgres","uuidv07", "r2d2", "chrono"] } dotenv = "0.14.1" env_logger = "0.6" -futures = "0.1" +futures = "0.3.1" r2d2 = "0.8" lazy_static = "1.3.0" serde = "1.0" @@ -22,4 +23,3 @@ serde_derive = "1.0" serde_json = "1.0" sparkpost = "0.5.2" uuid = { version = "0.7", features = ["serde", "v4"] } - diff --git a/simple-auth-server/src/auth_handler.rs b/simple-auth-server/src/auth_handler.rs index 9c75efc..e2aba86 100644 --- a/simple-auth-server/src/auth_handler.rs +++ b/simple-auth-server/src/auth_handler.rs @@ -1,3 +1,5 @@ +use std::pin::Pin; + use actix_identity::Identity; use actix_web::{ dev::Payload, error::BlockingError, web, Error, FromRequest, HttpRequest, @@ -5,7 +7,7 @@ use actix_web::{ }; use diesel::prelude::*; use diesel::PgConnection; -use futures::Future; +use futures::future::Future; use crate::errors::ServiceError; use crate::models::{Pool, SlimUser, User}; @@ -24,43 +26,47 @@ pub type LoggedUser = SlimUser; impl FromRequest for LoggedUser { type Config = (); type Error = Error; - type Future = Result; + type Future = Pin>>>; fn from_request(req: &HttpRequest, pl: &mut Payload) -> Self::Future { - if let Some(identity) = Identity::from_request(req, pl)?.identity() { - let user: LoggedUser = serde_json::from_str(&identity)?; - return Ok(user); - } - Err(ServiceError::Unauthorized.into()) + let fut = Identity::from_request(req, pl); + + Box::pin(async move { + if let Some(identity) = fut.await?.identity() { + let user: LoggedUser = serde_json::from_str(&identity)?; + return Ok(user); + }; + Err(ServiceError::Unauthorized.into()) + }) } } -pub fn logout(id: Identity) -> HttpResponse { +pub async fn logout(id: Identity) -> HttpResponse { id.forget(); HttpResponse::Ok().finish() } -pub fn login( +pub async fn login( auth_data: web::Json, id: Identity, pool: web::Data, -) -> impl Future { - web::block(move || query(auth_data.into_inner(), pool)).then( - move |res: Result>| match res { - Ok(user) => { - let user_string = serde_json::to_string(&user).unwrap(); - id.remember(user_string); - Ok(HttpResponse::Ok().finish()) - } - Err(err) => match err { - BlockingError::Error(service_error) => Err(service_error), - BlockingError::Canceled => Err(ServiceError::InternalServerError), - }, +) -> Result { + let res = web::block(move || query(auth_data.into_inner(), pool)).await; + + match res { + Ok(user) => { + let user_string = serde_json::to_string(&user).unwrap(); + id.remember(user_string); + Ok(HttpResponse::Ok().finish()) + } + Err(err) => match err { + BlockingError::Error(service_error) => Err(service_error), + BlockingError::Canceled => Err(ServiceError::InternalServerError), }, - ) + } } -pub fn get_me(logged_user: LoggedUser) -> HttpResponse { +pub async fn get_me(logged_user: LoggedUser) -> HttpResponse { HttpResponse::Ok().json(logged_user) } /// Diesel query diff --git a/simple-auth-server/src/invitation_handler.rs b/simple-auth-server/src/invitation_handler.rs index 11b137b..a076ea1 100644 --- a/simple-auth-server/src/invitation_handler.rs +++ b/simple-auth-server/src/invitation_handler.rs @@ -1,6 +1,5 @@ use actix_web::{error::BlockingError, web, HttpResponse}; use diesel::{prelude::*, PgConnection}; -use futures::Future; use crate::email_service::send_invitation; use crate::errors::ServiceError; @@ -11,20 +10,22 @@ pub struct InvitationData { pub email: String, } -pub fn post_invitation( +pub async fn post_invitation( invitation_data: web::Json, pool: web::Data, -) -> impl Future { +) -> Result { // run diesel blocking code - web::block(move || create_invitation(invitation_data.into_inner().email, pool)).then( - |res| match res { - Ok(_) => Ok(HttpResponse::Ok().finish()), - Err(err) => match err { - BlockingError::Error(service_error) => Err(service_error), - BlockingError::Canceled => Err(ServiceError::InternalServerError), - }, + let res = + web::block(move || create_invitation(invitation_data.into_inner().email, pool)) + .await; + + match res { + Ok(_) => Ok(HttpResponse::Ok().finish()), + Err(err) => match err { + BlockingError::Error(service_error) => Err(service_error), + BlockingError::Canceled => Err(ServiceError::InternalServerError), }, - ) + } } fn create_invitation( diff --git a/simple-auth-server/src/main.rs b/simple-auth-server/src/main.rs index 7271ec3..864766f 100644 --- a/simple-auth-server/src/main.rs +++ b/simple-auth-server/src/main.rs @@ -17,7 +17,8 @@ mod register_handler; mod schema; mod utils; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { dotenv::dotenv().ok(); std::env::set_var( "RUST_LOG", @@ -52,22 +53,23 @@ fn main() -> std::io::Result<()> { // everything under '/api/' route .service( web::scope("/api") - .service(web::resource("/invitation").route( - web::post().to_async(invitation_handler::post_invitation), - )) .service( - web::resource("/register/{invitation_id}").route( - web::post().to_async(register_handler::register_user), - ), + web::resource("/invitation") + .route(web::post().to(invitation_handler::post_invitation)), + ) + .service( + web::resource("/register/{invitation_id}") + .route(web::post().to(register_handler::register_user)), ) .service( web::resource("/auth") - .route(web::post().to_async(auth_handler::login)) + .route(web::post().to(auth_handler::login)) .route(web::delete().to(auth_handler::logout)) .route(web::get().to(auth_handler::get_me)), ), ) }) .bind("127.0.0.1:3000")? - .run() + .start() + .await } diff --git a/simple-auth-server/src/register_handler.rs b/simple-auth-server/src/register_handler.rs index 2b43a00..83a656f 100644 --- a/simple-auth-server/src/register_handler.rs +++ b/simple-auth-server/src/register_handler.rs @@ -1,6 +1,5 @@ use actix_web::{error::BlockingError, web, HttpResponse}; use diesel::prelude::*; -use futures::Future; use crate::errors::ServiceError; use crate::models::{Invitation, Pool, SlimUser, User}; @@ -11,25 +10,27 @@ pub struct UserData { pub password: String, } -pub fn register_user( +pub async fn register_user( invitation_id: web::Path, user_data: web::Json, pool: web::Data, -) -> impl Future { - web::block(move || { +) -> Result { + let res = web::block(move || { query( invitation_id.into_inner(), user_data.into_inner().password, pool, ) }) - .then(|res| match res { + .await; + + match res { Ok(user) => Ok(HttpResponse::Ok().json(&user)), Err(err) => match err { BlockingError::Error(service_error) => Err(service_error), BlockingError::Canceled => Err(ServiceError::InternalServerError), }, - }) + } } fn query( diff --git a/state/Cargo.toml b/state/Cargo.toml index ee4cbf3..98cf490 100644 --- a/state/Cargo.toml +++ b/state/Cargo.toml @@ -1,11 +1,12 @@ [package] name = "state" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" -futures = "0.1.25" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" +futures = "0.3.1" env_logger = "0.6" diff --git a/state/src/main.rs b/state/src/main.rs index 2567ecd..81e863c 100644 --- a/state/src/main.rs +++ b/state/src/main.rs @@ -17,14 +17,15 @@ use std::sync::Mutex; use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer}; /// simple handle -fn index(state: web::Data>, req: HttpRequest) -> HttpResponse { +async fn index(state: web::Data>, req: HttpRequest) -> HttpResponse { println!("{:?}", req); *(state.lock().unwrap()) += 1; HttpResponse::Ok().body(format!("Num of requests: {}", state.lock().unwrap())) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -40,5 +41,6 @@ fn main() -> io::Result<()> { .service(web::resource("/").to(index)) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/static_index/Cargo.toml b/static_index/Cargo.toml index 7cb6c40..5c0373f 100644 --- a/static_index/Cargo.toml +++ b/static_index/Cargo.toml @@ -1,13 +1,14 @@ [package] name = "static_index" -version = "0.1.0" +version = "2.0.0" authors = ["Jose Marinez "] workspace = ".." edition = "2018" [dependencies] -futures = "0.1" -env_logger = "0.5" +futures = "0.3.1" +env_logger = "0.6" -actix-web = "1.0.0" -actix-files = "0.1.1" +actix-web = "2.0.0-alpha.3" +actix-files = "0.2.0-alpha.3" +actix-rt = "1.0.0-alpha.3" diff --git a/static_index/src/main.rs b/static_index/src/main.rs index ced7b68..3b78f68 100644 --- a/static_index/src/main.rs +++ b/static_index/src/main.rs @@ -1,7 +1,8 @@ use actix_files as fs; use actix_web::{middleware, App, HttpServer}; -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -15,5 +16,6 @@ fn main() -> std::io::Result<()> { ) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/template_askama/Cargo.toml b/template_askama/Cargo.toml index 6d9cb6b..65b4be6 100644 --- a/template_askama/Cargo.toml +++ b/template_askama/Cargo.toml @@ -1,12 +1,13 @@ [package] name = "template-askama" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" env_logger = "0.6" askama = "0.8" diff --git a/template_askama/src/main.rs b/template_askama/src/main.rs index f117ab3..a2fe74c 100644 --- a/template_askama/src/main.rs +++ b/template_askama/src/main.rs @@ -14,7 +14,7 @@ struct UserTemplate<'a> { #[template(path = "index.html")] struct Index; -fn index(query: web::Query>) -> Result { +async fn index(query: web::Query>) -> Result { let s = if let Some(name) = query.get("name") { UserTemplate { name, @@ -28,11 +28,13 @@ fn index(query: web::Query>) -> Result { Ok(HttpResponse::Ok().content_type("text/html").body(s)) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { // start http server HttpServer::new(move || { App::new().service(web::resource("/").route(web::get().to(index))) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/template_handlebars/Cargo.toml b/template_handlebars/Cargo.toml index 11fb0c5..572a26e 100644 --- a/template_handlebars/Cargo.toml +++ b/template_handlebars/Cargo.toml @@ -1,10 +1,11 @@ [package] name = "template_handlebars" -version = "0.1.0" +version = "1.0.0" authors = ["Alexandru Tiniuc "] edition = "2018" [dependencies] -actix-web = "1.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" handlebars = "2.0.0-beta.2" serde_json = "1.0" \ No newline at end of file diff --git a/template_handlebars/src/main.rs b/template_handlebars/src/main.rs index 7d1f97b..905d1d5 100644 --- a/template_handlebars/src/main.rs +++ b/template_handlebars/src/main.rs @@ -13,7 +13,7 @@ use std::io; // Macro documentation can be found in the actix_web_codegen crate #[get("/")] -fn index(hb: web::Data) -> HttpResponse { +async fn index(hb: web::Data) -> HttpResponse { let data = json!({ "name": "Handlebars" }); @@ -23,7 +23,10 @@ fn index(hb: web::Data) -> HttpResponse { } #[get("/{user}/{data}")] -fn user(hb: web::Data, info: web::Path<(String, String)>) -> HttpResponse { +async fn user( + hb: web::Data, + info: web::Path<(String, String)>, +) -> HttpResponse { let data = json!({ "user": info.0, "data": info.1 @@ -33,7 +36,8 @@ fn user(hb: web::Data, info: web::Path<(String, String)>) -> HttpRes HttpResponse::Ok().body(body) } -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { // Handlebars uses a repository for the compiled templates. This object must be // shared between the application threads, and is therefore passed to the // Application Builder as an atomic reference-counted pointer. @@ -50,5 +54,6 @@ fn main() -> io::Result<()> { .service(user) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/template_tera/Cargo.toml b/template_tera/Cargo.toml index cbf2d30..97198d4 100644 --- a/template_tera/Cargo.toml +++ b/template_tera/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "template-tera" -version = "0.1.0" +version = "2.0.0" authors = ["Nikolay Kim "] workspace = ".." edition = "2018" @@ -8,4 +8,5 @@ edition = "2018" [dependencies] env_logger = "0.6" tera = "0.11" -actix-web = "1.0.0" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" diff --git a/template_tera/src/main.rs b/template_tera/src/main.rs index a594214..fac0ac7 100644 --- a/template_tera/src/main.rs +++ b/template_tera/src/main.rs @@ -6,7 +6,7 @@ use std::collections::HashMap; use actix_web::{error, middleware, web, App, Error, HttpResponse, HttpServer}; // store tera template in application state -fn index( +async fn index( tmpl: web::Data, query: web::Query>, ) -> Result { @@ -24,7 +24,8 @@ fn index( Ok(HttpResponse::Ok().content_type("text/html").body(s)) } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +async fn main() -> std::io::Result<()> { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); @@ -38,5 +39,6 @@ fn main() -> std::io::Result<()> { .service(web::resource("/").route(web::get().to(index))) }) .bind("127.0.0.1:8080")? - .run() + .start() + .await } diff --git a/tls/Cargo.toml b/tls/Cargo.toml deleted file mode 100644 index f68f361..0000000 --- a/tls/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "tls-example" -version = "0.2.0" -authors = ["Nikolay Kim "] -edition = "2018" -workspace = ".." - -[[bin]] -name = "tls-server" -path = "src/main.rs" - -[dependencies] -actix-rt = "0.2" -actix-web = { version="1.0.0", features=["ssl"] } -env_logger = "0.6" -openssl = { version="0.10" } diff --git a/actix_todo/.env b/todo/.env similarity index 100% rename from actix_todo/.env rename to todo/.env diff --git a/actix_todo/Cargo.toml b/todo/Cargo.toml similarity index 68% rename from actix_todo/Cargo.toml rename to todo/Cargo.toml index 4812c25..da29bd2 100644 --- a/actix_todo/Cargo.toml +++ b/todo/Cargo.toml @@ -1,17 +1,18 @@ [package] authors = ["Dan Munckton "] name = "actix-todo" -version = "0.1.0" +version = "2.0.0" workspace = ".." edition = "2018" [dependencies] -actix-web = "1.0.0" -actix-files = "0.1.1" -actix-session = "0.2.0" +actix-web = "2.0.0-alpha.3" +actix-files = "0.2.0-alpha.3" +actix-session = "0.3.0-alpha.3" +actix-rt = "1.0.0-alpha.3" dotenv = "0.13.0" env_logger = "0.5.10" -futures = "0.1.22" +futures = "0.3.1" log = "0.4.3" serde = "1.0.69" serde_derive = "1.0.69" diff --git a/actix_todo/README.md b/todo/README.md similarity index 100% rename from actix_todo/README.md rename to todo/README.md diff --git a/actix_todo/diesel.toml b/todo/diesel.toml similarity index 100% rename from actix_todo/diesel.toml rename to todo/diesel.toml diff --git a/actix_todo/migrations/.gitkeep b/todo/migrations/.gitkeep similarity index 100% rename from actix_todo/migrations/.gitkeep rename to todo/migrations/.gitkeep diff --git a/actix_todo/migrations/00000000000000_diesel_initial_setup/down.sql b/todo/migrations/00000000000000_diesel_initial_setup/down.sql similarity index 100% rename from actix_todo/migrations/00000000000000_diesel_initial_setup/down.sql rename to todo/migrations/00000000000000_diesel_initial_setup/down.sql diff --git a/actix_todo/migrations/00000000000000_diesel_initial_setup/up.sql b/todo/migrations/00000000000000_diesel_initial_setup/up.sql similarity index 100% rename from actix_todo/migrations/00000000000000_diesel_initial_setup/up.sql rename to todo/migrations/00000000000000_diesel_initial_setup/up.sql diff --git a/actix_todo/migrations/2018-07-05-163612_create_tasks_table/down.sql b/todo/migrations/2018-07-05-163612_create_tasks_table/down.sql similarity index 100% rename from actix_todo/migrations/2018-07-05-163612_create_tasks_table/down.sql rename to todo/migrations/2018-07-05-163612_create_tasks_table/down.sql diff --git a/actix_todo/migrations/2018-07-05-163612_create_tasks_table/up.sql b/todo/migrations/2018-07-05-163612_create_tasks_table/up.sql similarity index 100% rename from actix_todo/migrations/2018-07-05-163612_create_tasks_table/up.sql rename to todo/migrations/2018-07-05-163612_create_tasks_table/up.sql diff --git a/todo/src/api.rs b/todo/src/api.rs new file mode 100644 index 0000000..990af65 --- /dev/null +++ b/todo/src/api.rs @@ -0,0 +1,135 @@ +use actix_files::NamedFile; +use actix_session::Session; +use actix_web::middleware::errhandlers::ErrorHandlerResponse; +use actix_web::{dev, error, http, web, Error, HttpResponse, Result}; +use tera::{Context, Tera}; + +use crate::db; +use crate::session::{self, FlashMessage}; + +pub async fn index( + pool: web::Data, + tmpl: web::Data, + session: Session, +) -> Result { + let tasks = web::block(move || db::get_all_tasks(&pool)).await?; + + let mut context = Context::new(); + context.insert("tasks", &tasks); + + //Session is set during operations on other endpoints + //that can redirect to index + if let Some(flash) = session::get_flash(&session)? { + context.insert("msg", &(flash.kind, flash.message)); + session::clear_flash(&session); + } + + let rendered = tmpl + .render("index.html.tera", &context) + .map_err(|e| error::ErrorInternalServerError(e.description().to_owned()))?; + + Ok(HttpResponse::Ok().body(rendered)) +} + +#[derive(Deserialize)] +pub struct CreateForm { + description: String, +} + +pub async fn create( + params: web::Form, + pool: web::Data, + session: Session, +) -> Result { + if params.description.is_empty() { + session::set_flash( + &session, + FlashMessage::error("Description cannot be empty"), + )?; + Ok(redirect_to("/")) + } else { + web::block(move || db::create_task(params.into_inner().description, &pool)) + .await?; + session::set_flash(&session, FlashMessage::success("Task successfully added"))?; + Ok(redirect_to("/")) + } +} + +#[derive(Deserialize)] +pub struct UpdateParams { + id: i32, +} + +#[derive(Deserialize)] +pub struct UpdateForm { + _method: String, +} + +pub async fn update( + db: web::Data, + params: web::Path, + form: web::Form, + session: Session, +) -> Result { + match form._method.as_ref() { + "put" => toggle(db, params).await, + "delete" => delete(db, params, session).await, + unsupported_method => { + let msg = format!("Unsupported HTTP method: {}", unsupported_method); + Err(error::ErrorBadRequest(msg)) + } + } +} + +async fn toggle( + pool: web::Data, + params: web::Path, +) -> Result { + web::block(move || db::toggle_task(params.id, &pool)).await?; + Ok(redirect_to("/")) +} + +async fn delete( + pool: web::Data, + params: web::Path, + session: Session, +) -> Result { + web::block(move || db::delete_task(params.id, &pool)).await?; + session::set_flash(&session, FlashMessage::success("Task was deleted."))?; + Ok(redirect_to("/")) +} + +fn redirect_to(location: &str) -> HttpResponse { + HttpResponse::Found() + .header(http::header::LOCATION, location) + .finish() +} + +pub fn bad_request(res: dev::ServiceResponse) -> Result> { + let new_resp = NamedFile::open("static/errors/400.html")? + .set_status_code(res.status()) + .into_response(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} + +pub fn not_found(res: dev::ServiceResponse) -> Result> { + let new_resp = NamedFile::open("static/errors/404.html")? + .set_status_code(res.status()) + .into_response(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} + +pub fn internal_server_error( + res: dev::ServiceResponse, +) -> Result> { + let new_resp = NamedFile::open("static/errors/500.html")? + .set_status_code(res.status()) + .into_response(res.request())?; + Ok(ErrorHandlerResponse::Response( + res.into_response(new_resp.into_body()), + )) +} diff --git a/actix_todo/src/db.rs b/todo/src/db.rs similarity index 100% rename from actix_todo/src/db.rs rename to todo/src/db.rs diff --git a/actix_todo/src/main.rs b/todo/src/main.rs similarity index 84% rename from actix_todo/src/main.rs rename to todo/src/main.rs index aca105e..5fc6097 100644 --- a/actix_todo/src/main.rs +++ b/todo/src/main.rs @@ -24,7 +24,8 @@ mod session; static SESSION_SIGNING_KEY: &[u8] = &[0; 32]; -fn main() -> io::Result<()> { +#[actix_rt::main] +async fn main() -> io::Result<()> { dotenv().ok(); env::set_var("RUST_LOG", "actix_todo=debug,actix_web=info"); @@ -54,14 +55,12 @@ fn main() -> io::Result<()> { .wrap(Logger::default()) .wrap(session_store) .wrap(error_handlers) - .service(web::resource("/").route(web::get().to_async(api::index))) - .service(web::resource("/todo").route(web::post().to_async(api::create))) - .service( - web::resource("/todo/{id}").route(web::post().to_async(api::update)), - ) + .service(web::resource("/").route(web::get().to(api::index))) + .service(web::resource("/todo").route(web::post().to(api::create))) + .service(web::resource("/todo/{id}").route(web::post().to(api::update))) .service(fs::Files::new("/static", "static/")) }; debug!("Starting server"); - HttpServer::new(app).bind("localhost:8088")?.run() + HttpServer::new(app).bind("localhost:8088")?.start().await } diff --git a/actix_todo/src/model.rs b/todo/src/model.rs similarity index 100% rename from actix_todo/src/model.rs rename to todo/src/model.rs diff --git a/actix_todo/src/schema.rs b/todo/src/schema.rs similarity index 100% rename from actix_todo/src/schema.rs rename to todo/src/schema.rs diff --git a/actix_todo/src/session.rs b/todo/src/session.rs similarity index 100% rename from actix_todo/src/session.rs rename to todo/src/session.rs diff --git a/actix_todo/static/css/normalize.css b/todo/static/css/normalize.css similarity index 100% rename from actix_todo/static/css/normalize.css rename to todo/static/css/normalize.css diff --git a/actix_todo/static/css/skeleton.css b/todo/static/css/skeleton.css similarity index 100% rename from actix_todo/static/css/skeleton.css rename to todo/static/css/skeleton.css diff --git a/actix_todo/static/css/style.css b/todo/static/css/style.css similarity index 100% rename from actix_todo/static/css/style.css rename to todo/static/css/style.css diff --git a/actix_todo/static/errors/400.html b/todo/static/errors/400.html similarity index 100% rename from actix_todo/static/errors/400.html rename to todo/static/errors/400.html diff --git a/actix_todo/static/errors/404.html b/todo/static/errors/404.html similarity index 100% rename from actix_todo/static/errors/404.html rename to todo/static/errors/404.html diff --git a/actix_todo/static/errors/500.html b/todo/static/errors/500.html similarity index 100% rename from actix_todo/static/errors/500.html rename to todo/static/errors/500.html diff --git a/actix_todo/templates/index.html.tera b/todo/templates/index.html.tera similarity index 100% rename from actix_todo/templates/index.html.tera rename to todo/templates/index.html.tera diff --git a/unix-socket/Cargo.toml b/unix-socket/Cargo.toml index 0eba6e2..065a955 100644 --- a/unix-socket/Cargo.toml +++ b/unix-socket/Cargo.toml @@ -1,12 +1,11 @@ [package] name = "unix-socket" -version = "0.1.0" +version = "1.0.0" authors = ["Messense Lv "] workspace = ".." edition = "2018" [dependencies] -env_logger = "0.5" -tokio-uds = "0.2" - -actix-web = { version = "1.0.5", features = ["uds"] } +env_logger = "0.6" +actix-web = "2.0.0-alpha.3" +actix-rt = "1.0.0-alpha.3" diff --git a/unix-socket/src/main.rs b/unix-socket/src/main.rs index 055b597..ddd193d 100644 --- a/unix-socket/src/main.rs +++ b/unix-socket/src/main.rs @@ -1,10 +1,12 @@ use actix_web::{middleware, web, App, HttpRequest, HttpServer}; -fn index(_req: HttpRequest) -> &'static str { +async fn index(_req: HttpRequest) -> &'static str { "Hello world!" } -fn main() -> std::io::Result<()> { +#[actix_rt::main] +#[cfg(unix)] +async fn main() -> std::io::Result<()> { ::std::env::set_var("RUST_LOG", "actix_server=info,actix_web=info"); env_logger::init(); @@ -13,10 +15,18 @@ fn main() -> std::io::Result<()> { // enable logger - always register actix-web Logger middleware last .wrap(middleware::Logger::default()) .service( - web::resource("/index.html").route(web::get().to(|| "Hello world!")), + web::resource("/index.html") + .route(web::get().to(|| async { "Hello world!" })), ) .service(web::resource("/").to(index)) }) .bind_uds("/tmp/actix-uds.socket")? - .run() + .start() + .await +} + +#[cfg(not(unix))] +fn main() -> std::io::Result<()> { + println!("not supported"); + Ok(()) }